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(connection_struct *conn,
78 int fd = open(dname, O_DIRECTORY|O_RDONLY);
84 DIR *d = opendir(dname);
93 sync two meta data changes for 2 names
95 static void syncops_two_names(connection_struct *conn,
96 const struct smb_filename *name1,
97 const struct smb_filename *name2)
99 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
100 char *parent1, *parent2;
101 parent1 = parent_dir(tmp_ctx, name1->base_name);
102 parent2 = parent_dir(tmp_ctx, name2->base_name);
103 if (!parent1 || !parent2) {
104 talloc_free(tmp_ctx);
107 syncops_sync_directory(conn, parent1);
108 if (strcmp(parent1, parent2) != 0) {
109 syncops_sync_directory(conn, parent2);
111 talloc_free(tmp_ctx);
115 sync two meta data changes for 1 names
117 static void syncops_smb_fname(connection_struct *conn,
118 const struct smb_filename *smb_fname)
121 if (smb_fname != NULL) {
122 parent = parent_dir(NULL, smb_fname->base_name);
123 if (parent != NULL) {
124 syncops_sync_directory(conn, parent);
132 renameat needs special handling, as we may need to fsync two directories
134 static int syncops_renameat(vfs_handle_struct *handle,
135 files_struct *srcfsp,
136 const struct smb_filename *smb_fname_src,
137 files_struct *dstfsp,
138 const struct smb_filename *smb_fname_dst)
142 struct syncops_config_data *config;
144 SMB_VFS_HANDLE_GET_DATA(handle, config,
145 struct syncops_config_data,
148 ret = SMB_VFS_NEXT_RENAMEAT(handle,
153 if (ret == 0 && config->onmeta && !config->disable) {
154 syncops_two_names(handle->conn,
161 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
163 struct smb_filename *full_fname = NULL; \
164 struct syncops_config_data *config; \
165 SMB_VFS_HANDLE_GET_DATA(handle, config, \
166 struct syncops_config_data, \
168 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
171 if (full_fname == NULL) { \
174 ret = SMB_VFS_NEXT_ ## op args; \
176 && config->onmeta && !config->disable \
177 && fname) syncops_smb_fname(dirfsp->conn, full_fname); \
178 TALLOC_FREE(full_fname); \
182 static int syncops_symlinkat(vfs_handle_struct *handle,
183 const struct smb_filename *link_contents,
184 struct files_struct *dirfsp,
185 const struct smb_filename *smb_fname)
187 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
195 static int syncops_linkat(vfs_handle_struct *handle,
196 files_struct *srcfsp,
197 const struct smb_filename *old_smb_fname,
198 files_struct *dstfsp,
199 const struct smb_filename *new_smb_fname,
203 struct syncops_config_data *config;
204 struct smb_filename *old_full_fname = NULL;
205 struct smb_filename *new_full_fname = NULL;
207 SMB_VFS_HANDLE_GET_DATA(handle, config,
208 struct syncops_config_data,
211 old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
214 if (old_full_fname == NULL) {
217 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
220 if (new_full_fname == NULL) {
221 TALLOC_FREE(old_full_fname);
224 ret = SMB_VFS_NEXT_LINKAT(handle,
231 if (ret == 0 && config->onmeta && !config->disable) {
232 syncops_two_names(handle->conn,
236 TALLOC_FREE(old_full_fname);
237 TALLOC_FREE(new_full_fname);
241 static int syncops_openat(struct vfs_handle_struct *handle,
242 const struct files_struct *dirfsp,
243 const struct smb_filename *smb_fname,
244 struct files_struct *fsp,
248 SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
249 (handle, dirfsp, smb_fname, fsp, flags, mode));
252 static int syncops_unlinkat(vfs_handle_struct *handle,
253 files_struct *dirfsp,
254 const struct smb_filename *smb_fname,
257 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
265 static int syncops_mknodat(vfs_handle_struct *handle,
266 files_struct *dirfsp,
267 const struct smb_filename *smb_fname,
271 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
280 static int syncops_mkdirat(vfs_handle_struct *handle,
281 struct files_struct *dirfsp,
282 const struct smb_filename *smb_fname,
285 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
293 /* close needs to be handled specially */
294 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
296 struct syncops_config_data *config;
298 SMB_VFS_HANDLE_GET_DATA(handle, config,
299 struct syncops_config_data,
302 if (fsp->fsp_flags.can_write && config->onclose) {
303 /* ideally we'd only do this if we have written some
304 data, but there is no flag for that in fsp yet. */
305 fsync(fsp_get_io_fd(fsp));
307 return SMB_VFS_NEXT_CLOSE(handle, fsp);
310 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
314 struct syncops_config_data *config;
315 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
320 config = talloc_zero(handle->conn, struct syncops_config_data);
322 SMB_VFS_NEXT_DISCONNECT(handle);
323 DEBUG(0, ("talloc_zero() failed\n"));
327 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
330 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
333 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
336 SMB_VFS_HANDLE_SET_DATA(handle, config,
337 NULL, struct syncops_config_data,
344 static struct vfs_fn_pointers vfs_syncops_fns = {
345 .connect_fn = syncops_connect,
346 .mkdirat_fn = syncops_mkdirat,
347 .openat_fn = syncops_openat,
348 .renameat_fn = syncops_renameat,
349 .unlinkat_fn = syncops_unlinkat,
350 .symlinkat_fn = syncops_symlinkat,
351 .linkat_fn = syncops_linkat,
352 .mknodat_fn = syncops_mknodat,
353 .close_fn = syncops_close,
357 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
361 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
364 if (!NT_STATUS_IS_OK(ret))