Merge branch 'master' of ssh://git.samba.org/data/git/samba into selftest
[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         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         push_file_id_16((char *)id_buf, id);
126         return db_ctx->fetch_locked(db_ctx, mem_ctx,
127                                     make_tdb_data(id_buf, sizeof(id_buf)));
128 }
129
130 /*
131  * Save tdb_xattrs to a previously fetch_locked record
132  */
133
134 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
135                                      const struct tdb_xattrs *attribs)
136 {
137         TDB_DATA data = tdb_null;
138         NTSTATUS status;
139
140         status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
141
142         if (!NT_STATUS_IS_OK(status)) {
143                 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
144                           nt_errstr(status)));
145                 return status;
146         }
147
148         status = rec->store(rec, data, 0);
149
150         TALLOC_FREE(data.dptr);
151
152         return status;
153 }
154
155 /*
156  * Worker routine for getxattr and fgetxattr
157  */
158
159 static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
160                                  const struct file_id *id,
161                                  const char *name, void *value, size_t size)
162 {
163         struct tdb_xattrs *attribs;
164         uint32_t i;
165         ssize_t result = -1;
166         NTSTATUS status;
167
168         DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
169                    file_id_string_tos(id), name));
170
171         status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
172
173         if (!NT_STATUS_IS_OK(status)) {
174                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
175                            nt_errstr(status)));
176                 errno = EINVAL;
177                 return -1;
178         }
179
180         for (i=0; i<attribs->num_xattrs; i++) {
181                 if (strcmp(attribs->xattrs[i].name, name) == 0) {
182                         break;
183                 }
184         }
185
186         if (i == attribs->num_xattrs) {
187                 errno = ENOATTR;
188                 goto fail;
189         }
190
191         if (attribs->xattrs[i].value.length > size) {
192                 errno = ERANGE;
193                 goto fail;
194         }
195
196         memcpy(value, attribs->xattrs[i].value.data,
197                attribs->xattrs[i].value.length);
198         result = attribs->xattrs[i].value.length;
199
200  fail:
201         TALLOC_FREE(attribs);
202         return result;
203 }
204
205 static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
206                                   const char *path, const char *name,
207                                   void *value, size_t size)
208 {
209         SMB_STRUCT_STAT sbuf;
210         struct file_id id;
211         struct db_context *db;
212
213         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
214
215         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
216                 return -1;
217         }
218
219         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
220
221         return xattr_tdb_getattr(db, &id, name, value, size);
222 }
223
224 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
225                                    struct files_struct *fsp,
226                                    const char *name, void *value, size_t size)
227 {
228         SMB_STRUCT_STAT sbuf;
229         struct file_id id;
230         struct db_context *db;
231
232         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
233
234         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
235                 return -1;
236         }
237
238         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
239
240         return xattr_tdb_getattr(db, &id, name, value, size);
241 }
242
243 /*
244  * Worker routine for setxattr and fsetxattr
245  */
246
247 static int xattr_tdb_setattr(struct db_context *db_ctx,
248                              const struct file_id *id, const char *name,
249                              const void *value, size_t size, int flags)
250 {
251         NTSTATUS status;
252         struct db_record *rec;
253         struct tdb_xattrs *attribs;
254         uint32_t i;
255
256         DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
257                    file_id_string_tos(id), name));
258
259         rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
260
261         if (rec == NULL) {
262                 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
263                 errno = EINVAL;
264                 return -1;
265         }
266
267         status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
268
269         if (!NT_STATUS_IS_OK(status)) {
270                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
271                            nt_errstr(status)));
272                 TALLOC_FREE(rec);
273                 return -1;
274         }
275
276         for (i=0; i<attribs->num_xattrs; i++) {
277                 if (strcmp(attribs->xattrs[i].name, name) == 0) {
278                         if (flags & XATTR_CREATE) {
279                                 TALLOC_FREE(rec);
280                                 errno = EEXIST;
281                                 return -1;
282                         }
283                         break;
284                 }
285         }
286
287         if (i == attribs->num_xattrs) {
288                 struct tdb_xattr *tmp;
289
290                 if (flags & XATTR_REPLACE) {
291                         TALLOC_FREE(rec);
292                         errno = ENOATTR;
293                         return -1;
294                 }
295
296                 tmp = TALLOC_REALLOC_ARRAY(
297                         attribs, attribs->xattrs, struct tdb_xattr,
298                         attribs->num_xattrs + 1);
299
300                 if (tmp == NULL) {
301                         DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
302                         TALLOC_FREE(rec);
303                         errno = ENOMEM;
304                         return -1;
305                 }
306
307                 attribs->xattrs = tmp;
308                 attribs->num_xattrs += 1;
309         }
310
311         attribs->xattrs[i].name = name;
312         attribs->xattrs[i].value.data = CONST_DISCARD(uint8 *, value);
313         attribs->xattrs[i].value.length = size;
314
315         status = xattr_tdb_save_attrs(rec, attribs);
316
317         TALLOC_FREE(rec);
318
319         if (!NT_STATUS_IS_OK(status)) {
320                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
321                 return -1;
322         }
323
324         return 0;
325 }
326
327 static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
328                               const char *path, const char *name,
329                               const void *value, size_t size, int flags)
330 {
331         SMB_STRUCT_STAT sbuf;
332         struct file_id id;
333         struct db_context *db;
334
335         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
336
337         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
338                 return -1;
339         }
340
341         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
342
343         return xattr_tdb_setattr(db, &id, name, value, size, flags);
344 }
345
346 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
347                                struct files_struct *fsp,
348                                const char *name, const void *value,
349                                size_t size, int flags)
350 {
351         SMB_STRUCT_STAT sbuf;
352         struct file_id id;
353         struct db_context *db;
354
355         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
356
357         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
358                 return -1;
359         }
360
361         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
362
363         return xattr_tdb_setattr(db, &id, name, value, size, flags);
364 }
365
366 /*
367  * Worker routine for listxattr and flistxattr
368  */
369
370 static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
371                                   const struct file_id *id, char *list,
372                                   size_t size)
373 {
374         NTSTATUS status;
375         struct tdb_xattrs *attribs;
376         uint32_t i;
377         size_t len = 0;
378
379         status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
380
381         if (!NT_STATUS_IS_OK(status)) {
382                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
383                            nt_errstr(status)));
384                 errno = EINVAL;
385                 return -1;
386         }
387
388         DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
389                    attribs->num_xattrs));
390
391         for (i=0; i<attribs->num_xattrs; i++) {
392                 size_t tmp;
393
394                 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
395                            attribs->xattrs[i].name));
396
397                 tmp = strlen(attribs->xattrs[i].name);
398
399                 /*
400                  * Try to protect against overflow
401                  */
402
403                 if (len + (tmp+1) < len) {
404                         TALLOC_FREE(attribs);
405                         errno = EINVAL;
406                         return -1;
407                 }
408
409                 /*
410                  * Take care of the terminating NULL
411                  */
412                 len += (tmp + 1);
413         }
414
415         if (len > size) {
416                 TALLOC_FREE(attribs);
417                 errno = ERANGE;
418                 return -1;
419         }
420
421         len = 0;
422
423         for (i=0; i<attribs->num_xattrs; i++) {
424                 strlcpy(list+len, attribs->xattrs[i].name,
425                         size-len);
426                 len += (strlen(attribs->xattrs[i].name) + 1);
427         }
428
429         TALLOC_FREE(attribs);
430         return len;
431 }
432
433 static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
434                                    const char *path, char *list, size_t size)
435 {
436         SMB_STRUCT_STAT sbuf;
437         struct file_id id;
438         struct db_context *db;
439
440         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
441
442         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
443                 return -1;
444         }
445
446         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
447
448         return xattr_tdb_listattr(db, &id, list, size);
449 }
450
451 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
452                                     struct files_struct *fsp, char *list,
453                                     size_t size)
454 {
455         SMB_STRUCT_STAT sbuf;
456         struct file_id id;
457         struct db_context *db;
458
459         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
460
461         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
462                 return -1;
463         }
464
465         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
466
467         return xattr_tdb_listattr(db, &id, list, size);
468 }
469
470 /*
471  * Worker routine for removexattr and fremovexattr
472  */
473
474 static int xattr_tdb_removeattr(struct db_context *db_ctx,
475                                 const struct file_id *id, const char *name)
476 {
477         NTSTATUS status;
478         struct db_record *rec;
479         struct tdb_xattrs *attribs;
480         uint32_t i;
481
482         rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
483
484         if (rec == NULL) {
485                 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
486                 errno = EINVAL;
487                 return -1;
488         }
489
490         status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
491
492         if (!NT_STATUS_IS_OK(status)) {
493                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
494                            nt_errstr(status)));
495                 TALLOC_FREE(rec);
496                 return -1;
497         }
498
499         for (i=0; i<attribs->num_xattrs; i++) {
500                 if (strcmp(attribs->xattrs[i].name, name) == 0) {
501                         break;
502                 }
503         }
504
505         if (i == attribs->num_xattrs) {
506                 TALLOC_FREE(rec);
507                 errno = ENOATTR;
508                 return -1;
509         }
510
511         attribs->xattrs[i] =
512                 attribs->xattrs[attribs->num_xattrs-1];
513         attribs->num_xattrs -= 1;
514
515         if (attribs->num_xattrs == 0) {
516                 rec->delete_rec(rec);
517                 TALLOC_FREE(rec);
518                 return 0;
519         }
520
521         status = xattr_tdb_save_attrs(rec, attribs);
522
523         TALLOC_FREE(rec);
524
525         if (!NT_STATUS_IS_OK(status)) {
526                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
527                 return -1;
528         }
529
530         return 0;
531 }
532
533 static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
534                                  const char *path, const char *name)
535 {
536         SMB_STRUCT_STAT sbuf;
537         struct file_id id;
538         struct db_context *db;
539
540         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
541
542         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
543                 return -1;
544         }
545
546         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
547
548         return xattr_tdb_removeattr(db, &id, name);
549 }
550
551 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
552                                   struct files_struct *fsp, const char *name)
553 {
554         SMB_STRUCT_STAT sbuf;
555         struct file_id id;
556         struct db_context *db;
557
558         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
559
560         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
561                 return -1;
562         }
563
564         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
565
566         return xattr_tdb_removeattr(db, &id, name);
567 }
568
569 /*
570  * Open the tdb file upon VFS_CONNECT
571  */
572
573 static bool xattr_tdb_init(int snum, struct db_context **p_db)
574 {
575         struct db_context *db;
576         const char *dbname;
577
578         dbname = lp_parm_const_string(snum, "xattr_tdb", "file",
579                                       lock_path("xattr.tdb"));
580
581         if (dbname == NULL) {
582                 errno = ENOSYS;
583                 return false;
584         }
585
586         become_root();
587         db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
588         unbecome_root();
589
590         if (db == NULL) {
591 #if defined(ENOTSUP)
592                 errno = ENOTSUP;
593 #else
594                 errno = ENOSYS;
595 #endif
596                 return false;
597         }
598
599         *p_db = db;
600         return true;
601 }
602
603 /*
604  * On unlink we need to delete the tdb record
605  */
606 static int xattr_tdb_unlink(vfs_handle_struct *handle, const char *path)
607 {
608         SMB_STRUCT_STAT sbuf;
609         struct file_id id;
610         struct db_context *db;
611         struct db_record *rec;
612         int ret;
613
614         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
615
616         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
617                 return -1;
618         }
619
620         ret = SMB_VFS_NEXT_UNLINK(handle, path);
621
622         if (ret == -1) {
623                 return -1;
624         }
625
626         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
627
628         rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
629
630         /*
631          * If rec == NULL there's not much we can do about it
632          */
633
634         if (rec != NULL) {
635                 rec->delete_rec(rec);
636                 TALLOC_FREE(rec);
637         }
638
639         return 0;
640 }
641
642 /*
643  * On rmdir we need to delete the tdb record
644  */
645 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
646 {
647         SMB_STRUCT_STAT sbuf;
648         struct file_id id;
649         struct db_context *db;
650         struct db_record *rec;
651         int ret;
652
653         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
654
655         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
656                 return -1;
657         }
658
659         ret = SMB_VFS_NEXT_RMDIR(handle, path);
660
661         if (ret == -1) {
662                 return -1;
663         }
664
665         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
666
667         rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
668
669         /*
670          * If rec == NULL there's not much we can do about it
671          */
672
673         if (rec != NULL) {
674                 rec->delete_rec(rec);
675                 TALLOC_FREE(rec);
676         }
677
678         return 0;
679 }
680
681 /*
682  * Destructor for the VFS private data
683  */
684
685 static void close_xattr_db(void **data)
686 {
687         struct db_context **p_db = (struct db_context **)data;
688         TALLOC_FREE(*p_db);
689 }
690
691 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
692                           const char *user)
693 {
694         fstring sname;
695         int res, snum;
696         struct db_context *db;
697
698         res = SMB_VFS_NEXT_CONNECT(handle, service, user);
699         if (res < 0) {
700                 return res;
701         }
702
703         fstrcpy(sname, service);
704         snum = find_service(sname);
705         if (snum == -1) {
706                 /*
707                  * Should not happen, but we should not fail just *here*.
708                  */
709                 return 0;
710         }
711
712         if (!xattr_tdb_init(snum, &db)) {
713                 DEBUG(5, ("Could not init xattr tdb\n"));
714                 lp_do_parameter(snum, "ea support", "False");
715                 return 0;
716         }
717
718         lp_do_parameter(snum, "ea support", "True");
719
720         SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
721                                 struct db_context, return -1);
722
723         return 0;
724 }
725
726 /* VFS operations structure */
727
728 static const vfs_op_tuple xattr_tdb_ops[] = {
729         {SMB_VFS_OP(xattr_tdb_getxattr), SMB_VFS_OP_GETXATTR,
730          SMB_VFS_LAYER_TRANSPARENT},
731         {SMB_VFS_OP(xattr_tdb_fgetxattr), SMB_VFS_OP_FGETXATTR,
732          SMB_VFS_LAYER_TRANSPARENT},
733         {SMB_VFS_OP(xattr_tdb_setxattr), SMB_VFS_OP_SETXATTR,
734          SMB_VFS_LAYER_TRANSPARENT},
735         {SMB_VFS_OP(xattr_tdb_fsetxattr), SMB_VFS_OP_FSETXATTR,
736          SMB_VFS_LAYER_TRANSPARENT},
737         {SMB_VFS_OP(xattr_tdb_listxattr), SMB_VFS_OP_LISTXATTR,
738          SMB_VFS_LAYER_TRANSPARENT},
739         {SMB_VFS_OP(xattr_tdb_flistxattr), SMB_VFS_OP_FLISTXATTR,
740          SMB_VFS_LAYER_TRANSPARENT},
741         {SMB_VFS_OP(xattr_tdb_removexattr), SMB_VFS_OP_REMOVEXATTR,
742          SMB_VFS_LAYER_TRANSPARENT},
743         {SMB_VFS_OP(xattr_tdb_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
744          SMB_VFS_LAYER_TRANSPARENT},
745         {SMB_VFS_OP(xattr_tdb_unlink), SMB_VFS_OP_UNLINK,
746          SMB_VFS_LAYER_TRANSPARENT},
747         {SMB_VFS_OP(xattr_tdb_rmdir), SMB_VFS_OP_RMDIR,
748          SMB_VFS_LAYER_TRANSPARENT},
749         {SMB_VFS_OP(xattr_tdb_connect), SMB_VFS_OP_CONNECT,
750          SMB_VFS_LAYER_TRANSPARENT},
751         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
752 };
753
754 NTSTATUS vfs_xattr_tdb_init(void);
755 NTSTATUS vfs_xattr_tdb_init(void)
756 {
757         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
758                                 xattr_tdb_ops);
759 }