vfs:glusterfs_fuse: ensure fileids are constant across nodes
authorMichael Adam <obnox@samba.org>
Sat, 18 May 2019 09:28:54 +0000 (11:28 +0200)
committerGünther Deschner <gd@samba.org>
Sat, 13 Jul 2019 21:24:09 +0000 (23:24 +0200)
Instead of adding a new gluster-specific mode to the fileid module,
this patches provides a fileid algorithm as part of the glusterfs_fuse
vfs module. This can not be configured further, simply adding the
glusterfs_fuse vfs module to the vfs objects configuration will enable
the new fileid mode.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13972

Signed-off-by: Michael Adam <obnox@samba.org>
Signed-off-by: Guenther Deschner <gd@samba.org>
docs-xml/manpages/vfs_glusterfs_fuse.8.xml
source3/modules/vfs_glusterfs_fuse.c

index b9f7f42c6f2b39240974dd05f37526564f880b32..f2aa624353e1784b3f22230f1814c5946e360626 100644 (file)
                case of an exisiting filename.
        </para>
 
+       <para>
+               Furthermore, this module implements a substitute file-id
+               mechanism. The default file-id mechanism is not working
+               correctly for gluster fuse mount re-exports, so in order to
+               avoid data loss, users exporting gluster fuse mounts with
+               Samba should enable this module.
+       </para>
+
        <para>
                This module can be combined with other modules, but it
                should be the last module in the <command>vfs objects</command>
index 51515aa0df4cbfe55f17b7240c5164c8ac2c580e..c621f9abf8e460878d4d0b935605b5c9951218e3 100644 (file)
@@ -59,10 +59,201 @@ static int vfs_gluster_fuse_get_real_filename(struct vfs_handle_struct *handle,
        return 0;
 }
 
