s3/vfs: move ACE4_ADD_FILE/ACE4_DELETE_CHILD mapping from NFSv4 framework to vfs_zfsacl
[Samba.git] / source3 / modules / vfs_zfsacl.c
blobdca8f83303b690a0e9bd193467da446b8a111a32
1 /*
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
8 * issues.
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/>.
25 #include "includes.h"
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "nfs4_acls.h"
30 #if HAVE_FREEBSD_SUNACL_H
31 #include "sunacl.h"
32 #endif
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_VFS
37 #define ZFSACL_MODULE_NAME "zfsacl"
39 /* zfs_get_nt_acl()
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,
44 TALLOC_CTX *mem_ctx,
45 const struct smb_filename *smb_fname,
46 struct SMB4ACL_T **ppacl)
48 int naces, i;
49 ace_t *acebuf;
50 struct SMB4ACL_T *pacl;
51 SMB_STRUCT_STAT sbuf;
52 const SMB_STRUCT_STAT *psbuf = NULL;
53 int ret;
55 if (VALID_STAT(smb_fname->st)) {
56 psbuf = &smb_fname->st;
59 if (psbuf == NULL) {
60 ret = vfs_stat_smb_basename(conn, smb_fname, &sbuf);
61 if (ret != 0) {
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);
66 psbuf = &sbuf;
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) {
75 if(errno == ENOSYS) {
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));
79 } else {
80 DEBUG(9, ("acl(ACE_GETACLCNT, %s): %s ", smb_fname->base_name,
81 strerror(errno)));
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);
88 if(acebuf == NULL) {
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,
94 strerror(errno)));
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;
118 } else {
119 aceprop.flags = 0;
121 if(smb_add_ace4(pacl, &aceprop) == NULL)
122 return NT_STATUS_NO_MEMORY;
125 *ppacl = pacl;
126 return NT_STATUS_OK;
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;
134 ace_t *acebuf;
135 struct SMB4ACE_T *smbace;
136 TALLOC_CTX *mem_ctx;
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);
142 if(acebuf == NULL) {
143 errno = ENOMEM;
144 return False;
146 /* handle all aces */
147 for(smbace = smb_first_ace4(smbacl), i = 0;
148 smbace!=NULL;
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.
156 See bug #7909. */
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;
163 break;
164 case SMB_ACE4_WHO_OWNER:
165 acebuf[i].a_flags |= ACE_OWNER;
166 break;
167 case SMB_ACE4_WHO_GROUP:
168 acebuf[i].a_flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP;
169 break;
170 default:
171 DEBUG(8, ("unsupported special_id %d\n", \
172 aceprop->who.special_id));
173 continue; /* don't add it !!! */
175 have_special_id = true;
179 if (!have_special_id
180 && lp_parm_bool(fsp->conn->params->service, "zfsacl",
181 "denymissingspecial", false)) {
182 errno = EACCES;
183 return false;
186 SMB_ASSERT(i == naces);
188 /* store acl */
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)));
194 } else {
195 DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp),
196 strerror(errno)));
198 return 0;
201 return True;
204 /* zfs_set_nt_acl()
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,
213 zfs_process_smbacl);
216 static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle,
217 struct files_struct *fsp,
218 uint32_t security_info,
219 TALLOC_CTX *mem_ctx,
220 struct security_descriptor **ppdesc)
222 struct SMB4ACL_T *pacl;
223 NTSTATUS status;
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)) {
229 TALLOC_FREE(frame);
230 return status;
233 status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
234 ppdesc, pacl);
235 TALLOC_FREE(frame);
236 return status;
239 static NTSTATUS zfsacl_get_nt_acl(struct vfs_handle_struct *handle,
240 const struct smb_filename *smb_fname,
241 uint32_t security_info,
242 TALLOC_CTX *mem_ctx,
243 struct security_descriptor **ppdesc)
245 struct SMB4ACL_T *pacl;
246 NTSTATUS status;
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)) {
251 TALLOC_FREE(frame);
252 return status;
255 status = smb_get_nt_acl_nfs4(handle->conn,
256 smb_fname,
257 NULL,
258 security_info,
259 mem_ctx,
260 ppdesc,
261 pacl);
262 TALLOC_FREE(frame);
263 return status;
266 static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle,
267 files_struct *fsp,
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 :
276 See also
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
286 mounts on Solaris.
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
297 methods here.
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,
306 const char *path_p,
307 SMB_ACL_TYPE_T type,
308 TALLOC_CTX *mem_ctx)
310 return (SMB_ACL_T)NULL;
313 static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle,
314 files_struct *fsp,
315 TALLOC_CTX *mem_ctx)
317 return (SMB_ACL_T)NULL;
320 static int zfsacl_fail__sys_acl_set_file(vfs_handle_struct *handle,
321 const char *name,
322 SMB_ACL_TYPE_T type,
323 SMB_ACL_T theacl)
325 return -1;
328 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle,
329 files_struct *fsp,
330 SMB_ACL_T theacl)
332 return -1;
335 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
336 const char *path)
338 return -1;
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)
343 return -1;
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)
348 return -1;
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",
370 &zfsacl_fns);