| 1 | /*
 | 
|---|
| 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.
 | 
|---|
| 5 |  *
 | 
|---|
| 6 |  * Copyright (C) 2007, Stefan Metzmacher
 | 
|---|
| 7 |  *
 | 
|---|
| 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.
 | 
|---|
| 12 |  *
 | 
|---|
| 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.
 | 
|---|
| 17 |  *
 | 
|---|
| 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/>.
 | 
|---|
| 20 |  */
 | 
|---|
| 21 | 
 | 
|---|
| 22 | #include "includes.h"
 | 
|---|
| 23 | 
 | 
|---|
| 24 | static int vfs_fileid_debug_level = DBGC_VFS;
 | 
|---|
| 25 | 
 | 
|---|
| 26 | #undef DBGC_CLASS
 | 
|---|
| 27 | #define DBGC_CLASS vfs_fileid_debug_level
 | 
|---|
| 28 | 
 | 
|---|
| 29 | struct fileid_mount_entry {
 | 
|---|
| 30 |         SMB_DEV_T device;
 | 
|---|
| 31 |         const char *mnt_fsname;
 | 
|---|
| 32 |         fsid_t fsid;
 | 
|---|
| 33 |         uint64_t devid;
 | 
|---|
| 34 | };
 | 
|---|
| 35 | 
 | 
|---|
| 36 | struct fileid_handle_data {
 | 
|---|
| 37 |         uint64_t (*device_mapping_fn)(struct fileid_handle_data *data,
 | 
|---|
| 38 |                                       SMB_DEV_T dev);
 | 
|---|
| 39 |         unsigned num_mount_entries;
 | 
|---|
| 40 |         struct fileid_mount_entry *mount_entries;
 | 
|---|
| 41 | };
 | 
|---|
| 42 | 
 | 
|---|
| 43 | /* load all the mount entries from the mtab */
 | 
|---|
| 44 | static void fileid_load_mount_entries(struct fileid_handle_data *data)
 | 
|---|
| 45 | {
 | 
|---|
| 46 |         FILE *f;
 | 
|---|
| 47 |         struct mntent *m;
 | 
|---|
| 48 | 
 | 
|---|
| 49 |         data->num_mount_entries = 0;
 | 
|---|
| 50 |         TALLOC_FREE(data->mount_entries);
 | 
|---|
| 51 | 
 | 
|---|
| 52 |         f = setmntent("/etc/mtab", "r");
 | 
|---|
| 53 |         if (!f) return;
 | 
|---|
| 54 | 
 | 
|---|
| 55 |         while ((m = getmntent(f))) {
 | 
|---|
| 56 |                 struct stat st;
 | 
|---|
| 57 |                 struct statfs sfs;
 | 
|---|
| 58 |                 struct fileid_mount_entry *cur;
 | 
|---|
| 59 | 
 | 
|---|
| 60 |                 if (stat(m->mnt_dir, &st) != 0) continue;
 | 
|---|
| 61 |                 if (statfs(m->mnt_dir, &sfs) != 0) continue;
 | 
|---|
| 62 | 
 | 
|---|
| 63 |                 if (strncmp(m->mnt_fsname, "/dev/", 5) == 0) {
 | 
|---|
| 64 |                         m->mnt_fsname += 5;
 | 
|---|
| 65 |                 }
 | 
|---|
| 66 | 
 | 
|---|
| 67 |                 data->mount_entries = TALLOC_REALLOC_ARRAY(data,
 | 
|---|
| 68 |                                                            data->mount_entries,
 | 
|---|
| 69 |                                                            struct fileid_mount_entry,
 | 
|---|
| 70 |                                                            data->num_mount_entries+1);
 | 
|---|
| 71 |                 if (data->mount_entries == NULL) {
 | 
|---|
| 72 |                         goto nomem;
 | 
|---|
| 73 |                 }
 | 
|---|
| 74 | 
 | 
|---|
| 75 |                 cur = &data->mount_entries[data->num_mount_entries];
 | 
|---|
| 76 |                 cur->device     = st.st_dev;
 | 
|---|
| 77 |                 cur->mnt_fsname = talloc_strdup(data->mount_entries,
 | 
|---|
| 78 |                                                 m->mnt_fsname);
 | 
|---|
| 79 |                 if (!cur->mnt_fsname) goto nomem;
 | 
|---|
| 80 |                 cur->fsid       = sfs.f_fsid;
 | 
|---|
| 81 |                 cur->devid      = (uint64_t)-1;
 | 
|---|
| 82 | 
 | 
|---|
| 83 |                 data->num_mount_entries++;
 | 
|---|
| 84 |         }
 | 
|---|
| 85 |         endmntent(f);
 | 
|---|
| 86 |         return;
 | 
|---|
| 87 |         
 | 
|---|
| 88 | nomem:
 | 
|---|
| 89 |         if (f) endmntent(f);
 | 
|---|
| 90 | 
 | 
|---|
| 91 |         data->num_mount_entries = 0;
 | 
|---|
| 92 |         TALLOC_FREE(data->mount_entries);
 | 
|---|
| 93 | 
 | 
|---|
| 94 |         return;
 | 
|---|
| 95 | }
 | 
