mirror of
https://github.com/samba-team/samba.git
synced 2025-02-21 01:59:07 +03:00
This patch introduces struct stat_ex { dev_t st_ex_dev; ino_t st_ex_ino; mode_t st_ex_mode; nlink_t st_ex_nlink; uid_t st_ex_uid; gid_t st_ex_gid; dev_t st_ex_rdev; off_t st_ex_size; struct timespec st_ex_atime; struct timespec st_ex_mtime; struct timespec st_ex_ctime; struct timespec st_ex_btime; /* birthtime */ blksize_t st_ex_blksize; blkcnt_t st_ex_blocks; }; typedef struct stat_ex SMB_STRUCT_STAT; It is really large because due to the friendly libc headers playing macro tricks with fields like st_ino, so I renamed them to st_ex_xxx. Why this change? To support birthtime, we already have quite a few #ifdef's at places where it does not really belong. With a stat struct that we control, we can consolidate the nanosecond timestamps and the birthtime deep in the VFS stat calls. At this moment it is triggered by a request to support the birthtime field for GPFS. GPFS does not extend the system level struct stat, but instead has a separate call that gets us the additional information beyond posix. Without being able to do that within the VFS stat calls, that support would have to be scattered around the main smbd code. It will very likely break all the onefs modules, but I think the changes will be reasonably easy to do.
295 lines
7.0 KiB
C
295 lines
7.0 KiB
C
/*
|
|
* VFS module to alter the algorithm to calculate
|
|
* the struct file_id used as key for the share mode
|
|
* and byte range locking db's.
|
|
*
|
|
* Copyright (C) 2007, Stefan Metzmacher
|
|
*
|
|
* 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"
|
|
|
|
static int vfs_fileid_debug_level = DBGC_VFS;
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS vfs_fileid_debug_level
|
|
|
|
struct fileid_mount_entry {
|
|
SMB_DEV_T device;
|
|
const char *mnt_fsname;
|
|
fsid_t fsid;
|
|
uint64_t devid;
|
|
};
|
|
|
|
struct fileid_handle_data {
|
|
uint64_t (*device_mapping_fn)(struct fileid_handle_data *data,
|
|
SMB_DEV_T dev);
|
|
unsigned num_mount_entries;
|
|
struct fileid_mount_entry *mount_entries;
|
|
};
|
|
|
|
/* load all the mount entries from the mtab */
|
|
static void fileid_load_mount_entries(struct fileid_handle_data *data)
|
|
{
|
|
FILE *f;
|
|
struct mntent *m;
|
|
|
|
data->num_mount_entries = 0;
|
|
TALLOC_FREE(data->mount_entries);
|
|
|
|
f = setmntent("/etc/mtab", "r");
|
|
if (!f) return;
|
|
|
|
while ((m = getmntent(f))) {
|
|
struct stat st;
|
|
struct statfs sfs;
|
|
struct fileid_mount_entry *cur;
|
|
|
|
if (stat(m->mnt_dir, &st) != 0) continue;
|
|
if (statfs(m->mnt_dir, &sfs) != 0) continue;
|
|
|
|
if (strncmp(m->mnt_fsname, "/dev/", 5) == 0) {
|
|
m->mnt_fsname += 5;
|
|
}
|
|
|
|
data->mount_entries = TALLOC_REALLOC_ARRAY(data,
|
|
data->mount_entries,
|
|
struct fileid_mount_entry,
|
|
data->num_mount_entries+1);
|
|
if (data->mount_entries == NULL) {
|
|
goto nomem;
|
|
}
|
|
|
|
cur = &data->mount_entries[data->num_mount_entries];
|
|
cur->device = st.st_dev;
|
|
cur->mnt_fsname = talloc_strdup(data->mount_entries,
|
|
m->mnt_fsname);
|
|
if (!cur->mnt_fsname) goto nomem;
|
|
cur->fsid = sfs.f_fsid;
|
|
cur->devid = (uint64_t)-1;
|
|
|
|
data->num_mount_entries++;
|
|
}
|
|
endmntent(f);
|
|
return;
|
|
|
|
nomem:
|
|
if (f) endmntent(f);
|
|
|
|
data->num_mount_entries = 0;
|
|
TALLOC_FREE(data->mount_entries);
|
|
|
|
return;
|
|
}
|
|
|
|
/* find a mount entry given a dev_t */
|
|
static struct fileid_mount_entry *fileid_find_mount_entry(struct fileid_handle_data *data,
|
|
SMB_DEV_T dev)
|
|
{
|
|
int i;
|
|
|
|
if (data->num_mount_entries == 0) {
|
|
fileid_load_mount_entries(data);
|
|
}
|
|
for (i=0;i<data->num_mount_entries;i++) {
|
|
if (data->mount_entries[i].device == dev) {
|
|
return &data->mount_entries[i];
|
|
}
|
|
}
|
|
/* 2nd pass after reloading */
|
|
fileid_load_mount_entries(data);
|
|
for (i=0;i<data->num_mount_entries;i++) {
|
|
if (data->mount_entries[i].device == dev) {
|
|
return &data->mount_entries[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* a 64 bit hash, based on the one in tdb */
|
|
static uint64_t fileid_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 + (s[i] << (i*5 % 24)));
|
|
|
|
return (1103515243LL * value + 12345LL);
|
|
}
|
|
|
|
/* a device mapping using a fsname */
|
|
static uint64_t fileid_device_mapping_fsname(struct fileid_handle_data *data,
|
|
SMB_DEV_T dev)
|
|
{
|
|
struct fileid_mount_entry *m;
|
|
|
|
m = fileid_find_mount_entry(data, dev);
|
|
if (!m) return dev;
|
|
|
|
if (m->devid == (uint64_t)-1) {
|
|
m->devid = fileid_uint64_hash((uint8_t *)m->mnt_fsname,
|
|
strlen(m->mnt_fsname));
|
|
}
|
|
|
|
return m->devid;
|
|
}
|
|
|
|
/* device mapping functions using a fsid */
|
|
static uint64_t fileid_device_mapping_fsid(struct fileid_handle_data *data,
|
|
SMB_DEV_T dev)
|
|
{
|
|
struct fileid_mount_entry *m;
|
|
|
|
m = fileid_find_mount_entry(data, dev);
|
|
if (!m) return dev;
|
|
|
|
if (m->devid == (uint64_t)-1) {
|
|
if (sizeof(fsid_t) > sizeof(uint64_t)) {
|
|
m->devid = fileid_uint64_hash((uint8_t *)&m->fsid,
|
|
sizeof(m->fsid));
|
|
} else {
|
|
union {
|
|
uint64_t ret;
|
|
fsid_t fsid;
|
|
} u;
|
|
ZERO_STRUCT(u);
|
|
u.fsid = m->fsid;
|
|
m->devid = u.ret;
|
|
}
|
|
}
|
|
|
|
return m->devid;
|
|
}
|
|
|
|
static int fileid_connect(struct vfs_handle_struct *handle,
|
|
const char *service, const char *user)
|
|
{
|
|
struct fileid_handle_data *data;
|
|
const char *algorithm;
|
|
|
|
data = talloc_zero(handle->conn, struct fileid_handle_data);
|
|
if (!data) {
|
|
DEBUG(0, ("talloc_zero() failed\n"));
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* "fileid:mapping" is only here as fallback for old setups
|
|
* "fileid:algorithm" is the option new setups should use
|
|
*/
|
|
algorithm = lp_parm_const_string(SNUM(handle->conn),
|
|
"fileid", "mapping",
|
|
"fsname");
|
|
algorithm = lp_parm_const_string(SNUM(handle->conn),
|
|
"fileid", "algorithm",
|
|
algorithm);
|
|
if (strcmp("fsname", algorithm) == 0) {
|
|
data->device_mapping_fn = fileid_device_mapping_fsname;
|
|
} else if (strcmp("fsid", algorithm) == 0) {
|
|
data->device_mapping_fn = fileid_device_mapping_fsid;
|
|
} else {
|
|
DEBUG(0,("fileid_connect(): unknown algorithm[%s]\n", algorithm));
|
|
return -1;
|
|
}
|
|
|
|
SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
|
|
struct fileid_handle_data,
|
|
return -1);
|
|
|
|
DEBUG(10, ("fileid_connect(): connect to service[%s] with algorithm[%s]\n",
|
|
service, algorithm));
|
|
|
|
return SMB_VFS_NEXT_CONNECT(handle, service, user);
|
|
}
|
|
|
|
static void fileid_disconnect(struct vfs_handle_struct *handle)
|
|
{
|
|
DEBUG(10,("fileid_disconnect() connect to service[%s].\n",
|
|
lp_servicename(SNUM(handle->conn))));
|
|
|
|
SMB_VFS_NEXT_DISCONNECT(handle);
|
|
}
|
|
|
|
static struct file_id fileid_file_id_create(struct vfs_handle_struct *handle,
|
|
const SMB_STRUCT_STAT *sbuf)
|
|
{
|
|
struct fileid_handle_data *data;
|
|
struct file_id id;
|
|
|
|
ZERO_STRUCT(id);
|
|
|
|
SMB_VFS_HANDLE_GET_DATA(handle, data,
|
|
struct fileid_handle_data,
|
|
return id);
|
|
|
|
id.devid = data->device_mapping_fn(data, sbuf->st_ex_dev);
|
|
id.inode = sbuf->st_ex_ino;
|
|
|
|
return id;
|
|
}
|
|
|
|
static vfs_op_tuple fileid_ops[] = {
|
|
|
|
/* Disk operations */
|
|
{
|
|
SMB_VFS_OP(fileid_connect),
|
|
SMB_VFS_OP_CONNECT,
|
|
SMB_VFS_LAYER_TRANSPARENT
|
|
},
|
|
{
|
|
SMB_VFS_OP(fileid_disconnect),
|
|
SMB_VFS_OP_DISCONNECT,
|
|
SMB_VFS_LAYER_TRANSPARENT
|
|
},
|
|
|
|
/* File operations */
|
|
{
|
|
SMB_VFS_OP(fileid_file_id_create),
|
|
SMB_VFS_OP_FILE_ID_CREATE,
|
|
SMB_VFS_LAYER_OPAQUE
|
|
},
|
|
|
|
/* End marker */
|
|
{
|
|
SMB_VFS_OP(NULL),
|
|
SMB_VFS_OP_NOOP,
|
|
SMB_VFS_LAYER_NOOP
|
|
}
|
|
};
|
|
|
|
NTSTATUS vfs_fileid_init(void);
|
|
NTSTATUS vfs_fileid_init(void)
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fileid", fileid_ops);
|
|
if (!NT_STATUS_IS_OK(ret)) {
|
|
return ret;
|
|
}
|
|
|
|
vfs_fileid_debug_level = debug_add_class("fileid");
|
|
if (vfs_fileid_debug_level == -1) {
|
|
vfs_fileid_debug_level = DBGC_VFS;
|
|
DEBUG(0, ("vfs_fileid: Couldn't register custom debugging class!\n"));
|
|
} else {
|
|
DEBUG(10, ("vfs_fileid: Debug class number of 'fileid': %d\n", vfs_fileid_debug_level));
|
|
}
|
|
|
|
return ret;
|
|
}
|