2 * Convert ZFS/NFSv4 acls to NT acls and vice versa.
4 * Copyright (C) Jiri Sasek, 2007
5 * based on the foobar.c module which is copyrighted by Volker Lendecke
7 * Many thanks to Axel Apitz for help to fix the special ace's handling
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "nfs4_acls.h"
30 #if HAVE_FREEBSD_SUNACL_H
35 #define DBGC_CLASS DBGC_VFS
37 #define ZFSACL_MODULE_NAME "zfsacl"
40 * read the local file's acls and return it in NT form
41 * using the NFSv4 format conversion
43 static NTSTATUS
zfs_get_nt_acl_common(struct connection_struct
*conn
,
45 const struct smb_filename
*smb_fname
,
46 struct SMB4ACL_T
**ppacl
)
50 struct SMB4ACL_T
*pacl
;
52 const SMB_STRUCT_STAT
*psbuf
= NULL
;
55 if (VALID_STAT(smb_fname
->st
)) {
56 psbuf
= &smb_fname
->st
;
60 ret
= vfs_stat_smb_basename(conn
, smb_fname
, &sbuf
);
62 DBG_INFO("stat [%s]failed: %s\n",
63 smb_fname_str_dbg(smb_fname
), strerror(errno
));
64 return map_nt_error_from_unix(errno
);
69 if (S_ISDIR(psbuf
->st_ex_mode
) && (ace
->aceMask
& SMB_ACE4_ADD_FILE
)) {
70 ace
->aceMask
|= SMB_ACE4_DELETE_CHILD
;
73 /* read the number of file aces */
74 if((naces
= acl(smb_fname
->base_name
, ACE_GETACLCNT
, 0, NULL
)) == -1) {
76 DEBUG(9, ("acl(ACE_GETACLCNT, %s): Operation is not "
77 "supported on the filesystem where the file "
78 "reside\n", smb_fname
->base_name
));
80 DEBUG(9, ("acl(ACE_GETACLCNT, %s): %s ", smb_fname
->base_name
,
83 return map_nt_error_from_unix(errno
);
85 /* allocate the field of ZFS aces */
86 mem_ctx
= talloc_tos();
87 acebuf
= (ace_t
*) talloc_size(mem_ctx
, sizeof(ace_t
)*naces
);
89 return NT_STATUS_NO_MEMORY
;
91 /* read the aces into the field */
92 if(acl(smb_fname
->base_name
, ACE_GETACL
, naces
, acebuf
) < 0) {
93 DEBUG(9, ("acl(ACE_GETACL, %s): %s ", smb_fname
->base_name
,
95 return map_nt_error_from_unix(errno
);
97 /* create SMB4ACL data */
98 if((pacl
= smb_create_smb4acl(mem_ctx
)) == NULL
) {
99 return NT_STATUS_NO_MEMORY
;
101 for(i
=0; i
<naces
; i
++) {
102 SMB_ACE4PROP_T aceprop
;
104 aceprop
.aceType
= (uint32_t) acebuf
[i
].a_type
;
105 aceprop
.aceFlags
= (uint32_t) acebuf
[i
].a_flags
;
106 aceprop
.aceMask
= (uint32_t) acebuf
[i
].a_access_mask
;
107 aceprop
.who
.id
= (uint32_t) acebuf
[i
].a_who
;
109 if(aceprop
.aceFlags
& ACE_OWNER
) {
110 aceprop
.flags
= SMB_ACE4_ID_SPECIAL
;
111 aceprop
.who
.special_id
= SMB_ACE4_WHO_OWNER
;
112 } else if(aceprop
.aceFlags
& ACE_GROUP
) {
113 aceprop
.flags
= SMB_ACE4_ID_SPECIAL
;
114 aceprop
.who
.special_id
= SMB_ACE4_WHO_GROUP
;
115 } else if(aceprop
.aceFlags
& ACE_EVERYONE
) {
116 aceprop
.flags
= SMB_ACE4_ID_SPECIAL
;
117 aceprop
.who
.special_id
= SMB_ACE4_WHO_EVERYONE
;
121 if(smb_add_ace4(pacl
, &aceprop
) == NULL
)
122 return NT_STATUS_NO_MEMORY
;
129 /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */
130 static bool zfs_process_smbacl(vfs_handle_struct
*handle
, files_struct
*fsp
,
131 struct SMB4ACL_T
*smbacl
)
133 int naces
= smb_get_naces(smbacl
), i
;
135 struct SMB4ACE_T
*smbace
;
137 bool have_special_id
= false;
139 /* allocate the field of ZFS aces */
140 mem_ctx
= talloc_tos();
141 acebuf
= (ace_t
*) talloc_size(mem_ctx
, sizeof(ace_t
)*naces
);
146 /* handle all aces */
147 for(smbace
= smb_first_ace4(smbacl
), i
= 0;
149 smbace
= smb_next_ace4(smbace
), i
++) {
150 SMB_ACE4PROP_T
*aceprop
= smb_get_ace4(smbace
);
152 acebuf
[i
].a_type
= aceprop
->aceType
;
153 acebuf
[i
].a_flags
= aceprop
->aceFlags
;
154 acebuf
[i
].a_access_mask
= aceprop
->aceMask
;
155 /* SYNC on acls is a no-op on ZFS.
157 acebuf
[i
].a_access_mask
&= ~SMB_ACE4_SYNCHRONIZE
;
158 acebuf
[i
].a_who
= aceprop
->who
.id
;
159 if(aceprop
->flags
& SMB_ACE4_ID_SPECIAL
) {
160 switch(aceprop
->who
.special_id
) {
161 case SMB_ACE4_WHO_EVERYONE
:
162 acebuf
[i
].a_flags
|= ACE_EVERYONE
;
164 case SMB_ACE4_WHO_OWNER
:
165 acebuf
[i
].a_flags
|= ACE_OWNER
;
167 case SMB_ACE4_WHO_GROUP
:
168 acebuf
[i
].a_flags
|= ACE_GROUP
|ACE_IDENTIFIER_GROUP
;
171 DEBUG(8, ("unsupported special_id %d\n", \
172 aceprop
->who
.special_id
));
173 continue; /* don't add it !!! */
175 have_special_id
= true;
180 && lp_parm_bool(fsp
->conn
->params
->service
, "zfsacl",
181 "denymissingspecial", false)) {
186 SMB_ASSERT(i
== naces
);
189 if(acl(fsp
->fsp_name
->base_name
, ACE_SETACL
, naces
, acebuf
)) {
190 if(errno
== ENOSYS
) {
191 DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
192 "supported on the filesystem where the file "
193 "reside", fsp_str_dbg(fsp
)));
195 DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp
),
205 * set the local file's acls obtaining it in NT form
206 * using the NFSv4 format conversion
208 static NTSTATUS
zfs_set_nt_acl(vfs_handle_struct
*handle
, files_struct
*fsp
,
209 uint32_t security_info_sent
,
210 const struct security_descriptor
*psd
)
212 return smb_set_nt_acl_nfs4(handle
, fsp
, NULL
, security_info_sent
, psd
,
216 static NTSTATUS
zfsacl_fget_nt_acl(struct vfs_handle_struct
*handle
,
217 struct files_struct
*fsp
,
218 uint32_t security_info
,
220 struct security_descriptor
**ppdesc
)
222 struct SMB4ACL_T
*pacl
;
224 TALLOC_CTX
*frame
= talloc_stackframe();
226 status
= zfs_get_nt_acl_common(handle
->conn
, frame
,
227 fsp
->fsp_name
, &pacl
);
228 if (!NT_STATUS_IS_OK(status
)) {
233 status
= smb_fget_nt_acl_nfs4(fsp
, NULL
, security_info
, mem_ctx
,
239 static NTSTATUS
zfsacl_get_nt_acl(struct vfs_handle_struct
*handle
,
240 const struct smb_filename
*smb_fname
,
241 uint32_t security_info
,
243 struct security_descriptor
**ppdesc
)
245 struct SMB4ACL_T
*pacl
;
247 TALLOC_CTX
*frame
= talloc_stackframe();
249 status
= zfs_get_nt_acl_common(handle
->conn
, frame
, smb_fname
, &pacl
);
250 if (!NT_STATUS_IS_OK(status
)) {
255 status
= smb_get_nt_acl_nfs4(handle
->conn
,
266 static NTSTATUS
zfsacl_fset_nt_acl(vfs_handle_struct
*handle
,
268 uint32_t security_info_sent
,
269 const struct security_descriptor
*psd
)
271 return zfs_set_nt_acl(handle
, fsp
, security_info_sent
, psd
);
274 /* nils.goroll@hamburg.de 2008-06-16 :
277 - https://bugzilla.samba.org/show_bug.cgi?id=5446
278 - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240
280 Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2)
281 with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for
282 use by samba in this module.
284 As the acl(2) interface is identical for ZFS and for NFS, this module,
285 vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4
288 But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL
289 / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client
290 implemets a compatibility wrapper, which will make calls to
291 traditional ACL calls though vfs_solarisacl succeed. As the
292 compatibility wrapper's implementation is (by design) incomplete,
293 we want to make sure that it is never being called.
295 As long as Samba does not support an exiplicit method for a module
296 to define conflicting vfs methods, we should override all conflicting
299 For this to work, we need to make sure that this module is initialised
300 *after* vfs_solarisacl
302 Function declarations taken from vfs_solarisacl
305 static SMB_ACL_T
zfsacl_fail__sys_acl_get_file(vfs_handle_struct
*handle
,
310 return (SMB_ACL_T
)NULL
;
313 static SMB_ACL_T
zfsacl_fail__sys_acl_get_fd(vfs_handle_struct
*handle
,
317 return (SMB_ACL_T
)NULL
;
320 static int zfsacl_fail__sys_acl_set_file(vfs_handle_struct
*handle
,
328 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct
*handle
,
335 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct
*handle
,
341 static int zfsacl_fail__sys_acl_blob_get_file(vfs_handle_struct
*handle
, const char *path_p
, TALLOC_CTX
*mem_ctx
, char **blob_description
, DATA_BLOB
*blob
)
346 static int zfsacl_fail__sys_acl_blob_get_fd(vfs_handle_struct
*handle
, files_struct
*fsp
, TALLOC_CTX
*mem_ctx
, char **blob_description
, DATA_BLOB
*blob
)
351 /* VFS operations structure */
353 static struct vfs_fn_pointers zfsacl_fns
= {
354 .sys_acl_get_file_fn
= zfsacl_fail__sys_acl_get_file
,
355 .sys_acl_get_fd_fn
= zfsacl_fail__sys_acl_get_fd
,
356 .sys_acl_blob_get_file_fn
= zfsacl_fail__sys_acl_blob_get_file
,
357 .sys_acl_blob_get_fd_fn
= zfsacl_fail__sys_acl_blob_get_fd
,
358 .sys_acl_set_file_fn
= zfsacl_fail__sys_acl_set_file
,
359 .sys_acl_set_fd_fn
= zfsacl_fail__sys_acl_set_fd
,
360 .sys_acl_delete_def_file_fn
= zfsacl_fail__sys_acl_delete_def_file
,
361 .fget_nt_acl_fn
= zfsacl_fget_nt_acl
,
362 .get_nt_acl_fn
= zfsacl_get_nt_acl
,
363 .fset_nt_acl_fn
= zfsacl_fset_nt_acl
,
366 NTSTATUS
vfs_zfsacl_init(void);
367 NTSTATUS
vfs_zfsacl_init(void)
369 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "zfsacl",