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,
161 if (config->disable) {
164 if (!config->onmeta) {
168 syncops_two_names(handle->conn,
174 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
176 struct smb_filename *full_fname = NULL; \
177 struct syncops_config_data *config; \
178 SMB_VFS_HANDLE_GET_DATA(handle, config, \
179 struct syncops_config_data, \
181 ret = SMB_VFS_NEXT_ ## op args; \
185 if (config->disable) { \
188 if (!config->onmeta) { \
191 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
194 if (full_fname == NULL) { \
197 syncops_smb_fname(dirfsp->conn, full_fname); \
198 TALLOC_FREE(full_fname); \
202 static int syncops_symlinkat(vfs_handle_struct *handle,
203 const struct smb_filename *link_contents,
204 struct files_struct *dirfsp,
205 const struct smb_filename *smb_fname)
207 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
215 static int syncops_linkat(vfs_handle_struct *handle,
216 files_struct *srcfsp,
217 const struct smb_filename *old_smb_fname,
218 files_struct *dstfsp,
219 const struct smb_filename *new_smb_fname,
223 struct syncops_config_data *config;
224 struct smb_filename *old_full_fname = NULL;
225 struct smb_filename *new_full_fname = NULL;
227 SMB_VFS_HANDLE_GET_DATA(handle, config,
228 struct syncops_config_data,
231 ret = SMB_VFS_NEXT_LINKAT(handle,
241 if (config->disable) {
244 if (!config->onmeta) {
248 old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
251 if (old_full_fname == NULL) {
254 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
257 if (new_full_fname == NULL) {
258 TALLOC_FREE(old_full_fname);
261 syncops_two_names(handle->conn,
264 TALLOC_FREE(old_full_fname);
265 TALLOC_FREE(new_full_fname);
269 static int syncops_openat(struct vfs_handle_struct *handle,
270 const struct files_struct *dirfsp,
271 const struct smb_filename *smb_fname,
272 struct files_struct *fsp,
276 SYNCOPS_NEXT_SMB_FNAME(OPENAT, (flags & O_CREAT ? smb_fname : NULL),
277 (handle, dirfsp, smb_fname, fsp, flags, mode));
280 static int syncops_unlinkat(vfs_handle_struct *handle,
281 files_struct *dirfsp,
282 const struct smb_filename *smb_fname,
285 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
293 static int syncops_mknodat(vfs_handle_struct *handle,
294 files_struct *dirfsp,
295 const struct smb_filename *smb_fname,
299 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
308 static int syncops_mkdirat(vfs_handle_struct *handle,
309 struct files_struct *dirfsp,
310 const struct smb_filename *smb_fname,
313 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
321 /* close needs to be handled specially */
322 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
324 struct syncops_config_data *config;
326 SMB_VFS_HANDLE_GET_DATA(handle, config,
327 struct syncops_config_data,
330 if (fsp->fsp_flags.can_write && config->onclose) {
331 /* ideally we'd only do this if we have written some
332 data, but there is no flag for that in fsp yet. */
333 fsync(fsp_get_io_fd(fsp));
335 return SMB_VFS_NEXT_CLOSE(handle, fsp);
338 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
342 struct syncops_config_data *config;
343 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
348 config = talloc_zero(handle->conn, struct syncops_config_data);
350 SMB_VFS_NEXT_DISCONNECT(handle);
351 DEBUG(0, ("talloc_zero() failed\n"));
355 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
358 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
361 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
364 SMB_VFS_HANDLE_SET_DATA(handle, config,
365 NULL, struct syncops_config_data,
372 static struct vfs_fn_pointers vfs_syncops_fns = {
373 .connect_fn = syncops_connect,
374 .mkdirat_fn = syncops_mkdirat,
375 .openat_fn = syncops_openat,
376 .renameat_fn = syncops_renameat,
377 .unlinkat_fn = syncops_unlinkat,
378 .symlinkat_fn = syncops_symlinkat,
379 .linkat_fn = syncops_linkat,
380 .mknodat_fn = syncops_mknodat,
381 .close_fn = syncops_close,
385 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
389 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
392 if (!NT_STATUS_IS_OK(ret))