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
};
82 status
= OpenDir(talloc_tos(),
88 if (!NT_STATUS_IS_OK(status
)) {
89 errno
= map_errno_from_nt_status(status
);
93 dirfsp
= dir_hnd_fetch_fsp(dir_hnd
);
95 smb_vfs_fsync_sync(dirfsp
);
101 sync two meta data changes for 2 names
103 static void syncops_two_names(connection_struct
*conn
,
104 const struct smb_filename
*name1
,
105 const struct smb_filename
*name2
)
107 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
108 char *parent1
, *parent2
;
109 parent1
= parent_dir(tmp_ctx
, name1
->base_name
);
110 parent2
= parent_dir(tmp_ctx
, name2
->base_name
);
111 if (!parent1
|| !parent2
) {
112 talloc_free(tmp_ctx
);
115 syncops_sync_directory(conn
, parent1
);
116 if (strcmp(parent1
, parent2
) != 0) {
117 syncops_sync_directory(conn
, parent2
);
119 talloc_free(tmp_ctx
);
123 sync two meta data changes for 1 names
125 static void syncops_smb_fname(connection_struct
*conn
,
126 const struct smb_filename
*smb_fname
)
129 if (smb_fname
!= NULL
) {
130 parent
= parent_dir(NULL
, smb_fname
->base_name
);
131 if (parent
!= NULL
) {
132 syncops_sync_directory(conn
, parent
);
140 renameat needs special handling, as we may need to fsync two directories
142 static int syncops_renameat(vfs_handle_struct
*handle
,
143 files_struct
*srcfsp
,
144 const struct smb_filename
*smb_fname_src
,
145 files_struct
*dstfsp
,
146 const struct smb_filename
*smb_fname_dst
)
150 struct smb_filename
*full_fname_src
= NULL
;
151 struct smb_filename
*full_fname_dst
= NULL
;
152 struct syncops_config_data
*config
;
154 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
155 struct syncops_config_data
,
158 ret
= SMB_VFS_NEXT_RENAMEAT(handle
,
166 if (config
->disable
) {
169 if (!config
->onmeta
) {
173 full_fname_src
= full_path_from_dirfsp_atname(talloc_tos(),
176 if (full_fname_src
== NULL
) {
180 full_fname_dst
= full_path_from_dirfsp_atname(talloc_tos(),
183 if (full_fname_dst
== NULL
) {
184 TALLOC_FREE(full_fname_src
);
188 syncops_two_names(handle
->conn
,
191 TALLOC_FREE(full_fname_src
);
192 TALLOC_FREE(full_fname_dst
);
196 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
198 struct smb_filename *full_fname = NULL; \
199 struct syncops_config_data *config; \
200 SMB_VFS_HANDLE_GET_DATA(handle, config, \
201 struct syncops_config_data, \
203 ret = SMB_VFS_NEXT_ ## op args; \
207 if (config->disable) { \
210 if (!config->onmeta) { \
213 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
216 if (full_fname == NULL) { \
219 syncops_smb_fname(dirfsp->conn, full_fname); \
220 TALLOC_FREE(full_fname); \
224 static int syncops_symlinkat(vfs_handle_struct
*handle
,
225 const struct smb_filename
*link_contents
,
226 struct files_struct
*dirfsp
,
227 const struct smb_filename
*smb_fname
)
229 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT
,
237 static int syncops_linkat(vfs_handle_struct
*handle
,
238 files_struct
*srcfsp
,
239 const struct smb_filename
*old_smb_fname
,
240 files_struct
*dstfsp
,
241 const struct smb_filename
*new_smb_fname
,
245 struct syncops_config_data
*config
;
246 struct smb_filename
*old_full_fname
= NULL
;
247 struct smb_filename
*new_full_fname
= NULL
;
249 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
250 struct syncops_config_data
,
253 ret
= SMB_VFS_NEXT_LINKAT(handle
,
263 if (config
->disable
) {
266 if (!config
->onmeta
) {
270 old_full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
273 if (old_full_fname
== NULL
) {
276 new_full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
279 if (new_full_fname
== NULL
) {
280 TALLOC_FREE(old_full_fname
);
283 syncops_two_names(handle
->conn
,
286 TALLOC_FREE(old_full_fname
);
287 TALLOC_FREE(new_full_fname
);
291 static int syncops_openat(struct vfs_handle_struct
*handle
,
292 const struct files_struct
*dirfsp
,
293 const struct smb_filename
*smb_fname
,
294 struct files_struct
*fsp
,
295 const struct vfs_open_how
*how
)
297 SYNCOPS_NEXT_SMB_FNAME(OPENAT
, (how
->flags
& O_CREAT
? smb_fname
: NULL
),
298 (handle
, dirfsp
, smb_fname
, fsp
, how
));
301 static int syncops_unlinkat(vfs_handle_struct
*handle
,
302 files_struct
*dirfsp
,
303 const struct smb_filename
*smb_fname
,
306 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT
,
314 static int syncops_mknodat(vfs_handle_struct
*handle
,
315 files_struct
*dirfsp
,
316 const struct smb_filename
*smb_fname
,
320 SYNCOPS_NEXT_SMB_FNAME(MKNODAT
,
329 static int syncops_mkdirat(vfs_handle_struct
*handle
,
330 struct files_struct
*dirfsp
,
331 const struct smb_filename
*smb_fname
,
334 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT
,
342 /* close needs to be handled specially */
343 static int syncops_close(vfs_handle_struct
*handle
, files_struct
*fsp
)
345 struct syncops_config_data
*config
;
347 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
348 struct syncops_config_data
,
351 if (fsp
->fsp_flags
.can_write
&& config
->onclose
) {
352 /* ideally we'd only do this if we have written some
353 data, but there is no flag for that in fsp yet. */
354 fsync(fsp_get_io_fd(fsp
));
356 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
359 static int syncops_connect(struct vfs_handle_struct
*handle
, const char *service
,
363 struct syncops_config_data
*config
;
364 int ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
369 config
= talloc_zero(handle
->conn
, struct syncops_config_data
);
371 SMB_VFS_NEXT_DISCONNECT(handle
);
372 DEBUG(0, ("talloc_zero() failed\n"));
376 config
->onclose
= lp_parm_bool(SNUM(handle
->conn
), "syncops",
379 config
->onmeta
= lp_parm_bool(SNUM(handle
->conn
), "syncops",
382 config
->disable
= lp_parm_bool(SNUM(handle
->conn
), "syncops",
385 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
386 NULL
, struct syncops_config_data
,
393 static struct vfs_fn_pointers vfs_syncops_fns
= {
394 .connect_fn
= syncops_connect
,
395 .mkdirat_fn
= syncops_mkdirat
,
396 .openat_fn
= syncops_openat
,
397 .renameat_fn
= syncops_renameat
,
398 .unlinkat_fn
= syncops_unlinkat
,
399 .symlinkat_fn
= syncops_symlinkat
,
400 .linkat_fn
= syncops_linkat
,
401 .mknodat_fn
= syncops_mknodat
,
402 .close_fn
= syncops_close
,
406 NTSTATUS
vfs_syncops_init(TALLOC_CTX
*ctx
)
410 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "syncops",
413 if (!NT_STATUS_IS_OK(ret
))