s3:vfs_fileid: always add the 'nolock' behavior via file_id.extid
[samba.git] / source3 / modules / vfs_fileid.c
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 #include "smbd/smbd.h"
24 #include "system/filesys.h"
25
26 static int vfs_fileid_debug_level = DBGC_VFS;
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS vfs_fileid_debug_level
30
31 struct fileid_mount_entry {
32         SMB_DEV_T device;
33         const char *mnt_fsname;
34         fsid_t fsid;
35         uint64_t devid;
36 };
37
38 struct fileid_nolock_inode {
39         dev_t dev;
40         ino_t ino;
41 };
42
43 struct fileid_handle_data {
44         struct vfs_handle_struct *handle;
45         struct file_id (*mapping_fn)(struct fileid_handle_data *data,
46                                      const SMB_STRUCT_STAT *sbuf);
47         char **fstype_deny_list;
48         char **fstype_allow_list;
49         char **mntdir_deny_list;
50         char **mntdir_allow_list;
51         unsigned num_mount_entries;
52         struct fileid_mount_entry *mount_entries;
53         struct {
54                 uint64_t extid;
55                 size_t num_inodes;
56                 struct fileid_nolock_inode *inodes;
57         } nolock;
58 };
59
60 /* check if a mount entry is allowed based on fstype and mount directory */
61 static bool fileid_mount_entry_allowed(struct fileid_handle_data *data,
62                                        struct mntent *m)
63 {
64         int i;
65         char **fstype_deny = data->fstype_deny_list;
66         char **fstype_allow = data->fstype_allow_list;
67         char **mntdir_deny = data->mntdir_deny_list;
68         char **mntdir_allow = data->mntdir_allow_list;
69
70         if (fstype_deny != NULL) {
71                 for (i = 0; fstype_deny[i] != NULL; i++) {
72                         if (strcmp(m->mnt_type, fstype_deny[i]) == 0) {
73                                 return false;
74                         }
75                 }
76         }
77         if (fstype_allow != NULL) {
78                 for (i = 0; fstype_allow[i] != NULL; i++) {
79                         if (strcmp(m->mnt_type, fstype_allow[i]) == 0) {
80                                 break;
81                         }
82                 }
83                 if (fstype_allow[i] == NULL) {
84                         return false;
85                 }
86         }
87         if (mntdir_deny != NULL) {
88                 for (i=0; mntdir_deny[i] != NULL; i++) {
89                         if (strcmp(m->mnt_dir, mntdir_deny[i]) == 0) {
90                                 return false;
91                         }
92                 }
93         }
94         if (mntdir_allow != NULL) {
95                 for (i=0; mntdir_allow[i] != NULL; i++) {
96                         if (strcmp(m->mnt_dir, mntdir_allow[i]) == 0) {
97                                 break;
98                         }
99                 }
100                 if (mntdir_allow[i] == NULL) {
101                         return false;
102                 }
103         }
104         return true;
105 }
106
107
108 /* load all the mount entries from the mtab */
109 static void fileid_load_mount_entries(struct fileid_handle_data *data)
110 {
111         FILE *f;
112         struct mntent *m;
113
114         data->num_mount_entries = 0;
115         TALLOC_FREE(data->mount_entries);
116
117         f = setmntent("/etc/mtab", "r");
118         if (!f) return;
119
120         while ((m = getmntent(f))) {
121                 struct stat st;
122                 struct statfs sfs;
123                 struct fileid_mount_entry *cur;
124                 bool allowed;
125
126                 allowed = fileid_mount_entry_allowed(data, m);
127                 if (!allowed) {
128                         DBG_DEBUG("skipping mount entry %s\n", m->mnt_dir);
129                         continue;
130                 }
131                 if (stat(m->mnt_dir, &st) != 0) continue;
132                 if (statfs(m->mnt_dir, &sfs) != 0) continue;
133
134                 if (strncmp(m->mnt_fsname, "/dev/", 5) == 0) {
135                         m->mnt_fsname += 5;
136                 }
137
138                 data->mount_entries = talloc_realloc(data,
139                                                            data->mount_entries,
140                                                            struct fileid_mount_entry,
141                                                            data->num_mount_entries+1);
142                 if (data->mount_entries == NULL) {
143                         goto nomem;
144                 }
145
146                 cur = &data->mount_entries[data->num_mount_entries];
147                 cur->device     = st.st_dev;
148                 cur->mnt_fsname = talloc_strdup(data->mount_entries,
149                                                 m->mnt_fsname);
150                 if (!cur->mnt_fsname) goto nomem;
151                 cur->fsid       = sfs.f_fsid;
152                 cur->devid      = (uint64_t)-1;
153
154                 data->num_mount_entries++;
155         }
156         endmntent(f);
157         return;
158         
159 nomem:
160         if (f) endmntent(f);
161
162         data->num_mount_entries = 0;
163         TALLOC_FREE(data->mount_entries);
164
165         return;
166 }
167
168 /* find a mount entry given a dev_t */
169 static struct fileid_mount_entry *fileid_find_mount_entry(struct fileid_handle_data *data,
170                                                           SMB_DEV_T dev)
171 {
172         unsigned i;
173
174         if (data->num_mount_entries == 0) {
175                 fileid_load_mount_entries(data);
176         }
177         for (i=0;i<data->num_mount_entries;i++) {
178                 if (data->mount_entries[i].device == dev) {
179                         return &data->mount_entries[i];
180                 }
181         }
182         /* 2nd pass after reloading */
183         fileid_load_mount_entries(data);
184         for (i=0;i<data->num_mount_entries;i++) {
185                 if (data->mount_entries[i].device == dev) {
186                         return &data->mount_entries[i];
187                 }
188         }       
189         return NULL;
190 }
191
192
193 /* a 64 bit hash, based on the one in tdb */
194 static uint64_t fileid_uint64_hash(const uint8_t *s, size_t len)
195 {
196         uint64_t value; /* Used to compute the hash value.  */
197         uint32_t i;     /* Used to cycle through random values. */
198
199         /* Set the initial value from the key size. */
200         for (value = 0x238F13AFLL * len, i=0; i < len; i++)
201                 value = (value + (((uint64_t)s[i]) << (i*5 % 24)));
202
203         return (1103515243LL * value + 12345LL);
204 }
205
206 /* a device mapping using a fsname */
207 static uint64_t fileid_device_mapping_fsname(struct fileid_handle_data *data,
208                                              const SMB_STRUCT_STAT *sbuf)
209 {
210         struct fileid_mount_entry *m;
211
212         m = fileid_find_mount_entry(data, sbuf->st_ex_dev);
213         if (!m) return sbuf->st_ex_dev;
214
215         if (m->devid == (uint64_t)-1) {
216                 m->devid = fileid_uint64_hash((const uint8_t *)m->mnt_fsname,
217                                               strlen(m->mnt_fsname));
218         }
219
220         return m->devid;
221 }
222
223 static struct file_id fileid_mapping_fsname(struct fileid_handle_data *data,
224                                             const SMB_STRUCT_STAT *sbuf)
225 {
226         struct file_id id = { .inode = sbuf->st_ex_ino, };
227
228         id.devid = fileid_device_mapping_fsname(data, sbuf);
229
230         return id;
231 }
232
233 /* a device mapping using a hostname */
234 static uint64_t fileid_device_mapping_hostname(struct fileid_handle_data *data,
235                                                const SMB_STRUCT_STAT *sbuf)
236 {
237         char hostname[HOST_NAME_MAX+1];
238         char *devname = NULL;
239         uint64_t id;
240         size_t devname_len;
241         int rc;
242
243         rc = gethostname(hostname, HOST_NAME_MAX+1);
244         if (rc != 0) {
245                 DBG_ERR("gethostname failed\n");
246                 return UINT64_MAX;
247         }
248
249         devname = talloc_asprintf(talloc_tos(), "%s%ju",
250                                   hostname, (uintmax_t)sbuf->st_ex_dev);
251         if (devname == NULL) {
252                 DBG_ERR("talloc_asprintf failed\n");
253                 return UINT64_MAX;
254         }
255         devname_len = talloc_array_length(devname) - 1;
256
257         id = fileid_uint64_hash((uint8_t *)devname, devname_len);
258
259         TALLOC_FREE(devname);
260
261         return id;
262 }
263
264 static struct file_id fileid_mapping_hostname(struct fileid_handle_data *data,
265                                               const SMB_STRUCT_STAT *sbuf)
266 {
267         struct file_id id = { .inode = sbuf->st_ex_ino, };
268
269         id.devid = fileid_device_mapping_hostname(data, sbuf);
270
271         return id;
272 }
273
274 static bool fileid_is_nolock_inode(struct fileid_handle_data *data,
275                                    const SMB_STRUCT_STAT *sbuf)
276 {
277         size_t i;
278
279         /*
280          * We could make this a binary search over an sorted array,
281          * but for now we keep things simple.
282          */
283
284         for (i=0; i < data->nolock.num_inodes; i++) {
285                 if (data->nolock.inodes[i].ino != sbuf->st_ex_ino) {
286                         continue;
287                 }
288
289                 if (data->nolock.inodes[i].dev == 0) {
290                         /*
291                          * legacy "fileid:nolockinode"
292                          * handling ignoring dev
293                          */
294                         return true;
295                 }
296
297                 if (data->nolock.inodes[i].dev != sbuf->st_ex_dev) {
298                         continue;
299                 }
300
301                 return true;
302         }
303
304         return false;
305 }
306
307 static int fileid_add_nolock_inode(struct fileid_handle_data *data,
308                                    const SMB_STRUCT_STAT *sbuf)
309 {
310         bool exists = fileid_is_nolock_inode(data, sbuf);
311         struct fileid_nolock_inode *inodes = NULL;
312
313         if (exists) {
314                 return 0;
315         }
316
317         inodes = talloc_realloc(data, data->nolock.inodes,
318                                 struct fileid_nolock_inode,
319                                 data->nolock.num_inodes + 1);
320         if (inodes == NULL) {
321                 return -1;
322         }
323
324         inodes[data->nolock.num_inodes] = (struct fileid_nolock_inode) {
325                 .dev = sbuf->st_ex_dev,
326                 .ino = sbuf->st_ex_ino,
327         };
328         data->nolock.inodes = inodes;
329         data->nolock.num_inodes += 1;
330
331         return 0;
332 }
333
334 static uint64_t fileid_mapping_nolock_extid(uint64_t max_slots)
335 {
336         char buf[8+4+HOST_NAME_MAX+1] = { 0, };
337         uint64_t slot = 0;
338         uint64_t id;
339         int rc;
340
341         if (max_slots > 1) {
342                 slot = getpid() % max_slots;
343         }
344
345         PUSH_LE_U64(buf, 0, slot);
346         PUSH_LE_U32(buf, 8, get_my_vnn());
347
348         rc = gethostname(&buf[12], HOST_NAME_MAX+1);
349         if (rc != 0) {
350                 DBG_ERR("gethostname failed\n");
351                 return UINT64_MAX;
352         }
353
354         id = fileid_uint64_hash((uint8_t *)buf, ARRAY_SIZE(buf));
355
356         return id;
357 }
358
359 /* a device mapping using a fsname for files and hostname for dirs */
360 static struct file_id fileid_mapping_fsname_nodirs(
361         struct fileid_handle_data *data,
362         const SMB_STRUCT_STAT *sbuf)
363 {
364         if (S_ISDIR(sbuf->st_ex_mode)) {
365                 return fileid_mapping_hostname(data, sbuf);
366         }
367
368         return fileid_mapping_fsname(data, sbuf);
369 }
370
371 /* device mapping functions using a fsid */
372 static uint64_t fileid_device_mapping_fsid(struct fileid_handle_data *data,
373                                            const SMB_STRUCT_STAT *sbuf)
374 {
375         struct fileid_mount_entry *m;
376
377         m = fileid_find_mount_entry(data, sbuf->st_ex_dev);
378         if (!m) return sbuf->st_ex_dev;
379
380         if (m->devid == (uint64_t)-1) {
381                 if (sizeof(fsid_t) > sizeof(uint64_t)) {
382                         m->devid = fileid_uint64_hash((uint8_t *)&m->fsid,
383                                                       sizeof(m->fsid));
384                 } else {
385                         union {
386                                 uint64_t ret;
387                                 fsid_t fsid;
388                         } u;
389                         ZERO_STRUCT(u);
390                         u.fsid = m->fsid;
391                         m->devid = u.ret;
392                 }
393         }
394
395         return m->devid;
396 }
397
398 static struct file_id fileid_mapping_fsid(struct fileid_handle_data *data,
399                                           const SMB_STRUCT_STAT *sbuf)
400 {
401         struct file_id id = { .inode = sbuf->st_ex_ino, };
402
403         id.devid = fileid_device_mapping_fsid(data, sbuf);
404
405         return id;
406 }
407
408 static struct file_id fileid_mapping_next_module(struct fileid_handle_data *data,
409                                                  const SMB_STRUCT_STAT *sbuf)
410 {
411         return SMB_VFS_NEXT_FILE_ID_CREATE(data->handle, sbuf);
412 }
413
414 static int get_connectpath_ino(struct vfs_handle_struct *handle,
415                                const char *path,
416                                SMB_STRUCT_STAT *psbuf)
417 {
418         TALLOC_CTX *frame = talloc_stackframe();
419         struct smb_filename *fname = NULL;
420         const char *fullpath = NULL;
421         int ret;
422
423         if (path[0] == '/') {
424                 fullpath = path;
425         } else {
426                 fullpath = talloc_asprintf(frame,
427                                            "%s/%s",
428                                            handle->conn->connectpath,
429                                            path);
430                 if (fullpath == NULL) {
431                         DBG_ERR("talloc_asprintf() failed\n");
432                         TALLOC_FREE(frame);
433                         return -1;
434                 }
435         }
436
437         fname = synthetic_smb_fname(frame,
438                                     fullpath,
439                                     NULL,
440                                     NULL,
441                                     0,
442                                     0);
443         if (fname == NULL) {
444                 DBG_ERR("synthetic_smb_fname(%s) failed - %s\n",
445                         fullpath, strerror(errno));
446                 TALLOC_FREE(frame);
447                 return -1;
448         }
449
450         ret = SMB_VFS_NEXT_STAT(handle, fname);
451         if (ret != 0) {
452                 DBG_ERR("stat failed for %s with %s\n",
453                         fullpath, strerror(errno));
454                 TALLOC_FREE(frame);
455                 return -1;
456         }
457         *psbuf = fname->st;
458
459         TALLOC_FREE(frame);
460
461         return 0;
462 }
463
464 static int fileid_connect(struct vfs_handle_struct *handle,
465                           const char *service, const char *user)
466 {
467         struct fileid_handle_data *data;
468         const char *algorithm;
469         const char **fstype_deny_list = NULL;
470         const char **fstype_allow_list = NULL;
471         const char **mntdir_deny_list = NULL;
472         const char **mntdir_allow_list = NULL;
473         ino_t nolockinode;
474         uint64_t max_slots = 0;
475         bool rootdir_nolock = false;
476         int saved_errno;
477         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
478
479         if (ret < 0) {
480                 return ret;
481         }
482
483         data = talloc_zero(handle, struct fileid_handle_data);
484         if (!data) {
485                 saved_errno = errno;
486                 SMB_VFS_NEXT_DISCONNECT(handle);
487                 DEBUG(0, ("talloc_zero() failed\n"));
488                 errno = saved_errno;
489                 return -1;
490         }
491         data->handle = handle;
492
493         /*
494          * "fileid:mapping" is only here as fallback for old setups
495          * "fileid:algorithm" is the option new setups should use
496          */
497         algorithm = lp_parm_const_string(SNUM(handle->conn),
498                                          "fileid", "mapping",
499                                          "fsname");
500         algorithm = lp_parm_const_string(SNUM(handle->conn),
501                                          "fileid", "algorithm",
502                                          algorithm);
503         if (strcmp("fsname", algorithm) == 0) {
504                 data->mapping_fn = fileid_mapping_fsname;
505         } else if (strcmp("fsname_nodirs", algorithm) == 0) {
506                 data->mapping_fn = fileid_mapping_fsname_nodirs;
507         } else if (strcmp("fsid", algorithm) == 0) {
508                 data->mapping_fn = fileid_mapping_fsid;
509         } else if (strcmp("hostname", algorithm) == 0) {
510                 data->mapping_fn = fileid_mapping_hostname;
511         } else if (strcmp("fsname_norootdir", algorithm) == 0) {
512                 data->mapping_fn = fileid_mapping_fsname;
513                 rootdir_nolock = true;
514         } else if (strcmp("fsname_norootdir_ext", algorithm) == 0) {
515                 data->mapping_fn = fileid_mapping_fsname;
516                 rootdir_nolock = true;
517                 max_slots = UINT64_MAX;
518         } else if (strcmp("next_module", algorithm) == 0) {
519                 data->mapping_fn        = fileid_mapping_next_module;
520         } else {
521                 SMB_VFS_NEXT_DISCONNECT(handle);
522                 DEBUG(0,("fileid_connect(): unknown algorithm[%s]\n", algorithm));
523                 return -1;
524         }
525
526         fstype_deny_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
527                                                "fstype deny", NULL);
528         if (fstype_deny_list != NULL) {
529                 data->fstype_deny_list = str_list_copy(data, fstype_deny_list);
530                 if (data->fstype_deny_list == NULL) {
531                         saved_errno = errno;
532                         DBG_ERR("str_list_copy failed\n");
533                         SMB_VFS_NEXT_DISCONNECT(handle);
534                         errno = saved_errno;
535                         return -1;
536                 }
537         }
538
539         fstype_allow_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
540                                                 "fstype allow", NULL);
541         if (fstype_allow_list != NULL) {
542                 data->fstype_allow_list = str_list_copy(data, fstype_allow_list);
543                 if (data->fstype_allow_list == NULL) {
544                         saved_errno = errno;
545                         DBG_ERR("str_list_copy failed\n");
546                         SMB_VFS_NEXT_DISCONNECT(handle);
547                         errno = saved_errno;
548                         return -1;
549                 }
550         }
551
552         mntdir_deny_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
553                                                "mntdir deny", NULL);
554         if (mntdir_deny_list != NULL) {
555                 data->mntdir_deny_list = str_list_copy(data, mntdir_deny_list);
556                 if (data->mntdir_deny_list == NULL) {
557                         saved_errno = errno;
558                         DBG_ERR("str_list_copy failed\n");
559                         SMB_VFS_NEXT_DISCONNECT(handle);
560                         errno = saved_errno;
561                         return -1;
562                 }
563         }
564
565         mntdir_allow_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
566                                                 "mntdir allow", NULL);
567         if (mntdir_allow_list != NULL) {
568                 data->mntdir_allow_list = str_list_copy(data, mntdir_allow_list);
569                 if (data->mntdir_allow_list == NULL) {
570                         saved_errno = errno;
571                         DBG_ERR("str_list_copy failed\n");
572                         SMB_VFS_NEXT_DISCONNECT(handle);
573                         errno = saved_errno;
574                         return -1;
575                 }
576         }
577
578         max_slots = MAX(max_slots, 1);
579
580         data->nolock.extid = fileid_mapping_nolock_extid(max_slots);
581
582         nolockinode = lp_parm_ulong(SNUM(handle->conn), "fileid", "nolockinode", 0);
583         if (nolockinode != 0) {
584                 SMB_STRUCT_STAT tmpsbuf = { .st_ex_ino = nolockinode, };
585
586                 ret = fileid_add_nolock_inode(data, &tmpsbuf);
587                 if (ret != 0) {
588                         saved_errno = errno;
589                         SMB_VFS_NEXT_DISCONNECT(handle);
590                         errno = saved_errno;
591                         return -1;
592                 }
593         }
594
595         if (rootdir_nolock) {
596                 SMB_STRUCT_STAT rootdirsbuf;
597
598                 ret = get_connectpath_ino(handle, ".", &rootdirsbuf);
599                 if (ret != 0) {
600                         saved_errno = errno;
601                         SMB_VFS_NEXT_DISCONNECT(handle);
602                         errno = saved_errno;
603                         return -1;
604                 }
605
606                 ret = fileid_add_nolock_inode(data, &rootdirsbuf);
607                 if (ret != 0) {
608                         saved_errno = errno;
609                         SMB_VFS_NEXT_DISCONNECT(handle);
610                         errno = saved_errno;
611                         return -1;
612                 }
613         }
614
615         SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
616                                 struct fileid_handle_data,
617                                 return -1);
618
619         DBG_DEBUG("connect to service[%s] with algorithm[%s] nolock.inodes %zu\n",
620                   service, algorithm, data->nolock.num_inodes);
621
622         return 0;
623 }
624
625 static void fileid_disconnect(struct vfs_handle_struct *handle)
626 {
627         const struct loadparm_substitution *lp_sub =
628                 loadparm_s3_global_substitution();
629
630         DEBUG(10,("fileid_disconnect() connect to service[%s].\n",
631                   lp_servicename(talloc_tos(), lp_sub, SNUM(handle->conn))));
632
633         SMB_VFS_NEXT_DISCONNECT(handle);
634 }
635
636 static struct file_id fileid_file_id_create(struct vfs_handle_struct *handle,
637                                             const SMB_STRUCT_STAT *sbuf)
638 {
639         struct fileid_handle_data *data;
640         struct file_id id = { .inode = 0, };
641
642         SMB_VFS_HANDLE_GET_DATA(handle, data,
643                                 struct fileid_handle_data,
644                                 return id);
645
646         id = data->mapping_fn(data, sbuf);
647         if (id.extid == 0 && fileid_is_nolock_inode(data, sbuf)) {
648                 id.extid = data->nolock.extid;
649         }
650
651         DBG_DEBUG("Returning dev [%jx] inode [%jx] extid [%jx]\n",
652                   (uintmax_t)id.devid, (uintmax_t)id.inode, (uintmax_t)id.extid);
653
654         return id;
655 }
656
657 static struct vfs_fn_pointers vfs_fileid_fns = {
658         .connect_fn = fileid_connect,
659         .disconnect_fn = fileid_disconnect,
660         .file_id_create_fn = fileid_file_id_create
661 };
662
663 static_decl_vfs;
664 NTSTATUS vfs_fileid_init(TALLOC_CTX *ctx)
665 {
666         NTSTATUS ret;
667
668         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fileid",
669                                &vfs_fileid_fns);
670         if (!NT_STATUS_IS_OK(ret)) {
671                 return ret;
672         }
673
674         vfs_fileid_debug_level = debug_add_class("fileid");
675         if (vfs_fileid_debug_level == -1) {
676                 vfs_fileid_debug_level = DBGC_VFS;
677                 DEBUG(0, ("vfs_fileid: Couldn't register custom debugging class!\n"));
678         } else {
679                 DEBUG(10, ("vfs_fileid: Debug class number of 'fileid': %d\n", vfs_fileid_debug_level));
680         }
681
682         return ret;
683 }