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