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"
24 #include "smbd/smbd.h"
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
35 On those filesystems this module provides a way to perform those
38 most of the performance loss with this module is in fsync on close().
39 You can disable that with
41 that can be set either globally or per share.
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
46 This option can be set either globally or per share.
48 you can also disable the module completely for a share with
49 syncops:disable = true
53 struct syncops_config_data {
60 given a filename, find the parent directory
62 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
64 const char *p = strrchr(name, '/');
66 return talloc_strdup(mem_ctx, ".");
68 return talloc_strndup(mem_ctx, name, (p+1) - name);
72 fsync a directory by name
74 static void syncops_sync_directory(const char *dname)
77 int fd = open(dname, O_DIRECTORY|O_RDONLY);
83 DIR *d = opendir(dname);
92 sync two meta data changes for 2 names
94 static void syncops_two_names(const char *name1, const char *name2)
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);
104 syncops_sync_directory(parent1);
105 if (strcmp(parent1, parent2) != 0) {
106 syncops_sync_directory(parent2);
108 talloc_free(tmp_ctx);
112 sync two meta data changes for 1 names
114 static void syncops_smb_fname(const struct smb_filename *smb_fname)
117 if (smb_fname != NULL) {
118 parent = parent_dir(NULL, smb_fname->base_name);
119 if (parent != NULL) {
120 syncops_sync_directory(parent);
128 renameat needs special handling, as we may need to fsync two directories
130 static int syncops_renameat(vfs_handle_struct *handle,
131 files_struct *srcfsp,
132 const struct smb_filename *smb_fname_src,
133 files_struct *dstfsp,
134 const struct smb_filename *smb_fname_dst)
138 struct syncops_config_data *config;
140 SMB_VFS_HANDLE_GET_DATA(handle, config,
141 struct syncops_config_data,
144 ret = SMB_VFS_NEXT_RENAMEAT(handle,
149 if (ret == 0 && config->onmeta && !config->disable) {
150 syncops_two_names(smb_fname_src->base_name,
151 smb_fname_dst->base_name);
156 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
158 struct syncops_config_data *config; \
159 SMB_VFS_HANDLE_GET_DATA(handle, config, \
160 struct syncops_config_data, \
162 ret = SMB_VFS_NEXT_ ## op args; \
164 && config->onmeta && !config->disable \
165 && fname) syncops_smb_fname(fname); \
169 static int syncops_symlink(vfs_handle_struct *handle,
170 const char *link_contents,
171 const struct smb_filename *new_smb_fname)
174 struct syncops_config_data *config;
176 SMB_VFS_HANDLE_GET_DATA(handle, config,
177 struct syncops_config_data,
180 ret = SMB_VFS_NEXT_SYMLINK(handle, link_contents, new_smb_fname);
181 if (ret == 0 && config->onmeta && !config->disable) {
182 syncops_two_names(link_contents,
183 new_smb_fname->base_name);
188 static int syncops_symlinkat(vfs_handle_struct *handle,
189 const char *link_contents,
190 struct files_struct *dirfsp,
191 const struct smb_filename *new_smb_fname)
194 struct syncops_config_data *config;
196 SMB_VFS_HANDLE_GET_DATA(handle, config,
197 struct syncops_config_data,
200 ret = SMB_VFS_NEXT_SYMLINKAT(handle,
205 if (ret == 0 && config->onmeta && !config->disable) {
206 syncops_two_names(link_contents,
207 new_smb_fname->base_name);
212 static int syncops_linkat(vfs_handle_struct *handle,
213 files_struct *srcfsp,
214 const struct smb_filename *old_smb_fname,
215 files_struct *dstfsp,
216 const struct smb_filename *new_smb_fname,
220 struct syncops_config_data *config;
222 SMB_VFS_HANDLE_GET_DATA(handle, config,
223 struct syncops_config_data,
226 SMB_ASSERT(srcfsp == srcfsp->conn->cwd_fsp);
227 SMB_ASSERT(dstfsp == dstfsp->conn->cwd_fsp);
229 ret = SMB_VFS_NEXT_LINKAT(handle,
236 if (ret == 0 && config->onmeta && !config->disable) {
237 syncops_two_names(old_smb_fname->base_name,
238 new_smb_fname->base_name);
244 static int syncops_open(vfs_handle_struct *handle,
245 struct smb_filename *smb_fname, files_struct *fsp,
246 int flags, mode_t mode)
248 SYNCOPS_NEXT_SMB_FNAME(OPEN, (flags&O_CREAT?smb_fname:NULL),
249 (handle, smb_fname, fsp, flags, mode));
252 static int syncops_unlink(vfs_handle_struct *handle,
253 const struct smb_filename *smb_fname)
255 SYNCOPS_NEXT_SMB_FNAME(UNLINK, smb_fname, (handle, smb_fname));
258 static int syncops_mknodat(vfs_handle_struct *handle,
259 files_struct *dirfsp,
260 const struct smb_filename *smb_fname,
264 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
273 static int syncops_mkdir(vfs_handle_struct *handle,
274 const struct smb_filename *smb_fname,
277 SYNCOPS_NEXT_SMB_FNAME(MKDIR, smb_fname, (handle, smb_fname, mode));
280 static int syncops_rmdir(vfs_handle_struct *handle,
281 const struct smb_filename *smb_fname)
283 SYNCOPS_NEXT_SMB_FNAME(RMDIR, smb_fname, (handle, smb_fname));
286 /* close needs to be handled specially */
287 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
289 struct syncops_config_data *config;
291 SMB_VFS_HANDLE_GET_DATA(handle, config,
292 struct syncops_config_data,
295 if (fsp->can_write && config->onclose) {
296 /* ideally we'd only do this if we have written some
297 data, but there is no flag for that in fsp yet. */
300 return SMB_VFS_NEXT_CLOSE(handle, fsp);
303 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
307 struct syncops_config_data *config;
308 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
313 config = talloc_zero(handle->conn, struct syncops_config_data);
315 SMB_VFS_NEXT_DISCONNECT(handle);
316 DEBUG(0, ("talloc_zero() failed\n"));
320 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
323 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
326 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
329 SMB_VFS_HANDLE_SET_DATA(handle, config,
330 NULL, struct syncops_config_data,
337 static struct vfs_fn_pointers vfs_syncops_fns = {
338 .connect_fn = syncops_connect,
339 .mkdir_fn = syncops_mkdir,
340 .rmdir_fn = syncops_rmdir,
341 .open_fn = syncops_open,
342 .renameat_fn = syncops_renameat,
343 .unlink_fn = syncops_unlink,
344 .symlink_fn = syncops_symlink,
345 .symlinkat_fn = syncops_symlinkat,
346 .linkat_fn = syncops_linkat,
347 .mknodat_fn = syncops_mknodat,
348 .close_fn = syncops_close,
352 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
356 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
359 if (!NT_STATUS_IS_OK(ret))