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