s3: VFS: syncops: Do early returns in SYNCOPS_NEXT_SMB_FNAME() macro.
[samba.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(connection_struct *conn,
75                                    char *dname)
76 {
77         struct smb_Dir *dir_hnd = NULL;
78         struct files_struct *dirfsp = NULL;
79         struct smb_filename smb_dname = { .base_name = dname };
80
81         dir_hnd = OpenDir(talloc_tos(),
82                           conn,
83                           &smb_dname,
84                           "*",
85                           0);
86         if (dir_hnd == NULL) {
87                 return;
88         }
89
90         dirfsp = dir_hnd_fetch_fsp(dir_hnd);
91
92         smb_vfs_fsync_sync(dirfsp);
93
94         TALLOC_FREE(dir_hnd);
95 }
96
97 /*
98   sync two meta data changes for 2 names
99  */
100 static void syncops_two_names(connection_struct *conn,
101                               const struct smb_filename *name1,
102                               const struct smb_filename *name2)
103 {
104         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
105         char *parent1, *parent2;
106         parent1 = parent_dir(tmp_ctx, name1->base_name);
107         parent2 = parent_dir(tmp_ctx, name2->base_name);
108         if (!parent1 || !parent2) {
109                 talloc_free(tmp_ctx);
110                 return;
111         }
112         syncops_sync_directory(conn, parent1);
113         if (strcmp(parent1, parent2) != 0) {
114                 syncops_sync_directory(conn, parent2);
115         }
116         talloc_free(tmp_ctx);
117 }
118
119 /*
120   sync two meta data changes for 1 names
121  */
122 static void syncops_smb_fname(connection_struct *conn,
123                               const struct smb_filename *smb_fname)
124 {
125         char *parent = NULL;
126         if (smb_fname != NULL) {
127                 parent = parent_dir(NULL, smb_fname->base_name);
128                 if (parent != NULL) {
129                         syncops_sync_directory(conn, parent);
130                         talloc_free(parent);
131                 }
132         }
133 }
134
135
136 /*
137   renameat needs special handling, as we may need to fsync two directories
138  */
139 static int syncops_renameat(vfs_handle_struct *handle,
140                         files_struct *srcfsp,
141                         const struct smb_filename *smb_fname_src,
142                         files_struct *dstfsp,
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_RENAMEAT(handle,
154                         srcfsp,
155                         smb_fname_src,
156                         dstfsp,
157                         smb_fname_dst);
158         if (ret == 0 && config->onmeta && !config->disable) {
159                 syncops_two_names(handle->conn,
160                                   smb_fname_src,
161                                   smb_fname_dst);
162         }
163         return ret;
164 }
165
166 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
167         int ret; \
168         struct smb_filename *full_fname = NULL; \
169         struct syncops_config_data *config; \
170         SMB_VFS_HANDLE_GET_DATA(handle, config, \
171                                 struct syncops_config_data, \
172                                 return -1); \
173         ret = SMB_VFS_NEXT_ ## op args; \
174         if (ret != 0) { \
175                 return ret; \
176         } \
177         if (config->disable) { \
178                 return ret; \
179         } \
180         if (!config->onmeta) { \
181                 return ret; \
182         } \
183         full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
184                                 dirfsp, \
185                                 smb_fname); \
186         if (full_fname == NULL) { \
187                 return ret; \
188         } \
189         syncops_smb_fname(dirfsp->conn, full_fname); \
190         TALLOC_FREE(full_fname); \
191         return ret; \
192 } while (0)
193
194 static int syncops_symlinkat(vfs_handle_struct *handle,
195                         const struct smb_filename *link_contents,
196                         struct files_struct *dirfsp,
197                         const struct smb_filename *smb_fname)
198 {
199         SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
200                         smb_fname,
201                                 (handle,
202                                 link_contents,
203                                 dirfsp,
204                                 smb_fname));
205 }
206
207 static int syncops_linkat(vfs_handle_struct *handle,
208                         files_struct *srcfsp,
209                         const struct smb_filename *old_smb_fname,
210                         files_struct *dstfsp,
211                         const struct smb_filename *new_smb_fname,
212                         int flags)
213 {
214         int ret;
215         struct syncops_config_data *config;
216         struct smb_filename *old_full_fname = NULL;
217         struct smb_filename *new_full_fname = NULL;
218
219         SMB_VFS_HANDLE_GET_DATA(handle, config,
220                                 struct syncops_config_data,
221                                 return -1);
222
223         old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
224                                 srcfsp,
225                                 old_smb_fname);
226         if (old_full_fname == NULL) {
227                 return -1;
228         }
229         new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
230                                 dstfsp,
231                                 new_smb_fname);
232         if (new_full_fname == NULL) {
233                 TALLOC_FREE(old_full_fname);
234                 return -1;
235         }
236         ret = SMB_VFS_NEXT_LINKAT(handle,
237                         srcfsp,
238                         old_smb_fname,
239                         dstfsp,
240                         new_smb_fname,
241                         flags);
242
243         if (ret == 0 && config->onmeta && !config->disable) {
244                 syncops_two_names(handle->conn,
245                                   old_full_fname,
246                                   new_full_fname);
247         }
248         TALLOC_FREE(old_full_fname);
249         TALLOC_FREE(new_full_fname);
250         return ret;
251 }
252
253 static int syncops_openat(struct vfs_handle_struct *handle,
254                           const struct files_struct *dirfsp,
255                           const struct smb_filename *smb_fname,
256                           struct files_struct *fsp,
257                           int flags,
258                           mode_t mode)
259 {
260         SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
261                                (handle, dirfsp, smb_fname, fsp, flags, mode));
262 }
263
264 static int syncops_unlinkat(vfs_handle_struct *handle,
265                         files_struct *dirfsp,
266                         const struct smb_filename *smb_fname,
267                         int flags)
268 {
269         SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
270                         smb_fname,
271                                 (handle,
272                                 dirfsp,
273                                 smb_fname,
274                                 flags));
275 }
276
277 static int syncops_mknodat(vfs_handle_struct *handle,
278                         files_struct *dirfsp,
279                         const struct smb_filename *smb_fname,
280                         mode_t mode,
281                         SMB_DEV_T dev)
282 {
283         SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
284                         smb_fname,
285                                 (handle,
286                                 dirfsp,
287                                 smb_fname,
288                                 mode,
289                                 dev));
290 }
291
292 static int syncops_mkdirat(vfs_handle_struct *handle,
293                         struct files_struct *dirfsp,
294                         const struct smb_filename *smb_fname,
295                         mode_t mode)
296 {
297         SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
298                         full_fname,
299                                 (handle,
300                                 dirfsp,
301                                 smb_fname,
302                                 mode));
303 }
304
305 /* close needs to be handled specially */
306 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
307 {
308         struct syncops_config_data *config;
309
310         SMB_VFS_HANDLE_GET_DATA(handle, config,
311                                 struct syncops_config_data,
312                                 return -1);
313
314         if (fsp->fsp_flags.can_write && config->onclose) {
315                 /* ideally we'd only do this if we have written some
316                  data, but there is no flag for that in fsp yet. */
317                 fsync(fsp_get_io_fd(fsp));
318         }
319         return SMB_VFS_NEXT_CLOSE(handle, fsp);
320 }
321
322 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
323                            const char *user)
324 {
325
326         struct syncops_config_data *config;
327         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
328         if (ret < 0) {
329                 return ret;
330         }
331
332         config = talloc_zero(handle->conn, struct syncops_config_data);
333         if (!config) {
334                 SMB_VFS_NEXT_DISCONNECT(handle);
335                 DEBUG(0, ("talloc_zero() failed\n"));
336                 return -1;
337         }
338
339         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
340                                         "onclose", true);
341
342         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
343                                         "onmeta", true);
344
345         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
346                                         "disable", false);
347
348         SMB_VFS_HANDLE_SET_DATA(handle, config,
349                                 NULL, struct syncops_config_data,
350                                 return -1);
351
352         return 0;
353
354 }
355
356 static struct vfs_fn_pointers vfs_syncops_fns = {
357         .connect_fn = syncops_connect,
358         .mkdirat_fn = syncops_mkdirat,
359         .openat_fn = syncops_openat,
360         .renameat_fn = syncops_renameat,
361         .unlinkat_fn = syncops_unlinkat,
362         .symlinkat_fn = syncops_symlinkat,
363         .linkat_fn = syncops_linkat,
364         .mknodat_fn = syncops_mknodat,
365         .close_fn = syncops_close,
366 };
367
368 static_decl_vfs;
369 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
370 {
371         NTSTATUS ret;
372
373         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
374                                &vfs_syncops_fns);
375
376         if (!NT_STATUS_IS_OK(ret))
377                 return ret;
378
379         return ret;
380 }