s3:vfs:syncops add option to disable metasync per share
[samba.git] / source3 / modules / vfs_syncops.c
index d3f786840053eee7b8ecc30103b13afa87f9df8c..76072abc3308e92398b5a41b5cdd1ddb115498d1 100644 (file)
@@ -2,6 +2,7 @@
  * ensure meta data operations are performed synchronously
  *
  * Copyright (C) Andrew Tridgell     2007
+ * Copyright (C) Christian Ambach, 2010-2011
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
   On those filesystems this module provides a way to perform those
   operations safely.  
- */
 
-/*
   most of the performance loss with this module is in fsync on close(). 
-  You can disable that with syncops:onclose = no
+  You can disable that with
+     syncops:onclose = no
+  that can be set either globally or per share.
+
+  On certain filesystems that only require the last data written to be
+  fsync()'ed, you can disable the metadata synchronization of this module with
+     syncops:onmeta = no
+  This option can be set either globally or per share.
+
+  you can also disable the module completely for a share with
+     syncops:disable = true
+
  */
-static bool sync_onclose;
+
+struct syncops_config_data {
+       bool onclose;
+       bool onmeta;
+       bool disable;
+};
 
 /*
   given a filename, find the parent directory
@@ -104,24 +119,67 @@ static void syncops_name(const char *name)
        }
 }
 
+/*
+  sync two meta data changes for 1 names
+ */
+static void syncops_smb_fname(const struct smb_filename *smb_fname)
+{
+       char *parent;
+       parent = parent_dir(NULL, smb_fname->base_name);
+       if (parent) {
+               syncops_sync_directory(parent);
+               talloc_free(parent);
+       }
+}
+
 
 /*
   rename needs special handling, as we may need to fsync two directories
  */
 static int syncops_rename(vfs_handle_struct *handle,
-                         const char *oldname, const char *newname)
+                         const struct smb_filename *smb_fname_src,
+                         const struct smb_filename *smb_fname_dst)
 {
-       int ret = SMB_VFS_NEXT_RENAME(handle, oldname, newname);
-       if (ret == 0) {
-               syncops_two_names(oldname, newname);
+
+       int ret;
+       struct syncops_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct syncops_config_data,
+                               return -1);
+
+       ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
+       if (ret == 0 && config->onmeta && !config->disable) {
+               syncops_two_names(smb_fname_src->base_name,
+                                 smb_fname_dst->base_name);
        }
        return ret;
 }
 
 /* handle the rest with a macro */
 #define SYNCOPS_NEXT(op, fname, args) do {   \
-       int ret = SMB_VFS_NEXT_ ## op args; \
-       if (ret == 0 && fname) syncops_name(fname); \
+       int ret; \
+       struct syncops_config_data *config; \
+       SMB_VFS_HANDLE_GET_DATA(handle, config, \
+                               struct syncops_config_data, \
+                               return -1); \
+       ret = SMB_VFS_NEXT_ ## op args; \
+       if (ret == 0 \
+               && config->onmeta && !config->disable  \
+               && fname) syncops_name(fname); \
+       return ret; \
+} while (0)
+
+#define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do {   \
+       int ret; \
+       struct syncops_config_data *config; \
+       SMB_VFS_HANDLE_GET_DATA(handle, config, \
+                               struct syncops_config_data, \
+                               return -1); \
+       ret = SMB_VFS_NEXT_ ## op args; \
+       if (ret == 0 \
+       && config->onmeta && !config->disable \
+       && fname) syncops_smb_fname(fname); \
        return ret; \
 } while (0)
 
@@ -138,14 +196,17 @@ static int syncops_link(vfs_handle_struct *handle,
 }
 
 static int syncops_open(vfs_handle_struct *handle,
-                       const char *fname, files_struct *fsp, int flags, mode_t mode)
+                       struct smb_filename *smb_fname, files_struct *fsp,
+                       int flags, mode_t mode)
 {
-       SYNCOPS_NEXT(OPEN, (flags&O_CREAT?fname:NULL), (handle, fname, fsp, flags, mode));
+       SYNCOPS_NEXT_SMB_FNAME(OPEN, (flags&O_CREAT?smb_fname:NULL),
+                              (handle, smb_fname, fsp, flags, mode));
 }
 
