s3:vfs:syncops add option to disable metasync per share
[Samba.git] / source3 / modules / vfs_syncops.c
blob76072abc3308e92398b5a41b5cdd1ddb115498d1
1 /*
2 * ensure meta data operations are performed synchronously
4 * Copyright (C) Andrew Tridgell 2007
5 * Copyright (C) Christian Ambach, 2010-2011
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.
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.
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.
22 #include "includes.h"
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
33 On those filesystems this module provides a way to perform those
34 operations safely.
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.
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.
46 you can also disable the module completely for a share with
47 syncops:disable = true
51 struct syncops_config_data {
52 bool onclose;
53 bool onmeta;
54 bool disable;
58 given a filename, find the parent directory
60 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
62 const char *p = strrchr(name, '/');
63 if (p == NULL) {
64 return talloc_strdup(mem_ctx, ".");
66 return talloc_strndup(mem_ctx, name, (p+1) - name);
70 fsync a directory by name
72 static void syncops_sync_directory(const char *dname)
74 #ifdef O_DIRECTORY
75 int fd = open(dname, O_DIRECTORY|O_RDONLY);
76 if (fd != -1) {
77 fsync(fd);
78 close(fd);
80 #else
81 DIR *d = opendir(dname);
82 if (d != NULL) {
83 fsync(dirfd(d));
84 closedir(d);
86 #endif
90 sync two meta data changes for 2 names
92 static void syncops_two_names(const char *name1, const char *name2)
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;
102 syncops_sync_directory(parent1);
103 if (strcmp(parent1, parent2) != 0) {
104 syncops_sync_directory(parent2);
106 talloc_free(tmp_ctx);
110 sync two meta data changes for 1 names
112 static void syncops_name(const char *name)
114 char *parent;
115 parent = parent_dir(NULL, name);
116 if (parent) {
117 syncops_sync_directory(parent);
118 talloc_free(parent);
123 sync two meta data changes for 1 names
125 static void syncops_smb_fname(const struct smb_filename *smb_fname)
127 char *parent;
128 parent = parent_dir(NULL, smb_fname->base_name);
129 if (parent) {
130 syncops_sync_directory(parent);
131 talloc_free(parent);
137 rename needs special handling, as we may need to fsync two directories
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)
144 int ret;
145 struct syncops_config_data *config;
147 SMB_VFS_HANDLE_GET_DATA(handle, config,
148 struct syncops_config_data,
149 return -1);
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);
156 return ret;
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)
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)
186 static int syncops_symlink(vfs_handle_struct *handle,
187 const char *oldname, const char *newname)
189 SYNCOPS_NEXT(SYMLINK, newname, (handle, oldname, newname));
192 static int syncops_link(vfs_handle_struct *handle,
193 const char *oldname, const char *newname)
195 SYNCOPS_NEXT(LINK, newname, (handle, oldname, newname));
198 static int syncops_open(vfs_handle_struct *handle,
199 struct smb_filename *smb_fname, files_struct *fsp,
200 int flags, mode_t mode)
202 SYNCOPS_NEXT_SMB_FNAME(OPEN, (flags&O_CREAT?smb_fname:NULL),
203 (handle, smb_fname, fsp, flags, mode));
206 static int syncops_unlink(vfs_handle_struct *handle,
207 const struct smb_filename *smb_fname)
209 SYNCOPS_NEXT_SMB_FNAME(UNLINK, smb_fname, (handle, smb_fname));
212 static int syncops_mknod(vfs_handle_struct *handle,
213 const char *fname, mode_t mode, SMB_DEV_T dev)
215 SYNCOPS_NEXT(MKNOD, fname, (handle, fname, mode, dev));
218 static int syncops_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
220 SYNCOPS_NEXT(MKDIR, fname, (handle, fname, mode));
223 static int syncops_rmdir(vfs_handle_struct *handle, const char *fname)
225 SYNCOPS_NEXT(RMDIR, fname, (handle, fname));
228 /* close needs to be handled specially */
229 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
231 struct syncops_config_data *config;
233 SMB_VFS_HANDLE_GET_DATA(handle, config,
234 struct syncops_config_data,
235 return -1);
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);
242 return SMB_VFS_NEXT_CLOSE(handle, fsp);
245 int syncops_connect(struct vfs_handle_struct *handle, const char *service,
246 const char *user)
249 struct syncops_config_data *config;
250 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
251 if (ret < 0) {
252 return ret;
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;
262 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
263 "onclose", true);
265 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
266 "onmeta", true);
268 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
269 "disable", false);
271 SMB_VFS_HANDLE_SET_DATA(handle, config,
272 NULL, struct syncops_config_data,
273 return -1);
275 return 0;
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,
292 NTSTATUS vfs_syncops_init(void)
294 NTSTATUS ret;
296 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
297 &vfs_syncops_fns);
299 if (!NT_STATUS_IS_OK(ret))
300 return ret;
302 return ret;