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