summaryrefslogtreecommitdiffstats
path: root/source3/modules/vfs_glusterfs_fuse.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/modules/vfs_glusterfs_fuse.c')
-rw-r--r--source3/modules/vfs_glusterfs_fuse.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/source3/modules/vfs_glusterfs_fuse.c b/source3/modules/vfs_glusterfs_fuse.c
new file mode 100644
index 0000000..88c740c
--- /dev/null
+++ b/source3/modules/vfs_glusterfs_fuse.c
@@ -0,0 +1,273 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (c) 2019 Guenther Deschner <gd@samba.org>
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/smbd.h"
+#include "system/filesys.h"
+
+#define GLUSTER_NAME_MAX 255
+
+static NTSTATUS vfs_gluster_fuse_get_real_filename_at(
+ struct vfs_handle_struct *handle,
+ struct files_struct *dirfsp,
+ const char *name,
+ TALLOC_CTX *mem_ctx,
+ char **_found_name)
+{
+ int ret, dirfd;
+ char key_buf[GLUSTER_NAME_MAX + 64];
+ char val_buf[GLUSTER_NAME_MAX + 1];
+ char *found_name = NULL;
+
+ if (strlen(name) >= GLUSTER_NAME_MAX) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ snprintf(key_buf, GLUSTER_NAME_MAX + 64,
+ "glusterfs.get_real_filename:%s", name);
+
+ dirfd = openat(fsp_get_pathref_fd(dirfsp), ".", O_RDONLY);
+ if (dirfd == -1) {
+ NTSTATUS status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("Could not open '.' in %s: %s\n",
+ fsp_str_dbg(dirfsp),
+ strerror(errno));
+ return status;
+ }
+
+ ret = fgetxattr(dirfd, key_buf, val_buf, GLUSTER_NAME_MAX + 1);
+ close(dirfd);
+ if (ret == -1) {
+ if (errno == ENOATTR) {
+ errno = ENOENT;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ found_name = talloc_strdup(mem_ctx, val_buf);
+ if (found_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *_found_name = found_name;
+ return NT_STATUS_OK;
+}
+
+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]\n",
+ (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 = {
+
+ .connect_fn = vfs_glusterfs_fuse_connect,
+ .get_real_filename_at_fn = vfs_gluster_fuse_get_real_filename_at,
+ .file_id_create_fn = vfs_glusterfs_fuse_file_id_create,
+};
+
+static_decl_vfs;
+NTSTATUS vfs_glusterfs_fuse_init(TALLOC_CTX *ctx)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+ "glusterfs_fuse", &glusterfs_fuse_fns);
+}