2 * ensure meta data operations are performed synchronously
4 * Copyright (C) Andrew Tridgell 2007
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 Some filesystems (even some journaled filesystems) require that a
26 fsync() be performed on many meta data operations to ensure that the
27 operation is guaranteed to remain in the filesystem after a power
28 failure. This is particularly important for some cluster filesystems
29 which are participating in a node failover system with clustered
32 On those filesystems this module provides a way to perform those
35 most of the performance loss with this module is in fsync on close().
36 You can disable that with
38 that can be set either globally or per share.
40 you can also disable the module completely for a service with
41 syncops:disable = true
45 struct syncops_config_data
{
51 given a filename, find the parent directory
53 static char *parent_dir(TALLOC_CTX
*mem_ctx
, const char *name
)
55 const char *p
= strrchr(name
, '/');
57 return talloc_strdup(mem_ctx
, ".");
59 return talloc_strndup(mem_ctx
, name
, (p
+1) - name
);
63 fsync a directory by name
65 static void syncops_sync_directory(const char *dname
)
68 int fd
= open(dname
, O_DIRECTORY
|O_RDONLY
);
74 DIR *d
= opendir(dname
);
83 sync two meta data changes for 2 names
85 static void syncops_two_names(const char *name1
, const char *name2
)
87 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
88 char *parent1
, *parent2
;
89 parent1
= parent_dir(tmp_ctx
, name1
);
90 parent2
= parent_dir(tmp_ctx
, name2
);
91 if (!parent1
|| !parent2
) {
95 syncops_sync_directory(parent1
);
96 if (strcmp(parent1
, parent2
) != 0) {
97 syncops_sync_directory(parent2
);
103 sync two meta data changes for 1 names
105 static void syncops_name(const char *name
)
108 parent
= parent_dir(NULL
, name
);
110 syncops_sync_directory(parent
);
116 sync two meta data changes for 1 names
118 static void syncops_smb_fname(const struct smb_filename
*smb_fname
)
121 parent
= parent_dir(NULL
, smb_fname
->base_name
);
123 syncops_sync_directory(parent
);
130 rename needs special handling, as we may need to fsync two directories
132 static int syncops_rename(vfs_handle_struct
*handle
,
133 const struct smb_filename
*smb_fname_src
,
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_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
145 if (ret
== 0 && !config
->disable
) {
146 syncops_two_names(smb_fname_src
->base_name
,
147 smb_fname_dst
->base_name
);
152 /* handle the rest with a macro */
153 #define SYNCOPS_NEXT(op, fname, args) do { \
155 struct syncops_config_data *config; \
156 SMB_VFS_HANDLE_GET_DATA(handle, config, \
157 struct syncops_config_data, \
159 ret = SMB_VFS_NEXT_ ## op args; \
161 && !config->disable \
162 && fname) syncops_name(fname); \
166 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
168 struct syncops_config_data *config; \
169 SMB_VFS_HANDLE_GET_DATA(handle, config, \
170 struct syncops_config_data, \
172 ret = SMB_VFS_NEXT_ ## op args; \
174 && !config->disable \
175 && fname) syncops_smb_fname(fname); \
179 static int syncops_symlink(vfs_handle_struct
*handle
,
180 const char *oldname
, const char *newname
)
182 SYNCOPS_NEXT(SYMLINK
, newname
, (handle
, oldname
, newname
));
185 static int syncops_link(vfs_handle_struct
*handle
,
186 const char *oldname
, const char *newname
)
188 SYNCOPS_NEXT(LINK
, newname
, (handle
, oldname
, newname
));
191 static int syncops_open(vfs_handle_struct
*handle
,
192 struct smb_filename
*smb_fname
, files_struct
*fsp
,
193 int flags
, mode_t mode
)
195 SYNCOPS_NEXT_SMB_FNAME(OPEN
, (flags
&O_CREAT
?smb_fname
:NULL
),
196 (handle
, smb_fname
, fsp
, flags
, mode
));
199 static int syncops_unlink(vfs_handle_struct
*handle
,
200 const struct smb_filename
*smb_fname
)
202 SYNCOPS_NEXT_SMB_FNAME(UNLINK
, smb_fname
, (handle
, smb_fname
));
205 static int syncops_mknod(vfs_handle_struct
*handle
,
206 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
208 SYNCOPS_NEXT(MKNOD
, fname
, (handle
, fname
, mode
, dev
));
211 static int syncops_mkdir(vfs_handle_struct
*handle
, const char *fname
, mode_t mode
)
213 SYNCOPS_NEXT(MKDIR
, fname
, (handle
, fname
, mode
));
216 static int syncops_rmdir(vfs_handle_struct
*handle
, const char *fname
)
218 SYNCOPS_NEXT(RMDIR
, fname
, (handle
, fname
));
221 /* close needs to be handled specially */
222 static int syncops_close(vfs_handle_struct
*handle
, files_struct
*fsp
)
224 struct syncops_config_data
*config
;
226 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
227 struct syncops_config_data
,
230 if (fsp
->can_write
&& config
->onclose
) {
231 /* ideally we'd only do this if we have written some
232 data, but there is no flag for that in fsp yet. */
235 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
238 int syncops_connect(struct vfs_handle_struct
*handle
, const char *service
,
242 struct syncops_config_data
*config
;
243 int ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
248 config
= talloc_zero(handle
->conn
, struct syncops_config_data
);
250 SMB_VFS_NEXT_DISCONNECT(handle
);
251 DEBUG(0, ("talloc_zero() failed\n"));
255 config
->onclose
= lp_parm_bool(SNUM(handle
->conn
), "syncops",
258 config
->disable
= lp_parm_bool(SNUM(handle
->conn
), "syncops",
261 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
262 NULL
, struct syncops_config_data
,
269 static struct vfs_fn_pointers vfs_syncops_fns
= {
270 .connect_fn
= syncops_connect
,
271 .mkdir
= syncops_mkdir
,
272 .rmdir
= syncops_rmdir
,
273 .open
= syncops_open
,
274 .rename
= syncops_rename
,
275 .unlink
= syncops_unlink
,
276 .symlink
= syncops_symlink
,
277 .link
= syncops_link
,
278 .mknod
= syncops_mknod
,
279 .close_fn
= syncops_close
,
282 NTSTATUS
vfs_syncops_init(void)
286 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "syncops",
289 if (!NT_STATUS_IS_OK(ret
))