-static int syncops_unlink(vfs_handle_struct *handle, const char *fname)
+static int syncops_unlink(vfs_handle_struct *handle,
+                         const struct smb_filename *smb_fname)
 {
-        SYNCOPS_NEXT(UNLINK, fname, (handle, fname));
+        SYNCOPS_NEXT_SMB_FNAME(UNLINK, smb_fname, (handle, smb_fname));
 }
 
 static int syncops_mknod(vfs_handle_struct *handle,
@@ -167,7 +228,13 @@ static int syncops_rmdir(vfs_handle_struct *handle,  const char *fname)
 /* close needs to be handled specially */
 static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
 {
-       if (fsp->can_write && sync_onclose) {
+       struct syncops_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct syncops_config_data,
+                               return -1);
+
+       if (fsp->can_write && config->onclose) {
                /* ideally we'd only do this if we have written some
                 data, but there is no flag for that in fsp yet. */
                fsync(fsp->fh->fd);
@@ -175,36 +242,62 @@ static int syncops_close(vfs_handle_struct *handle, files_struct *fsp)
        return SMB_VFS_NEXT_CLOSE(handle, fsp);
 }
 
+int syncops_connect(struct vfs_handle_struct *handle, const char *service,
+                       const char *user)
+{
+
+       struct syncops_config_data *config;
+       int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       if (ret < 0) {
+               return ret;
+       }
+
+       config = talloc_zero(handle->conn, struct syncops_config_data);
+       if (!config) {
+               SMB_VFS_NEXT_DISCONNECT(handle);
+               DEBUG(0, ("talloc_zero() failed\n"));
+               return -1;
+       }
 
-/* VFS operations structure */
+       config->onclose = lp_parm_bool(SNUM(handle->conn), "syncops",
+                                       "onclose", true);
 
-static vfs_op_tuple syncops_ops[] = {
-       /* directory operations */
-        {SMB_VFS_OP(syncops_mkdir),       SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_TRANSPARENT},
-        {SMB_VFS_OP(syncops_rmdir),       SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
+       config->onmeta = lp_parm_bool(SNUM(handle->conn), "syncops",
+                                       "onmeta", true);
 
-        /* File operations */
-        {SMB_VFS_OP(syncops_open),       SMB_VFS_OP_OPEN,     SMB_VFS_LAYER_TRANSPARENT},
-        {SMB_VFS_OP(syncops_rename),     SMB_VFS_OP_RENAME,   SMB_VFS_LAYER_TRANSPARENT},
-        {SMB_VFS_OP(syncops_unlink),     SMB_VFS_OP_UNLINK,   SMB_VFS_LAYER_TRANSPARENT},
-        {SMB_VFS_OP(syncops_symlink),    SMB_VFS_OP_SYMLINK,  SMB_VFS_LAYER_TRANSPARENT},
-        {SMB_VFS_OP(syncops_link),       SMB_VFS_OP_LINK,     SMB_VFS_LAYER_TRANSPARENT},
-        {SMB_VFS_OP(syncops_mknod),      SMB_VFS_OP_MKNOD,    SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(syncops_close),      SMB_VFS_OP_CLOSE,    SMB_VFS_LAYER_TRANSPARENT},
+       config->disable = lp_parm_bool(SNUM(handle->conn), "syncops",
+                                       "disable", false);
+
+       SMB_VFS_HANDLE_SET_DATA(handle, config,
+                               NULL, struct syncops_config_data,
+                               return -1);
+
+       return 0;
+
+}
 
-       {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+static struct vfs_fn_pointers vfs_syncops_fns = {
+       .connect_fn = syncops_connect,
+        .mkdir = syncops_mkdir,
+        .rmdir = syncops_rmdir,
+        .open = syncops_open,
+        .rename = syncops_rename,
+        .unlink = syncops_unlink,
+        .symlink = syncops_symlink,
+        .link = syncops_link,
+        .mknod = syncops_mknod,
+       .close_fn = syncops_close,
 };
 
 NTSTATUS vfs_syncops_init(void)
 {
        NTSTATUS ret;
 
-       ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops", syncops_ops);
+       ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "syncops",
+                              &vfs_syncops_fns);
 
        if (!NT_STATUS_IS_OK(ret))
                return ret;
 
-       sync_onclose = lp_parm_bool(-1, "syncops", "onclose", true);
-       
        return ret;
 }