s3-includes: only include system/filesys.h when needed.
[ira/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
25 /*
26
27   Some filesystems (even some journaled filesystems) require that a
28   fsync() be performed on many meta data operations to ensure that the
29   operation is guaranteed to remain in the filesystem after a power
30   failure. This is particularly important for some cluster filesystems
31   which are participating in a node failover system with clustered
32   Samba
33
34   On those filesystems this module provides a way to perform those
35   operations safely.  
36
37   most of the performance loss with this module is in fsync on close(). 
38   You can disable that with
39      syncops:onclose = no
40   that can be set either globally or per share.
41
42   On certain filesystems that only require the last data written to be
43   fsync()'ed, you can disable the metadata synchronization of this module with
44      syncops:onmeta = no
45   This option can be set either globally or per share.
46
47   you can also disable the module completely for a share with
48      syncops:disable = true
49
50  */
51
52 struct syncops_config_data {
53         bool onclose;
54         bool onmeta;
55         bool disable;
56 };
57
58 /*
59   given a filename, find the parent directory
60  */
61 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
62 {
63         const char *p = strrchr(name, '/');
64         if (p == NULL) {
65                 return talloc_strdup(mem_ctx, ".");
66         }
67         return talloc_strndup(mem_ctx, name, (p+1) - name);
68 }
69
70 /*
71   fsync a directory by name
72  */
73 static void syncops_sync_directory(const char *dname)
74 {
75 #ifdef O_DIRECTORY
76         int fd = open(dname, O_DIRECTORY|O_RDONLY);
77         if (fd != -1) {
78                 fsync(fd);
79                 close(fd);
80         }
81 #else
82         DIR *d = opendir(dname);
83         if (d != NULL) {
84                 fsync(dirfd(d));
85                 closedir(d);
86         }
87 #endif
88 }
89
90 /*
91   sync two meta data changes for 2 names
92  */
93 static void syncops_two_names(const char *name1, const char *name2)
94 {
95         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
96         char *parent1, *parent2;
97         parent1 = parent_dir(tmp_ctx, name1);
98         parent2 = parent_dir(tmp_ctx, name2);
99         if (!parent1 || !parent2) {
100                 talloc_free(tmp_ctx);
101                 return;
102         }
103         syncops_sync_directory(parent1);
104         if (strcmp(parent1, parent2) != 0) {
105                 syncops_sync_directory(parent2);                
106         }
107         talloc_free(tmp_ctx);
108 }
109
110 /*
111   sync two meta data changes for 1 names
112  */
113 static void syncops_name(const char *name)
114 {
115         char *parent;
116         parent = parent_dir(NULL, name);
117         if (parent) {
118                 syncops_sync_directory(parent);
119                 talloc_free(parent);
120         }
121 }
122
123 /*
124   sync two meta data changes for 1 names
125  */
126 static void syncops_smb_fname(const struct smb_filename *smb_fname)
127 {
128         char *parent;
129         parent = parent_dir(NULL, smb_fname->base_name);
130         if (parent) {
131                 syncops_sync_directory(parent);
132                 talloc_free(parent);
133         }
134 }
135
136
137 /*
138   rename needs special handling, as we may need to fsync two directories
139  */
140 static int syncops_rename(vfs_handle_struct *handle,
141                           const struct smb_filename *smb_fname_src,
142                           const struct smb_filename *smb_fname_dst)
143 {
144
145         int ret;
146         struct syncops_config_data *config;
147
148         SMB_VFS_HANDLE_GET_DATA(handle, config,
149                                 struct syncops_config_data,
150                                 return -1);
151
152         ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
153         if (ret == 0 && config->onmeta && !config->disable) {
154                 syncops_two_names(smb_fname_src->base_name,
155                                   smb_fname_dst->base_name);
156         }
157         return ret;
158 }
159
160 /* handle the rest with a macro */
161 #define SYNCOPS_NEXT(op, fname, args) do {   \
162         int ret; \
163         struct syncops_config_data *config; \
164         SMB_VFS_HANDLE_GET_DATA(handle, config, \
165                                 struct syncops_config_data, \
166                                 return -1); \
167         ret = SMB_VFS_NEXT_ ## op args; \
168         if (ret == 0 \
169                 && config->onmeta && !config->disable  \
170                 && fname) syncops_name(fname); \
171         return ret; \
172 } while (0)
173
174 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
175         int ret; \
176         struct syncops_config_data *config; \
177         SMB_VFS_HANDLE_GET_DATA(handle, config, \
178                                 struct syncops_config_data, \
179                                 return -1); \
180         ret = SMB_VFS_NEXT_ ## op args; \
181         if (ret == 0 \
182         && config->onmeta && !config->disable \
183         && fname) syncops_smb_fname(fname); \
184         return ret; \
185 } while (0)
186
187 static int syncops_symlink(vfs_handle_struct *handle,
188                            const char *oldname, const char *newname)
189 {
190         SYNCOPS_NEXT(SYMLINK, newname, (handle, oldname, newname));
191 }
192
193 static int syncops_link(vfs_handle_struct *handle,
194                          const char *oldname, const char *newname)
195 {
196         SYNCOPS_NEXT(LINK, newname, (handle, oldname, newname));
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 char *fname, mode_t mode, SMB_DEV_T dev)
215 {
216         SYNCOPS_NEXT(MKNOD, fname, (handle, fname, mode, dev));
217 }
218
219 static int syncops_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
220 {
221         SYNCOPS_NEXT(MKDIR, fname, (handle, fname, mode));
222 }
223
224 static int syncops_rmdir(vfs_handle_struct *handle,  const char *fname)
225 {
226         SYNCOPS_NEXT(RMDIR, fname, (handle, fname));
227 }
228
229 /* close needs to be handled specially */
230 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
231 {
232         struct syncops_config_data *config;
233
234         SMB_VFS_HANDLE_GET_DATA(handle, config,
235                                 struct syncops_config_data,
236                                 return -1);
237
238         if (fsp->can_write && config->onclose) {
239                 /* ideally we'd only do this if we have written some
240                  data, but there is no flag for that in fsp yet. */
241                 fsync(fsp->fh->fd);
242         }
243         return SMB_VFS_NEXT_CLOSE(handle, fsp);
244 }
245
246 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
247                            const char *user)
248 {
249
250         struct syncops_config_data *config;
251         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
252         if (ret < 0) {
253                 return ret;
254         }
255
256         config = talloc_zero(handle->conn, struct syncops_config_data);
257         if (!config) {
258                 SMB_VFS_NEXT_DISCONNECT(handle);
259                 DEBUG(0, ("talloc_zero() failed\n"));
260                 return -1;
261         }
262
263         config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
264                                         "onclose", true);
265
266         config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
267                                         "onmeta", true);
268
269         config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
270                                         "disable", false);
271
272         SMB_VFS_HANDLE_SET_DATA(handle, config,
273                                 NULL, struct syncops_config_data,
274                                 return -1);
275
276         return 0;
277
278 }
279
280 static struct vfs_fn_pointers vfs_syncops_fns = {
281         .connect_fn = syncops_connect,
282         .mkdir = syncops_mkdir,
283         .rmdir = syncops_rmdir,
284         .open = syncops_open,
285         .rename = syncops_rename,
286         .unlink = syncops_unlink,
287         .symlink = syncops_symlink,
288         .link = syncops_link,
289         .mknod = syncops_mknod,
290         .close_fn = syncops_close,
291 };
292
293 NTSTATUS vfs_syncops_init(void)
294 {
295         NTSTATUS ret;
296
297         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
298                                &vfs_syncops_fns);
299
300         if (!NT_STATUS_IS_OK(ret))
301                 return ret;
302
303         return ret;
304 }