From cbf9cbed0b194ec42b735f6da60c4653945afdc9 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 18 May 2019 11:28:54 +0200 Subject: [PATCH] vfs:glusterfs_fuse: ensure fileids are constant across nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Guenther Deschner Autobuild-User(master): Günther Deschner Autobuild-Date(master): Sat Jul 13 22:54:56 UTC 2019 on sn-devel-184 (cherry picked from commit 5522aa1a4c34ee1a1e81db73cf41594bb10bd989) --- docs-xml/manpages/vfs_glusterfs_fuse.8.xml | 8 + source3/modules/vfs_glusterfs_fuse.c | 193 ++++++++++++++++++++- 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/docs-xml/manpages/vfs_glusterfs_fuse.8.xml b/docs-xml/manpages/vfs_glusterfs_fuse.8.xml index b9f7f42c6f2..f2aa624353e 100644 --- a/docs-xml/manpages/vfs_glusterfs_fuse.8.xml +++ b/docs-xml/manpages/vfs_glusterfs_fuse.8.xml @@ -48,6 +48,14 @@ case of an exisiting filename. + + 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. + + This module can be combined with other modules, but it should be the last module in the vfs objects diff --git a/source3/modules/vfs_glusterfs_fuse.c b/source3/modules/vfs_glusterfs_fuse.c index 51515aa0df4..c621f9abf8e 100644 --- a/source3/modules/vfs_glusterfs_fuse.c +++ b/source3/modules/vfs_glusterfs_fuse.c @@ -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; -- 2.21.0