8cf04d79ad935ae4f60b2beffab368372316ed8e
[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 ea_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 ea_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 ea_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 = ea_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 *ea_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 ea_tdb_save_attrs(struct db_record *rec,
135                                   const struct tdb_xattrs *attribs)
136 {
137         TDB_DATA data;
138         NTSTATUS status;
139
140         status = ea_tdb_push_attrs(talloc_tos(), attribs, &data);
141
142         if (!NT_STATUS_IS_OK(status)) {
143                 DEBUG(0, ("ea_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 ea_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 = ea_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
169
170         if (!NT_STATUS_IS_OK(status)) {
171                 DEBUG(10, ("ea_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 ea_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 ea_tdb_getattr(db, &id, name, value, size);
219 }
220
221 static ssize_t ea_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 ea_tdb_getattr(db, &id, name, value, size);
238 }
239
240 /*
241  * Worker routine for setxattr and fsetxattr
242  */
243
244 static int ea_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 = ea_tdb_lock_attrs(talloc_tos(), db_ctx, id);
254
255         if (rec == NULL) {
256                 DEBUG(0, ("ea_tdb_lock_attrs failed\n"));
257                 errno = EINVAL;
258                 return -1;
259         }
260
261         status = ea_tdb_pull_attrs(rec, &rec->value, &attribs);
262
263         if (!NT_STATUS_IS_OK(status)) {
264                 DEBUG(10, ("ea_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 = ea_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 ea_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 ea_tdb_setattr(db, &id, name, value, size, flags);
327 }
328
329 static int ea_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 ea_tdb_setattr(db, &id, name, value, size, flags);
347 }
348
349 /*
350  * Worker routine for listxattr and flistxattr
351  */
352
353 static ssize_t ea_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 = ea_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
363
364         if (!NT_STATUS_IS_OK(status)) {
365                 DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
366                            nt_errstr(status)));
367                 errno = EINVAL;
368                 return -1;
369         }
370
371         DEBUG(10, ("ea_tdb_listattr: Found %d xattrs\n", attribs->num_xattrs));
372
373         for (i=0; i<attribs->num_xattrs; i++) {
374                 size_t tmp;
375
376                 DEBUG(10, ("ea_tdb_listattr: xattrs[i].name: %s\n",
377                            attribs->xattrs[i].name));
378
379                 tmp = strlen(attribs->xattrs[i].name);
380
381                 /*
382                  * Try to protect against overflow
383                  */
384
385                 if (len + (tmp+1) < len) {
386                         TALLOC_FREE(attribs);
387                         errno = EINVAL;
388                         return -1;
389                 }
390
391                 /*
392                  * Take care of the terminating NULL
393                  */
394                 len += (tmp + 1);
395         }
396
397         if (len > size) {
398                 TALLOC_FREE(attribs);
399                 errno = ERANGE;
400                 return -1;
401         }
402
403         len = 0;
404
405         for (i=0; i<attribs->num_xattrs; i++) {
406                 strlcpy(list+len, attribs->xattrs[i].name,
407                         size-len);
408                 len += (strlen(attribs->xattrs[i].name) + 1);
409         }
410
411         TALLOC_FREE(attribs);
412         return len;
413 }
414
415 static ssize_t ea_tdb_listxattr(struct vfs_handle_struct *handle,
416                                 const char *path, char *list, size_t size)
417 {
418         SMB_STRUCT_STAT sbuf;
419         struct file_id id;
420         struct db_context *db;
421
422         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
423
424         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
425                 return -1;
426         }
427
428         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
429
430         return ea_tdb_listattr(db, &id, list, size);
431 }
432
433 static ssize_t ea_tdb_flistxattr(struct vfs_handle_struct *handle,
434                                  struct files_struct *fsp, char *list,
435                                  size_t size)
436 {
437         SMB_STRUCT_STAT sbuf;
438         struct file_id id;
439         struct db_context *db;
440
441         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
442
443         if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
444                 return -1;
445         }
446
447         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
448
449         return ea_tdb_listattr(db, &id, list, size);
450 }
451
452 /*
453  * Worker routine for removexattr and fremovexattr
454  */
455
456 static int ea_tdb_removeattr(struct db_context *db_ctx,
457                              const struct file_id *id, const char *name)
458 {
459         NTSTATUS status;
460         struct db_record *rec;
461         struct tdb_xattrs *attribs;
462         uint32_t i;
463
464         rec = ea_tdb_lock_attrs(talloc_tos(), db_ctx, id);
465
466         if (rec == NULL) {
467                 DEBUG(0, ("ea_tdb_lock_attrs failed\n"));
468                 errno = EINVAL;
469                 return -1;
470         }
471
472         status = ea_tdb_pull_attrs(rec, &rec->value, &attribs);
473
474         if (!NT_STATUS_IS_OK(status)) {
475                 DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
476                            nt_errstr(status)));
477                 TALLOC_FREE(rec);
478                 return -1;
479         }
480
481         for (i=0; i<attribs->num_xattrs; i++) {
482                 if (strcmp(attribs->xattrs[i].name, name) == 0) {
483                         break;
484                 }
485         }
486
487         if (i == attribs->num_xattrs) {
488                 TALLOC_FREE(rec);
489                 errno = ENOATTR;
490                 return -1;
491         }
492
493         attribs->xattrs[i] =
494                 attribs->xattrs[attribs->num_xattrs-1];
495         attribs->num_xattrs -= 1;
496
497         if (attribs->num_xattrs == 0) {
498                 rec->delete_rec(rec);
499                 TALLOC_FREE(rec);
500                 return 0;
501         }
502
503         status = ea_tdb_save_attrs(rec, attribs);
504
505         TALLOC_FREE(rec);
506
507         if (!NT_STATUS_IS_OK(status)) {
508                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
509                 return -1;
510         }
511
512         return 0;
513 }
514
515 static int ea_tdb_removexattr(struct vfs_handle_struct *handle,
516                               const char *path, const char *name)
517 {
518         SMB_STRUCT_STAT sbuf;
519         struct file_id id;
520         struct db_context *db;
521
522         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
523
524         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
525                 return -1;
526         }
527
528         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
529
530         return ea_tdb_removeattr(db, &id, name);
531 }
532
533 static int ea_tdb_fremovexattr(struct vfs_handle_struct *handle,
534                                struct files_struct *fsp, 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_FSTAT(fsp, &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 ea_tdb_removeattr(db, &id, name);
549 }
550
551 /*
552  * Open the tdb file upon VFS_CONNECT
553  */
554
555 static bool ea_tdb_init(int snum, struct db_context **p_db)
556 {
557         struct db_context *db;
558         const char *dbname;
559
560         dbname = lp_parm_const_string(snum, "ea", "tdb", lock_path("eas.tdb"));
561
562         if (dbname == NULL) {
563                 errno = ENOTSUP;
564                 return false;
565         }
566
567         become_root();
568         db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
569         unbecome_root();
570
571         if (db == NULL) {
572                 errno = ENOTSUP;
573                 return false;
574         }
575
576         *p_db = db;
577         return true;
578 }
579
580 /*
581  * On unlink we need to delete the tdb record
582  */
583 static int ea_tdb_unlink(vfs_handle_struct *handle, const char *path)
584 {
585         SMB_STRUCT_STAT sbuf;
586         struct file_id id;
587         struct db_context *db;
588         struct db_record *rec;
589         int ret;
590
591         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
592
593         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
594                 return -1;
595         }
596
597         ret = SMB_VFS_NEXT_UNLINK(handle, path);
598
599         if (ret == -1) {
600                 return -1;
601         }
602
603         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
604
605         rec = ea_tdb_lock_attrs(talloc_tos(), db, &id);
606
607         /*
608          * If rec == NULL there's not much we can do about it
609          */
610
611         if (rec != NULL) {
612                 rec->delete_rec(rec);
613                 TALLOC_FREE(rec);
614         }
615
616         return 0;
617 }
618
619 /*
620  * On rmdir we need to delete the tdb record
621  */
622 static int ea_tdb_rmdir(vfs_handle_struct *handle, const char *path)
623 {
624         SMB_STRUCT_STAT sbuf;
625         struct file_id id;
626         struct db_context *db;
627         struct db_record *rec;
628         int ret;
629
630         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
631
632         if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
633                 return -1;
634         }
635
636         ret = SMB_VFS_NEXT_RMDIR(handle, path);
637
638         if (ret == -1) {
639                 return -1;
640         }
641
642         id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
643
644         rec = ea_tdb_lock_attrs(talloc_tos(), db, &id);
645
646         /*
647          * If rec == NULL there's not much we can do about it
648          */
649
650         if (rec != NULL) {
651                 rec->delete_rec(rec);
652                 TALLOC_FREE(rec);
653         }
654
655         return 0;
656 }
657
658 /*
659  * Destructor for the VFS private data
660  */
661
662 static void close_ea_db(void **data)
663 {
664         struct db_context **p_db = (struct db_context **)data;
665         TALLOC_FREE(*p_db);
666 }
667
668 static int ea_tdb_connect(vfs_handle_struct *handle, const char *service,
669                           const char *user)
670 {
671         fstring sname;
672         int res, snum;
673         struct db_context *db;
674
675         res = SMB_VFS_NEXT_CONNECT(handle, service, user);
676         if (res < 0) {
677                 return res;
678         }
679
680         fstrcpy(sname, service);
681         snum = find_service(sname);
682         if (snum == -1) {
683                 /*
684                  * Should not happen, but we should not fail just *here*.
685                  */
686                 return 0;
687         }
688
689         if (!ea_tdb_init(snum, &db)) {
690                 DEBUG(5, ("Could not init ea tdb\n"));
691                 lp_do_parameter(snum, "ea support", "False");
692                 return 0;
693         }
694
695         lp_do_parameter(snum, "ea support", "True");
696
697         SMB_VFS_HANDLE_SET_DATA(handle, db, close_ea_db,
698                                 struct db_context, return -1);
699
700         return 0;
701 }
702
703 /* VFS operations structure */
704
705 static const vfs_op_tuple ea_tdb_ops[] = {
706         {SMB_VFS_OP(ea_tdb_getxattr), SMB_VFS_OP_GETXATTR,
707          SMB_VFS_LAYER_TRANSPARENT},
708         {SMB_VFS_OP(ea_tdb_fgetxattr), SMB_VFS_OP_FGETXATTR,
709          SMB_VFS_LAYER_TRANSPARENT},
710         {SMB_VFS_OP(ea_tdb_setxattr), SMB_VFS_OP_SETXATTR,
711          SMB_VFS_LAYER_TRANSPARENT},
712         {SMB_VFS_OP(ea_tdb_fsetxattr), SMB_VFS_OP_FSETXATTR,
713          SMB_VFS_LAYER_TRANSPARENT},
714         {SMB_VFS_OP(ea_tdb_listxattr), SMB_VFS_OP_LISTXATTR,
715          SMB_VFS_LAYER_TRANSPARENT},
716         {SMB_VFS_OP(ea_tdb_flistxattr), SMB_VFS_OP_FLISTXATTR,
717          SMB_VFS_LAYER_TRANSPARENT},
718         {SMB_VFS_OP(ea_tdb_removexattr), SMB_VFS_OP_REMOVEXATTR,
719          SMB_VFS_LAYER_TRANSPARENT},
720         {SMB_VFS_OP(ea_tdb_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
721          SMB_VFS_LAYER_TRANSPARENT},
722         {SMB_VFS_OP(ea_tdb_unlink), SMB_VFS_OP_UNLINK,
723          SMB_VFS_LAYER_TRANSPARENT},
724         {SMB_VFS_OP(ea_tdb_rmdir), SMB_VFS_OP_RMDIR,
725          SMB_VFS_LAYER_TRANSPARENT},
726         {SMB_VFS_OP(ea_tdb_connect), SMB_VFS_OP_CONNECT,
727          SMB_VFS_LAYER_TRANSPARENT},
728         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
729 };
730
731 NTSTATUS vfs_ea_tdb_init(void);
732 NTSTATUS vfs_ea_tdb_init(void)
733 {
734         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ea_tdb",
735                                 ea_tdb_ops);
736 }