s3: VFS: Change SMB_VFS_MKNOD to use const struct smb_filename * instead of const...
[kai/samba-autobuild/.git] / source3 / modules / vfs_syncops.c
1 /* 
2  * ensure meta data operations are performed synchronously
3  *
4  * Copyright (C) Andrew Tridgell     2007
5  * Copyright (C) Christian Ambach, 2010-2011
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *  
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *  
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "smbd/smbd.h"
25
26 /*
27
28   Some filesystems (even some journaled filesystems) require that a
29   fsync() be performed on many meta data operations to ensure that the
30   operation is guaranteed to remain in the filesystem after a power
31   failure. This is particularly important for some cluster filesystems
32   which are participating in a node failover system with clustered
33   Samba
34
35   On those filesystems this module provides a way to perform those
36   operations safely.  
37
38   most of the performance loss with this module is in fsync on close(). 
39   You can disable that with
40      syncops:onclose = no
41   that can be set either globally or per share.
42
43   On certain filesystems that only require the last data written to be
44   fsync()'ed, you can disable the metadata synchronization of this module with
45      syncops:onmeta = no
46   This option can be set either globally or per share.
47
48   you can also disable the module completely for a share with
49      syncops:disable = true
50
51  */
52
53 struct syncops_config_data {
54         bool onclose;
55         bool onmeta;
56         bool disable;
57 };
58
59 /*
60   given a filename, find the parent directory
61  */
62 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
63 {
64         const char *p = strrchr(name, '/');
65         if (p == NULL) {
66                 return talloc_strdup(mem_ctx, ".");
67         }
68         return talloc_strndup(mem_ctx, name, (p+1) - name);
69 }
70
71 /*
72   fsync a directory by name
73  */
74 static void syncops_sync_directory(const char *dname)
75 {
76 #ifdef O_DIRECTORY
77         int fd = open(dname, O_DIRECTORY|O_RDONLY);
78         if (fd != -1) {
79                 fsync(fd);
80                 close(fd);
81         }
82 #else
83         DIR *d = opendir(dname);
84         if (d != NULL) {
85                 fsync(dirfd(d));
86                 closedir(d);
87         }
88 #endif
89 }
90
91 /*
92   sync two meta data changes for 2 names
93  */
94 static void syncops_two_names(const char *name1, const char *name2)
95 {
96         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
97         char *parent1, *parent2;
98         parent1 = parent_dir(tmp_ctx, name1);
99         parent2 = parent_dir(tmp_ctx, name2);
100         if (!parent1 || !parent2) {
101                 talloc_free(tmp_ctx);
102                 return;
103         }
104         syncops_sync_directory(parent1);
105         if (strcmp(parent1, parent2) != 0) {
106                 syncops_sync_directory(parent2);                
107         }
108         talloc_free(tmp_ctx);
109 }
110
111 /*
112   sync two meta data changes for 1 names
113  */
114 static void syncops_name(const char *name)
115 {
116         char *parent;
117         parent = parent_dir(NULL, name);
118         if (parent) {
119                 syncops_sync_directory(parent);
120                 talloc_free(parent);
121         }
122 }
123
124 /*
125   sync two meta data changes for 1 names
126  */
127 static void syncops_smb_fname(const struct smb_filename *smb_fname)
128 {
129         char *parent;
130         parent = parent_dir(NULL, smb_fname->base_name);
131         if (parent) {
132                 syncops_sync_directory(parent);
133                 talloc_free(parent);
134         }
135 }
136
137
138 /*
139   rename needs special handling, as we may need to fsync two directories
140  */
141 static int syncops_rename(vfs_handle_struct *handle,
142                           const struct smb_filename *smb_fname_src,
143                           const struct smb_filename *smb_fname_dst)
144 {
145
146         int ret;
147         struct syncops_config_data *config;
148
149         SMB_VFS_HANDLE_GET_DATA(handle, config,
150                                 struct syncops_config_data,
151                                 return -1);
152
153         ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
154         if (ret == 0 && config->onmeta && !config->disable) {
155                 syncops_two_names(smb_fname_src->base_name,
156                                   smb_fname_dst->base_name);
157         }
158         return ret;
159 }
160
161 /* handle the rest with a macro */
162 #define SYNCOPS_NEXT(op, fname, args) do {   \
163         int ret; \
164         struct syncops_config_data *config; \
165         SMB_VFS_HANDLE_GET_DATA(handle, config, \
166                                 struct syncops_config_data, \
167                                 return -1); \
168         ret = SMB_VFS_NEXT_ ## op args; \
169         if (ret == 0 \
170                 && config->onmeta && !config->disable  \
171                 && fname) syncops_name(fname); \
172         return ret; \
173 } while (0)
174
175 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
176         int ret; \
177         struct syncops_config_data *config; \
178         SMB_VFS_HANDLE_GET_DATA(handle, config, \
179                                 struct syncops_config_data, \
180                                 return -1); \
181         ret = SMB_VFS_NEXT_ ## op args; \
182         if (ret == 0 \
183         && config->onmeta && !config->disable \
184         && fname) syncops_smb_fname(fname); \
185         return ret; \
186 } while (0)
187
188 static int syncops_symlink(vfs_handle_struct *handle,
189                            const char *oldname, const char *newname)
190 {
191         SYNCOPS_NEXT(SYMLINK, newname, (handle, oldname, newname));
192 }
193
194 static int syncops_link(vfs_handle_struct *handle,
195                          const char *oldname, const char *newname)
196 {
197         SYNCOPS_NEXT(LINK, newname, (handle, oldname, newname));
198 }
199
200 static int syncops_open(vfs_handle_struct *handle,
201                         struct smb_filename *smb_fname, files_struct *fsp,
202                         int flags, mode_t mode)
203 {
204         SYNCOPS_NEXT_SMB_FNAME(OPEN, (flags&O_CREAT?smb_fname:NULL),
205                                (handle, smb_fname, fsp, flags, mode));
206 }
207
208 static int syncops_unlink(vfs_handle_struct *handle,
209                           const struct smb_filename *smb_fname)
210 {
211         SYNCOPS_NEXT_SMB_FNAME(UNLINK, smb_fname, (handle, smb_fname));
212 }
213
214 static int syncops_mknod(vfs_handle_struct *handle,
215                         const struct smb_filename *smb_fname,
216                         mode_t mode,
217                         SMB_DEV_T dev)
218 {
219         SYNCOPS_NEXT_SMB_FNAME(MKNOD,
220                         smb_fname, (handle, smb_fname, mode, dev));
221 }
222
223 static int syncops_mkdir(vfs_handle_struct *handle,
224                         const struct smb_filename *smb_fname,
225                         mode_t mode)
226 {
227         SYNCOPS_NEXT_SMB_FNAME(MKDIR, smb_fname, (handle, smb_fname, mode));
228 }
229
230 static int syncops_rmdir(vfs_handle_struct *handle,
231                         const struct smb_filename *smb_fname)
232 {
233         SYNCOPS_NEXT_SMB_FNAME(RMDIR, smb_fname, (handle, smb_fname));
234 }
235
236 /* close needs to be handled specially */
237 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
238 {
239         struct syncops_config_data *config;
240
241         SMB_VFS_HANDLE_GET_DATA(handle, config,
242                                 struct syncops_config_data,
243                                 return -1);
244
245         if (fsp->can_write && config->onclose) {
246                 /* ideally we'd only do this if we have written some
247                  data, but there is no flag for that in fsp yet. */
248                 fsync(fsp->fh->fd);
249         }
250         return SMB_VFS_NEXT_CLOSE(handle, fsp);
251 }
252
253 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
254                            const char *user)
255 {
256
257         struct syncops_config_data *config;
258         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
259         if (ret < 0) {
260                 return ret;
261         }
262
263         config = talloc_zero(handle->conn, struct syncops_config_data);
264         if (!config) {
265                 SMB_VFS_NEXT_DISCONNECT(handle);
266                 DEBUG(0, ("talloc_zero() failed\n"));
267                 return -1;
268         }
269
270         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
271                                         "onclose", true);
272
273         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
274                                         "onmeta", true);
275
276         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
277                                         "disable", false);
278
279         SMB_VFS_HANDLE_SET_DATA(handle, config,
280                                 NULL, struct syncops_config_data,
281                                 return -1);
282
283         return 0;
284
285 }
286
287 static struct vfs_fn_pointers vfs_syncops_fns = {
288         .connect_fn = syncops_connect,
289         .mkdir_fn = syncops_mkdir,
290         .rmdir_fn = syncops_rmdir,
291         .open_fn = syncops_open,
292         .rename_fn = syncops_rename,
293         .unlink_fn = syncops_unlink,
294         .symlink_fn = syncops_symlink,
295         .link_fn = syncops_link,
296         .mknod_fn = syncops_mknod,
297         .close_fn = syncops_close,
298 };
299
300 static_decl_vfs;
301 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
302 {
303         NTSTATUS ret;
304
305         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
306                                &vfs_syncops_fns);
307
308         if (!NT_STATUS_IS_OK(ret))
309                 return ret;
310
311         return ret;
312 }