mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
cef6692ba0
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
274 lines
6.5 KiB
C
274 lines
6.5 KiB
C
/*
|
|
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);
|
|
}
|