|---|
| 96 | 
 | 
|---|
| 97 | /* find a mount entry given a dev_t */
 | 
|---|
| 98 | static struct fileid_mount_entry *fileid_find_mount_entry(struct fileid_handle_data *data,
 | 
|---|
| 99 |                                                           SMB_DEV_T dev)
 | 
|---|
| 100 | {
 | 
|---|
| 101 |         int i;
 | 
|---|
| 102 | 
 | 
|---|
| 103 |         if (data->num_mount_entries == 0) {
 | 
|---|
| 104 |                 fileid_load_mount_entries(data);
 | 
|---|
| 105 |         }
 | 
|---|
| 106 |         for (i=0;i<data->num_mount_entries;i++) {
 | 
|---|
| 107 |                 if (data->mount_entries[i].device == dev) {
 | 
|---|
| 108 |                         return &data->mount_entries[i];
 | 
|---|
| 109 |                 }
 | 
|---|
| 110 |         }
 | 
|---|
| 111 |         /* 2nd pass after reloading */
 | 
|---|
| 112 |         fileid_load_mount_entries(data);
 | 
|---|
| 113 |         for (i=0;i<data->num_mount_entries;i++) {
 | 
|---|
| 114 |                 if (data->mount_entries[i].device == dev) {
 | 
|---|
| 115 |                         return &data->mount_entries[i];
 | 
|---|
| 116 |                 }
 | 
|---|
| 117 |         }       
 | 
|---|
| 118 |         return NULL;
 | 
|---|
| 119 | }
 | 
|---|
| 120 | 
 | 
|---|
| 121 | 
 | 
|---|
| 122 | /* a 64 bit hash, based on the one in tdb */
 | 
|---|
| 123 | static uint64_t fileid_uint64_hash(const uint8_t *s, size_t len)
 | 
|---|
| 124 | {
 | 
|---|
| 125 |         uint64_t value; /* Used to compute the hash value.  */
 | 
|---|
| 126 |         uint32_t i;     /* Used to cycle through random values. */
 | 
|---|
| 127 | 
 | 
|---|
| 128 |         /* Set the initial value from the key size. */
 | 
|---|
| 129 |         for (value = 0x238F13AFLL * len, i=0; i < len; i++)
 | 
|---|
| 130 |                 value = (value + (s[i] << (i*5 % 24)));
 | 
|---|
| 131 | 
 | 
|---|
| 132 |         return (1103515243LL * value + 12345LL);
 | 
|---|
| 133 | }
 | 
|---|
| 134 | 
 | 
|---|
| 135 | /* a device mapping using a fsname */
 | 
|---|
| 136 | static uint64_t fileid_device_mapping_fsname(struct fileid_handle_data *data,
 | 
|---|
| 137 |                                              SMB_DEV_T dev)
 | 
|---|
| 138 | {
 | 
|---|
| 139 |         struct fileid_mount_entry *m;
 | 
|---|
| 140 | 
 | 
|---|
| 141 |         m = fileid_find_mount_entry(data, dev);
 | 
|---|
| 142 |         if (!m) return dev;
 | 
|---|
| 143 | 
 | 
|---|
| 144 |         if (m->devid == (uint64_t)-1) {
 | 
|---|
| 145 |                 m->devid = fileid_uint64_hash((uint8_t *)m->mnt_fsname,
 | 
|---|
| 146 |                                               strlen(m->mnt_fsname));
 | 
|---|
| 147 |         }
 | 
|---|
| 148 | 
 | 
|---|
| 149 |         return m->devid;
 | 
|---|
| 150 | }
 | 
