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