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