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