selftest:Samba4: report when samba is started and ready
[Samba.git] / source3 / modules / vfs_vxfs.c
blob5baa38568676f2b1ae71594bb03ab9176e5f3f73
1 /*
2 Unix SMB/CIFS implementation.
3 Wrap VxFS calls in vfs functions.
4 This module is for ACL and XATTR handling.
6 Copyright (C) Symantec Corporation <www.symantec.com> 2014
7 Copyright (C) Veritas Technologies LLC <www.veritas.com> 2016
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.
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.
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/>.
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"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_VFS
34 #define MODULE_NAME "vxfs"
37 * WARNING !! WARNING !!
39 * DO NOT CHANGE THIS FROM "system." space to
40 * "user." space unless you are shipping a product
41 * that RESTRICTS access to extended attributes
42 * to smbd-only. "system." space is restricted
43 * to root access only, "user." space is available
44 * to ANY USER.
46 * If this is changed to "user." and access
47 * to extended attributes is available via
48 * local processes or other remote file system
49 * (e.g. NFS) then the security of the system
50 * WILL BE COMPROMISED. i.e. non-root users
51 * WILL be able to overwrite Samba ACLs on
52 * the file system.
54 * If you need to modify this define, do
55 * so using CFLAGS on your build command
56 * line.
57 * e.g. CFLAGS=-DXATTR_USER_NTACL="user.NTACL"
59 * Added by: <jra@samba.org> 17 Sept. 2014.
63 #ifndef XATTR_USER_NTACL
64 #define XATTR_USER_NTACL "system.NTACL"
65 #endif
67 /* type values */
68 #define VXFS_ACL_UNDEFINED_TYPE 0
69 #define VXFS_ACL_USER_OBJ 1
70 #define VXFS_ACL_GROUP_OBJ 2
71 #define VXFS_ACL_USER 3
72 #define VXFS_ACL_GROUP 4
73 #define VXFS_ACL_OTHER 5
74 #define VXFS_ACL_MASK 6
78 * Compare aces
79 * This will compare two ace entries for sorting
80 * each entry contains: type, perms and id
81 * Sort by type first, if type is same sort by id.
83 static int vxfs_ace_cmp(const void *ace1, const void *ace2)
85 int ret = 0;
86 uint16_t type_a1, type_a2;
87 uint32_t id_a1, id_a2;
89 /* Type must be compared first */
90 type_a1 = SVAL(ace1, 0);
91 type_a2 = SVAL(ace2, 0);
93 ret = (type_a1 - type_a2);
94 if (!ret) {
95 /* Compare ID under type */
96 /* skip perm thus take offset as 4*/
97 id_a1 = IVAL(ace1, 4);
98 id_a2 = IVAL(ace2, 4);
99 ret = id_a1 - id_a2;
102 return ret;
105 static void vxfs_print_ace_buf(char *buf, int count) {
107 int i, offset = 0;
108 uint16_t type, perm;
109 uint32_t id;
111 DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
112 for (i = 0; i < count; i++) {
113 type = SVAL(buf, offset);
114 offset += 2;
115 perm = SVAL(buf, offset);
116 offset += 2;
117 id = IVAL(buf, offset);
118 offset += 4;
120 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
121 (unsigned int)type, (unsigned int)perm,
122 (unsigned int)id));
127 * Sort aces so that comparing 2 ACLs will be straight forward.
128 * This function will fill buffer as follows:
129 * For each ace:
130 * 1. ace->a_type will be filled as first 2 bytes in buf.
131 * 2. ace->a_perm will be filled as next 2 bytes.
132 * 3. ace->xid will be filled as next 4 bytes.
133 * Thus each ace entry in buf is equal to 8 bytes.
134 * Also a_type is mapped to VXFS_ACL_* so that ordering aces
135 * becomes easy.
137 static char * vxfs_sort_acl(SMB_ACL_T theacl, TALLOC_CTX *mem_ctx,
138 uint32_t o_uid,
139 uint32_t o_gid) {
141 struct smb_acl_entry *smb_ace;
142 int i, count;
143 uint16_t type, perm;
144 uint32_t id;
145 int offset = 0;
146 char *buf = NULL;
148 count = theacl->count;
150 buf = talloc_zero_size(mem_ctx, count * 8);
151 if (!buf) {
152 return NULL;
155 smb_ace = theacl->acl;
157 for (i = 0; i < count; i++) {
158 /* Calculate type */
159 /* Map type to SMB_ACL_* to VXFS_ACL_* */
160 switch(smb_ace->a_type) {
161 case SMB_ACL_USER:
162 type = VXFS_ACL_USER;
163 break;
164 case SMB_ACL_USER_OBJ:
165 type = VXFS_ACL_USER_OBJ;
166 break;
167 case SMB_ACL_GROUP:
168 type = VXFS_ACL_GROUP;
169 break;
170 case SMB_ACL_GROUP_OBJ:
171 type = VXFS_ACL_GROUP_OBJ;
172 break;
173 case SMB_ACL_OTHER:
174 type = VXFS_ACL_OTHER;
175 break;
176 case SMB_ACL_MASK:
177 type = VXFS_ACL_MASK;
178 break;
179 default:
180 type = -1;
181 talloc_free(buf);
182 return NULL;
185 type = type & 0xff;
187 /* Calculate id:
188 * We get owner uid and owner group gid in o_uid and o_gid
189 * Put these ids instead of -1
191 switch(smb_ace->a_type) {
192 case SMB_ACL_USER:
193 id = smb_ace->info.user.uid;
194 break;
195 case SMB_ACL_GROUP:
196 id = smb_ace->info.group.gid;
197 break;
198 case SMB_ACL_USER_OBJ:
199 id = o_uid;
200 break;
201 case SMB_ACL_GROUP_OBJ:
202 id = o_gid;
203 break;
204 case SMB_ACL_MASK:
205 case SMB_ACL_OTHER:
206 id = -1;
207 break;
208 default:
209 /* Can't happen.. */
210 id = -1;
211 break;
214 /* Calculate perm */
215 perm = smb_ace->a_perm & 0xff;
217 /* TYPE is the first 2 bytes of an entry */
218 SSVAL(buf, offset, type);
219 offset += 2;
221 /* PERM is the next 2 bytes of an entry */
222 SSVAL(buf, offset, perm);
223 offset += 2;
225 /* ID is the last 4 bytes of an entry */
226 SIVAL(buf, offset, id);
227 offset += 4;
229 smb_ace++;
232 qsort(buf, count, 8, vxfs_ace_cmp);
234 DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
235 vxfs_print_ace_buf(buf, count);
237 return buf;
240 /* This function gets e_buf as an arg which is sorted and created out of
241 * existing ACL. This function will compact this e_buf to c_buf where USER
242 * and GROUP aces matching with USER_OBJ and GROUP_OBJ will be merged
243 * respectively.
244 * This is similar to what posix_acls.c does. This will make sure existing
245 * acls are converted much similar to what posix_acls calculates.
248 static char * vxfs_compact_buf(char *e_buf, int *new_count, int count,
249 TALLOC_CTX *mem_ctx)
251 int i, e_offset = 0, c_offset = 0;
252 uint16_t type, perm, o_perm;
253 uint32_t id, owner_id, group_id;
254 char *c_buf = NULL;
257 if (count < 2) {
258 return NULL;
261 c_buf = talloc_zero_size(mem_ctx, count * 8);
262 if (!c_buf) {
263 return NULL;
266 /*Copy first two enries from e_buf to c_buf
267 *These are USER_OBJ and GROUP_OBJ
270 memcpy(c_buf, e_buf, 16);
272 (*new_count) = 2;
274 owner_id = IVAL(e_buf, 4);
275 group_id = IVAL(e_buf, 12);
277 c_offset = e_offset = 16;
279 /* Start comparing other entries */
280 for (i = 2; i < count; i++) {
282 type = SVAL(e_buf, e_offset);
283 e_offset += 2;
284 perm = SVAL(e_buf, e_offset);
285 e_offset += 2;
286 id = IVAL(e_buf, e_offset);
287 e_offset += 4;
289 switch(type) {
290 case VXFS_ACL_USER:
291 if (id == owner_id) {
292 o_perm = SVAL(c_buf, 2);
293 o_perm |= perm;
294 SSVAL(c_buf, 2, o_perm);
295 DEBUG(10, ("vfs_vxfs: merging with owner"
296 "e_type = %u,"
297 "e_perm = %u,"
298 "e_id = %u\n", (unsigned int)type,
299 (unsigned int)perm,
300 (unsigned int)id));
301 continue;
303 break;
304 case VXFS_ACL_GROUP:
305 if (id == group_id) {
306 o_perm = SVAL(c_buf, 10);
307 o_perm |= perm;
308 SSVAL(c_buf, 10, o_perm);
309 DEBUG(10, ("vfs_vxfs: merging with owner group"
310 "e_type = %u,"
311 "e_perm = %u,"
312 "e_id = %u\n", (unsigned int)type,
313 (unsigned int)perm,
314 (unsigned int)id));
315 continue;
317 break;
320 SSVAL(c_buf, c_offset, type);
321 c_offset += 2;
323 SSVAL(c_buf, c_offset, perm);
324 c_offset += 2;
326 SIVAL(c_buf, c_offset, id);
327 c_offset += 4;
329 (*new_count)++;
331 DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count));
332 return c_buf;
335 /* Actually compare New ACL and existing ACL buf */
336 static bool vxfs_compare_acls(char *e_buf, char *n_buf, int n_count,
337 int e_count) {
339 uint16_t e_type, n_type, e_perm, n_perm;
340 uint32_t e_id, n_id;
341 int i, offset = 0;
343 if (!e_buf && !n_buf) {
344 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
345 return false;
348 if ((e_count < 2) || (n_count < 2)) {
349 return false;
351 /*Get type from last entry from both buffers.
352 * It may or may not be ACL_MASK
354 n_type = SVAL(n_buf, offset + (8 * (n_count-1)));
355 e_type = SVAL(e_buf, offset + (8 * (e_count-1)));
357 /* Check for ACL_MASK entry properly. Handle all 4 cases*/
359 /* If ACL_MASK entry is present in any of the buffers,
360 * it will be always the last one. Calculate count to compare
361 * based on if ACL_MASK is present on new and existing ACL
363 if ((n_type != VXFS_ACL_MASK) && (e_type == VXFS_ACL_MASK)){
364 DEBUG(10, ("vfs_vxfs: New ACL does not have mask entry,"
365 "reduce count by 1 and compare\n"));
366 e_count = e_count -1;
368 if ((n_type == VXFS_ACL_MASK) && (e_type != VXFS_ACL_MASK)){
369 DEBUG(10, ("vfs_vxfs: new ACL to be set contains mask"
370 "existing ACL does not have mask entry\n"
371 "Need to set New ACL\n"));
372 return false;
375 if (memcmp(e_buf, n_buf, (e_count * 8)) != 0) {
376 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
377 "buffers not same!\n"));
378 return false;
381 return true;
384 /* In VxFS, POSIX ACLs are pointed by separate inode for each file/dir.
385 * However, files/dir share same POSIX ACL inode if ACLs are inherited
386 * from parent.
387 * To retain this behaviour, below function avoids ACL set call if
388 * underlying ACLs are already same and thus saves creating extra inode.
390 * This function will execute following steps:
391 * 1. Get existing ACL
392 * 2. Sort New ACL and existing ACL into buffers
393 * 3. Compact existing ACL buf
394 * 4. Finally compare New ACL buf and Compact buf
395 * 5. If same, return true
396 * 6. Else need to set New ACL
399 static bool vxfs_compare(connection_struct *conn, char *name, SMB_ACL_T the_acl,
400 SMB_ACL_TYPE_T the_acl_type)
402 SMB_ACL_T existing_acl = NULL;
403 bool ret = false;
404 int i, count = 0;
405 TALLOC_CTX *mem_ctx = talloc_tos();
406 char *existing_buf = NULL, *new_buf = NULL, *compact_buf = NULL;
407 struct smb_filename *smb_fname = NULL;
408 int status;
410 DEBUG(10, ("vfs_vxfs: Getting existing ACL for %s\n", name));
412 smb_fname = synthetic_smb_fname(mem_ctx, name, NULL, NULL, 0);
413 if (smb_fname == NULL) {
414 DEBUG(10, ("vfs_vxfs: Failed to create smb_fname\n"));
415 goto out;
418 existing_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, smb_fname, the_acl_type,
419 mem_ctx);
420 if (existing_acl == NULL) {
421 DEBUG(10, ("vfs_vxfs: Failed to get ACL\n"));
422 goto out;
425 DEBUG(10, ("vfs_vxfs: Existing ACL count=%d\n", existing_acl->count));
426 DEBUG(10, ("vfs_vxfs: New ACL count=%d\n", the_acl->count));
428 if (existing_acl->count == 0) {
429 DEBUG(10, ("vfs_vxfs: ACL count is 0, Need to set\n"));
430 goto out;
433 status = SMB_VFS_STAT(conn, smb_fname);
434 if (status == -1) {
435 DEBUG(10, ("vfs_vxfs: stat failed!\n"));
436 goto out;
439 DEBUG(10, ("vfs_vxfs: Sorting existing ACL\n"));
440 existing_buf = vxfs_sort_acl(existing_acl, mem_ctx,
441 smb_fname->st.st_ex_uid,
442 smb_fname->st.st_ex_gid);
443 if (!existing_buf)
444 goto out;
446 DEBUG(10, ("vfs_vxfs: Sorting new ACL\n"));
447 new_buf = vxfs_sort_acl(the_acl, mem_ctx, smb_fname->st.st_ex_uid,
448 smb_fname->st.st_ex_gid);
449 if (!new_buf) {
450 goto out;
453 DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
454 compact_buf = vxfs_compact_buf(existing_buf, &count,
455 existing_acl->count,
456 mem_ctx);
457 if (!compact_buf) {
458 goto out;
461 vxfs_print_ace_buf(compact_buf, count);
463 /* COmpare ACLs only if count is same or mismatch by 1 */
464 if ((count == the_acl->count) ||
465 (count == the_acl->count + 1) ||
466 (count+1 == the_acl->count)) {
468 if (vxfs_compare_acls(compact_buf, new_buf, the_acl->count,
469 count)) {
470 DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
471 ret = true;
472 goto out;
473 } else
474 DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
475 } else {
476 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
479 out:
481 TALLOC_FREE(existing_acl);
482 TALLOC_FREE(smb_fname);
483 TALLOC_FREE(existing_buf);
484 TALLOC_FREE(compact_buf);
485 TALLOC_FREE(new_buf);
487 return ret;
490 static int vxfs_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp,
491 SMB_ACL_T theacl)
494 if (vxfs_compare(fsp->conn, fsp->fsp_name->base_name, theacl,
495 SMB_ACL_TYPE_ACCESS)) {
496 return 0;
499 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl);
502 static int vxfs_sys_acl_set_file(vfs_handle_struct *handle,
503 const struct smb_filename *smb_fname,
504 SMB_ACL_TYPE_T acltype,
505 SMB_ACL_T theacl)
507 if (vxfs_compare(handle->conn, (char *)smb_fname->base_name,
508 theacl, acltype)) {
509 return 0;
512 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, smb_fname,
513 acltype, theacl);
516 static int vxfs_set_xattr(struct vfs_handle_struct *handle,
517 const struct smb_filename *smb_fname_in,
518 const char *name,
519 const void *value,
520 size_t size,
521 int flags)
523 struct smb_filename *smb_fname = NULL;
524 bool is_dir = false;
525 int ret = 0;
526 int saved_errno = 0;
528 DEBUG(10, ("In vxfs_set_xattr\n"));
530 smb_fname = cp_smb_filename_nostream(talloc_tos(), smb_fname_in);
531 if (smb_fname == NULL) {
532 errno = ENOMEM;
533 return -1;
536 if (SMB_VFS_NEXT_STAT(handle, smb_fname) != 0) {
537 TALLOC_FREE(smb_fname);
538 return -1;
541 is_dir = S_ISDIR(smb_fname->st.st_ex_mode);
543 ret = vxfs_setxattr_path(smb_fname_in->base_name, name, value, size,
544 flags, is_dir);
545 if ((ret == 0) ||
546 ((ret == -1) && (errno != ENOTSUP) && (errno != ENOSYS))) {
548 * Now remve old style xattr if it exists
550 SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
552 * Do not bother about return value
554 if (ret != 0) {
555 saved_errno = errno;
557 goto fail;
560 DEBUG(10, ("Fallback to xattr\n"));
561 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
562 ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
563 XATTR_USER_NTACL,
564 value, size, flags);
565 if (ret != 0) {
566 saved_errno = errno;
567 goto fail;
569 return 0;
572 /* Clients can't set XATTR_USER_NTACL directly. */
573 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
574 saved_errno = EACCES;
575 ret = -1;
576 goto fail;
579 ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
580 name, value, size, flags);
581 if (ret != 0) {
582 saved_errno = errno;
583 goto fail;
586 fail:
587 TALLOC_FREE(smb_fname);
588 if (saved_errno != 0) {
589 saved_errno = errno;
591 return ret;
594 static int vxfs_fset_xattr(struct vfs_handle_struct *handle,
595 struct files_struct *fsp, const char *name,
596 const void *value, size_t size, int flags){
597 int ret = 0;
599 DEBUG(10, ("In vxfs_fset_xattr\n"));
601 ret = vxfs_setxattr_fd(fsp->fh->fd, name, value, size, flags);
602 if ((ret == 0) ||
603 ((ret == -1) && (errno != ENOTSUP) && (errno != ENOSYS))) {
604 SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
605 return ret;
608 DEBUG(10, ("Fallback to xattr"));
609 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
610 return SMB_VFS_NEXT_FSETXATTR(handle, fsp, XATTR_USER_NTACL,
611 value, size, flags);
614 /* Clients can't set XATTR_USER_NTACL directly. */
615 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
616 errno = EACCES;
617 return -1;
620 return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
623 static ssize_t vxfs_get_xattr(struct vfs_handle_struct *handle,
624 const struct smb_filename *smb_fname,
625 const char *name,
626 void *value,
627 size_t size){
628 int ret;
630 DEBUG(10, ("In vxfs_get_xattr\n"));
631 ret = vxfs_getxattr_path(smb_fname->base_name, name, value, size);
632 if ((ret != -1) || ((errno != ENOTSUP) &&
633 (errno != ENOSYS) && (errno != ENODATA))) {
634 return ret;
637 DEBUG(10, ("Fallback to xattr\n"));
638 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
639 return SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
640 XATTR_USER_NTACL, value, size);
643 /* Clients can't see XATTR_USER_NTACL directly. */
644 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
645 errno = ENOATTR;
646 return -1;
649 return SMB_VFS_NEXT_GETXATTR(handle, smb_fname, name, value, size);
652 static ssize_t vxfs_fget_xattr(struct vfs_handle_struct *handle,
653 struct files_struct *fsp, const char *name,
654 void *value, size_t size){
655 int ret;
657 DEBUG(10, ("In vxfs_fget_xattr\n"));
659 ret = vxfs_getxattr_fd(fsp->fh->fd, name, value, size);
660 if ((ret != -1) || ((errno != ENOTSUP) &&
661 (errno != ENOSYS) && (errno != ENODATA))) {
662 return ret;
665 DEBUG(10, ("Fallback to xattr\n"));
666 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
667 return SMB_VFS_NEXT_FGETXATTR(handle, fsp, XATTR_USER_NTACL,
668 value, size);
671 /* Clients can't see XATTR_USER_NTACL directly. */
672 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
673 errno = ENOATTR;
674 return -1;
677 return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size);
680 static int vxfs_remove_xattr(struct vfs_handle_struct *handle,
681 const struct smb_filename *smb_fname_in,
682 const char *name)
684 bool is_dir = false;
685 int ret = 0, ret_new = 0, old_errno;
686 struct smb_filename *smb_fname = NULL;
688 DEBUG(10, ("In vxfs_remove_xattr\n"));
690 smb_fname = cp_smb_filename_nostream(talloc_tos(), smb_fname_in);
691 if (smb_fname == NULL) {
692 errno = ENOMEM;
693 return -1;
696 /* Remove with old way */
697 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
698 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname,
699 XATTR_USER_NTACL);
700 } else {
701 if (strcasecmp(name, XATTR_USER_NTACL) != 0) {
702 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname,
703 name);
706 /* Remove with new way */
707 old_errno = errno;
709 if (SMB_VFS_NEXT_STAT(handle, smb_fname) != 0) {
710 TALLOC_FREE(smb_fname);
711 return -1;
714 is_dir = S_ISDIR(smb_fname->st.st_ex_mode);
715 TALLOC_FREE(smb_fname);
717 * If both fail, return failuer else return whichever succeeded
719 ret_new = vxfs_removexattr_path(smb_fname_in->base_name, name, is_dir);
720 if (errno == ENOTSUP || errno == ENOSYS) {
721 errno = old_errno;
723 if ((ret_new != -1) && (ret == -1)) {
724 ret = ret_new;
727 return ret;
731 static int vxfs_fremove_xattr(struct vfs_handle_struct *handle,
732 struct files_struct *fsp, const char *name){
733 int ret = 0, ret_new = 0, old_errno;
735 DEBUG(10, ("In vxfs_fremove_xattr\n"));
737 /* Remove with old way */
738 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
739 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp,
740 XATTR_USER_NTACL);
741 } else {
742 /* Clients can't remove XATTR_USER_NTACL directly. */
743 if (strcasecmp(name, XATTR_USER_NTACL) != 0) {
744 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp,
745 name);
748 old_errno = errno;
750 /* Remove with new way */
751 ret_new = vxfs_removexattr_fd(fsp->fh->fd, name);
753 * If both fail, return failuer else return whichever succeeded
755 if (errno == ENOTSUP || errno == ENOSYS) {
756 errno = old_errno;
758 if ((ret_new != -1) && (ret == -1)) {
759 ret = ret_new;
762 return ret;
766 static size_t vxfs_filter_list(char *list, size_t size)
768 char *str = list;
770 while (str - list < size) {
771 size_t element_len = strlen(str) + 1;
772 if (strcasecmp(str, XATTR_USER_NTACL) == 0) {
773 memmove(str,
774 str + element_len,
775 size - (str - list) - element_len);
776 size -= element_len;
777 continue;
779 str += element_len;
781 return size;
784 static ssize_t vxfs_listxattr(vfs_handle_struct *handle,
785 const struct smb_filename *smb_fname,
786 char *list,
787 size_t size)
789 ssize_t result;
791 result = vxfs_listxattr_path(smb_fname->base_name, list, size);
792 if (result >= 0 || ((errno != ENOTSUP) && (errno != ENOSYS))) {
793 return result;
796 result = SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
798 if (result <= 0) {
799 return result;
802 /* Remove any XATTR_USER_NTACL elements from the returned list. */
803 result = vxfs_filter_list(list, result);
805 return result;
808 static ssize_t vxfs_flistxattr(struct vfs_handle_struct *handle,
809 struct files_struct *fsp, char *list,
810 size_t size)
812 ssize_t result;
814 result = vxfs_listxattr_fd(fsp->fh->fd, list, size);
815 if (result >= 0 || ((errno != ENOTSUP) && (errno != ENOSYS))) {
816 return result;
819 result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
821 if (result <= 0) {
822 return result;
825 /* Remove any XATTR_USER_NTACL elements from the returned list. */
826 result = vxfs_filter_list(list, result);
828 return result;
831 static NTSTATUS vxfs_set_ea_dos_attributes(struct vfs_handle_struct *handle,
832 const struct smb_filename *smb_fname,
833 uint32_t dosmode)
835 NTSTATUS err;
836 int ret = 0;
837 bool attrset = false;
838 bool is_dir = false;
840 DBG_DEBUG("Entered function\n");
842 is_dir = S_ISDIR(smb_fname->st.st_ex_mode);
843 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
844 ret = vxfs_checkwxattr_path(smb_fname->base_name);
845 if (ret == -1) {
846 DBG_DEBUG("ret:%d\n", ret);
847 if ((errno != EOPNOTSUPP) && (errno != ENOENT)) {
848 return map_nt_error_from_unix(errno);
852 if (dosmode & FILE_ATTRIBUTE_READONLY) {
853 ret = vxfs_setwxattr_path(smb_fname->base_name, is_dir);
854 DBG_DEBUG("ret:%d\n", ret);
855 if (ret == -1) {
856 if ((errno != EOPNOTSUPP) && (errno != EINVAL)) {
857 return map_nt_error_from_unix(errno);
859 } else {
860 attrset = true;
863 err = SMB_VFS_NEXT_SET_DOS_ATTRIBUTES(handle, smb_fname, dosmode);
864 if (!NT_STATUS_IS_OK(err)) {
865 if (attrset) {
866 ret = vxfs_clearwxattr_path(smb_fname->base_name, is_dir);
867 DBG_DEBUG("ret:%d\n", ret);
868 if ((ret == -1) && (errno != ENOENT)) {
869 return map_nt_error_from_unix(errno);
874 return err;
877 static NTSTATUS vxfs_fset_ea_dos_attributes(struct vfs_handle_struct *handle,
878 struct files_struct *fsp,
879 uint32_t dosmode)
881 NTSTATUS err;
882 int ret = 0;
883 bool attrset = false;
885 DBG_DEBUG("Entered function\n");
887 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
888 ret = vxfs_checkwxattr_fd(fsp->fh->fd);
889 if (ret == -1) {
890 DBG_DEBUG("ret:%d\n", ret);
891 if ((errno != EOPNOTSUPP) && (errno != ENOENT)) {
892 return map_nt_error_from_unix(errno);
896 if (dosmode & FILE_ATTRIBUTE_READONLY) {
897 ret = vxfs_setwxattr_fd(fsp->fh->fd);
898 DBG_DEBUG("ret:%d\n", ret);
899 if (ret == -1) {
900 if ((errno != EOPNOTSUPP) && (errno != EINVAL)) {
901 return map_nt_error_from_unix(errno);
903 } else {
904 attrset = true;
907 err = SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
908 if (!NT_STATUS_IS_OK(err)) {
909 if (attrset) {
910 ret = vxfs_clearwxattr_fd(fsp->fh->fd);
911 DBG_DEBUG("ret:%d\n", ret);
912 if ((ret == -1) && (errno != ENOENT)) {
913 return map_nt_error_from_unix(errno);
917 return err;
920 static int vfs_vxfs_connect(struct vfs_handle_struct *handle,
921 const char *service, const char *user)
924 int ret;
926 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
927 if (ret < 0) {
928 return ret;
931 vxfs_init();
933 return 0;
936 static struct vfs_fn_pointers vfs_vxfs_fns = {
937 .connect_fn = vfs_vxfs_connect,
939 #ifdef VXFS_ACL_SHARE
940 .sys_acl_set_file_fn = vxfs_sys_acl_set_file,
941 .sys_acl_set_fd_fn = vxfs_sys_acl_set_fd,
942 #endif
944 .set_dos_attributes_fn = vxfs_set_ea_dos_attributes,
945 .fset_dos_attributes_fn = vxfs_fset_ea_dos_attributes,
946 .getxattr_fn = vxfs_get_xattr,
947 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
948 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
949 .fgetxattr_fn = vxfs_fget_xattr,
950 .listxattr_fn = vxfs_listxattr,
951 .flistxattr_fn = vxfs_flistxattr,
952 .removexattr_fn = vxfs_remove_xattr,
953 .fremovexattr_fn = vxfs_fremove_xattr,
954 .setxattr_fn = vxfs_set_xattr,
955 .fsetxattr_fn = vxfs_fset_xattr,
958 static_decl_vfs;
959 NTSTATUS vfs_vxfs_init(TALLOC_CTX *ctx)
961 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "vxfs",
962 &vfs_vxfs_fns);