s3:dbwrap: move db_open() to a file dbwrap_open.c of its own.
[samba.git] / source3 / modules / vfs_xattr_tdb.c
1 /*
2  * Store posix-level xattrs in a tdb
3  *
4  * Copyright (C) Volker Lendecke, 2007
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "smbd/smbd.h"
23 #include "librpc/gen_ndr/xattr.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "../librpc/gen_ndr/ndr_netlogon.h"
26 #include "dbwrap.h"
27 #include "dbwrap/dbwrap_open.h"
28 #include "util_tdb.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_VFS
32
33 /*
34  * unmarshall tdb_xattrs
35  */
36
37 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
38                                      const TDB_DATA *data,
39                                      struct tdb_xattrs **presult)
40 {
41         DATA_BLOB blob;
42         enum ndr_err_code ndr_err;
43         struct tdb_xattrs *result;
44
45         if (!(result = talloc_zero(mem_ctx, struct tdb_xattrs))) {
46                 return NT_STATUS_NO_MEMORY;
47         }
48
49         if (data->dsize == 0) {
50                 *presult = result;
51                 return NT_STATUS_OK;
52         }
53
54         blob = data_blob_const(data->dptr, data->dsize);
55
56         ndr_err = ndr_pull_struct_blob(&blob, result, result,
57                 (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
58
59         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
60                 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
61                           ndr_errstr(ndr_err)));
62                 TALLOC_FREE(result);
63                 return ndr_map_error2ntstatus(ndr_err);
64         }
65
66         *presult = result;
67         return NT_STATUS_OK;
68 }
69
70 /*
71  * marshall tdb_xattrs
72  */
73
74 static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
75                                      const struct tdb_xattrs *attribs,
76                                      TDB_DATA *data)
77 {
78         DATA_BLOB blob;
79         enum ndr_err_code ndr_err;
80
81         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, attribs,
82                 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
83
84         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
85                 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
86                           ndr_errstr(ndr_err)));
87                 return ndr_map_error2ntstatus(ndr_err);
88         }
89
90         *data = make_tdb_data(blob.data, blob.length);
91         return NT_STATUS_OK;
92 }
93
94 /*
95  * Load tdb_xattrs for a file from the tdb
96  */
97
98 static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
99                                      struct db_context *db_ctx,
100                                      const struct file_id *id,
101                                      struct tdb_xattrs **presult)
102 {
103         uint8 id_buf[16];
104         NTSTATUS status;
105         TDB_DATA data;
106
107         /* For backwards compatibility only store the dev/inode. */
108         push_file_id_16((char *)id_buf, id);
109
110         if (db_ctx->fetch(db_ctx, mem_ctx,
111                           make_tdb_data(id_buf, sizeof(id_buf)),
112                           &data) != 0) {
113                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
114         }
115
116         status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
117         TALLOC_FREE(data.dptr);
118         return status;
119 }
120
121 /*
122  * fetch_lock the tdb_ea record for a file
123  */
124
125 static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
126                                               struct db_context *db_ctx,
127                                               const struct file_id *id)
128 {
129         uint8 id_buf[16];
130
131         /* For backwards compatibility only store the dev/inode. */
132         push_file_id_16((char *)id_buf, id);
133         return db_ctx->fetch_locked(db_ctx, mem_ctx,
134                                     make_tdb_data(id_buf, sizeof(id_buf)));
135 }
136
137 /*
138  * Save tdb_xattrs to a previously fetch_locked record
139  */
140
141 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
142                                      const struct tdb_xattrs *attribs)
143 {
144         TDB_DATA data = tdb_null;
145         NTSTATUS status;
146
147         status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
148
149         if (!NT_STATUS_IS_OK(status)) {
150                 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
151                           nt_errstr(status)));
152                 return status;
153         }
154
155         status = rec->store(rec, data, 0);
156
157         TALLOC_FREE(data.dptr);
158
159         return status;
160 }
161
162 /*
163  * Worker routine for getxattr and fgetxattr
164  */
165
166 static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
167                                  const struct file_id *id,
168                                  const char *name, void *value, size_t size)
169 {
170         struct tdb_xattrs *attribs;
171         uint32_t i;
172         ssize_t result = -1;
173         NTSTATUS status;
174
175         DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
176                    file_id_string_tos(id), name));
177
178         status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
179
180         if (!NT_STATUS_IS_OK(status)) {
181                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
182                            nt_errstr(status)));
183                 errno = EINVAL;
184                 return -1;
185         }
186
187         for (i=0; i<attribs->num_eas; i++) {
188                 if (strcmp(attribs->eas[i].name, name) == 0) {
189                         break;
190                 }
191         }
192
193         if (i == attribs->num_eas) {
194                 errno = ENOATTR;
195                 goto fail;
196         }
197
198         if (attribs->eas[i].value.length > size) {
199                 errno = ERANGE;
200                 goto fail;
201         }
202
203         memcpy(value, attribs->eas[i].value.data,
204                attribs->eas[i].value.length);
205         result = attribs->eas[i].value.length;
206
207  fail:
208         TALLOC_FREE(attribs);
209         return result;
210 }
211
212 static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
213                                   const char *path, const char *name,
214                                   void *value, size_t size)
215 {
216         SMB_STRUCT_STAT sbuf;
217         struct file_id id;
218         struct db_context *db;
219
220         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
221
222         if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
223                 return -1;
224         }
225
226         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
227
228         return xattr_tdb_getattr(db, &id, name, value, size);
229 }
230
231 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
232                                    struct files_struct *fsp,
233                                    const char *name, void *value, size_t size)
234 {
235         SMB_STRUCT_STAT sbuf;
236         struct file_id id;
237         struct db_context *db;
238
239         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
240
241         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
242                 return -1;
243         }
244
245         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
246
247         return xattr_tdb_getattr(db, &id, name, value, size);
248 }
249
250 /*
251  * Worker routine for setxattr and fsetxattr
252  */
253
254 static int xattr_tdb_setattr(struct db_context *db_ctx,
255                              const struct file_id *id, const char *name,
256                              const void *value, size_t size, int flags)
257 {
258         NTSTATUS status;
259         struct db_record *rec;
260         struct tdb_xattrs *attribs;
261         uint32_t i;
262
263         DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
264                    file_id_string_tos(id), name));
265
266         rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
267
268         if (rec == NULL) {
269                 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
270                 errno = EINVAL;
271                 return -1;
272         }
273
274         status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
275
276         if (!NT_STATUS_IS_OK(status)) {
277                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
278                            nt_errstr(status)));
279                 TALLOC_FREE(rec);
280                 return -1;
281         }
282
283         for (i=0; i<attribs->num_eas; i++) {
284                 if (strcmp(attribs->eas[i].name, name) == 0) {
285                         if (flags & XATTR_CREATE) {
286                                 TALLOC_FREE(rec);
287                                 errno = EEXIST;
288                                 return -1;
289                         }
290                         break;
291                 }
292         }
293
294         if (i == attribs->num_eas) {
295                 struct xattr_EA *tmp;
296
297                 if (flags & XATTR_REPLACE) {
298                         TALLOC_FREE(rec);
299                         errno = ENOATTR;
300                         return -1;
301                 }
302
303                 tmp = talloc_realloc(
304                         attribs, attribs->eas, struct xattr_EA,
305                         attribs->num_eas+ 1);
306
307                 if (tmp == NULL) {
308                         DEBUG(0, ("talloc_realloc failed\n"));
309                         TALLOC_FREE(rec);
310                         errno = ENOMEM;
311                         return -1;
312                 }
313
314                 attribs->eas = tmp;
315                 attribs->num_eas += 1;
316         }
317
318         attribs->eas[i].name = name;
319         attribs->eas[i].value.data = discard_const_p(uint8, value);
320         attribs->eas[i].value.length = size;
321
322         status = xattr_tdb_save_attrs(rec, attribs);
323
324         TALLOC_FREE(rec);
325
326         if (!NT_STATUS_IS_OK(status)) {
327                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
328                 return -1;
329         }
330
331         return 0;
332 }
333
334 static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
335                               const char *path, const char *name,
336                               const void *value, size_t size, int flags)
337 {
338         SMB_STRUCT_STAT sbuf;
339         struct file_id id;
340         struct db_context *db;
341
342         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
343
344         if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
345                 return -1;
346         }
347
348         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
349
350         return xattr_tdb_setattr(db, &id, name, value, size, flags);
351 }
352
353 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
354                                struct files_struct *fsp,
355                                const char *name, const void *value,
356                                size_t size, int flags)
357 {
358         SMB_STRUCT_STAT sbuf;
359         struct file_id id;
360         struct db_context *db;
361
362         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
363
364         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
365                 return -1;
366         }
367
368         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
369
370         return xattr_tdb_setattr(db, &id, name, value, size, flags);
371 }
372
373 /*
374  * Worker routine for listxattr and flistxattr
375  */
376
377 static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
378                                   const struct file_id *id, char *list,
379                                   size_t size)
380 {
381         NTSTATUS status;
382         struct tdb_xattrs *attribs;
383         uint32_t i;
384         size_t len = 0;
385
386         status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
387
388         if (!NT_STATUS_IS_OK(status)) {
389                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
390                            nt_errstr(status)));
391                 errno = EINVAL;
392                 return -1;
393         }
394
395         DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
396                    attribs->num_eas));
397
398         for (i=0; i<attribs->num_eas; i++) {
399                 size_t tmp;
400
401                 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
402                            attribs->eas[i].name));
403
404                 tmp = strlen(attribs->eas[i].name);
405
406                 /*
407                  * Try to protect against overflow
408                  */
409
410                 if (len + (tmp+1) < len) {
411                         TALLOC_FREE(attribs);
412                         errno = EINVAL;
413                         return -1;
414                 }
415
416                 /*
417                  * Take care of the terminating NULL
418                  */
419                 len += (tmp + 1);
420         }
421
422         if (len > size) {
423                 TALLOC_FREE(attribs);
424                 errno = ERANGE;
425                 return -1;
426         }
427
428         len = 0;
429
430         for (i=0; i<attribs->num_eas; i++) {
431                 strlcpy(list+len, attribs->eas[i].name,
432                         size-len);
433                 len += (strlen(attribs->eas[i].name) + 1);
434         }
435
436         TALLOC_FREE(attribs);
437         return len;
438 }
439
440 static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
441                                    const char *path, char *list, size_t size)
442 {
443         SMB_STRUCT_STAT sbuf;
444         struct file_id id;
445         struct db_context *db;
446
447         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
448
449         if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
450                 return -1;
451         }
452
453         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
454
455         return xattr_tdb_listattr(db, &id, list, size);
456 }
457
458 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
459                                     struct files_struct *fsp, char *list,
460                                     size_t size)
461 {
462         SMB_STRUCT_STAT sbuf;
463         struct file_id id;
464         struct db_context *db;
465
466         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
467
468         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
469                 return -1;
470         }
471
472         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
473
474         return xattr_tdb_listattr(db, &id, list, size);
475 }
476
477 /*
478  * Worker routine for removexattr and fremovexattr
479  */
480
481 static int xattr_tdb_removeattr(struct db_context *db_ctx,
482                                 const struct file_id *id, const char *name)
483 {
484         NTSTATUS status;
485         struct db_record *rec;
486         struct tdb_xattrs *attribs;
487         uint32_t i;
488
489         rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
490
491         if (rec == NULL) {
492                 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
493                 errno = EINVAL;
494                 return -1;
495         }
496
497         status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
498
499         if (!NT_STATUS_IS_OK(status)) {
500                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
501                            nt_errstr(status)));
502                 TALLOC_FREE(rec);
503                 return -1;
504         }
505
506         for (i=0; i<attribs->num_eas; i++) {
507                 if (strcmp(attribs->eas[i].name, name) == 0) {
508                         break;
509                 }
510         }
511
512         if (i == attribs->num_eas) {
513                 TALLOC_FREE(rec);
514                 errno = ENOATTR;
515                 return -1;
516         }
517
518         attribs->eas[i] =
519                 attribs->eas[attribs->num_eas-1];
520         attribs->num_eas -= 1;
521
522         if (attribs->num_eas == 0) {
523                 rec->delete_rec(rec);
524                 TALLOC_FREE(rec);
525                 return 0;
526         }
527
528         status = xattr_tdb_save_attrs(rec, attribs);
529
530         TALLOC_FREE(rec);
531
532         if (!NT_STATUS_IS_OK(status)) {
533                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
534                 return -1;
535         }
536
537         return 0;
538 }
539
540 static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
541                                  const char *path, const char *name)
542 {
543         SMB_STRUCT_STAT sbuf;
544         struct file_id id;
545         struct db_context *db;
546
547         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
548
549         if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
550                 return -1;
551         }
552
553         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
554
555         return xattr_tdb_removeattr(db, &id, name);
556 }
557
558 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
559                                   struct files_struct *fsp, const char *name)
560 {
561         SMB_STRUCT_STAT sbuf;
562         struct file_id id;
563         struct db_context *db;
564
565         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
566
567         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
568                 return -1;
569         }
570
571         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
572
573         return xattr_tdb_removeattr(db, &id, name);
574 }
575
576 /*
577  * Open the tdb file upon VFS_CONNECT
578  */
579
580 static bool xattr_tdb_init(int snum, struct db_context **p_db)
581 {
582         struct db_context *db;
583         const char *dbname;
584         char *def_dbname;
585
586         def_dbname = state_path("xattr.tdb");
587         if (def_dbname == NULL) {
588                 errno = ENOSYS;
589                 return false;
590         }
591
592         dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
593
594         /* now we know dbname is not NULL */
595
596         become_root();
597         db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
598         unbecome_root();
599
600         if (db == NULL) {
601 #if defined(ENOTSUP)
602                 errno = ENOTSUP;
603 #else
604                 errno = ENOSYS;
605 #endif
606                 TALLOC_FREE(def_dbname);
607                 return false;
608         }
609
610         *p_db = db;
611         TALLOC_FREE(def_dbname);
612         return true;
613 }
614
615 /*
616  * On unlink we need to delete the tdb record
617  */
618 static int xattr_tdb_unlink(vfs_handle_struct *handle,
619                             const struct smb_filename *smb_fname)
620 {
621         struct smb_filename *smb_fname_tmp = NULL;
622         struct file_id id;
623         struct db_context *db;
624         struct db_record *rec;
625         NTSTATUS status;
626         int ret = -1;
627         bool remove_record = false;
628
629         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
630
631         status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
632         if (!NT_STATUS_IS_OK(status)) {
633                 errno = map_errno_from_nt_status(status);
634                 return -1;
635         }
636
637         if (lp_posix_pathnames()) {
638                 ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
639         } else {
640                 ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
641         }
642         if (ret == -1) {
643                 goto out;
644         }
645
646         if (smb_fname_tmp->st.st_ex_nlink == 1) {
647                 /* Only remove record on last link to file. */
648                 remove_record = true;
649         }
650
651         ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
652
653         if (ret == -1) {
654                 goto out;
655         }
656
657         if (!remove_record) {
658                 goto out;
659         }
660
661         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &smb_fname_tmp->st);
662
663         rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
664
665         /*
666          * If rec == NULL there's not much we can do about it
667          */
668
669         if (rec != NULL) {
670                 rec->delete_rec(rec);
671                 TALLOC_FREE(rec);
672         }
673
674  out:
675         TALLOC_FREE(smb_fname_tmp);
676         return ret;
677 }
678
679 /*
680  * On rmdir we need to delete the tdb record
681  */
682 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
683 {
684         SMB_STRUCT_STAT sbuf;
685         struct file_id id;
686         struct db_context *db;
687         struct db_record *rec;
688         int ret;
689
690         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
691
692         if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
693                 return -1;
694         }
695
696         ret = SMB_VFS_NEXT_RMDIR(handle, path);
697
698         if (ret == -1) {
699                 return -1;
700         }
701
702         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
703
704         rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
705
706         /*
707          * If rec == NULL there's not much we can do about it
708          */
709
710         if (rec != NULL) {
711                 rec->delete_rec(rec);
712                 TALLOC_FREE(rec);
713         }
714
715         return 0;
716 }
717
718 /*
719  * Destructor for the VFS private data
720  */
721
722 static void close_xattr_db(void **data)
723 {
724         struct db_context **p_db = (struct db_context **)data;
725         TALLOC_FREE(*p_db);
726 }
727
728 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
729                           const char *user)
730 {
731         char *sname = NULL;
732         int res, snum;
733         struct db_context *db;
734
735         res = SMB_VFS_NEXT_CONNECT(handle, service, user);
736         if (res < 0) {
737                 return res;
738         }
739
740         snum = find_service(talloc_tos(), service, &sname);
741         if (snum == -1 || sname == NULL) {
742                 /*
743                  * Should not happen, but we should not fail just *here*.
744                  */
745                 return 0;
746         }
747
748         if (!xattr_tdb_init(snum, &db)) {
749                 DEBUG(5, ("Could not init xattr tdb\n"));
750                 lp_do_parameter(snum, "ea support", "False");
751                 return 0;
752         }
753
754         lp_do_parameter(snum, "ea support", "True");
755
756         SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
757                                 struct db_context, return -1);
758
759         return 0;
760 }
761
762 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
763         .getxattr = xattr_tdb_getxattr,
764         .fgetxattr = xattr_tdb_fgetxattr,
765         .setxattr = xattr_tdb_setxattr,
766         .fsetxattr = xattr_tdb_fsetxattr,
767         .listxattr = xattr_tdb_listxattr,
768         .flistxattr = xattr_tdb_flistxattr,
769         .removexattr = xattr_tdb_removexattr,
770         .fremovexattr = xattr_tdb_fremovexattr,
771         .unlink = xattr_tdb_unlink,
772         .rmdir = xattr_tdb_rmdir,
773         .connect_fn = xattr_tdb_connect,
774 };
775
776 NTSTATUS vfs_xattr_tdb_init(void);
777 NTSTATUS vfs_xattr_tdb_init(void)
778 {
779         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
780                                 &vfs_xattr_tdb_fns);
781 }