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