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