+struct device_mapping_entry {
+       SMB_DEV_T device;       /* the local device, for reference */
+       uint64_t mapped_device; /* the mapped device */
+};
+
+struct vfs_glusterfs_fuse_handle_data {
+       unsigned num_mapped_devices;
+       struct device_mapping_entry *mapped_devices;
+};
+
+/* a 64 bit hash, based on the one in tdb, copied from vfs_fileied */
+static uint64_t vfs_glusterfs_fuse_uint64_hash(const uint8_t *s, size_t len)
+{
+       uint64_t value; /* Used to compute the hash value.  */
+       uint32_t i;     /* Used to cycle through random values. */
+
+       /* Set the initial value from the key size. */
+       for (value = 0x238F13AFLL * len, i=0; i < len; i++)
+               value = (value + (((uint64_t)s[i]) << (i*5 % 24)));
+
+       return (1103515243LL * value + 12345LL);
+}
+
+static void vfs_glusterfs_fuse_load_devices(
+               struct vfs_glusterfs_fuse_handle_data *data)
+{
+       FILE *f;
+       struct mntent *m;
+
+       data->num_mapped_devices = 0;
+       TALLOC_FREE(data->mapped_devices);
+
+       f = setmntent("/etc/mtab", "r");
+       if (!f) {
+               return;
+       }
+
+       while ((m = getmntent(f))) {
+               struct stat st;
+               char *p;
+               uint64_t mapped_device;
+
+               if (stat(m->mnt_dir, &st) != 0) {
+                       /* TODO: log? */
+                       continue;
+               }
+
+               /* strip the host part off of the fsname */
+               p = strrchr(m->mnt_fsname, ':');
+               if (p == NULL) {
+                       p = m->mnt_fsname;
+               } else {
+                       /* TODO: consider the case of '' ? */
+                       p++;
+               }
+
+               mapped_device = vfs_glusterfs_fuse_uint64_hash(
+                                               (const uint8_t *)p,
+                                               strlen(p));
+
+               data->mapped_devices = talloc_realloc(data,
+                                               data->mapped_devices,
+                                               struct device_mapping_entry,
+                                               data->num_mapped_devices + 1);
+               if (data->mapped_devices == NULL) {
+                       goto nomem;
+               }
+
+               data->mapped_devices[data->num_mapped_devices].device =
+                                                               st.st_dev;
+               data->mapped_devices[data->num_mapped_devices].mapped_device =
+                                                               mapped_device;
+
+               data->num_mapped_devices++;
+       }
+
+       endmntent(f);
+       return;
+
+nomem:
+       data->num_mapped_devices = 0;
+       TALLOC_FREE(data->mapped_devices);
+
+       endmntent(f);
+       return;
+}
+
+static int vfs_glusterfs_fuse_map_device_cached(
+                               struct vfs_glusterfs_fuse_handle_data *data,
+                               SMB_DEV_T device,
+                               uint64_t *mapped_device)
+{
+       unsigned i;
+
+       for (i = 0; i < data->num_mapped_devices; i++) {
+               if (data->mapped_devices[i].device == device) {
+                       *mapped_device = data->mapped_devices[i].mapped_device;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int vfs_glusterfs_fuse_map_device(
+                               struct vfs_glusterfs_fuse_handle_data *data,
+                               SMB_DEV_T device,
+                               uint64_t *mapped_device)
+{
+       int ret;
+
+       ret = vfs_glusterfs_fuse_map_device_cached(data, device, mapped_device);
+       if (ret == 0) {
+               return 0;
+       }
+
+       vfs_glusterfs_fuse_load_devices(data);
+
+       ret = vfs_glusterfs_fuse_map_device_cached(data, device, mapped_device);
+
+       return ret;
+}
+
+static struct file_id vfs_glusterfs_fuse_file_id_create(
+                       struct vfs_handle_struct *handle,
+                       const SMB_STRUCT_STAT *sbuf)
+{
+       struct vfs_glusterfs_fuse_handle_data *data;
+       struct file_id id;
+       uint64_t mapped_device;
+       int ret;
+
+       ZERO_STRUCT(id);
+
+       id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, sbuf);
+
+       SMB_VFS_HANDLE_GET_DATA(handle, data,
+                               struct vfs_glusterfs_fuse_handle_data,
+                               return id);
+
+       ret = vfs_glusterfs_fuse_map_device(data, sbuf->st_ex_dev,
+                                           &mapped_device);
+       if (ret == 0) {
+               id.devid = mapped_device;
+       } else {
+               DBG_WARNING("Failed to map device [%jx], falling back to "
+                           "standard file_id [%jx]",
+                           (uintmax_t)sbuf->st_ex_dev,
+                           (uintmax_t)id.devid);
+       }
+
+       DBG_DEBUG("Returning dev [%jx] inode [%jx]\n",
+                 (uintmax_t)id.devid, (uintmax_t)id.inode);
+
+       return id;
+}
+
+static int vfs_glusterfs_fuse_connect(struct vfs_handle_struct *handle,
+                                     const char *service, const char *user)
+{
+       struct vfs_glusterfs_fuse_handle_data *data;
+       int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+
+       if (ret < 0) {
+               return ret;
+       }
+
+       data = talloc_zero(handle->conn, struct vfs_glusterfs_fuse_handle_data);
+       if (data == NULL) {
+               DBG_ERR("talloc_zero() failed.\n");
+               SMB_VFS_NEXT_DISCONNECT(handle);
+               return -1;
+       }
+
+       /*
+        * Fill the cache in the tree connect, so that the first file/dir access
+        * has chances of being fast...
+        */
+       vfs_glusterfs_fuse_load_devices(data);
+
+       SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
+                               struct vfs_glusterfs_fuse_handle_data,
+                               return -1);
+
+       DBG_DEBUG("vfs_glusterfs_fuse_connect(): connected to service[%s]\n",
+                 service);
+
+       return 0;
+}
+
 struct vfs_fn_pointers glusterfs_fuse_fns = {
 
-       /* File Operations */
+       .connect_fn = vfs_glusterfs_fuse_connect,
        .get_real_filename_fn = vfs_gluster_fuse_get_real_filename,
+       .file_id_create_fn = vfs_glusterfs_fuse_file_id_create,
 };
 
 static_decl_vfs;