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