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