s3: Add extid to the dev/inode pair
[tprouty/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
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_VFS
26
27 /*
28  * unmarshall tdb_xattrs
29  */
30
31 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
32                                      const TDB_DATA *data,
33                                      struct tdb_xattrs **presult)
34 {
35         DATA_BLOB blob;
36         enum ndr_err_code ndr_err;
37         struct tdb_xattrs *result;
38
39         if (!(result = TALLOC_ZERO_P(mem_ctx, struct tdb_xattrs))) {
40                 return NT_STATUS_NO_MEMORY;
41         }
42
43         if (data->dsize == 0) {
44                 *presult = result;
45                 return NT_STATUS_OK;
46         }
47
48         blob = data_blob_const(data->dptr, data->dsize);
49
50         ndr_err = ndr_pull_struct_blob(
51                 &blob, result, NULL, 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(
77                 &blob, mem_ctx, NULL, 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 (SMB_VFS_STAT(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 (SMB_VFS_STAT(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 (SMB_VFS_STAT(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 (SMB_VFS_STAT(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, const char *path)
615 {
616         SMB_STRUCT_STAT sbuf;
617         struct file_id id;
618         struct db_context *db;
619         struct db_record *rec;
620         int ret;
621
622         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
623
624         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
625                 return -1;
626         }
627
628         ret = SMB_VFS_NEXT_UNLINK(handle, path);
629
630         if (ret == -1) {
631                 return -1;
632         }
633
634         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
635
636         rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
637
638         /*
639          * If rec == NULL there's not much we can do about it
640          */
641
642         if (rec != NULL) {
643                 rec->delete_rec(rec);
644                 TALLOC_FREE(rec);
645         }
646
647         return 0;
648 }
649
650 /*
651  * On rmdir we need to delete the tdb record
652  */
653 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
654 {
655         SMB_STRUCT_STAT sbuf;
656         struct file_id id;
657         struct db_context *db;
658         struct db_record *rec;
659         int ret;
660
661         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
662
663         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
664                 return -1;
665         }
666
667         ret = SMB_VFS_NEXT_RMDIR(handle, path);
668
669         if (ret == -1) {
670                 return -1;
671         }
672
673         id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
674
675         rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
676
677         /*
678          * If rec == NULL there's not much we can do about it
679          */
680
681         if (rec != NULL) {
682                 rec->delete_rec(rec);
683                 TALLOC_FREE(rec);
684         }
685
686         return 0;
687 }
688
689 /*
690  * Destructor for the VFS private data
691  */
692
693 static void close_xattr_db(void **data)
694 {
695         struct db_context **p_db = (struct db_context **)data;
696         TALLOC_FREE(*p_db);
697 }
698
699 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
700                           const char *user)
701 {
702         fstring sname;
703         int res, snum;
704         struct db_context *db;
705
706         res = SMB_VFS_NEXT_CONNECT(handle, service, user);
707         if (res < 0) {
708                 return res;
709         }
710
711         fstrcpy(sname, service);
712         snum = find_service(sname);
713         if (snum == -1) {
714                 /*
715                  * Should not happen, but we should not fail just *here*.
716                  */
717                 return 0;
718         }
719
720         if (!xattr_tdb_init(snum, &db)) {
721                 DEBUG(5, ("Could not init xattr tdb\n"));
722                 lp_do_parameter(snum, "ea support", "False");
723                 return 0;
724         }
725
726         lp_do_parameter(snum, "ea support", "True");
727
728         SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
729                                 struct db_context, return -1);
730
731         return 0;
732 }
733
734 /* VFS operations structure */
735
736 static const vfs_op_tuple xattr_tdb_ops[] = {
737         {SMB_VFS_OP(xattr_tdb_getxattr), SMB_VFS_OP_GETXATTR,
738          SMB_VFS_LAYER_TRANSPARENT},
739         {SMB_VFS_OP(xattr_tdb_fgetxattr), SMB_VFS_OP_FGETXATTR,
740          SMB_VFS_LAYER_TRANSPARENT},
741         {SMB_VFS_OP(xattr_tdb_setxattr), SMB_VFS_OP_SETXATTR,
742          SMB_VFS_LAYER_TRANSPARENT},
743         {SMB_VFS_OP(xattr_tdb_fsetxattr), SMB_VFS_OP_FSETXATTR,
744          SMB_VFS_LAYER_TRANSPARENT},
745         {SMB_VFS_OP(xattr_tdb_listxattr), SMB_VFS_OP_LISTXATTR,
746          SMB_VFS_LAYER_TRANSPARENT},
747         {SMB_VFS_OP(xattr_tdb_flistxattr), SMB_VFS_OP_FLISTXATTR,
748          SMB_VFS_LAYER_TRANSPARENT},
749         {SMB_VFS_OP(xattr_tdb_removexattr), SMB_VFS_OP_REMOVEXATTR,
750          SMB_VFS_LAYER_TRANSPARENT},
751         {SMB_VFS_OP(xattr_tdb_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
752          SMB_VFS_LAYER_TRANSPARENT},
753         {SMB_VFS_OP(xattr_tdb_unlink), SMB_VFS_OP_UNLINK,
754          SMB_VFS_LAYER_TRANSPARENT},
755         {SMB_VFS_OP(xattr_tdb_rmdir), SMB_VFS_OP_RMDIR,
756          SMB_VFS_LAYER_TRANSPARENT},
757         {SMB_VFS_OP(xattr_tdb_connect), SMB_VFS_OP_CONNECT,
758          SMB_VFS_LAYER_TRANSPARENT},
759         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
760 };
761
762 NTSTATUS vfs_xattr_tdb_init(void);
763 NTSTATUS vfs_xattr_tdb_init(void)
764 {
765         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
766                                 xattr_tdb_ops);
767 }