2 * VFS module to alter the algorithm to calculate
3 * the struct file_id used as key for the share mode
4 * and byte range locking db's.
6 * Copyright (C) 2007, Stefan Metzmacher
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 #include "smbd/smbd.h"
24 #include "system/filesys.h"
26 static int vfs_fileid_debug_level = DBGC_VFS;
29 #define DBGC_CLASS vfs_fileid_debug_level
31 struct fileid_mount_entry {
33 const char *mnt_fsname;
38 struct fileid_handle_data {
39 uint64_t (*device_mapping_fn)(struct fileid_handle_data *data,
40 const SMB_STRUCT_STAT *sbuf);
41 char **fstype_deny_list;
42 char **fstype_allow_list;
43 char **mntdir_deny_list;
44 char **mntdir_allow_list;
45 unsigned num_mount_entries;
46 struct fileid_mount_entry *mount_entries;
49 /* check if a mount entry is allowed based on fstype and mount directory */
50 static bool fileid_mount_entry_allowed(struct fileid_handle_data *data,
54 char **fstype_deny = data->fstype_deny_list;
55 char **fstype_allow = data->fstype_allow_list;
56 char **mntdir_deny = data->mntdir_deny_list;
57 char **mntdir_allow = data->mntdir_allow_list;
59 if (fstype_deny != NULL) {
60 for (i = 0; fstype_deny[i] != NULL; i++) {
61 if (strcmp(m->mnt_type, fstype_deny[i]) == 0) {
66 if (fstype_allow != NULL) {
67 for (i = 0; fstype_allow[i] != NULL; i++) {
68 if (strcmp(m->mnt_type, fstype_allow[i]) == 0) {
72 if (fstype_allow[i] == NULL) {
76 if (mntdir_deny != NULL) {
77 for (i=0; mntdir_deny[i] != NULL; i++) {
78 if (strcmp(m->mnt_dir, mntdir_deny[i]) == 0) {
83 if (mntdir_allow != NULL) {
84 for (i=0; mntdir_allow[i] != NULL; i++) {
85 if (strcmp(m->mnt_dir, mntdir_allow[i]) == 0) {
89 if (mntdir_allow[i] == NULL) {
97 /* load all the mount entries from the mtab */
98 static void fileid_load_mount_entries(struct fileid_handle_data *data)
103 data->num_mount_entries = 0;
104 TALLOC_FREE(data->mount_entries);
106 f = setmntent("/etc/mtab", "r");
109 while ((m = getmntent(f))) {
112 struct fileid_mount_entry *cur;
115 allowed = fileid_mount_entry_allowed(data, m);
117 DBG_DEBUG("skipping mount entry %s\n", m->mnt_dir);
120 if (stat(m->mnt_dir, &st) != 0) continue;
121 if (statfs(m->mnt_dir, &sfs) != 0) continue;
123 if (strncmp(m->mnt_fsname, "/dev/", 5) == 0) {
127 data->mount_entries = talloc_realloc(data,
129 struct fileid_mount_entry,
130 data->num_mount_entries+1);
131 if (data->mount_entries == NULL) {
135 cur = &data->mount_entries[data->num_mount_entries];
136 cur->device = st.st_dev;
137 cur->mnt_fsname = talloc_strdup(data->mount_entries,
139 if (!cur->mnt_fsname) goto nomem;
140 cur->fsid = sfs.f_fsid;
141 cur->devid = (uint64_t)-1;
143 data->num_mount_entries++;
151 data->num_mount_entries = 0;
152 TALLOC_FREE(data->mount_entries);
157 /* find a mount entry given a dev_t */
158 static struct fileid_mount_entry *fileid_find_mount_entry(struct fileid_handle_data *data,
163 if (data->num_mount_entries == 0) {
164 fileid_load_mount_entries(data);
166 for (i=0;i<data->num_mount_entries;i++) {
167 if (data->mount_entries[i].device == dev) {
168 return &data->mount_entries[i];
171 /* 2nd pass after reloading */
172 fileid_load_mount_entries(data);
173 for (i=0;i<data->num_mount_entries;i++) {
174 if (data->mount_entries[i].device == dev) {
175 return &data->mount_entries[i];
182 /* a 64 bit hash, based on the one in tdb */
183 static uint64_t fileid_uint64_hash(const uint8_t *s, size_t len)
185 uint64_t value; /* Used to compute the hash value. */
186 uint32_t i; /* Used to cycle through random values. */
188 /* Set the initial value from the key size. */
189 for (value = 0x238F13AFLL * len, i=0; i < len; i++)
190 value = (value + (((uint64_t)s[i]) << (i*5 % 24)));
192 return (1103515243LL * value + 12345LL);
195 /* a device mapping using a fsname */
196 static uint64_t fileid_device_mapping_fsname(struct fileid_handle_data *data,
197 const SMB_STRUCT_STAT *sbuf)
199 struct fileid_mount_entry *m;
201 m = fileid_find_mount_entry(data, sbuf->st_ex_dev);
202 if (!m) return sbuf->st_ex_dev;
204 if (m->devid == (uint64_t)-1) {
205 m->devid = fileid_uint64_hash((const uint8_t *)m->mnt_fsname,
206 strlen(m->mnt_fsname));
212 /* a device mapping using a hostname */
213 static uint64_t fileid_device_mapping_hostname(struct fileid_handle_data *data,
214 const SMB_STRUCT_STAT *sbuf)
216 char hostname[HOST_NAME_MAX+1];
217 char *devname = NULL;
222 rc = gethostname(hostname, HOST_NAME_MAX+1);
224 DBG_ERR("gethostname failed\n");
228 devname = talloc_asprintf(talloc_tos(), "%s%lu",
229 hostname, sbuf->st_ex_dev);
230 if (devname == NULL) {
231 DBG_ERR("talloc_asprintf failed\n");
234 devname_len = talloc_array_length(devname) - 1;
235 TALLOC_FREE(devname);
237 id = fileid_uint64_hash((uint8_t *)devname, devname_len);
241 /* device mapping functions using a fsid */
242 static uint64_t fileid_device_mapping_fsid(struct fileid_handle_data *data,
243 const SMB_STRUCT_STAT *sbuf)
245 struct fileid_mount_entry *m;
247 m = fileid_find_mount_entry(data, sbuf->st_ex_dev);
248 if (!m) return sbuf->st_ex_dev;
250 if (m->devid == (uint64_t)-1) {
251 if (sizeof(fsid_t) > sizeof(uint64_t)) {
252 m->devid = fileid_uint64_hash((uint8_t *)&m->fsid,
268 static int fileid_connect(struct vfs_handle_struct *handle,
269 const char *service, const char *user)
271 struct fileid_handle_data *data;
272 const char *algorithm;
273 const char **fstype_deny_list = NULL;
274 const char **fstype_allow_list = NULL;
275 const char **mntdir_deny_list = NULL;
276 const char **mntdir_allow_list = NULL;
278 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
284 data = talloc_zero(handle->conn, struct fileid_handle_data);
287 SMB_VFS_NEXT_DISCONNECT(handle);
288 DEBUG(0, ("talloc_zero() failed\n"));
294 * "fileid:mapping" is only here as fallback for old setups
295 * "fileid:algorithm" is the option new setups should use
297 algorithm = lp_parm_const_string(SNUM(handle->conn),
300 algorithm = lp_parm_const_string(SNUM(handle->conn),
301 "fileid", "algorithm",
303 if (strcmp("fsname", algorithm) == 0) {
304 data->device_mapping_fn = fileid_device_mapping_fsname;
305 } else if (strcmp("fsid", algorithm) == 0) {
306 data->device_mapping_fn = fileid_device_mapping_fsid;
307 } else if (strcmp("hostname", algorithm) == 0) {
308 data->device_mapping_fn = fileid_device_mapping_hostname;
310 SMB_VFS_NEXT_DISCONNECT(handle);
311 DEBUG(0,("fileid_connect(): unknown algorithm[%s]\n", algorithm));
315 fstype_deny_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
316 "fstype deny", NULL);
317 if (fstype_deny_list != NULL) {
318 data->fstype_deny_list = str_list_copy(data, fstype_deny_list);
319 if (data->fstype_deny_list == NULL) {
321 DBG_ERR("str_list_copy failed\n");
322 SMB_VFS_NEXT_DISCONNECT(handle);
328 fstype_allow_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
329 "fstype allow", NULL);
330 if (fstype_allow_list != NULL) {
331 data->fstype_allow_list = str_list_copy(data, fstype_allow_list);
332 if (data->fstype_allow_list == NULL) {
334 DBG_ERR("str_list_copy failed\n");
335 SMB_VFS_NEXT_DISCONNECT(handle);
341 mntdir_deny_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
342 "mntdir deny", NULL);
343 if (mntdir_deny_list != NULL) {
344 data->mntdir_deny_list = str_list_copy(data, mntdir_deny_list);
345 if (data->mntdir_deny_list == NULL) {
347 DBG_ERR("str_list_copy failed\n");
348 SMB_VFS_NEXT_DISCONNECT(handle);
354 mntdir_allow_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
355 "mntdir allow", NULL);
356 if (mntdir_allow_list != NULL) {
357 data->mntdir_allow_list = str_list_copy(data, mntdir_allow_list);
358 if (data->mntdir_allow_list == NULL) {
360 DBG_ERR("str_list_copy failed\n");
361 SMB_VFS_NEXT_DISCONNECT(handle);
367 SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
368 struct fileid_handle_data,
371 DEBUG(10, ("fileid_connect(): connect to service[%s] with algorithm[%s]\n",
372 service, algorithm));
377 static void fileid_disconnect(struct vfs_handle_struct *handle)
379 DEBUG(10,("fileid_disconnect() connect to service[%s].\n",
380 lp_servicename(talloc_tos(), SNUM(handle->conn))));
382 SMB_VFS_NEXT_DISCONNECT(handle);
385 static struct file_id fileid_file_id_create(struct vfs_handle_struct *handle,
386 const SMB_STRUCT_STAT *sbuf)
388 struct fileid_handle_data *data;
393 SMB_VFS_HANDLE_GET_DATA(handle, data,
394 struct fileid_handle_data,
397 id.devid = data->device_mapping_fn(data, sbuf);
398 id.inode = sbuf->st_ex_ino;
400 DBG_DEBUG("Returning dev [%jx] inode [%jx]\n",
401 (uintmax_t)id.devid, (uintmax_t)id.inode);
406 static struct vfs_fn_pointers vfs_fileid_fns = {
407 .connect_fn = fileid_connect,
408 .disconnect_fn = fileid_disconnect,
409 .file_id_create_fn = fileid_file_id_create
413 NTSTATUS vfs_fileid_init(TALLOC_CTX *ctx)
417 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fileid",
419 if (!NT_STATUS_IS_OK(ret)) {
423 vfs_fileid_debug_level = debug_add_class("fileid");
424 if (vfs_fileid_debug_level == -1) {
425 vfs_fileid_debug_level = DBGC_VFS;
426 DEBUG(0, ("vfs_fileid: Couldn't register custom debugging class!\n"));
428 DEBUG(10, ("vfs_fileid: Debug class number of 'fileid': %d\n", vfs_fileid_debug_level));