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