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