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.
42 struct syncops_config_data
{
47 given a filename, find the parent directory
49 static char *parent_dir(TALLOC_CTX
*mem_ctx
, const char *name
)
51 const char *p
= strrchr(name
, '/');
53 return talloc_strdup(mem_ctx
, ".");
55 return talloc_strndup(mem_ctx
, name
, (p
+1) - name
);
59 fsync a directory by name
61 static void syncops_sync_directory(const char *dname
)
64 int fd
= open(dname
, O_DIRECTORY
|O_RDONLY
);
70 DIR *d
= opendir(dname
);
79 sync two meta data changes for 2 names
81 static void syncops_two_names(const char *name1
, const char *name2
)
83 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
84 char *parent1
, *parent2
;
85 parent1
= parent_dir(tmp_ctx
, name1
);
86 parent2
= parent_dir(tmp_ctx
, name2
);
87 if (!parent1
|| !parent2
) {
91 syncops_sync_directory(parent1
);
92 if (strcmp(parent1
, parent2
) != 0) {
93 syncops_sync_directory(parent2
);
99 sync two meta data changes for 1 names
101 static void syncops_name(const char *name
)
104 parent
= parent_dir(NULL
, name
);
106 syncops_sync_directory(parent
);
112 sync two meta data changes for 1 names
114 static void syncops_smb_fname(const struct smb_filename
*smb_fname
)
117 parent
= parent_dir(NULL
, smb_fname
->base_name
);
119 syncops_sync_directory(parent
);
126 rename needs special handling, as we may need to fsync two directories
128 static int syncops_rename(vfs_handle_struct
*handle
,
129 const struct smb_filename
*smb_fname_src
,
130 const struct smb_filename
*smb_fname_dst
)
132 int ret
= SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
134 syncops_two_names(smb_fname_src
->base_name
,
135 smb_fname_dst
->base_name
);
140 /* handle the rest with a macro */
141 #define SYNCOPS_NEXT(op, fname, args) do { \
142 int ret = SMB_VFS_NEXT_ ## op args; \
143 if (ret == 0 && fname) syncops_name(fname); \
147 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
148 int ret = SMB_VFS_NEXT_ ## op args; \
149 if (ret == 0 && fname) syncops_smb_fname(fname); \
153 static int syncops_symlink(vfs_handle_struct
*handle
,
154 const char *oldname
, const char *newname
)
156 SYNCOPS_NEXT(SYMLINK
, newname
, (handle
, oldname
, newname
));
159 static int syncops_link(vfs_handle_struct
*handle
,
160 const char *oldname
, const char *newname
)
162 SYNCOPS_NEXT(LINK
, newname
, (handle
, oldname
, newname
));
165 static int syncops_open(vfs_handle_struct
*handle
,
166 struct smb_filename
*smb_fname
, files_struct
*fsp
,
167 int flags
, mode_t mode
)
169 SYNCOPS_NEXT_SMB_FNAME(OPEN
, (flags
&O_CREAT
?smb_fname
:NULL
),
170 (handle
, smb_fname
, fsp
, flags
, mode
));
173 static int syncops_unlink(vfs_handle_struct
*handle
,
174 const struct smb_filename
*smb_fname
)
176 SYNCOPS_NEXT_SMB_FNAME(UNLINK
, smb_fname
, (handle
, smb_fname
));
179 static int syncops_mknod(vfs_handle_struct
*handle
,
180 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
182 SYNCOPS_NEXT(MKNOD
, fname
, (handle
, fname
, mode
, dev
));
185 static int syncops_mkdir(vfs_handle_struct
*handle
, const char *fname
, mode_t mode
)
187 SYNCOPS_NEXT(MKDIR
, fname
, (handle
, fname
, mode
));
190 static int syncops_rmdir(vfs_handle_struct
*handle
, const char *fname
)
192 SYNCOPS_NEXT(RMDIR
, fname
, (handle
, fname
));
195 /* close needs to be handled specially */
196 static int syncops_close(vfs_handle_struct
*handle
, files_struct
*fsp
)
198 struct syncops_config_data
*config
;
200 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
201 struct syncops_config_data
,
204 if (fsp
->can_write
&& config
->onclose
) {
205 /* ideally we'd only do this if we have written some
206 data, but there is no flag for that in fsp yet. */
209 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
212 int syncops_connect(struct vfs_handle_struct
*handle
, const char *service
,
216 struct syncops_config_data
*config
;
217 int ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
222 config
= talloc_zero(handle
->conn
, struct syncops_config_data
);
224 SMB_VFS_NEXT_DISCONNECT(handle
);
225 DEBUG(0, ("talloc_zero() failed\n"));
229 config
->onclose
= lp_parm_bool(SNUM(handle
->conn
), "syncops",
232 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
233 NULL
, struct syncops_config_data
,
240 static struct vfs_fn_pointers vfs_syncops_fns
= {
241 .connect_fn
= syncops_connect
,
242 .mkdir
= syncops_mkdir
,
243 .rmdir
= syncops_rmdir
,
244 .open
= syncops_open
,
245 .rename
= syncops_rename
,
246 .unlink
= syncops_unlink
,
247 .symlink
= syncops_symlink
,
248 .link
= syncops_link
,
249 .mknod
= syncops_mknod
,
250 .close_fn
= syncops_close
,
253 NTSTATUS
vfs_syncops_init(void)
257 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "syncops",
260 if (!NT_STATUS_IS_OK(ret
))