|---|
| 151 | 
 | 
|---|
| 152 | /* device mapping functions using a fsid */
 | 
|---|
| 153 | static uint64_t fileid_device_mapping_fsid(struct fileid_handle_data *data,
 | 
|---|
| 154 |                                            SMB_DEV_T dev)
 | 
|---|
| 155 | {
 | 
|---|
| 156 |         struct fileid_mount_entry *m;
 | 
|---|
| 157 | 
 | 
|---|
| 158 |         m = fileid_find_mount_entry(data, dev);
 | 
|---|
| 159 |         if (!m) return dev;
 | 
|---|
| 160 | 
 | 
|---|
| 161 |         if (m->devid == (uint64_t)-1) {
 | 
|---|
| 162 |                 if (sizeof(fsid_t) > sizeof(uint64_t)) {
 | 
|---|
| 163 |                         m->devid = fileid_uint64_hash((uint8_t *)&m->fsid,
 | 
|---|
| 164 |                                                       sizeof(m->fsid));
 | 
|---|
| 165 |                 } else {
 | 
|---|
| 166 |                         union {
 | 
|---|
| 167 |                                 uint64_t ret;
 | 
|---|
| 168 |                                 fsid_t fsid;
 | 
|---|
| 169 |                         } u;
 | 
|---|
| 170 |                         ZERO_STRUCT(u);
 | 
|---|
| 171 |                         u.fsid = m->fsid;
 | 
|---|
| 172 |                         m->devid = u.ret;
 | 
|---|
| 173 |                 }
 | 
|---|
| 174 |         }
 | 
|---|
| 175 | 
 | 
|---|
| 176 |         return m->devid;
 | 
|---|
| 177 | }
 | 
|---|
| 178 | 
 | 
|---|
| 179 | static int fileid_connect(struct vfs_handle_struct *handle,
 | 
|---|
| 180 |                           const char *service, const char *user)
 | 
|---|
| 181 | {
 | 
|---|
| 182 |         struct fileid_handle_data *data;
 | 
|---|
| 183 |         const char *algorithm;
 | 
|---|
| 184 |         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
 | 
|---|
| 185 | 
 | 
|---|
| 186 |         if (ret < 0) {
 | 
|---|
| 187 |                 return ret;
 | 
|---|
| 188 |         }
 | 
|---|
| 189 | 
 | 
|---|
| 190 |         data = talloc_zero(handle->conn, struct fileid_handle_data);
 | 
|---|
| 191 |         if (!data) {
 | 
|---|
| 192 |                 SMB_VFS_NEXT_DISCONNECT(handle);
 | 
|---|
| 193 |                 DEBUG(0, ("talloc_zero() failed\n"));
 | 
|---|
| 194 |                 return -1;
 | 
|---|
| 195 |         }
 | 
|---|
| 196 | 
 | 
|---|
| 197 |         /*
 | 
|---|
| 198 |          * "fileid:mapping" is only here as fallback for old setups
 | 
|---|
| 199 |          * "fileid:algorithm" is the option new setups should use
 | 
|---|
| 200 |          */
 | 
|---|
| 201 |         algorithm = lp_parm_const_string(SNUM(handle->conn),
 | 
|---|
| 202 |                                          "fileid", "mapping",
 | 
|---|
| 203 |                                          "fsname");
 | 
|---|
| 204 |         algorithm = lp_parm_const_string(SNUM(handle->conn),
 | 
|---|
| 205 |                                          "fileid", "algorithm",
 | 
|---|
| 206 |                                          algorithm);
 | 
|---|
| 207 |         if (strcmp("fsname", algorithm) == 0) {
 | 
|---|
| 208 |                 data->device_mapping_fn = fileid_device_mapping_fsname;
 | 
|---|
| 209 |         } else if (strcmp("fsid", algorithm) == 0) {
 | 
|---|
| 210 |                 data->device_mapping_fn = fileid_device_mapping_fsid;
 | 
|---|
| 211 |         } else {
 | 
|---|
| 212 |                 SMB_VFS_NEXT_DISCONNECT(handle);
 | 
|---|
| 213 |                 DEBUG(0,("fileid_connect(): unknown algorithm[%s]\n", algorithm));
 | 
|---|
| 214 |                 return -1;
 | 
|---|
| 215 |         }
 | 
|---|
| 216 | 
 | 
|---|
| 217 |         SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
 | 
|---|
| 218 |                                 struct fileid_handle_data,
 | 
|---|
| 219 |                                 return -1);
 | 
