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(connection_struct *conn,
95 const struct smb_filename *name1,
96 const struct smb_filename *name2)
98 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
99 char *parent1, *parent2;
100 parent1 = parent_dir(tmp_ctx, name1->base_name);
101 parent2 = parent_dir(tmp_ctx, name2->base_name);
102 if (!parent1 || !parent2) {
103 talloc_free(tmp_ctx);
106 syncops_sync_directory(parent1);
107 if (strcmp(parent1, parent2) != 0) {
108 syncops_sync_directory(parent2);
110 talloc_free(tmp_ctx);
114 sync two meta data changes for 1 names
116 static void syncops_smb_fname(connection_struct *conn,
117 const struct smb_filename *smb_fname)
120 if (smb_fname != NULL) {
121 parent = parent_dir(NULL, smb_fname->base_name);
122 if (parent != NULL) {
123 syncops_sync_directory(parent);
131 renameat needs special handling, as we may need to fsync two directories
133 static int syncops_renameat(vfs_handle_struct *handle,
134 files_struct *srcfsp,
135 const struct smb_filename *smb_fname_src,
136 files_struct *dstfsp,
137 const struct smb_filename *smb_fname_dst)
141 struct syncops_config_data *config;
143 SMB_VFS_HANDLE_GET_DATA(handle, config,
144 struct syncops_config_data,
147 ret = SMB_VFS_NEXT_RENAMEAT(handle,
152 if (ret == 0 && config->onmeta && !config->disable) {
153 syncops_two_names(handle->conn,
160 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
162 struct smb_filename *full_fname = NULL; \
163 struct syncops_config_data *config; \
164 SMB_VFS_HANDLE_GET_DATA(handle, config, \
165 struct syncops_config_data, \
167 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
170 if (full_fname == NULL) { \
173 ret = SMB_VFS_NEXT_ ## op args; \
175 && config->onmeta && !config->disable \
176 && fname) syncops_smb_fname(dirfsp->conn, full_fname); \
177 TALLOC_FREE(full_fname); \
181 static int syncops_symlinkat(vfs_handle_struct *handle,
182 const struct smb_filename *link_contents,
183 struct files_struct *dirfsp,
184 const struct smb_filename *smb_fname)
186 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
194 static int syncops_linkat(vfs_handle_struct *handle,
195 files_struct *srcfsp,
196 const struct smb_filename *old_smb_fname,
197 files_struct *dstfsp,
198 const struct smb_filename *new_smb_fname,
202 struct syncops_config_data *config;
203 struct smb_filename *old_full_fname = NULL;
204 struct smb_filename *new_full_fname = NULL;
206 SMB_VFS_HANDLE_GET_DATA(handle, config,
207 struct syncops_config_data,
210 old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
213 if (old_full_fname == NULL) {
216 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
219 if (new_full_fname == NULL) {
220 TALLOC_FREE(old_full_fname);
223 ret = SMB_VFS_NEXT_LINKAT(handle,
230 if (ret == 0 && config->onmeta && !config->disable) {
231 syncops_two_names(handle->conn,
235 TALLOC_FREE(old_full_fname);
236 TALLOC_FREE(new_full_fname);
240 static int syncops_openat(struct vfs_handle_struct *handle,
241 const struct files_struct *dirfsp,
242 const struct smb_filename *smb_fname,
243 struct files_struct *fsp,
247 SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
248 (handle, dirfsp, smb_fname, fsp, flags, mode));
251 static int syncops_unlinkat(vfs_handle_struct *handle,
252 files_struct *dirfsp,
253 const struct smb_filename *smb_fname,
256 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
264 static int syncops_mknodat(vfs_handle_struct *handle,
265 files_struct *dirfsp,
266 const struct smb_filename *smb_fname,
270 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
279 static int syncops_mkdirat(vfs_handle_struct *handle,
280 struct files_struct *dirfsp,
281 const struct smb_filename *smb_fname,
284 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
292 /* close needs to be handled specially */
293 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
295 struct syncops_config_data *config;
297 SMB_VFS_HANDLE_GET_DATA(handle, config,
298 struct syncops_config_data,
301 if (fsp->fsp_flags.can_write && config->onclose) {
302 /* ideally we'd only do this if we have written some
303 data, but there is no flag for that in fsp yet. */
304 fsync(fsp_get_io_fd(fsp));
306 return SMB_VFS_NEXT_CLOSE(handle, fsp);
309 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
313 struct syncops_config_data *config;
314 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
319 config = talloc_zero(handle->conn, struct syncops_config_data);
321 SMB_VFS_NEXT_DISCONNECT(handle);
322 DEBUG(0, ("talloc_zero() failed\n"));
326 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
329 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
332 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
335 SMB_VFS_HANDLE_SET_DATA(handle, config,
336 NULL, struct syncops_config_data,
343 static struct vfs_fn_pointers vfs_syncops_fns = {
344 .connect_fn = syncops_connect,
345 .mkdirat_fn = syncops_mkdirat,
346 .openat_fn = syncops_openat,
347 .renameat_fn = syncops_renameat,
348 .unlinkat_fn = syncops_unlinkat,
349 .symlinkat_fn = syncops_symlinkat,
350 .linkat_fn = syncops_linkat,
351 .mknodat_fn = syncops_mknodat,
352 .close_fn = syncops_close,
356 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
360 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
363 if (!NT_STATUS_IS_OK(ret))