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
37 most of the performance loss with this module is in fsync on close().
38 You can disable that with syncops:onclose = no
40 static bool sync_onclose
;
43 given a filename, find the parent directory
45 static char *parent_dir(TALLOC_CTX
*mem_ctx
, const char *name
)
47 const char *p
= strrchr(name
, '/');
49 return talloc_strdup(mem_ctx
, ".");
51 return talloc_strndup(mem_ctx
, name
, (p
+1) - name
);
55 fsync a directory by name
57 static void syncops_sync_directory(const char *dname
)
60 int fd
= open(dname
, O_DIRECTORY
|O_RDONLY
);
66 DIR *d
= opendir(dname
);
75 sync two meta data changes for 2 names
77 static void syncops_two_names(const char *name1
, const char *name2
)
79 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
80 char *parent1
, *parent2
;
81 parent1
= parent_dir(tmp_ctx
, name1
);
82 parent2
= parent_dir(tmp_ctx
, name2
);
83 if (!parent1
|| !parent2
) {
87 syncops_sync_directory(parent1
);
88 if (strcmp(parent1
, parent2
) != 0) {
89 syncops_sync_directory(parent2
);
95 sync two meta data changes for 1 names
97 static void syncops_name(const char *name
)
100 parent
= parent_dir(NULL
, name
);
102 syncops_sync_directory(parent
);
109 rename needs special handling, as we may need to fsync two directories
111 static int syncops_rename(vfs_handle_struct
*handle
,
112 const char *oldname
, const char *newname
)
114 int ret
= SMB_VFS_NEXT_RENAME(handle
, oldname
, newname
);
116 syncops_two_names(oldname
, newname
);
121 /* handle the rest with a macro */
122 #define SYNCOPS_NEXT(op, fname, args) do { \
123 int ret = SMB_VFS_NEXT_ ## op args; \
124 if (ret == 0 && fname) syncops_name(fname); \
128 static int syncops_symlink(vfs_handle_struct
*handle
,
129 const char *oldname
, const char *newname
)
131 SYNCOPS_NEXT(SYMLINK
, newname
, (handle
, oldname
, newname
));
134 static int syncops_link(vfs_handle_struct
*handle
,
135 const char *oldname
, const char *newname
)
137 SYNCOPS_NEXT(LINK
, newname
, (handle
, oldname
, newname
));
140 static int syncops_open(vfs_handle_struct
*handle
,
141 const char *fname
, files_struct
*fsp
, int flags
, mode_t mode
)
143 SYNCOPS_NEXT(OPEN
, (flags
&O_CREAT
?fname
:NULL
), (handle
, fname
, fsp
, flags
, mode
));
146 static int syncops_unlink(vfs_handle_struct
*handle
, const char *fname
)
148 SYNCOPS_NEXT(UNLINK
, fname
, (handle
, fname
));
151 static int syncops_mknod(vfs_handle_struct
*handle
,
152 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
154 SYNCOPS_NEXT(MKNOD
, fname
, (handle
, fname
, mode
, dev
));
157 static int syncops_mkdir(vfs_handle_struct
*handle
, const char *fname
, mode_t mode
)
159 SYNCOPS_NEXT(MKDIR
, fname
, (handle
, fname
, mode
));
162 static int syncops_rmdir(vfs_handle_struct
*handle
, const char *fname
)
164 SYNCOPS_NEXT(RMDIR
, fname
, (handle
, fname
));
167 /* close needs to be handled specially */
168 static int syncops_close(vfs_handle_struct
*handle
, files_struct
*fsp
)
170 if (fsp
->can_write
&& sync_onclose
) {
171 /* ideally we'd only do this if we have written some
172 data, but there is no flag for that in fsp yet. */
175 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
179 /* VFS operations structure */
181 static vfs_op_tuple syncops_ops
[] = {
182 /* directory operations */
183 {SMB_VFS_OP(syncops_mkdir
), SMB_VFS_OP_MKDIR
, SMB_VFS_LAYER_TRANSPARENT
},
184 {SMB_VFS_OP(syncops_rmdir
), SMB_VFS_OP_RMDIR
, SMB_VFS_LAYER_TRANSPARENT
},
186 /* File operations */
187 {SMB_VFS_OP(syncops_open
), SMB_VFS_OP_OPEN
, SMB_VFS_LAYER_TRANSPARENT
},
188 {SMB_VFS_OP(syncops_rename
), SMB_VFS_OP_RENAME
, SMB_VFS_LAYER_TRANSPARENT
},
189 {SMB_VFS_OP(syncops_unlink
), SMB_VFS_OP_UNLINK
, SMB_VFS_LAYER_TRANSPARENT
},
190 {SMB_VFS_OP(syncops_symlink
), SMB_VFS_OP_SYMLINK
, SMB_VFS_LAYER_TRANSPARENT
},
191 {SMB_VFS_OP(syncops_link
), SMB_VFS_OP_LINK
, SMB_VFS_LAYER_TRANSPARENT
},
192 {SMB_VFS_OP(syncops_mknod
), SMB_VFS_OP_MKNOD
, SMB_VFS_LAYER_TRANSPARENT
},
193 {SMB_VFS_OP(syncops_close
), SMB_VFS_OP_CLOSE
, SMB_VFS_LAYER_TRANSPARENT
},
195 {SMB_VFS_OP(NULL
), SMB_VFS_OP_NOOP
, SMB_VFS_LAYER_NOOP
}
198 NTSTATUS
vfs_syncops_init(void)
202 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "syncops", syncops_ops
);
204 if (!NT_STATUS_IS_OK(ret
))
207 sync_onclose
= lp_parm_bool(-1, "syncops", "onclose", true);