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