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.
23 #include "system/filesys.h"
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
34 On those filesystems this module provides a way to perform those
37 most of the performance loss with this module is in fsync on close().
38 You can disable that with
40 that can be set either globally or per share.
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
45 This option can be set either globally or per share.
47 you can also disable the module completely for a share with
48 syncops:disable = true
52 struct syncops_config_data {
59 given a filename, find the parent directory
61 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
63 const char *p = strrchr(name, '/');
65 return talloc_strdup(mem_ctx, ".");
67 return talloc_strndup(mem_ctx, name, (p+1) - name);
71 fsync a directory by name
73 static void syncops_sync_directory(const char *dname)
76 int fd = open(dname, O_DIRECTORY|O_RDONLY);
82 DIR *d = opendir(dname);
91 sync two meta data changes for 2 names
93 static void syncops_two_names(const char *name1, const char *name2)
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);
103 syncops_sync_directory(parent1);
104 if (strcmp(parent1, parent2) != 0) {
105 syncops_sync_directory(parent2);
107 talloc_free(tmp_ctx);
111 sync two meta data changes for 1 names
113 static void syncops_name(const char *name)
116 parent = parent_dir(NULL, name);
118 syncops_sync_directory(parent);
124 sync two meta data changes for 1 names
126 static void syncops_smb_fname(const struct smb_filename *smb_fname)
129 parent = parent_dir(NULL, smb_fname->base_name);
131 syncops_sync_directory(parent);
138 rename needs special handling, as we may need to fsync two directories
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)
146 struct syncops_config_data *config;
148 SMB_VFS_HANDLE_GET_DATA(handle, config,
149 struct syncops_config_data,
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);
160 /* handle the rest with a macro */
161 #define SYNCOPS_NEXT(op, fname, args) do { \
163 struct syncops_config_data *config; \
164 SMB_VFS_HANDLE_GET_DATA(handle, config, \
165 struct syncops_config_data, \
167 ret = SMB_VFS_NEXT_ ## op args; \
169 && config->onmeta && !config->disable \
170 && fname) syncops_name(fname); \
174 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
176 struct syncops_config_data *config; \
177 SMB_VFS_HANDLE_GET_DATA(handle, config, \
178 struct syncops_config_data, \
180 ret = SMB_VFS_NEXT_ ## op args; \
182 && config->onmeta && !config->disable \
183 && fname) syncops_smb_fname(fname); \
187 static int syncops_symlink(vfs_handle_struct *handle,
188 const char *oldname, const char *newname)
190 SYNCOPS_NEXT(SYMLINK, newname, (handle, oldname, newname));
193 static int syncops_link(vfs_handle_struct *handle,
194 const char *oldname, const char *newname)
196 SYNCOPS_NEXT(LINK, newname, (handle, oldname, newname));
199 static int syncops_open(vfs_handle_struct *handle,
200 struct smb_filename *smb_fname, files_struct *fsp,
201 int flags, mode_t mode)
203 SYNCOPS_NEXT_SMB_FNAME(OPEN, (flags&O_CREAT?smb_fname:NULL),
204 (handle, smb_fname, fsp, flags, mode));
207 static int syncops_unlink(vfs_handle_struct *handle,
208 const struct smb_filename *smb_fname)
210 SYNCOPS_NEXT_SMB_FNAME(UNLINK, smb_fname, (handle, smb_fname));
213 static int syncops_mknod(vfs_handle_struct *handle,
214 const char *fname, mode_t mode, SMB_DEV_T dev)
216 SYNCOPS_NEXT(MKNOD, fname, (handle, fname, mode, dev));
219 static int syncops_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
221 SYNCOPS_NEXT(MKDIR, fname, (handle, fname, mode));
224 static int syncops_rmdir(vfs_handle_struct *handle, const char *fname)
226 SYNCOPS_NEXT(RMDIR, fname, (handle, fname));
229 /* close needs to be handled specially */
230 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
232 struct syncops_config_data *config;
234 SMB_VFS_HANDLE_GET_DATA(handle, config,
235 struct syncops_config_data,
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. */
243 return SMB_VFS_NEXT_CLOSE(handle, fsp);
246 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
250 struct syncops_config_data *config;
251 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
256 config = talloc_zero(handle->conn, struct syncops_config_data);
258 SMB_VFS_NEXT_DISCONNECT(handle);
259 DEBUG(0, ("talloc_zero() failed\n"));
263 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
266 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
269 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
272 SMB_VFS_HANDLE_SET_DATA(handle, config,
273 NULL, struct syncops_config_data,
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,
293 NTSTATUS vfs_syncops_init(void)
297 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
300 if (!NT_STATUS_IS_OK(ret))