bcd7ae3a91f44f7551680ad667d68749b7a756d9
[samba.git] / source3 / modules / vfs_vxfs.c
1 /*
2 Unix SMB/CIFS implementation.
3 Wrap VxFS calls in vfs functions.
4 This module is for ACL handling.
5
6 Copyright (C) Symantec Corporation <www.symantec.com> 2014
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "../libcli/security/security.h"
26 #include "../librpc/gen_ndr/ndr_security.h"
27 #include "system/filesys.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_VFS
31
32 #define MODULE_NAME "vxfs"
33
34 /*
35  * WARNING !! WARNING !!
36  *
37  * DO NOT CHANGE THIS FROM "system." space to
38  * "user." space unless you are shipping a product
39  * that RESTRICTS access to extended attributes
40  * to smbd-only. "system." space is restricted
41  * to root access only, "user." space is available
42  * to ANY USER.
43  *
44  * If this is changed to "user." and access
45  * to extended attributes is available via
46  * local processes or other remote file system
47  * (e.g. NFS) then the security of the system
48  * WILL BE COMPROMISED. i.e. non-root users
49  * WILL be able to overwrite Samba ACLs on
50  * the file system.
51  *
52  * If you need to modify this define, do
53  * so using CFLAGS on your build command
54  * line.
55  * e.g. CFLAGS=-DXATTR_USER_NTACL="user.NTACL"
56  *
57  * Added by: <jra@samba.org> 17 Sept. 2014.
58  *
59  */
60
61 #ifndef XATTR_USER_NTACL
62 #define XATTR_USER_NTACL "system.NTACL"
63 #endif
64
65 /* type values */
66 #define VXFS_ACL_UNDEFINED_TYPE  0
67 #define VXFS_ACL_USER_OBJ        1
68 #define VXFS_ACL_GROUP_OBJ       2
69 #define VXFS_ACL_USER            3
70 #define VXFS_ACL_GROUP           4
71 #define VXFS_ACL_OTHER           5
72 #define VXFS_ACL_MASK            6
73
74
75 /*
76  * Compare aces
77  * This will compare two ace entries for sorting
78  * each entry contains: type, perms and id
79  * Sort by type first, if type is same sort by id.
80  */
81 static int vxfs_ace_cmp(const void *ace1, const void *ace2)
82 {
83         int ret = 0;
84         uint16_t type_a1, type_a2;
85         uint32_t id_a1, id_a2;
86
87         /* Type must be compared first */
88         type_a1 = SVAL(ace1, 0);
89         type_a2 = SVAL(ace2, 0);
90
91         ret = (type_a1 - type_a2);
92         if (!ret) {
93                 /* Compare ID under type */
94                 /* skip perm thus take offset as 4*/
95                 id_a1 = IVAL(ace1, 4);
96                 id_a2 = IVAL(ace2, 4);
97                 ret = id_a1 - id_a2;
98         }
99
100         return ret;
101 }
102
103 static void vxfs_print_ace_buf(char *buf, int count) {
104
105         int i, offset = 0;
106         uint16_t type, perm;
107         uint32_t id;
108
109         DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
110         for (i = 0; i < count; i++) {
111                 type = SVAL(buf, offset);
112                 offset += 2;
113                 perm = SVAL(buf, offset);
114                 offset += 2;
115                 id = IVAL(buf, offset);
116                 offset += 4;
117
118                 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
119                           (unsigned int)type, (unsigned int)perm,
120                           (unsigned int)id));
121         }
122 }
123
124 /*
125  * Sort aces so that comparing 2 ACLs will be straight forward.
126  * This function will fill buffer as follows:
127  * For each ace:
128  *      1. ace->a_type will be filled as first 2 bytes in buf.
129  *      2. ace->a_perm will be filled as next 2 bytes.
130  *      3. ace->xid will be filled as next 4 bytes.
131  * Thus each ace entry in buf is equal to 8 bytes.
132  * Also a_type is mapped to VXFS_ACL_* so that ordering aces
133  * becomes easy.
134  */
135 static char * vxfs_sort_acl(SMB_ACL_T theacl, TALLOC_CTX *mem_ctx,
136                             uint32_t o_uid,
137                             uint32_t o_gid) {
138
139         struct smb_acl_entry *smb_ace;
140         int i, count;
141         uint16_t type, perm;
142         uint32_t id;
143         int offset = 0;
144         char *buf = NULL;
145
146         count = theacl->count;
147
148         buf = talloc_zero_size(mem_ctx, count * 8);
149         if (!buf) {
150                 return NULL;
151         }
152
153         smb_ace = theacl->acl;
154
155         for (i = 0; i < count; i++) {
156                 /* Calculate type */
157                 /* Map type to SMB_ACL_* to VXFS_ACL_* */
158                 switch(smb_ace->a_type) {
159                 case SMB_ACL_USER:
160                         type = VXFS_ACL_USER;
161                         break;
162                 case SMB_ACL_USER_OBJ:
163                         type = VXFS_ACL_USER_OBJ;
164                         break;
165                 case SMB_ACL_GROUP:
166                         type = VXFS_ACL_GROUP;
167                         break;
168                 case SMB_ACL_GROUP_OBJ:
169                         type = VXFS_ACL_GROUP_OBJ;
170                         break;
171                 case SMB_ACL_OTHER:
172                         type = VXFS_ACL_OTHER;
173                         break;
174                 case SMB_ACL_MASK:
175                         type = VXFS_ACL_MASK;
176                         break;
177                 default:
178                         type = -1;
179                         talloc_free(buf);
180                         return NULL;
181                 }
182
183                 type = type & 0xff;
184
185                 /* Calculate id:
186                  * We get owner uid and owner group gid in o_uid and o_gid
187                  * Put these ids instead of -1
188                  */
189                 switch(smb_ace->a_type) {
190                 case SMB_ACL_USER:
191                         id = smb_ace->info.user.uid;
192                         break;
193                 case SMB_ACL_GROUP:
194                         id = smb_ace->info.group.gid;
195                         break;
196                 case SMB_ACL_USER_OBJ:
197                         id = o_uid;
198                         break;
199                 case SMB_ACL_GROUP_OBJ:
200                         id = o_gid;
201                         break;
202                 case SMB_ACL_MASK:
203                 case SMB_ACL_OTHER:
204                         id = -1;
205                         break;
206                 default:
207                         /* Can't happen.. */
208                         id = -1;
209                         break;
210                 }
211
212                 /* Calculate perm */
213                 perm = smb_ace->a_perm & 0xff;
214
215                 /* TYPE is the first 2 bytes of an entry */
216                 SSVAL(buf, offset, type);
217                 offset += 2;
218
219                 /* PERM is the next 2 bytes of an entry */
220                 SSVAL(buf, offset, perm);
221                 offset += 2;
222
223                 /* ID is the last 4 bytes of an entry */
224                 SIVAL(buf, offset, id);
225                 offset += 4;
226
227                 smb_ace++;
228         }
229
230         qsort(buf, count, 8, vxfs_ace_cmp);
231
232         DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
233         vxfs_print_ace_buf(buf, count);
234
235         return buf;
236 }
237
238 /* This function gets e_buf as an arg which is sorted and created out of
239  * existing ACL. This function will compact this e_buf to c_buf where USER
240  * and GROUP aces matching with USER_OBJ and GROUP_OBJ will be merged
241  * respectively.
242  * This is similar to what posix_acls.c does. This will make sure existing
243  * acls are converted much similar to what posix_acls calculates.
244  */
245
246 static char * vxfs_compact_buf(char *e_buf, int *new_count, int count,
247                                TALLOC_CTX *mem_ctx)
248 {
249         int i, e_offset = 0, c_offset = 0;
250         uint16_t type, perm, o_perm;
251         uint32_t id, owner_id, group_id;
252         char *c_buf = NULL;
253
254
255         if (count < 2) {
256                 return NULL;
257         }
258
259         c_buf = talloc_zero_size(mem_ctx, count * 8);
260         if (!c_buf) {
261                 return NULL;
262         }
263
264         /*Copy first two enries from e_buf to c_buf
265          *These are USER_OBJ and GROUP_OBJ
266          */
267
268         memcpy(c_buf, e_buf, 16);
269
270         (*new_count) = 2;
271
272         owner_id = IVAL(e_buf, 4);
273         group_id = IVAL(e_buf, 12);
274
275         c_offset = e_offset = 16;
276
277         /* Start comparing other entries */
278         for (i = 2; i < count; i++) {
279
280                 type = SVAL(e_buf, e_offset);
281                 e_offset += 2;
282                 perm = SVAL(e_buf, e_offset);
283                 e_offset += 2;
284                 id = IVAL(e_buf, e_offset);
285                 e_offset += 4;
286
287                 switch(type) {
288                 case VXFS_ACL_USER:
289                         if (id == owner_id) {
290                                 o_perm = SVAL(c_buf, 2);
291                                 o_perm |= perm;
292                                 SSVAL(c_buf, 2, o_perm);
293                                 DEBUG(10, ("vfs_vxfs: merging with owner"
294                                           "e_type = %u,"
295                                           "e_perm = %u,"
296                                           "e_id = %u\n", (unsigned int)type,
297                                           (unsigned int)perm,
298                                           (unsigned int)id));
299                                 continue;
300                         }
301                         break;
302                 case VXFS_ACL_GROUP:
303                         if (id == group_id) {
304                                 o_perm = SVAL(c_buf, 10);
305                                 o_perm |= perm;
306                                 SSVAL(c_buf, 10, o_perm);
307                                 DEBUG(10, ("vfs_vxfs: merging with owner group"
308                                           "e_type = %u,"
309                                           "e_perm = %u,"
310                                           "e_id = %u\n", (unsigned int)type,
311                                           (unsigned int)perm,
312                                           (unsigned int)id));
313                                 continue;
314                         }
315                         break;
316                 }
317
318                 SSVAL(c_buf, c_offset, type);
319                 c_offset += 2;
320
321                 SSVAL(c_buf, c_offset, perm);
322                 c_offset += 2;
323
324                 SIVAL(c_buf, c_offset, id);
325                 c_offset += 4;
326
327                 (*new_count)++;
328         }
329         DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count));
330         return c_buf;
331 }
332
333 /* Actually compare New ACL and existing ACL buf */
334 static bool vxfs_compare_acls(char *e_buf, char *n_buf, int n_count,
335                               int e_count) {
336
337         uint16_t e_type, n_type, e_perm, n_perm;
338         uint32_t e_id, n_id;
339         int i, offset = 0;
340
341         if (!e_buf && !n_buf) {
342                 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
343                 return false;
344         }
345
346         if ((e_count < 2) || (n_count < 2)) {
347                 return false;
348         }
349         /*Get type from last entry from both buffers.
350          * It may or may not be ACL_MASK
351          */
352         n_type = SVAL(n_buf, offset + (8 * (n_count-1)));
353         e_type = SVAL(e_buf, offset + (8 * (e_count-1)));
354
355         /* Check for ACL_MASK entry properly. Handle all 4 cases*/
356
357         /* If ACL_MASK entry is present in any of the buffers,
358          * it will be always the last one. Calculate count to compare
359          * based on if ACL_MASK is present on new and existing ACL
360          */
361         if ((n_type != VXFS_ACL_MASK) && (e_type == VXFS_ACL_MASK)){
362                 DEBUG(10, ("vfs_vxfs: New ACL does not have mask entry,"
363                            "reduce count by 1 and compare\n"));
364                 e_count = e_count -1;
365         }
366         if ((n_type == VXFS_ACL_MASK) && (e_type != VXFS_ACL_MASK)){
367                 DEBUG(10, ("vfs_vxfs: new ACL to be set contains mask"
368                            "existing ACL does not have mask entry\n"
369                            "Need to set New ACL\n"));
370                 return false;
371         }
372
373         if (memcmp(e_buf, n_buf, (e_count * 8)) != 0) {
374                 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
375                            "buffers not same!\n"));
376                 return false;
377         }
378
379         return true;
380 }
381
382 /* In VxFS, POSIX ACLs are pointed by separate inode for each file/dir.
383  * However, files/dir share same POSIX ACL inode if ACLs are inherited
384  * from parent.
385  * To retain this behaviour, below function avoids ACL set call if
386  * underlying ACLs are already same and thus saves creating extra inode.
387  *
388  * This function will execute following steps:
389  * 1. Get existing ACL
390  * 2. Sort New ACL and existing ACL into buffers
391  * 3. Compact existing ACL buf
392  * 4. Finally compare New ACL buf and Compact buf
393  * 5. If same, return true
394  * 6. Else need to set New ACL
395  */
396
397 static bool vxfs_compare(connection_struct *conn, char *name, SMB_ACL_T the_acl,
398                          SMB_ACL_TYPE_T the_acl_type)
399 {
400         SMB_ACL_T existing_acl = NULL;
401         bool ret = false;
402         int i, count = 0;
403         TALLOC_CTX *mem_ctx = talloc_tos();
404         char *existing_buf = NULL, *new_buf = NULL, *compact_buf = NULL;
405         struct smb_filename *smb_fname = NULL;
406         int status;
407
408         DEBUG(10, ("vfs_vxfs: Getting existing ACL for %s\n", name));
409         existing_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, the_acl_type,
410                                                 mem_ctx);
411         if (existing_acl == NULL) {
412                 DEBUG(10, ("vfs_vxfs: Failed to get ACL\n"));
413                 goto out;
414         }
415
416         DEBUG(10, ("vfs_vxfs: Existing ACL count=%d\n", existing_acl->count));
417         DEBUG(10, ("vfs_vxfs: New ACL count=%d\n", the_acl->count));
418
419         if (existing_acl->count == 0) {
420                 DEBUG(10, ("vfs_vxfs: ACL count is 0, Need to set\n"));
421                 goto out;
422         }
423
424         smb_fname = synthetic_smb_fname(mem_ctx, name, NULL, NULL, 0);
425         if (smb_fname == NULL) {
426                 DEBUG(10, ("vfs_vxfs: Failed to create smb_fname\n"));
427                 goto out;
428         }
429
430         status = SMB_VFS_STAT(conn, smb_fname);
431         if (status == -1) {
432                 DEBUG(10, ("vfs_vxfs: stat failed!\n"));
433                 goto out;
434         }
435
436         DEBUG(10, ("vfs_vxfs: Sorting existing ACL\n"));
437         existing_buf = vxfs_sort_acl(existing_acl, mem_ctx,
438                                      smb_fname->st.st_ex_uid,
439                                      smb_fname->st.st_ex_gid);
440         if (!existing_buf)
441                 goto out;
442
443         DEBUG(10, ("vfs_vxfs: Sorting new ACL\n"));
444         new_buf = vxfs_sort_acl(the_acl, mem_ctx, smb_fname->st.st_ex_uid,
445                                 smb_fname->st.st_ex_gid);
446         if (!new_buf) {
447                 goto out;
448         }
449
450         DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
451         compact_buf = vxfs_compact_buf(existing_buf, &count,
452                                        existing_acl->count,
453                                        mem_ctx);
454         if (!compact_buf) {
455                 goto out;
456         }
457
458         vxfs_print_ace_buf(compact_buf, count);
459
460         /* COmpare ACLs only if count is same or mismatch by 1 */
461         if ((count == the_acl->count) ||
462            (count == the_acl->count + 1) ||
463            (count+1 == the_acl->count)) {
464
465                 if (vxfs_compare_acls(compact_buf, new_buf, the_acl->count,
466                                      count)) {
467                         DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
468                         ret = true;
469                         goto out;
470                 } else
471                         DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
472         } else {
473                 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
474         }
475
476 out:
477
478         TALLOC_FREE(existing_acl);
479         TALLOC_FREE(smb_fname);
480         TALLOC_FREE(existing_buf);
481         TALLOC_FREE(compact_buf);
482         TALLOC_FREE(new_buf);
483
484         return ret;
485 }
486
487 static int vxfs_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp,
488                                SMB_ACL_T theacl)
489 {
490
491         if (vxfs_compare(fsp->conn, fsp->fsp_name->base_name, theacl,
492                          SMB_ACL_TYPE_ACCESS)) {
493                 return 0;
494         }
495
496         return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl);
497 }
498
499 static int vxfs_sys_acl_set_file(vfs_handle_struct *handle,  const char *name,
500                                  SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
501 {
502         if (vxfs_compare(handle->conn, (char *)name, theacl, acltype)) {
503                 return 0;
504         }
505
506         return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name, acltype, theacl);
507 }
508
509 static int vxfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
510                           const char *name, const void *value, size_t size,
511                           int flags){
512
513         DEBUG(10, ("In vxfs_set_xattr\n"));
514
515         if (strcmp(name, XATTR_NTACL_NAME) == 0) {
516                 return SMB_VFS_NEXT_SETXATTR(handle, path, XATTR_USER_NTACL,
517                                              value, size, flags);
518         }
519
520         /* Clients can't set XATTR_USER_NTACL directly. */
521         if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
522                 errno = EACCES;
523                 return -1;
524         }
525
526         return SMB_VFS_NEXT_SETXATTR(handle, path, name, value, size, flags);
527 }
528
529 static int vxfs_fset_xattr(struct vfs_handle_struct *handle,
530                            struct files_struct *fsp, const char *name,
531                            const void *value, size_t size,  int flags){
532
533         DEBUG(10, ("In vxfs_fset_xattr\n"));
534
535         if (strcmp(name, XATTR_NTACL_NAME) == 0) {
536                 return SMB_VFS_NEXT_FSETXATTR(handle, fsp, XATTR_USER_NTACL,
537                                               value, size, flags);
538         }
539
540         /* Clients can't set XATTR_USER_NTACL directly. */
541         if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
542                 errno = EACCES;
543                 return -1;
544         }
545
546         return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
547 }
548
549 static ssize_t vxfs_get_xattr(struct vfs_handle_struct *handle,
550                               const char *path, const char *name,
551                               void *value, size_t size){
552
553         DEBUG(10, ("In vxfs_get_xattr\n"));
554
555         if (strcmp(name, XATTR_NTACL_NAME) == 0) {
556                 return SMB_VFS_NEXT_GETXATTR(handle, path, XATTR_USER_NTACL,
557                                              value, size);
558         }
559
560         /* Clients can't see XATTR_USER_NTACL directly. */
561         if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
562                 errno = ENOATTR;
563                 return -1;
564         }
565
566         return SMB_VFS_NEXT_GETXATTR(handle, path, name, value, size);
567 }
568
569 static ssize_t vxfs_fget_xattr(struct vfs_handle_struct *handle,
570                                struct files_struct *fsp, const char *name,
571                                void *value, size_t size){
572
573         DEBUG(10, ("In vxfs_fget_xattr\n"));
574
575         if (strcmp(name, XATTR_NTACL_NAME) == 0) {
576                 return SMB_VFS_NEXT_FGETXATTR(handle, fsp, XATTR_USER_NTACL,
577                                               value, size);
578         }
579
580         /* Clients can't see XATTR_USER_NTACL directly. */
581         if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
582                 errno = ENOATTR;
583                 return -1;
584         }
585
586         return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size);
587 }
588
589 static int vxfs_remove_xattr(struct vfs_handle_struct *handle,
590                              const char *path, const char *name){
591
592         DEBUG(10, ("In vxfs_remove_xattr\n"));
593
594         if (strcmp(name, XATTR_NTACL_NAME) == 0) {
595                 return SMB_VFS_NEXT_REMOVEXATTR(handle, path, XATTR_USER_NTACL);
596         }
597
598         /* Clients can't see XATTR_USER_NTACL directly. */
599         if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
600                 errno = ENOATTR;
601                 return -1;
602         }
603
604         return SMB_VFS_NEXT_REMOVEXATTR(handle, path, name);
605 }
606
607 static int vxfs_fremove_xattr(struct vfs_handle_struct *handle,
608                               struct files_struct *fsp, const char *name){
609
610         DEBUG(10, ("In vxfs_fremove_xattr\n"));
611
612         if (strcmp(name, XATTR_NTACL_NAME) == 0) {
613                 return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, XATTR_USER_NTACL);
614         }
615
616         /* Clients can't remove XATTR_USER_NTACL directly. */
617         if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
618                 errno = ENOATTR;
619                 return -1;
620         }
621
622         return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
623 }
624
625 static size_t vxfs_filter_list(char *list, size_t size)
626 {
627         char *str = list;
628
629         while (str - list < size) {
630                 size_t element_len = strlen(str) + 1;
631                 if (strcasecmp(str, XATTR_USER_NTACL) == 0) {
632                         memmove(str,
633                                 str + element_len,
634                                 size - (str - list) - element_len);
635                         size -= element_len;
636                         continue;
637                 }
638                 str += element_len;
639         }
640         return size;
641 }
642
643 static ssize_t vxfs_listxattr(vfs_handle_struct *handle, const char *path,
644                               char *list, size_t size)
645 {
646         ssize_t result;
647
648         result = SMB_VFS_NEXT_LISTXATTR(handle, path, list, size);
649
650         if (result <= 0) {
651                 return result;
652         }
653
654         /* Remove any XATTR_USER_NTACL elements from the returned list. */
655         result = vxfs_filter_list(list, result);
656
657         return result;
658 }
659
660 static ssize_t vxfs_flistxattr(struct vfs_handle_struct *handle,
661                                 struct files_struct *fsp, char *list,
662                                 size_t size)
663 {
664         ssize_t result;
665
666         result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
667
668         if (result <= 0) {
669                 return result;
670         }
671
672         /* Remove any XATTR_USER_NTACL elements from the returned list. */
673         result = vxfs_filter_list(list, result);
674
675         return result;
676 }
677
678 static int vfs_vxfs_connect(struct vfs_handle_struct *handle,
679                             const char *service, const char *user)
680 {
681
682         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
683
684         if (ret < 0) {
685                 return ret;
686         }
687         return 0;
688 }
689
690 static struct vfs_fn_pointers vfs_vxfs_fns = {
691         .connect_fn = vfs_vxfs_connect,
692
693         .sys_acl_set_file_fn = vxfs_sys_acl_set_file,
694         .sys_acl_set_fd_fn = vxfs_sys_acl_set_fd,
695
696         .getxattr_fn = vxfs_get_xattr,
697         .fgetxattr_fn = vxfs_fget_xattr,
698         .listxattr_fn = vxfs_listxattr,
699         .flistxattr_fn = vxfs_flistxattr,
700         .removexattr_fn = vxfs_remove_xattr,
701         .fremovexattr_fn = vxfs_fremove_xattr,
702         .setxattr_fn = vxfs_set_xattr,
703         .fsetxattr_fn = vxfs_fset_xattr,
704 };
705
706 NTSTATUS vfs_vxfs_init(void);
707 NTSTATUS vfs_vxfs_init(void)
708 {
709         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "vxfs",
710                                 &vfs_vxfs_fns);
711 }