libsmb: Convert cli_NetServerEnum() away from cli_api()
[samba4-gss.git] / source3 / modules / vfs_syncops.c
bloba0d9809329683a6c94866e54b5b6944d233f3b9e
1 /*
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.
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "smbd/smbd.h"
25 #include "source3/smbd/dir.h"
29 Some filesystems (even some journaled filesystems) require that a
30 fsync() be performed on many meta data operations to ensure that the
31 operation is guaranteed to remain in the filesystem after a power
32 failure. This is particularly important for some cluster filesystems
33 which are participating in a node failover system with clustered
34 Samba
36 On those filesystems this module provides a way to perform those
37 operations safely.
39 most of the performance loss with this module is in fsync on close().
40 You can disable that with
41 syncops:onclose = no
42 that can be set either globally or per share.
44 On certain filesystems that only require the last data written to be
45 fsync()'ed, you can disable the metadata synchronization of this module with
46 syncops:onmeta = no
47 This option can be set either globally or per share.
49 you can also disable the module completely for a share with
50 syncops:disable = true
54 struct syncops_config_data {
55 bool onclose;
56 bool onmeta;
57 bool disable;
61 given a filename, find the parent directory
63 static char *parent_dir(TALLOC_CTX *mem_ctx, const char *name)
65 const char *p = strrchr(name, '/');
66 if (p == NULL) {
67 return talloc_strdup(mem_ctx, ".");
69 return talloc_strndup(mem_ctx, name, (p+1) - name);
73 fsync a directory by name
75 static void syncops_sync_directory(connection_struct *conn,
76 char *dname)
78 struct smb_Dir *dir_hnd = NULL;
79 struct files_struct *dirfsp = NULL;
80 struct smb_filename smb_dname = { .base_name = dname };
81 NTSTATUS status;
83 status = OpenDir(talloc_tos(),
84 conn,
85 &smb_dname,
86 "*",
88 &dir_hnd);
89 if (!NT_STATUS_IS_OK(status)) {
90 errno = map_errno_from_nt_status(status);
91 return;
94 dirfsp = dir_hnd_fetch_fsp(dir_hnd);
96 smb_vfs_fsync_sync(dirfsp);
98 TALLOC_FREE(dir_hnd);
102 sync two meta data changes for 2 names
104 static void syncops_two_names(connection_struct *conn,
105 const struct smb_filename *name1,
106 const struct smb_filename *name2)
108 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
109 char *parent1, *parent2;
110 parent1 = parent_dir(tmp_ctx, name1->base_name);
111 parent2 = parent_dir(tmp_ctx, name2->base_name);
112 if (!parent1 || !parent2) {
113 talloc_free(tmp_ctx);
114 return;
116 syncops_sync_directory(conn, parent1);
117 if (strcmp(parent1, parent2) != 0) {
118 syncops_sync_directory(conn, parent2);
120 talloc_free(tmp_ctx);
124 sync two meta data changes for 1 names
126 static void syncops_smb_fname(connection_struct *conn,
127 const struct smb_filename *smb_fname)
129 char *parent = NULL;
130 if (smb_fname != NULL) {
131 parent = parent_dir(NULL, smb_fname->base_name);
132 if (parent != NULL) {
133 syncops_sync_directory(conn, parent);
134 talloc_free(parent);
141 renameat needs special handling, as we may need to fsync two directories
143 static int syncops_renameat(vfs_handle_struct *handle,
144 files_struct *srcfsp,
145 const struct smb_filename *smb_fname_src,
146 files_struct *dstfsp,
147 const struct smb_filename *smb_fname_dst)
150 int ret;
151 struct smb_filename *full_fname_src = NULL;
152 struct smb_filename *full_fname_dst = NULL;
153 struct syncops_config_data *config;
155 SMB_VFS_HANDLE_GET_DATA(handle, config,
156 struct syncops_config_data,
157 return -1);
159 ret = SMB_VFS_NEXT_RENAMEAT(handle,
160 srcfsp,
161 smb_fname_src,
162 dstfsp,
163 smb_fname_dst);
164 if (ret == -1) {
165 return ret;
167 if (config->disable) {
168 return ret;
170 if (!config->onmeta) {
171 return ret;
174 full_fname_src = full_path_from_dirfsp_atname(talloc_tos(),
175 srcfsp,
176 smb_fname_src);
177 if (full_fname_src == NULL) {
178 errno = ENOMEM;
179 return ret;
181 full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(),
182 dstfsp,
183 smb_fname_dst);
184 if (full_fname_dst == NULL) {
185 TALLOC_FREE(full_fname_src);
186 errno = ENOMEM;
187 return ret;
189 syncops_two_names(handle->conn,
190 full_fname_src,
191 full_fname_dst);
192 TALLOC_FREE(full_fname_src);
193 TALLOC_FREE(full_fname_dst);
194 return ret;
197 #define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
198 int ret; \
199 struct smb_filename *full_fname = NULL; \
200 struct syncops_config_data *config; \
201 SMB_VFS_HANDLE_GET_DATA(handle, config, \
202 struct syncops_config_data, \
203 return -1); \
204 ret = SMB_VFS_NEXT_ ## op args; \
205 if (ret != 0) { \
206 return ret; \
208 if (config->disable) { \
209 return ret; \
211 if (!config->onmeta) { \
212 return ret; \
214 full_fname = full_path_from_dirfsp_atname(talloc_tos(), \
215 dirfsp, \
216 smb_fname); \
217 if (full_fname == NULL) { \
218 return ret; \
220 syncops_smb_fname(dirfsp->conn, full_fname); \
221 TALLOC_FREE(full_fname); \
222 return ret; \
223 } while (0)
225 static int syncops_symlinkat(vfs_handle_struct *handle,
226 const struct smb_filename *link_contents,
227 struct files_struct *dirfsp,
228 const struct smb_filename *smb_fname)
230 SYNCOPS_NEXT_SMB_FNAME(SYMLINKAT,
231 smb_fname,
232 (handle,
233 link_contents,
234 dirfsp,
235 smb_fname));
238 static int syncops_linkat(vfs_handle_struct *handle,
239 files_struct *srcfsp,
240 const struct smb_filename *old_smb_fname,
241 files_struct *dstfsp,
242 const struct smb_filename *new_smb_fname,
243 int flags)
245 int ret;
246 struct syncops_config_data *config;
247 struct smb_filename *old_full_fname = NULL;
248 struct smb_filename *new_full_fname = NULL;
250 SMB_VFS_HANDLE_GET_DATA(handle, config,
251 struct syncops_config_data,
252 return -1);
254 ret = SMB_VFS_NEXT_LINKAT(handle,
255 srcfsp,
256 old_smb_fname,
257 dstfsp,
258 new_smb_fname,
259 flags);
261 if (ret == -1) {
262 return ret;
264 if (config->disable) {
265 return ret;
267 if (!config->onmeta) {
268 return ret;
271 old_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
272 srcfsp,
273 old_smb_fname);
274 if (old_full_fname == NULL) {
275 return ret;
277 new_full_fname = full_path_from_dirfsp_atname(talloc_tos(),
278 dstfsp,
279 new_smb_fname);
280 if (new_full_fname == NULL) {
281 TALLOC_FREE(old_full_fname);
282 return ret;
284 syncops_two_names(handle->conn,
285 old_full_fname,
286 new_full_fname);
287 TALLOC_FREE(old_full_fname);
288 TALLOC_FREE(new_full_fname);
289 return ret;
292 static int syncops_openat(struct vfs_handle_struct *handle,
293 const struct files_struct *dirfsp,
294 const struct smb_filename *smb_fname,
295 struct files_struct *fsp,
296 const struct vfs_open_how *how)
298 SYNCOPS_NEXT_SMB_FNAME(OPENAT, (how->flags & O_CREAT ? smb_fname : NULL),
299 (handle, dirfsp, smb_fname, fsp, how));
302 static int syncops_unlinkat(vfs_handle_struct *handle,
303 files_struct *dirfsp,
304 const struct smb_filename *smb_fname,
305 int flags)
307 SYNCOPS_NEXT_SMB_FNAME(UNLINKAT,
308 smb_fname,
309 (handle,
310 dirfsp,
311 smb_fname,
312 flags));
315 static int syncops_mknodat(vfs_handle_struct *handle,
316 files_struct *dirfsp,
317 const struct smb_filename *smb_fname,
318 mode_t mode,
319 SMB_DEV_T dev)
321 SYNCOPS_NEXT_SMB_FNAME(MKNODAT,
322 smb_fname,
323 (handle,
324 dirfsp,
325 smb_fname,
326 mode,
327 dev));
330 static int syncops_mkdirat(vfs_handle_struct *handle,
331 struct files_struct *dirfsp,
332 const struct smb_filename *smb_fname,
333 mode_t mode)
335 SYNCOPS_NEXT_SMB_FNAME(MKDIRAT,
336 full_fname,
337 (handle,
338 dirfsp,
339 smb_fname,
340 mode));
343 /* close needs to be handled specially */
344 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
346 struct syncops_config_data *config;
348 SMB_VFS_HANDLE_GET_DATA(handle, config,
349 struct syncops_config_data,
350 return -1);
352 if (fsp->fsp_flags.can_write && config->onclose) {
353 /* ideally we'd only do this if we have written some
354 data, but there is no flag for that in fsp yet. */
355 fsync(fsp_get_io_fd(fsp));
357 return SMB_VFS_NEXT_CLOSE(handle, fsp);
360 static int syncops_connect(struct vfs_handle_struct *handle, const char *service,
361 const char *user)
364 struct syncops_config_data *config;
365 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
366 if (ret < 0) {
367 return ret;
370 config = talloc_zero(handle->conn, struct syncops_config_data);
371 if (!config) {
372 SMB_VFS_NEXT_DISCONNECT(handle);
373 DEBUG(0, ("talloc_zero() failed\n"));
374 return -1;
377 config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
378 "onclose", true);
380 config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
381 "onmeta", true);
383 config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
384 "disable", false);
386 SMB_VFS_HANDLE_SET_DATA(handle, config,
387 NULL, struct syncops_config_data,
388 return -1);
390 return 0;
394 static struct vfs_fn_pointers vfs_syncops_fns = {
395 .connect_fn = syncops_connect,
396 .mkdirat_fn = syncops_mkdirat,
397 .openat_fn = syncops_openat,
398 .renameat_fn = syncops_renameat,
399 .unlinkat_fn = syncops_unlinkat,
400 .symlinkat_fn = syncops_symlinkat,
401 .linkat_fn = syncops_linkat,
402 .mknodat_fn = syncops_mknodat,
403 .close_fn = syncops_close,
406 static_decl_vfs;
407 NTSTATUS vfs_syncops_init(TALLOC_CTX *ctx)
409 NTSTATUS ret;
411 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
412 &vfs_syncops_fns);
414 if (!NT_STATUS_IS_OK(ret))
415 return ret;
417 return ret;