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,
77 struct smb_Dir *dir_hnd = NULL;
78 struct files_struct *dirfsp = NULL;
79 struct smb_filename smb_dname = { .base_name = dname };
81 dir_hnd = OpenDir(talloc_tos(),
86 if (dir_hnd == NULL) {
90 dirfsp = dir_hnd_fetch_fsp(dir_hnd);
92 smb_vfs_fsync_sync(dirfsp);
98 sync two meta data changes for 2 names
100 static void syncops_two_names(connection_struct *conn,
101 const struct smb_filename *name1,
102 const struct smb_filename *name2)
104 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
105 char *parent1, *parent2;
106 parent1 = parent_dir(tmp_ctx, name1->base_name);
107 parent2 = parent_dir(tmp_ctx, name2->base_name);
108 if (!parent1 || !parent2) {
109 talloc_free(tmp_ctx);
112 syncops_sync_directory(conn, parent1);
113 if (strcmp(parent1, parent2) != 0) {
114 syncops_sync_directory(conn, parent2);
116 talloc_free(tmp_ctx);
120 sync two meta data changes for 1 names
122 static void syncops_smb_fname(connection_struct *conn,
123 const struct smb_filename *smb_fname)
126 if (smb_fname != NULL) {
127 parent = parent_dir(NULL, smb_fname->base_name);
128 if (parent != NULL) {
129 syncops_sync_directory(conn, parent);
137 renameat needs special handling, as we may need to fsync two directories
139 static int syncops_renameat(vfs_handle_struct *handle,
140 files_struct *srcfsp,
141 const struct smb_filename *smb_fname_src,
142 files_struct *dstfsp,
143 const struct smb_filename *smb_fname_dst)
147 struct syncops_config_data *config;
149 SMB_VFS_HANDLE_GET_DATA(handle, config,
150 struct syncops_config_data,
153 ret = SMB_VFS_NEXT_RENAMEAT(handle,
158 if (ret == 0 && config->onmeta && !config->disable) {
159 syncops_two_names(handle->conn,
166 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
168 struct smb_filename *full_fname = NULL; \
169 struct syncops_config_data *config; \
170 SMB_VFS_HANDLE_GET_DATA(handle, config, \
171 struct syncops_config_data, \
173 ret = SMB_VFS_NEXT_ ## op args; \
177 if (config->disable) { \
180 if (!config->onmeta) { \
183 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
186 if (full_fname == NULL) { \
189 syncops_smb_fname(dirfsp->conn, full_fname); \
190 TALLOC_FREE(full_fname); \
194 static int syncops_symlinkat(vfs_handle_struct *handle,
195 const struct smb_filename *link_contents,
196 struct files_struct *dirfsp,
197 const struct smb_filename *smb_fname)
199 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
207 static int syncops_linkat(vfs_handle_struct *handle,
208 files_struct *srcfsp,
209 const struct smb_filename *old_smb_fname,
210 files_struct *dstfsp,
211 const struct smb_filename *new_smb_fname,
215 struct syncops_config_data *config;
216 struct smb_filename *old_full_fname = NULL;
217 struct smb_filename *new_full_fname = NULL;
219 SMB_VFS_HANDLE_GET_DATA(handle, config,
220 struct syncops_config_data,
223 old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
226 if (old_full_fname == NULL) {
229 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
232 if (new_full_fname == NULL) {
233 TALLOC_FREE(old_full_fname);
236 ret = SMB_VFS_NEXT_LINKAT(handle,
243 if (ret == 0 && config->onmeta && !config->disable) {
244 syncops_two_names(handle->conn,
248 TALLOC_FREE(old_full_fname);
249 TALLOC_FREE(new_full_fname);
253 static int syncops_openat(struct vfs_handle_struct *handle,
254 const struct files_struct *dirfsp,
255 const struct smb_filename *smb_fname,
256 struct files_struct *fsp,
260 SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
261 (handle, dirfsp, smb_fname, fsp, flags, mode));
264 static int syncops_unlinkat(vfs_handle_struct *handle,
265 files_struct *dirfsp,
266 const struct smb_filename *smb_fname,
269 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
277 static int syncops_mknodat(vfs_handle_struct *handle,
278 files_struct *dirfsp,
279 const struct smb_filename *smb_fname,
283 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
292 static int syncops_mkdirat(vfs_handle_struct *handle,
293 struct files_struct *dirfsp,
294 const struct smb_filename *smb_fname,
297 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
305 /* close needs to be handled specially */
306 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
308 struct syncops_config_data *config;
310 SMB_VFS_HANDLE_GET_DATA(handle, config,
311 struct syncops_config_data,
314 if (fsp->fsp_flags.can_write && config->onclose) {
315 /* ideally we'd only do this if we have written some
316 data, but there is no flag for that in fsp yet. */
317 fsync(fsp_get_io_fd(fsp));
319 return SMB_VFS_NEXT_CLOSE(handle, fsp);
322 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
326 struct syncops_config_data *config;
327 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
332 config = talloc_zero(handle->conn, struct syncops_config_data);
334 SMB_VFS_NEXT_DISCONNECT(handle);
335 DEBUG(0, ("talloc_zero() failed\n"));
339 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
342 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
345 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
348 SMB_VFS_HANDLE_SET_DATA(handle, config,
349 NULL, struct syncops_config_data,
356 static struct vfs_fn_pointers vfs_syncops_fns = {
357 .connect_fn = syncops_connect,
358 .mkdirat_fn = syncops_mkdirat,
359 .openat_fn = syncops_openat,
360 .renameat_fn = syncops_renameat,
361 .unlinkat_fn = syncops_unlinkat,
362 .symlinkat_fn = syncops_symlinkat,
363 .linkat_fn = syncops_linkat,
364 .mknodat_fn = syncops_mknodat,
365 .close_fn = syncops_close,
369 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
373 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
376 if (!NT_STATUS_IS_OK(ret))