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 smb_filename *full_fname = NULL; \
159 struct syncops_config_data *config; \
160 SMB_VFS_HANDLE_GET_DATA(handle, config, \
161 struct syncops_config_data, \
163 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
166 if (full_fname == NULL) { \
169 ret = SMB_VFS_NEXT_ ## op args; \
171 && config->onmeta && !config->disable \
172 && fname) syncops_smb_fname(full_fname); \
173 TALLOC_FREE(full_fname); \
177 static int syncops_symlinkat(vfs_handle_struct *handle,
178 const struct smb_filename *link_contents,
179 struct files_struct *dirfsp,
180 const struct smb_filename *smb_fname)
182 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
190 static int syncops_linkat(vfs_handle_struct *handle,
191 files_struct *srcfsp,
192 const struct smb_filename *old_smb_fname,
193 files_struct *dstfsp,
194 const struct smb_filename *new_smb_fname,
198 struct syncops_config_data *config;
199 struct smb_filename *old_full_fname = NULL;
200 struct smb_filename *new_full_fname = NULL;
202 SMB_VFS_HANDLE_GET_DATA(handle, config,
203 struct syncops_config_data,
206 old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
209 if (old_full_fname == NULL) {
212 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
215 if (new_full_fname == NULL) {
216 TALLOC_FREE(old_full_fname);
219 ret = SMB_VFS_NEXT_LINKAT(handle,
226 if (ret == 0 && config->onmeta && !config->disable) {
227 syncops_two_names(old_full_fname->base_name,
228 new_full_fname->base_name);
230 TALLOC_FREE(old_full_fname);
231 TALLOC_FREE(new_full_fname);
235 static int syncops_openat(struct vfs_handle_struct *handle,
236 const struct files_struct *dirfsp,
237 const struct smb_filename *smb_fname,
238 struct files_struct *fsp,
242 SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
243 (handle, dirfsp, smb_fname, fsp, flags, mode));
246 static int syncops_unlinkat(vfs_handle_struct *handle,
247 files_struct *dirfsp,
248 const struct smb_filename *smb_fname,
251 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
259 static int syncops_mknodat(vfs_handle_struct *handle,
260 files_struct *dirfsp,
261 const struct smb_filename *smb_fname,
265 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
274 static int syncops_mkdirat(vfs_handle_struct *handle,
275 struct files_struct *dirfsp,
276 const struct smb_filename *smb_fname,
279 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
287 /* close needs to be handled specially */
288 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
290 struct syncops_config_data *config;
292 SMB_VFS_HANDLE_GET_DATA(handle, config,
293 struct syncops_config_data,
296 if (fsp->fsp_flags.can_write && config->onclose) {
297 /* ideally we'd only do this if we have written some
298 data, but there is no flag for that in fsp yet. */
299 fsync(fsp_get_io_fd(fsp));
301 return SMB_VFS_NEXT_CLOSE(handle, fsp);
304 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
308 struct syncops_config_data *config;
309 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
314 config = talloc_zero(handle->conn, struct syncops_config_data);
316 SMB_VFS_NEXT_DISCONNECT(handle);
317 DEBUG(0, ("talloc_zero() failed\n"));
321 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
324 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
327 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
330 SMB_VFS_HANDLE_SET_DATA(handle, config,
331 NULL, struct syncops_config_data,
338 static struct vfs_fn_pointers vfs_syncops_fns = {
339 .connect_fn = syncops_connect,
340 .mkdirat_fn = syncops_mkdirat,
341 .openat_fn = syncops_openat,
342 .renameat_fn = syncops_renameat,
343 .unlinkat_fn = syncops_unlinkat,
344 .symlinkat_fn = syncops_symlinkat,
345 .linkat_fn = syncops_linkat,
346 .mknodat_fn = syncops_mknodat,
347 .close_fn = syncops_close,
351 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
355 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
358 if (!NT_STATUS_IS_OK(ret))