2 Unix SMB/Netbios implementation.
3 VFS module to get and set posix acls through xattr
4 Copyright (c) 2013 Anand Avati <avati@redhat.com>
5 Copyright (c) 2016 Yan, Zheng <zyan@redhat.com>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "modules/posixacl_xattr.h"
29 Size = 4 (header) + N * 8 (entry)
31 Offset Size Field (Little Endian)
32 -------------------------------------
35 4-5 2-byte Entry-1 tag
36 6-7 2-byte Entry-1 perm
37 8-11 4-byte Entry-1 id
39 12-13 2-byte Entry-2 tag
40 14-15 2-byte Entry-2 perm
41 16-19 4-byte Entry-2 id
49 /* private functions */
51 #define ACL_EA_ACCESS "system.posix_acl_access"
52 #define ACL_EA_DEFAULT "system.posix_acl_default"
53 #define ACL_EA_VERSION 0x0002
54 #define ACL_EA_HEADER_SIZE 4
55 #define ACL_EA_ENTRY_SIZE 8
57 #define ACL_EA_SIZE(n) (ACL_EA_HEADER_SIZE + ((n) * ACL_EA_ENTRY_SIZE))
59 static SMB_ACL_T
mode_to_smb_acl(mode_t mode
, TALLOC_CTX
*mem_ctx
)
61 struct smb_acl_t
*result
;
65 result
= sys_acl_init(mem_ctx
);
70 result
->acl
= talloc_array(result
, struct smb_acl_entry
, count
);
77 result
->count
= count
;
79 result
->acl
[0].a_type
= SMB_ACL_USER_OBJ
;
80 result
->acl
[0].a_perm
= (mode
& S_IRWXU
) >> 6;
82 result
->acl
[1].a_type
= SMB_ACL_GROUP_OBJ
;
83 result
->acl
[1].a_perm
= (mode
& S_IRWXG
) >> 3;
85 result
->acl
[2].a_type
= SMB_ACL_OTHER
;
86 result
->acl
[2].a_perm
= mode
& S_IRWXO
;
91 static SMB_ACL_T
posixacl_xattr_to_smb_acl(const char *buf
, size_t xattr_size
,
96 struct smb_acl_entry
*smb_ace
;
97 struct smb_acl_t
*result
;
106 if (size
< ACL_EA_HEADER_SIZE
) {
107 /* ACL should be at least as big as the header (4 bytes) */
112 /* Version is the first 4 bytes of the ACL */
113 if (IVAL(buf
, 0) != ACL_EA_VERSION
) {
114 DEBUG(0, ("Unknown ACL EA version: %d\n",
119 offset
= ACL_EA_HEADER_SIZE
;
121 size
-= ACL_EA_HEADER_SIZE
;
122 if (size
% ACL_EA_ENTRY_SIZE
) {
123 /* Size of entries must strictly be a multiple of
124 size of an ACE (8 bytes)
126 DEBUG(0, ("Invalid ACL EA size: %d\n", size
));
131 count
= size
/ ACL_EA_ENTRY_SIZE
;
133 result
= sys_acl_init(mem_ctx
);
138 result
->acl
= talloc_array(result
, struct smb_acl_entry
, count
);
145 result
->count
= count
;
147 smb_ace
= result
->acl
;
149 for (i
= 0; i
< count
; i
++) {
150 /* TAG is the first 2 bytes of an entry */
151 tag
= SVAL(buf
, offset
);
154 /* PERM is the next 2 bytes of an entry */
155 perm
= SVAL(buf
, offset
);
158 /* ID is the last 4 bytes of an entry */
159 id
= IVAL(buf
, offset
);
164 smb_ace
->a_type
= SMB_ACL_USER
;
167 smb_ace
->a_type
= SMB_ACL_USER_OBJ
;
170 smb_ace
->a_type
= SMB_ACL_GROUP
;
173 smb_ace
->a_type
= SMB_ACL_GROUP_OBJ
;
176 smb_ace
->a_type
= SMB_ACL_OTHER
;
179 smb_ace
->a_type
= SMB_ACL_MASK
;
182 DEBUG(0, ("unknown tag type %d\n", (unsigned int) tag
));
188 switch(smb_ace
->a_type
) {
190 smb_ace
->info
.user
.uid
= id
;
193 smb_ace
->info
.group
.gid
= id
;
200 smb_ace
->a_perm
|= ((perm
& ACL_READ
) ? SMB_ACL_READ
: 0);
201 smb_ace
->a_perm
|= ((perm
& ACL_WRITE
) ? SMB_ACL_WRITE
: 0);
202 smb_ace
->a_perm
|= ((perm
& ACL_EXECUTE
) ? SMB_ACL_EXECUTE
: 0);
211 static int posixacl_xattr_entry_compare(const void *left
, const void *right
)
214 uint16_t tag_left
, tag_right
;
215 uint32_t id_left
, id_right
;
219 - Smaller TAG values must be earlier.
220 - Within same TAG, smaller identifiers must be earlier, E.g:
221 UID 0 entry must be earlier than UID 200
222 GID 17 entry must be earlier than GID 19
225 /* TAG is the first element in the entry */
226 tag_left
= SVAL(left
, 0);
227 tag_right
= SVAL(right
, 0);
229 ret
= (tag_left
- tag_right
);
231 /* ID is the third element in the entry, after two short
232 integers (tag and perm), i.e at offset 4.
234 id_left
= IVAL(left
, 4);
235 id_right
= IVAL(right
, 4);
236 ret
= id_left
- id_right
;
243 static int smb_acl_to_posixacl_xattr(SMB_ACL_T theacl
, char *buf
, size_t len
)
246 struct smb_acl_entry
*smb_ace
;
254 count
= theacl
->count
;
256 size
= ACL_EA_SIZE(count
);
263 smb_ace
= theacl
->acl
;
265 /* Version is the first 4 bytes of the ACL */
266 SIVAL(buf
, 0, ACL_EA_VERSION
);
267 offset
= ACL_EA_HEADER_SIZE
;
269 for (i
= 0; i
< count
; i
++) {
271 switch(smb_ace
->a_type
) {
275 case SMB_ACL_USER_OBJ
:
281 case SMB_ACL_GROUP_OBJ
:
291 DEBUG(0, ("Unknown tag value %d\n",
298 switch(smb_ace
->a_type
) {
300 id
= smb_ace
->info
.user
.uid
;
303 id
= smb_ace
->info
.group
.gid
;
306 id
= ACL_UNDEFINED_ID
;
312 perm
|= (smb_ace
->a_perm
& SMB_ACL_READ
) ? ACL_READ
: 0;
313 perm
|= (smb_ace
->a_perm
& SMB_ACL_WRITE
) ? ACL_WRITE
: 0;
314 perm
|= (smb_ace
->a_perm
& SMB_ACL_EXECUTE
) ? ACL_EXECUTE
: 0;
316 /* TAG is the first 2 bytes of an entry */
317 SSVAL(buf
, offset
, tag
);
320 /* PERM is the next 2 bytes of an entry */
321 SSVAL(buf
, offset
, perm
);
324 /* ID is the last 4 bytes of an entry */
325 SIVAL(buf
, offset
, id
);
331 /* Skip the header, sort @count number of 8-byte entries */
332 qsort(buf
+ACL_EA_HEADER_SIZE
, count
, ACL_EA_ENTRY_SIZE
,
333 posixacl_xattr_entry_compare
);
338 SMB_ACL_T
posixacl_xattr_acl_get_file(vfs_handle_struct
*handle
,
339 const struct smb_filename
*smb_fname
,
348 if (type
== SMB_ACL_TYPE_ACCESS
) {
349 name
= ACL_EA_ACCESS
;
350 } else if (type
== SMB_ACL_TYPE_DEFAULT
) {
351 name
= ACL_EA_DEFAULT
;
357 size
= ACL_EA_SIZE(20);
363 ret
= SMB_VFS_GETXATTR(handle
->conn
, smb_fname
,
365 if (ret
< 0 && errno
== ERANGE
) {
366 size
= SMB_VFS_GETXATTR(handle
->conn
, smb_fname
,
373 ret
= SMB_VFS_GETXATTR(handle
->conn
,
380 return posixacl_xattr_to_smb_acl(buf
, ret
, mem_ctx
);
382 if (ret
== 0 || errno
== ENOATTR
|| errno
== ENODATA
) {
384 TALLOC_CTX
*frame
= talloc_stackframe();
385 struct smb_filename
*smb_fname_tmp
=
386 cp_smb_filename_nostream(frame
, smb_fname
);
387 if (smb_fname_tmp
== NULL
) {
391 ret
= SMB_VFS_STAT(handle
->conn
, smb_fname_tmp
);
393 mode
= smb_fname_tmp
->st
.st_ex_mode
;
398 if (type
== SMB_ACL_TYPE_ACCESS
) {
399 return mode_to_smb_acl(mode
, mem_ctx
);
402 return sys_acl_init(mem_ctx
);
410 SMB_ACL_T
posixacl_xattr_acl_get_fd(vfs_handle_struct
*handle
,
415 int size
= ACL_EA_SIZE(20);
416 char *buf
= alloca(size
);
422 ret
= SMB_VFS_FGETXATTR(fsp
, ACL_EA_ACCESS
, buf
, size
);
423 if (ret
< 0 && errno
== ERANGE
) {
424 size
= SMB_VFS_FGETXATTR(fsp
, ACL_EA_ACCESS
, NULL
, 0);
430 ret
= SMB_VFS_FGETXATTR(fsp
, ACL_EA_ACCESS
, buf
, size
);
435 return posixacl_xattr_to_smb_acl(buf
, ret
, mem_ctx
);
437 if (ret
== 0 || errno
== ENOATTR
|| errno
== ENODATA
) {
438 SMB_STRUCT_STAT sbuf
;
439 ret
= SMB_VFS_FSTAT(fsp
, &sbuf
);
441 return mode_to_smb_acl(sbuf
.st_ex_mode
, mem_ctx
);
446 int posixacl_xattr_acl_set_file(vfs_handle_struct
*handle
,
447 const struct smb_filename
*smb_fname
,
456 size
= smb_acl_to_posixacl_xattr(theacl
, NULL
, 0);
462 ret
= smb_acl_to_posixacl_xattr(theacl
, buf
, size
);
468 if (type
== SMB_ACL_TYPE_ACCESS
) {
469 name
= ACL_EA_ACCESS
;
470 } else if (type
== SMB_ACL_TYPE_DEFAULT
) {
471 name
= ACL_EA_DEFAULT
;
477 return SMB_VFS_SETXATTR(handle
->conn
, smb_fname
,
481 int posixacl_xattr_acl_set_fd(vfs_handle_struct
*handle
,
482 files_struct
*fsp
, SMB_ACL_T theacl
)
488 size
= smb_acl_to_posixacl_xattr(theacl
, NULL
, 0);
494 ret
= smb_acl_to_posixacl_xattr(theacl
, buf
, size
);
500 return SMB_VFS_FSETXATTR(fsp
, ACL_EA_ACCESS
, buf
, size
, 0);
503 int posixacl_xattr_acl_delete_def_file(vfs_handle_struct
*handle
,
504 const struct smb_filename
*smb_fname
)
506 return SMB_VFS_REMOVEXATTR(handle
->conn
,