|---|
| 220 | 
 | 
|---|
| 221 |         DEBUG(10, ("fileid_connect(): connect to service[%s] with algorithm[%s]\n",
 | 
|---|
| 222 |                 service, algorithm));
 | 
|---|
| 223 | 
 | 
|---|
| 224 |         return 0;
 | 
|---|
| 225 | }
 | 
|---|
| 226 | 
 | 
|---|
| 227 | static void fileid_disconnect(struct vfs_handle_struct *handle)
 | 
|---|
| 228 | {
 | 
|---|
| 229 |         DEBUG(10,("fileid_disconnect() connect to service[%s].\n",
 | 
|---|
| 230 |                 lp_servicename(SNUM(handle->conn))));
 | 
|---|
| 231 | 
 | 
|---|
| 232 |         SMB_VFS_NEXT_DISCONNECT(handle);
 | 
|---|
| 233 | }
 | 
|---|
| 234 | 
 | 
|---|
| 235 | static struct file_id fileid_file_id_create(struct vfs_handle_struct *handle,
 | 
|---|
| 236 |                                             const SMB_STRUCT_STAT *sbuf)
 | 
|---|
| 237 | {
 | 
|---|
| 238 |         struct fileid_handle_data *data;
 | 
|---|
| 239 |         struct file_id id;
 | 
|---|
| 240 | 
 | 
|---|
| 241 |         ZERO_STRUCT(id);
 | 
|---|
| 242 | 
 | 
|---|
| 243 |         SMB_VFS_HANDLE_GET_DATA(handle, data,
 | 
|---|
| 244 |                                 struct fileid_handle_data,
 | 
|---|
| 245 |                                 return id);
 | 
|---|
| 246 | 
 | 
|---|
| 247 |         id.devid        = data->device_mapping_fn(data, sbuf->st_ex_dev);
 | 
|---|
| 248 |         id.inode        = sbuf->st_ex_ino;
 | 
|---|
| 249 | 
 | 
|---|
| 250 |         return id;
 | 
|---|
| 251 | }
 | 
|---|
| 252 | 
 | 
|---|
| 253 | static struct vfs_fn_pointers vfs_fileid_fns = {
 | 
|---|
| 254 |         .connect_fn = fileid_connect,
 | 
|---|
| 255 |         .disconnect = fileid_disconnect,
 | 
|---|
| 256 |         .file_id_create = fileid_file_id_create
 | 
|---|
| 257 | };
 | 
|---|
| 258 | 
 | 
|---|
| 259 | NTSTATUS vfs_fileid_init(void);
 | 
|---|
| 260 | NTSTATUS vfs_fileid_init(void)
 | 
|---|
| 261 | {
 | 
|---|
| 262 |         NTSTATUS ret;
 | 
|---|
| 263 | 
 | 
|---|
| 264 |         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fileid",
 | 
|---|
| 265 |                                &vfs_fileid_fns);
 | 
|---|
| 266 |         if (!NT_STATUS_IS_OK(ret)) {
 | 
|---|
| 267 |                 return ret;
 | 
|---|
| 268 |         }
 | 
|---|
| 269 | 
 | 
|---|
| 270 |         vfs_fileid_debug_level = debug_add_class("fileid");
 | 
|---|
| 271 |         if (vfs_fileid_debug_level == -1) {
 | 
|---|
| 272 |                 vfs_fileid_debug_level = DBGC_VFS;
 | 
|---|
| 273 |                 DEBUG(0, ("vfs_fileid: Couldn't register custom debugging class!\n"));
 | 
|---|
| 274 |         } else {
 | 
|---|
| 275 |                 DEBUG(10, ("vfs_fileid: Debug class number of 'fileid': %d\n", vfs_fileid_debug_level));
 | 
|---|
| 276 |         }
 | 
|---|
| 277 | 
 | 
|---|
| 278 |         return ret;
 | 
|---|
| 279 | }
 | 
|---|