s3: VFS: syncops. Do early return in syncops_renameat().
[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 == -1) {
159                 return ret;
160         }
161         if (config->disable) {
162                 return ret;
163         }
164         if (!config->onmeta) {
165                 return ret;
166         }
167
168         syncops_two_names(handle->conn,
169                           smb_fname_src,
170                           smb_fname_dst);
171         return ret;
172 }
173
174 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
175         int ret; \
176         struct smb_filename *full_fname = NULL; \
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                 return ret; \
184         } \
185         if (config->disable) { \
186                 return ret; \
187         } \
188         if (!config->onmeta) { \
189                 return ret; \
190         } \
191         full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
192                                 dirfsp, \
193                                 smb_fname); \
194         if (full_fname == NULL) { \
195                 return ret; \
196         } \
197         syncops_smb_fname(dirfsp->conn, full_fname); \
198         TALLOC_FREE(full_fname); \
199         return ret; \
200 } while (0)
201
202 static int syncops_symlinkat(vfs_handle_struct *handle,
203                         const struct smb_filename *link_contents,
204                         struct files_struct *dirfsp,
205                         const struct smb_filename *smb_fname)
206 {
207         SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
208                         smb_fname,
209                                 (handle,
210                                 link_contents,
211                                 dirfsp,
212                                 smb_fname));
213 }
214
215 static int syncops_linkat(vfs_handle_struct *handle,
216                         files_struct *srcfsp,
217                         const struct smb_filename *old_smb_fname,
218                         files_struct *dstfsp,
219                         const struct smb_filename *new_smb_fname,
220                         int flags)
221 {
222         int ret;
223         struct syncops_config_data *config;
224         struct smb_filename *old_full_fname = NULL;
225         struct smb_filename *new_full_fname = NULL;
226
227         SMB_VFS_HANDLE_GET_DATA(handle, config,
228                                 struct syncops_config_data,
229                                 return -1);
230
231         ret = SMB_VFS_NEXT_LINKAT(handle,
232                         srcfsp,
233                         old_smb_fname,
234                         dstfsp,
235                         new_smb_fname,
236                         flags);
237
238         if (ret == -1) {
239                 return ret;
240         }
241         if (config->disable) {
242                 return ret;
243         }
244         if (!config->onmeta) {
245                 return ret;
246         }
247
248         old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
249                                                       srcfsp,
250                                                       old_smb_fname);
251         if (old_full_fname == NULL) {
252                 return ret;
253         }
254         new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
255                                                       dstfsp,
256                                                       new_smb_fname);
257         if (new_full_fname == NULL) {
258                 TALLOC_FREE(old_full_fname);
259                 return ret;
260         }
261         syncops_two_names(handle->conn,
262                           old_full_fname,
263                           new_full_fname);
264         TALLOC_FREE(old_full_fname);
265         TALLOC_FREE(new_full_fname);
266         return ret;
267 }
268
269 static int syncops_openat(struct vfs_handle_struct *handle,
270                           const struct files_struct *dirfsp,
271                           const struct smb_filename *smb_fname,
272                           struct files_struct *fsp,
273                           int flags,
274                           mode_t mode)
275 {
276         SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
277                                (handle, dirfsp, smb_fname, fsp, flags, mode));
278 }
279
280 static int syncops_unlinkat(vfs_handle_struct *handle,
281                         files_struct *dirfsp,
282                         const struct smb_filename *smb_fname,
283                         int flags)
284 {
285         SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
286                         smb_fname,
287                                 (handle,
288                                 dirfsp,
289                                 smb_fname,
290                                 flags));
291 }
292
293 static int syncops_mknodat(vfs_handle_struct *handle,
294                         files_struct *dirfsp,
295                         const struct smb_filename *smb_fname,
296                         mode_t mode,
297                         SMB_DEV_T dev)
298 {
299         SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
300                         smb_fname,
301                                 (handle,
302                                 dirfsp,
303                                 smb_fname,
304                                 mode,
305                                 dev));
306 }
307
308 static int syncops_mkdirat(vfs_handle_struct *handle,
309                         struct files_struct *dirfsp,
310                         const struct smb_filename *smb_fname,
311                         mode_t mode)
312 {
313         SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
314                         full_fname,
315                                 (handle,
316                                 dirfsp,
317                                 smb_fname,
318                                 mode));
319 }
320
321 /* close needs to be handled specially */
322 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
323 {
324         struct syncops_config_data *config;
325
326         SMB_VFS_HANDLE_GET_DATA(handle, config,
327                                 struct syncops_config_data,
328                                 return -1);
329
330         if (fsp->fsp_flags.can_write && config->onclose) {
331                 /* ideally we'd only do this if we have written some
332                  data, but there is no flag for that in fsp yet. */
333                 fsync(fsp_get_io_fd(fsp));
334         }
335         return SMB_VFS_NEXT_CLOSE(handle, fsp);
336 }
337
338 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
339                            const char *user)
340 {
341
342         struct syncops_config_data *config;
343         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
344         if (ret < 0) {
345                 return ret;
346         }
347
348         config = talloc_zero(handle->conn, struct syncops_config_data);
349         if (!config) {
350                 SMB_VFS_NEXT_DISCONNECT(handle);
351                 DEBUG(0, ("talloc_zero() failed\n"));
352                 return -1;
353         }
354
355         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
356                                         "onclose", true);
357
358         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
359                                         "onmeta", true);
360
361         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
362                                         "disable", false);
363
364         SMB_VFS_HANDLE_SET_DATA(handle, config,
365                                 NULL, struct syncops_config_data,
366                                 return -1);
367
368         return 0;
369
370 }
371
372 static struct vfs_fn_pointers vfs_syncops_fns = {
373         .connect_fn = syncops_connect,
374         .mkdirat_fn = syncops_mkdirat,
375         .openat_fn = syncops_openat,
376         .renameat_fn = syncops_renameat,
377         .unlinkat_fn = syncops_unlinkat,
378         .symlinkat_fn = syncops_symlinkat,
379         .linkat_fn = syncops_linkat,
380         .mknodat_fn = syncops_mknodat,
381         .close_fn = syncops_close,
382 };
383
384 static_decl_vfs;
385 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
386 {
387         NTSTATUS ret;
388
389         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
390                                &vfs_syncops_fns);
391
392         if (!NT_STATUS_IS_OK(ret))
393                 return ret;
394
395         return ret;
396 }