2 * Copyright (c) 1995-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/namei.h>
38 #include <sys/kernel.h>
40 #include <sys/vnode_internal.h>
41 #include <sys/mount_internal.h>
42 #include <sys/proc_internal.h>
43 #include <sys/kauth.h>
44 #include <sys/uio_internal.h>
45 #include <sys/malloc.h>
47 #include <sys/sysproto.h>
48 #include <sys/xattr.h>
49 #include <sys/fsevents.h>
50 #include <kern/kalloc.h>
51 #include <miscfs/specfs/specdev.h>
55 #include <security/mac_framework.h>
58 #define ATTR_TIME_SIZE -1
61 * Structure describing the state of an in-progress attrlist operation.
63 struct _attrlist_buf
{
73 * Pack (count) bytes from (source) into (buf).
76 attrlist_pack_fixed(struct _attrlist_buf
*ab
, void *source
, ssize_t count
)
80 /* how much room left in the buffer? */
81 fit
= imin(count
, ab
->allocated
- (ab
->fixedcursor
- ab
->base
));
83 bcopy(source
, ab
->fixedcursor
, fit
);
85 /* always move in increments of 4 */
86 ab
->fixedcursor
+= roundup(count
, 4);
89 attrlist_pack_variable2(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
, const void *ext
, ssize_t extcount
)
91 struct attrreference ar
;
94 /* pack the reference to the variable object */
95 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
96 ar
.attr_length
= count
+ extcount
;
97 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
99 /* calculate space and pack the variable object */
100 fit
= imin(count
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
103 bcopy(source
, ab
->varcursor
, fit
);
104 ab
->varcursor
+= fit
;
106 fit
= imin(extcount
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
109 bcopy(ext
, ab
->varcursor
, fit
);
110 ab
->varcursor
+= fit
;
112 /* always move in increments of 4 */
113 ab
->varcursor
= (char *)roundup((uintptr_t)ab
->varcursor
, 4);
116 attrlist_pack_variable(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
)
118 attrlist_pack_variable2(ab
, source
, count
, NULL
, 0);
121 attrlist_pack_string(struct _attrlist_buf
*ab
, const char *source
, ssize_t count
)
123 struct attrreference ar
;
128 * Supplied count is character count of string text, excluding trailing nul
129 * which we always supply here.
131 if (source
== NULL
) {
133 } else if (count
== 0) {
134 count
= strlen(source
);
138 * Make the reference and pack it.
139 * Note that this is entirely independent of how much we get into
142 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
143 ar
.attr_length
= count
+ 1;
144 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
146 /* calculate how much of the string text we can copy, and do that */
147 space
= ab
->allocated
- (ab
->varcursor
- ab
->base
);
148 fit
= imin(count
, space
);
150 bcopy(source
, ab
->varcursor
, fit
);
151 /* is there room for our trailing nul? */
153 ab
->varcursor
[fit
] = '\0';
155 /* always move in increments of 4 */
156 ab
->varcursor
+= roundup(count
+ 1, 4);
159 #define ATTR_PACK4(AB, V) \
161 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
162 *(uint32_t *)AB.fixedcursor = V; \
163 AB.fixedcursor += 4; \
167 #define ATTR_PACK8(AB, V) \
169 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
170 *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V; \
171 AB.fixedcursor += 8; \
175 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
176 #define ATTR_PACK_CAST(b, t, v) \
182 #define ATTR_PACK_TIME(b, v, is64) \
185 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
194 * Table-driven setup for all valid common/volume attributes.
196 struct getvolattrlist_attrtab
{
199 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
202 static struct getvolattrlist_attrtab getvolattrlist_common_tab
[] = {
203 {ATTR_CMN_NAME
, 0, sizeof(struct attrreference
)},
204 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
)},
205 {ATTR_CMN_FSID
, 0, sizeof(fsid_t
)},
206 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
)},
207 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
)},
208 {ATTR_CMN_OBJID
, 0, sizeof(fsobj_id_t
)},
209 {ATTR_CMN_OBJPERMANENTID
, 0, sizeof(fsobj_id_t
)},
210 {ATTR_CMN_PAROBJID
, 0, sizeof(fsobj_id_t
)},
211 {ATTR_CMN_SCRIPT
, 0, sizeof(text_encoding_t
)},
212 {ATTR_CMN_CRTIME
, VFSATTR_BIT(f_create_time
), ATTR_TIME_SIZE
},
213 {ATTR_CMN_MODTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
214 {ATTR_CMN_CHGTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
215 {ATTR_CMN_ACCTIME
, VFSATTR_BIT(f_access_time
), ATTR_TIME_SIZE
},
216 {ATTR_CMN_BKUPTIME
, VFSATTR_BIT(f_backup_time
), ATTR_TIME_SIZE
},
217 {ATTR_CMN_FNDRINFO
, 0, 32},
218 {ATTR_CMN_OWNERID
, 0, sizeof(uid_t
)},
219 {ATTR_CMN_GRPID
, 0, sizeof(gid_t
)},
220 {ATTR_CMN_ACCESSMASK
, 0, sizeof(uint32_t)},
221 {ATTR_CMN_FLAGS
, 0, sizeof(uint32_t)},
222 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t)},
226 static struct getvolattrlist_attrtab getvolattrlist_vol_tab
[] = {
227 {ATTR_VOL_FSTYPE
, 0, sizeof(uint32_t)},
228 {ATTR_VOL_SIGNATURE
, VFSATTR_BIT(f_signature
), sizeof(uint32_t)},
229 {ATTR_VOL_SIZE
, VFSATTR_BIT(f_blocks
), sizeof(off_t
)},
230 {ATTR_VOL_SPACEFREE
, VFSATTR_BIT(f_bfree
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
231 {ATTR_VOL_SPACEAVAIL
, VFSATTR_BIT(f_bavail
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
232 {ATTR_VOL_MINALLOCATION
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
233 {ATTR_VOL_ALLOCATIONCLUMP
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
234 {ATTR_VOL_IOBLOCKSIZE
, VFSATTR_BIT(f_iosize
), sizeof(uint32_t)},
235 {ATTR_VOL_OBJCOUNT
, VFSATTR_BIT(f_objcount
), sizeof(uint32_t)},
236 {ATTR_VOL_FILECOUNT
, VFSATTR_BIT(f_filecount
), sizeof(uint32_t)},
237 {ATTR_VOL_DIRCOUNT
, VFSATTR_BIT(f_dircount
), sizeof(uint32_t)},
238 {ATTR_VOL_MAXOBJCOUNT
, VFSATTR_BIT(f_maxobjcount
), sizeof(uint32_t)},
239 {ATTR_VOL_MOUNTPOINT
, 0, sizeof(struct attrreference
)},
240 {ATTR_VOL_NAME
, VFSATTR_BIT(f_vol_name
), sizeof(struct attrreference
)},
241 {ATTR_VOL_MOUNTFLAGS
, 0, sizeof(uint32_t)},
242 {ATTR_VOL_MOUNTEDDEVICE
, 0, sizeof(struct attrreference
)},
243 {ATTR_VOL_ENCODINGSUSED
, 0, sizeof(uint64_t)},
244 {ATTR_VOL_CAPABILITIES
, VFSATTR_BIT(f_capabilities
), sizeof(vol_capabilities_attr_t
)},
245 {ATTR_VOL_ATTRIBUTES
, VFSATTR_BIT(f_attributes
), sizeof(vol_attributes_attr_t
)},
246 {ATTR_VOL_INFO
, 0, 0},
251 getvolattrlist_parsetab(struct getvolattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vfs_attr
*vsp
,
252 ssize_t
*sizep
, int is_64bit
)
254 attrgroup_t recognised
;
258 /* is this attribute set? */
259 if (tab
->attr
& attrs
) {
260 recognised
|= tab
->attr
;
261 vsp
->f_active
|= tab
->bits
;
262 if (tab
->size
== ATTR_TIME_SIZE
) {
264 *sizep
+= sizeof(struct user_timespec
);
266 *sizep
+= sizeof(struct timespec
);
272 } while ((++tab
)->attr
!= 0);
274 /* check to make sure that we recognised all of the passed-in attributes */
275 if (attrs
& ~recognised
)
281 * Given the attributes listed in alp, configure vap to request
282 * the data from a filesystem.
285 getvolattrlist_setupvfsattr(struct attrlist
*alp
, struct vfs_attr
*vsp
, ssize_t
*sizep
, int is_64bit
)
290 * Parse the above tables.
292 *sizep
= sizeof(uint32_t); /* length count */
293 if (alp
->commonattr
&&
294 (error
= getvolattrlist_parsetab(getvolattrlist_common_tab
, alp
->commonattr
, vsp
, sizep
, is_64bit
)) != 0)
297 (error
= getvolattrlist_parsetab(getvolattrlist_vol_tab
, alp
->volattr
, vsp
, sizep
, is_64bit
)) != 0)
304 * Table-driven setup for all valid common/dir/file/fork attributes against files.
306 struct getattrlist_attrtab
{
309 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
311 kauth_action_t action
;
313 static struct getattrlist_attrtab getattrlist_common_tab
[] = {
314 {ATTR_CMN_NAME
, VATTR_BIT(va_name
), sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
315 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
316 {ATTR_CMN_FSID
, VATTR_BIT(va_fsid
), sizeof(fsid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
317 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
318 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
319 {ATTR_CMN_OBJID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
320 {ATTR_CMN_OBJPERMANENTID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
321 {ATTR_CMN_PAROBJID
, VATTR_BIT(va_parentid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
322 {ATTR_CMN_SCRIPT
, VATTR_BIT(va_encoding
), sizeof(text_encoding_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
323 {ATTR_CMN_CRTIME
, VATTR_BIT(va_create_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
324 {ATTR_CMN_MODTIME
, VATTR_BIT(va_modify_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
325 {ATTR_CMN_CHGTIME
, VATTR_BIT(va_change_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
326 {ATTR_CMN_ACCTIME
, VATTR_BIT(va_access_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
327 {ATTR_CMN_BKUPTIME
, VATTR_BIT(va_backup_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
328 {ATTR_CMN_FNDRINFO
, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES
},
329 {ATTR_CMN_OWNERID
, VATTR_BIT(va_uid
), sizeof(uid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
330 {ATTR_CMN_GRPID
, VATTR_BIT(va_gid
), sizeof(gid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
331 {ATTR_CMN_ACCESSMASK
, VATTR_BIT(va_mode
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
332 {ATTR_CMN_FLAGS
, VATTR_BIT(va_flags
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
333 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
334 {ATTR_CMN_EXTENDED_SECURITY
, VATTR_BIT(va_acl
), sizeof(struct attrreference
), KAUTH_VNODE_READ_SECURITY
},
335 {ATTR_CMN_UUID
, VATTR_BIT(va_uuuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
336 {ATTR_CMN_GRPUUID
, VATTR_BIT(va_guuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
337 {ATTR_CMN_FILEID
, VATTR_BIT(va_fileid
), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES
},
338 {ATTR_CMN_PARENTID
, VATTR_BIT(va_parentid
), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES
},
341 static struct getattrlist_attrtab getattrlist_dir_tab
[] = {
342 {ATTR_DIR_LINKCOUNT
, VATTR_BIT(va_dirlinkcount
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
343 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nchildren
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
344 /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
345 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nlink
), 0, KAUTH_VNODE_READ_ATTRIBUTES
},
346 {ATTR_DIR_MOUNTSTATUS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
349 static struct getattrlist_attrtab getattrlist_file_tab
[] = {
350 {ATTR_FILE_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
351 {ATTR_FILE_TOTALSIZE
, VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
352 {ATTR_FILE_ALLOCSIZE
, VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
353 {ATTR_FILE_IOBLOCKSIZE
, VATTR_BIT(va_iosize
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
354 {ATTR_FILE_DEVTYPE
, VATTR_BIT(va_rdev
), sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
355 {ATTR_FILE_DATALENGTH
, VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
356 {ATTR_FILE_DATAALLOCSIZE
, VATTR_BIT(va_total_alloc
)| VATTR_BIT(va_data_alloc
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
357 {ATTR_FILE_RSRCLENGTH
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
358 {ATTR_FILE_RSRCALLOCSIZE
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
363 * The following are attributes that VFS can derive.
365 * A majority of them are the same attributes that are required for stat(2) and statfs(2).
367 #define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
368 ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
369 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
370 ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
371 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
372 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
375 #define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
376 ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
377 ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
378 ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
379 ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
380 ATTR_CMN_ACCTIME | ATTR_CMN_FNDRINFO | \
381 ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
382 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
383 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
386 #define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
388 #define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
389 ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
390 ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
391 ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
392 ATTR_FILE_RSRCALLOCSIZE)
395 getattrlist_parsetab(struct getattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vnode_attr
*vap
,
396 ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
)
398 attrgroup_t recognised
;
402 /* is this attribute set? */
403 if (tab
->attr
& attrs
) {
404 recognised
|= tab
->attr
;
405 vap
->va_active
|= tab
->bits
;
406 if (tab
->size
== ATTR_TIME_SIZE
) {
408 *sizep
+= sizeof(struct user_timespec
);
410 *sizep
+= sizeof(struct timespec
);
415 *actionp
|= tab
->action
;
417 } while ((++tab
)->attr
!= 0);
419 /* check to make sure that we recognised all of the passed-in attributes */
420 if (attrs
& ~recognised
)
426 * Given the attributes listed in alp, configure vap to request
427 * the data from a filesystem.
430 getattrlist_setupvattr(struct attrlist
*alp
, struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
, int isdir
)
435 * Parse the above tables.
437 *sizep
= sizeof(uint32_t); /* length count */
439 if (alp
->commonattr
&&
440 (error
= getattrlist_parsetab(getattrlist_common_tab
, alp
->commonattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
442 if (isdir
&& alp
->dirattr
&&
443 (error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
445 if (!isdir
&& alp
->fileattr
&&
446 (error
= getattrlist_parsetab(getattrlist_file_tab
, alp
->fileattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
453 setattrlist_setfinderinfo(vnode_t vp
, char *fndrinfo
, struct vfs_context
*ctx
)
456 char uio_buf
[UIO_SIZEOF(1)];
459 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
462 uio_addiov(auio
, CAST_USER_ADDR_T(fndrinfo
), 32);
463 error
= vn_setxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, XATTR_NOSECURITY
, ctx
);
468 if (error
== 0 && need_fsevent(FSE_FINDER_INFO_CHANGED
, vp
)) {
469 add_fsevent(FSE_FINDER_INFO_CHANGED
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
477 * Find something resembling a terminal component name in the mountedonname for vp
481 getattrlist_findnamecomp(const char *mn
, const char **np
, ssize_t
*nl
)
487 * We're looking for the last sequence of non / characters, but
488 * not including any trailing / characters.
493 for (cp
= mn
; *cp
!= 0; cp
++) {
495 /* start of run of chars */
501 /* end of run of chars */
508 /* need to close run? */
515 getvolattrlist(vnode_t vp
, struct getattrlist_args
*uap
, struct attrlist
*alp
, vfs_context_t ctx
, int is_64bit
)
518 struct vnode_attr va
;
519 struct _attrlist_buf ab
;
521 ssize_t fixedsize
, varsize
;
522 const char *cnp
= NULL
; /* protected by ATTR_CMN_NAME */
523 ssize_t cnl
= 0; /* protected by ATTR_CMN_NAME */
529 vs
.f_vol_name
= NULL
;
534 * For now, the vnode must be the root of its filesystem.
535 * To relax this, we need to be able to find the root vnode of a filesystem
536 * from any vnode in the filesystem.
538 if (!vnode_isvroot(vp
)) {
540 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
545 * Set up the vfs_attr structure and call the filesystem.
547 if ((error
= getvolattrlist_setupvfsattr(alp
, &vs
, &fixedsize
, is_64bit
)) != 0) {
548 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
551 if (vs
.f_active
!= 0) {
552 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
553 if (VFSATTR_IS_ACTIVE(&vs
, f_vol_name
)) {
554 vs
.f_vol_name
= (char *) kalloc(MAXPATHLEN
);
555 if (vs
.f_vol_name
== NULL
) {
557 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
563 error
= mac_mount_check_getattr(ctx
, mnt
, &vs
);
567 VFS_DEBUG(ctx
, vp
, "ATTRLIST - calling to get %016llx with supported %016llx", vs
.f_active
, vs
.f_supported
);
568 if ((error
= vfs_getattr(mnt
, &vs
, ctx
)) != 0) {
569 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
574 * Did we ask for something the filesystem doesn't support?
576 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
577 /* default value for volume subtype */
578 if (VFSATTR_IS_ACTIVE(&vs
, f_fssubtype
)
579 && !VFSATTR_IS_SUPPORTED(&vs
, f_fssubtype
))
580 VFSATTR_RETURN(&vs
, f_fssubtype
, 0);
583 * If the file system didn't supply f_signature, then
584 * default it to 'BD', which is the generic signature
585 * that most Carbon file systems should return.
587 if (VFSATTR_IS_ACTIVE(&vs
, f_signature
)
588 && !VFSATTR_IS_SUPPORTED(&vs
, f_signature
))
589 VFSATTR_RETURN(&vs
, f_signature
, 0x4244);
591 /* default for block size */
592 if (VFSATTR_IS_ACTIVE(&vs
, f_bsize
)
593 && !VFSATTR_IS_SUPPORTED(&vs
, f_bsize
))
594 VFSATTR_RETURN(&vs
, f_bsize
, mnt
->mnt_devblocksize
);
596 /* default value for volume f_attributes */
597 if (VFSATTR_IS_ACTIVE(&vs
, f_attributes
)
598 && !VFSATTR_IS_SUPPORTED(&vs
, f_attributes
)) {
599 vol_attributes_attr_t
*attrp
= &vs
.f_attributes
;
601 attrp
->validattr
.commonattr
= VFS_DFLT_ATTR_CMN
;
602 attrp
->validattr
.volattr
= VFS_DFLT_ATTR_VOL
;
603 attrp
->validattr
.dirattr
= VFS_DFLT_ATTR_DIR
;
604 attrp
->validattr
.fileattr
= VFS_DFLT_ATTR_FILE
;
605 attrp
->validattr
.forkattr
= 0;
607 attrp
->nativeattr
.commonattr
= 0;
608 attrp
->nativeattr
.volattr
= 0;
609 attrp
->nativeattr
.dirattr
= 0;
610 attrp
->nativeattr
.fileattr
= 0;
611 attrp
->nativeattr
.forkattr
= 0;
612 VFSATTR_SET_SUPPORTED(&vs
, f_attributes
);
615 /* default value for volume f_capabilities */
616 if (VFSATTR_IS_ACTIVE(&vs
, f_capabilities
)) {
617 /* getattrlist is always supported now. */
618 if (!VFSATTR_IS_SUPPORTED(&vs
, f_capabilities
)) {
619 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] = 0;
620 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] = VOL_CAP_INT_ATTRLIST
;
621 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_RESERVED1
] = 0;
622 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_RESERVED2
] = 0;
624 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] = 0;
625 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] = VOL_CAP_INT_ATTRLIST
;
626 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_RESERVED1
] = 0;
627 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_RESERVED2
] = 0;
628 VFSATTR_SET_SUPPORTED(&vs
, f_capabilities
);
631 /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
632 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_ATTRLIST
;
633 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_ATTRLIST
;
637 /* check to see if our fixups were enough */
638 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
640 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested volume attributes");
641 VFS_DEBUG(ctx
, vp
, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
642 vs
.f_active
, vs
.f_supported
, vs
.f_active
& ~vs
.f_supported
);
649 * Some fields require data from the root vp
651 if (alp
->commonattr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
| ATTR_CMN_ACCESSMASK
| ATTR_CMN_FLAGS
| ATTR_CMN_SCRIPT
)) {
652 VATTR_WANTED(&va
, va_uid
);
653 VATTR_WANTED(&va
, va_gid
);
654 VATTR_WANTED(&va
, va_mode
);
655 VATTR_WANTED(&va
, va_flags
);
656 VATTR_WANTED(&va
, va_encoding
);
658 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
659 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp
);
663 if (VATTR_IS_ACTIVE(&va
, va_encoding
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
664 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
668 * Compute variable-size buffer requirements.
671 if (alp
->commonattr
& ATTR_CMN_NAME
) {
672 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
673 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
674 /* special case for boot volume. Use root name when it's
675 * available (which is the volume name) or just the mount on
676 * name of "/". we must do this for binary compatibility with
677 * pre Tiger code. returning nothing for the boot volume name
678 * breaks installers - 3961058
680 cnp
= vnode_getname(vp
);
682 /* just use "/" as name */
683 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
688 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
690 if (alp
->commonattr
& ATTR_CMN_NAME
)
691 varsize
+= roundup(cnl
+ 1, 4);
693 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
694 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntonname
) + 1, 4);
695 if (alp
->volattr
& ATTR_VOL_NAME
) {
696 vs
.f_vol_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
697 varsize
+= roundup(strlen(vs
.f_vol_name
) + 1, 4);
699 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
700 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntfromname
) + 1, 4);
703 * Allocate a target buffer for attribute results.
704 * Note that since we won't ever copy out more than the caller requested,
705 * we never need to allocate more than they offer.
707 ab
.allocated
= imin(uap
->bufferSize
, fixedsize
+ varsize
);
708 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
710 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
713 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
714 if (ab
.base
== NULL
) {
716 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
721 * Pack results into the destination buffer.
723 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
724 ab
.varcursor
= ab
.base
+ fixedsize
;
725 ab
.needed
= fixedsize
+ varsize
;
727 /* common attributes **************************************************/
728 if (alp
->commonattr
& ATTR_CMN_NAME
)
729 attrlist_pack_string(&ab
, cnp
, cnl
);
730 if (alp
->commonattr
& ATTR_CMN_DEVID
)
731 ATTR_PACK4(ab
, mnt
->mnt_vfsstat
.f_fsid
.val
[0]);
732 if (alp
->commonattr
& ATTR_CMN_FSID
)
733 ATTR_PACK8(ab
, mnt
->mnt_vfsstat
.f_fsid
);
734 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
)
736 if (alp
->commonattr
& ATTR_CMN_OBJTAG
)
737 ATTR_PACK4(ab
, vp
->v_tag
);
738 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
739 fsobj_id_t f
= {0, 0};
742 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
743 fsobj_id_t f
= {0, 0};
746 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
747 fsobj_id_t f
= {0, 0};
750 /* note that this returns the encoding for the volume name, not the node name */
751 if (alp
->commonattr
& ATTR_CMN_SCRIPT
)
752 ATTR_PACK4(ab
, va
.va_encoding
);
753 if (alp
->commonattr
& ATTR_CMN_CRTIME
)
754 ATTR_PACK_TIME(ab
, vs
.f_create_time
, is_64bit
);
755 if (alp
->commonattr
& ATTR_CMN_MODTIME
)
756 ATTR_PACK_TIME(ab
, vs
.f_modify_time
, is_64bit
);
757 if (alp
->commonattr
& ATTR_CMN_CHGTIME
)
758 ATTR_PACK_TIME(ab
, vs
.f_modify_time
, is_64bit
);
759 if (alp
->commonattr
& ATTR_CMN_ACCTIME
)
760 ATTR_PACK_TIME(ab
, vs
.f_access_time
, is_64bit
);
761 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
)
762 ATTR_PACK_TIME(ab
, vs
.f_backup_time
, is_64bit
);
763 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
766 * This attribute isn't really Finder Info, at least for HFS.
768 if (vp
->v_tag
== VT_HFS
) {
769 if ((error
= VNOP_IOCTL(vp
, HFS_GET_BOOT_INFO
, (caddr_t
)&f
, 0, ctx
)) != 0)
772 /* XXX we could at least pass out the volume UUID here */
773 bzero(&f
, sizeof(f
));
775 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
777 if (alp
->commonattr
& ATTR_CMN_OWNERID
)
778 ATTR_PACK4(ab
, va
.va_uid
);
779 if (alp
->commonattr
& ATTR_CMN_GRPID
)
780 ATTR_PACK4(ab
, va
.va_gid
);
781 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
)
782 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
783 if (alp
->commonattr
& ATTR_CMN_FLAGS
)
784 ATTR_PACK4(ab
, va
.va_flags
);
785 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* XXX this is expensive and also duplicate work */
787 if (vnode_isdir(vp
)) {
788 if (vnode_authorize(vp
, NULL
,
789 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
791 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
793 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
796 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
798 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
800 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
805 * Rather than MAC preceding DAC, in this case we want
806 * the smallest set of permissions granted by both MAC & DAC
807 * checks. We won't add back any permissions.
810 if (mac_vnode_check_access(ctx
, vp
, W_OK
) != 0)
813 if (mac_vnode_check_access(ctx
, vp
, R_OK
) != 0)
816 if (mac_vnode_check_access(ctx
, vp
, X_OK
) != 0)
819 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms
);
820 ATTR_PACK4(ab
, perms
);
823 /* volume attributes **************************************************/
825 if (alp
->volattr
& ATTR_VOL_FSTYPE
)
826 ATTR_PACK_CAST(&ab
, uint32_t, vfs_typenum(mnt
));
827 if (alp
->volattr
& ATTR_VOL_SIGNATURE
)
828 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_signature
);
829 if (alp
->volattr
& ATTR_VOL_SIZE
)
830 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_blocks
);
831 if (alp
->volattr
& ATTR_VOL_SPACEFREE
)
832 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bfree
);
833 if (alp
->volattr
& ATTR_VOL_SPACEAVAIL
)
834 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bavail
);
835 if (alp
->volattr
& ATTR_VOL_MINALLOCATION
)
836 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
);
837 if (alp
->volattr
& ATTR_VOL_ALLOCATIONCLUMP
)
838 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
); /* not strictly true */
839 if (alp
->volattr
& ATTR_VOL_IOBLOCKSIZE
)
840 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_iosize
);
841 if (alp
->volattr
& ATTR_VOL_OBJCOUNT
)
842 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_objcount
);
843 if (alp
->volattr
& ATTR_VOL_FILECOUNT
)
844 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_filecount
);
845 if (alp
->volattr
& ATTR_VOL_DIRCOUNT
)
846 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_dircount
);
847 if (alp
->volattr
& ATTR_VOL_MAXOBJCOUNT
)
848 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_maxobjcount
);
849 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
850 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntonname
, 0);
851 if (alp
->volattr
& ATTR_VOL_NAME
)
852 attrlist_pack_string(&ab
, vs
.f_vol_name
, 0);
853 if (alp
->volattr
& ATTR_VOL_MOUNTFLAGS
)
854 ATTR_PACK_CAST(&ab
, uint32_t, mnt
->mnt_flag
);
855 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
856 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntfromname
, 0);
857 if (alp
->volattr
& ATTR_VOL_ENCODINGSUSED
)
858 ATTR_PACK_CAST(&ab
, uint64_t, ~0LL); /* return all encodings */
859 if (alp
->volattr
& ATTR_VOL_CAPABILITIES
) {
860 /* fix up volume capabilities */
861 if (vfs_extendedsecurity(mnt
)) {
862 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
864 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] &= ~VOL_CAP_INT_EXTENDED_SECURITY
;
866 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
867 ATTR_PACK(&ab
, vs
.f_capabilities
);
869 if (alp
->volattr
& ATTR_VOL_ATTRIBUTES
) {
870 /* fix up volume attribute information */
872 vs
.f_attributes
.validattr
.commonattr
|= VFS_DFLT_ATTR_CMN
;
873 vs
.f_attributes
.validattr
.volattr
|= VFS_DFLT_ATTR_VOL
;
874 vs
.f_attributes
.validattr
.dirattr
|= VFS_DFLT_ATTR_DIR
;
875 vs
.f_attributes
.validattr
.fileattr
|= VFS_DFLT_ATTR_FILE
;
877 if (vfs_extendedsecurity(mnt
)) {
878 vs
.f_attributes
.validattr
.commonattr
|= (ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
880 vs
.f_attributes
.validattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
881 vs
.f_attributes
.nativeattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
883 ATTR_PACK(&ab
, vs
.f_attributes
);
887 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
888 panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
889 fixedsize
, ab
.fixedcursor
- ab
.base
, alp
->commonattr
, alp
->volattr
);
890 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
891 panic("packed variable field size mismatch; used %d but expected %ld", ab
.varcursor
- ab
.base
, ab
.needed
);
894 * In the compatible case, we report the smaller of the required and returned sizes.
895 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
896 * of the result buffer, even if we copied less out. The caller knows how big a buffer
897 * they gave us, so they can always check for truncation themselves.
899 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
901 error
= copyout(ab
.base
, uap
->attributeBuffer
, ab
.allocated
);
904 if (vs
.f_vol_name
!= NULL
)
905 kfree(vs
.f_vol_name
, MAXPATHLEN
);
907 FREE(ab
.base
, M_TEMP
);
908 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
913 * Obtain attribute information about a filesystem object.
916 getattrlist(proc_t p
, struct getattrlist_args
*uap
, __unused register_t
*retval
)
919 struct vnode_attr va
;
920 struct vfs_context
*ctx
;
922 struct _attrlist_buf ab
;
925 kauth_action_t action
;
926 ssize_t fixedsize
, varsize
;
928 const char *vname
= NULL
;
933 ctx
= vfs_context_current();
936 proc_is64
= proc_is64bit(p
);
946 nameiflags
= NOTRIGGER
| AUDITVNPATH1
;
947 if (!(uap
->options
& FSOPT_NOFOLLOW
))
948 nameiflags
|= FOLLOW
;
949 NDINIT(&nd
, LOOKUP
, nameiflags
, UIO_USERSPACE
, uap
->path
, ctx
);
951 if ((error
= namei(&nd
)) != 0)
957 * Fetch the attribute request.
959 if ((error
= copyin(uap
->alist
, &al
, sizeof(al
))) != 0)
961 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
966 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
967 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
968 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
971 error
= mac_vnode_check_getattrlist(ctx
, vp
, &al
);
977 * It is legal to request volume or file attributes,
981 if (al
.fileattr
|| al
.dirattr
|| al
.forkattr
) {
983 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
986 /* handle volume attribute request */
987 error
= getvolattrlist(vp
, uap
, &al
, ctx
, proc_is64
);
992 * Set up the vnode_attr structure and authorise.
994 if ((error
= getattrlist_setupvattr(&al
, &va
, &fixedsize
, &action
, proc_is64
, vnode_isdir(vp
))) != 0) {
995 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
998 if ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0) {
999 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorisation failed/denied");
1003 if (va
.va_active
!= 0) {
1005 * If we're going to ask for va_name, allocate a buffer to point it at
1007 if (VATTR_IS_ACTIVE(&va
, va_name
)) {
1008 va
.va_name
= (char *) kalloc(MAXPATHLEN
);
1009 if (va
.va_name
== NULL
) {
1011 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: cannot allocate va_name buffer");
1017 * Call the filesystem.
1019 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
1020 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1024 /* did we ask for something the filesystem doesn't support? */
1025 if (!VATTR_ALL_SUPPORTED(&va
)) {
1028 * There are a couple of special cases. If we are after object IDs,
1029 * we can make do with va_fileid.
1031 if ((al
.commonattr
& (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
| ATTR_CMN_FILEID
)) && !VATTR_IS_SUPPORTED(&va
, va_linkid
))
1032 VATTR_CLEAR_ACTIVE(&va
, va_linkid
); /* forget we wanted this */
1034 * Many (most?) filesystems don't know their parent object id. We can get it the
1037 if ((al
.commonattr
& (ATTR_CMN_PAROBJID
| ATTR_CMN_PARENTID
)) && !VATTR_IS_SUPPORTED(&va
, va_parentid
))
1038 VATTR_CLEAR_ACTIVE(&va
, va_parentid
);
1040 * And we can report datasize/alloc from total.
1042 if ((al
.fileattr
& ATTR_FILE_DATALENGTH
) && !VATTR_IS_SUPPORTED(&va
, va_data_size
))
1043 VATTR_CLEAR_ACTIVE(&va
, va_data_size
);
1044 if ((al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) && !VATTR_IS_SUPPORTED(&va
, va_data_alloc
))
1045 VATTR_CLEAR_ACTIVE(&va
, va_data_alloc
);
1048 * If we don't have an encoding, go with UTF-8
1050 if ((al
.commonattr
& ATTR_CMN_SCRIPT
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
1051 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
1054 * If we don't have a name, we'll get one from the vnode or mount point.
1056 if ((al
.commonattr
& ATTR_CMN_NAME
) && !VATTR_IS_SUPPORTED(&va
, va_name
)) {
1057 VATTR_CLEAR_ACTIVE(&va
, va_name
);
1060 /* If va_dirlinkcount isn't supported use a default of 1. */
1061 if ((al
.dirattr
& ATTR_DIR_LINKCOUNT
) && !VATTR_IS_SUPPORTED(&va
, va_dirlinkcount
)) {
1062 VATTR_RETURN(&va
, va_dirlinkcount
, 1);
1066 if (!VATTR_ALL_SUPPORTED(&va
)) {
1068 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested file attributes");
1069 VFS_DEBUG(ctx
, vp
, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
1070 va
.va_supported
, va
.va_active
, va
.va_active
& ~va
.va_supported
);
1077 * Compute variable-space requirements.
1079 varsize
= 0; /* length count */
1080 if (al
.commonattr
& ATTR_CMN_NAME
) {
1081 if (VATTR_IS_SUPPORTED(&va
, va_name
)) {
1082 va
.va_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
1086 if (vnode_isvroot(vp
)) {
1087 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
1088 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
1089 /* special case for boot volume. Use root name when it's
1090 * available (which is the volume name) or just the mount on
1091 * name of "/". we must do this for binary compatibility with
1092 * pre Tiger code. returning nothing for the boot volume name
1093 * breaks installers - 3961058
1095 cnp
= vname
= vnode_getname(vp
);
1097 /* just use "/" as name */
1098 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
1103 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
1106 cnp
= vname
= vnode_getname(vp
);
1113 varsize
+= roundup(cnl
+ 1, 4);
1117 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
1119 * XXX This needs to change at some point; since the blob is opaque in
1120 * user-space this is OK.
1122 if ((al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) &&
1123 VATTR_IS_SUPPORTED(&va
, va_acl
) &&
1124 (va
.va_acl
!= NULL
))
1125 varsize
+= roundup(KAUTH_FILESEC_SIZE(va
.va_acl
->acl_entrycount
), 4);
1128 * Allocate a target buffer for attribute results.
1130 * Note that we won't ever copy out more than the caller requested, even though
1131 * we might have to allocate more than they offer so that the diagnostic checks
1132 * don't result in a panic if the caller's buffer is too small..
1134 ab
.allocated
= fixedsize
+ varsize
;
1135 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
1137 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
1140 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
1141 if (ab
.base
== NULL
) {
1143 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
1147 /* set the S_IFMT bits for the mode */
1148 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1149 switch (vp
->v_type
) {
1151 va
.va_mode
|= S_IFREG
;
1154 va
.va_mode
|= S_IFDIR
;
1157 va
.va_mode
|= S_IFBLK
;
1160 va
.va_mode
|= S_IFCHR
;
1163 va
.va_mode
|= S_IFLNK
;
1166 va
.va_mode
|= S_IFSOCK
;
1169 va
.va_mode
|= S_IFIFO
;
1178 * Pack results into the destination buffer.
1180 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
1181 ab
.varcursor
= ab
.base
+ fixedsize
;
1182 ab
.needed
= ab
.allocated
;
1184 /* common attributes **************************************************/
1185 if (al
.commonattr
& ATTR_CMN_NAME
)
1186 attrlist_pack_string(&ab
, cnp
, cnl
);
1187 if (al
.commonattr
& ATTR_CMN_DEVID
)
1188 ATTR_PACK4(ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
1189 if (al
.commonattr
& ATTR_CMN_FSID
)
1190 ATTR_PACK8(ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
);
1191 if (al
.commonattr
& ATTR_CMN_OBJTYPE
)
1192 ATTR_PACK4(ab
, vp
->v_type
);
1193 if (al
.commonattr
& ATTR_CMN_OBJTAG
)
1194 ATTR_PACK4(ab
, vp
->v_tag
);
1195 if (al
.commonattr
& ATTR_CMN_OBJID
) {
1198 * Carbon can't deal with us reporting the target ID
1199 * for links. So we ask the filesystem to give us the
1200 * source ID as well, and if it gives us one, we use
1203 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1204 f
.fid_objno
= va
.va_linkid
;
1206 f
.fid_objno
= va
.va_fileid
;
1208 f
.fid_generation
= 0;
1211 if (al
.commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1214 * Carbon can't deal with us reporting the target ID
1215 * for links. So we ask the filesystem to give us the
1216 * source ID as well, and if it gives us one, we use
1219 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1220 f
.fid_objno
= va
.va_linkid
;
1222 f
.fid_objno
= va
.va_fileid
;
1224 f
.fid_generation
= 0;
1227 if (al
.commonattr
& ATTR_CMN_PAROBJID
) {
1230 * If the filesystem doesn't know the parent ID, we can
1231 * try to get it via v->v_parent. Don't need to worry
1232 * about links here, as we dont allow hardlinks to
1235 if (VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
1236 f
.fid_objno
= va
.va_parentid
;
1238 struct vnode_attr lva
;
1241 pvp
= vnode_getparent(vp
);
1243 if (pvp
== NULLVP
) {
1248 VATTR_WANTED(&lva
, va_fileid
);
1249 error
= vnode_getattr(pvp
, &lva
, ctx
);
1254 f
.fid_objno
= lva
.va_fileid
;
1256 f
.fid_generation
= 0;
1259 if (al
.commonattr
& ATTR_CMN_SCRIPT
)
1260 ATTR_PACK4(ab
, va
.va_encoding
);
1261 if (al
.commonattr
& ATTR_CMN_CRTIME
)
1262 ATTR_PACK_TIME(ab
, va
.va_create_time
, proc_is64
);
1263 if (al
.commonattr
& ATTR_CMN_MODTIME
)
1264 ATTR_PACK_TIME(ab
, va
.va_modify_time
, proc_is64
);
1265 if (al
.commonattr
& ATTR_CMN_CHGTIME
)
1266 ATTR_PACK_TIME(ab
, va
.va_change_time
, proc_is64
);
1267 if (al
.commonattr
& ATTR_CMN_ACCTIME
)
1268 ATTR_PACK_TIME(ab
, va
.va_access_time
, proc_is64
);
1269 if (al
.commonattr
& ATTR_CMN_BKUPTIME
)
1270 ATTR_PACK_TIME(ab
, va
.va_backup_time
, proc_is64
);
1271 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1274 char uio_buf
[UIO_SIZEOF(1)];
1276 fisize
= imin(32, ab
.allocated
- (ab
.fixedcursor
- ab
.base
));
1278 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1282 uio_addiov(auio
, CAST_USER_ADDR_T(ab
.fixedcursor
), fisize
);
1283 error
= vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, &fisize
, XATTR_NOSECURITY
, ctx
);
1287 if ((error
== ENOATTR
) || (error
== ENOENT
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1288 VFS_DEBUG(ctx
, vp
, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1289 bzero(ab
.fixedcursor
, 32);
1292 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1297 VFS_DEBUG(ctx
, vp
, "ATTRLIST - no room in caller buffer for FINDERINFO");
1299 ab
.fixedcursor
+= 32;
1301 if (al
.commonattr
& ATTR_CMN_OWNERID
)
1302 ATTR_PACK4(ab
, va
.va_uid
);
1303 if (al
.commonattr
& ATTR_CMN_GRPID
)
1304 ATTR_PACK4(ab
, va
.va_gid
);
1305 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
)
1306 ATTR_PACK4(ab
, va
.va_mode
);
1307 if (al
.commonattr
& ATTR_CMN_FLAGS
)
1308 ATTR_PACK4(ab
, va
.va_flags
);
1309 if (al
.commonattr
& ATTR_CMN_USERACCESS
) { /* this is expensive */
1311 if (vnode_isdir(vp
)) {
1312 if (vnode_authorize(vp
, NULL
,
1313 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
1315 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
1317 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
1320 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
1322 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
1324 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
1330 * Rather than MAC preceding DAC, in this case we want
1331 * the smallest set of permissions granted by both MAC & DAC
1332 * checks. We won't add back any permissions.
1335 if (mac_vnode_check_access(ctx
, vp
, W_OK
) != 0)
1338 if (mac_vnode_check_access(ctx
, vp
, R_OK
) != 0)
1341 if (mac_vnode_check_access(ctx
, vp
, X_OK
) != 0)
1344 VFS_DEBUG(ctx
, vp
, "ATTRLIST - granting perms %d", perms
);
1345 ATTR_PACK4(ab
, perms
);
1347 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1348 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
)) {
1349 struct kauth_filesec fsec
;
1351 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1353 fsec
.fsec_magic
= KAUTH_FILESEC_MAGIC
;
1354 fsec
.fsec_owner
= kauth_null_guid
;
1355 fsec
.fsec_group
= kauth_null_guid
;
1356 attrlist_pack_variable2(&ab
, &fsec
, ((char *)&fsec
.fsec_acl
- (char *)&fsec
), va
.va_acl
, KAUTH_ACL_COPYSIZE(va
.va_acl
));
1358 attrlist_pack_variable(&ab
, NULL
, 0);
1361 if (al
.commonattr
& ATTR_CMN_UUID
) {
1362 if (!VATTR_IS_SUPPORTED(&va
, va_uuuid
)) {
1363 ATTR_PACK(&ab
, kauth_null_guid
);
1365 ATTR_PACK(&ab
, va
.va_uuuid
);
1368 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1369 if (!VATTR_IS_SUPPORTED(&va
, va_guuid
)) {
1370 ATTR_PACK(&ab
, kauth_null_guid
);
1372 ATTR_PACK(&ab
, va
.va_guuid
);
1375 if (al
.commonattr
& ATTR_CMN_FILEID
) {
1376 ATTR_PACK8(ab
, va
.va_fileid
);
1378 if (al
.commonattr
& ATTR_CMN_PARENTID
) {
1381 * If the filesystem doesn't know the parent ID, we can
1382 * try to get it via v->v_parent.
1384 if (VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
1385 fileid
= va
.va_parentid
;
1387 struct vnode_attr lva
;
1390 pvp
= vnode_getparent(vp
);
1392 if (pvp
== NULLVP
) {
1397 VATTR_WANTED(&lva
, va_fileid
);
1398 error
= vnode_getattr(pvp
, &lva
, ctx
);
1403 fileid
= lva
.va_fileid
;
1405 ATTR_PACK8(ab
, fileid
);
1408 /* directory attributes **************************************************/
1409 if (vnode_isdir(vp
)) {
1410 if (al
.dirattr
& ATTR_DIR_LINKCOUNT
) /* full count of entries */
1411 ATTR_PACK4(ab
, (uint32_t)va
.va_dirlinkcount
);
1412 if (al
.dirattr
& ATTR_DIR_ENTRYCOUNT
)
1413 ATTR_PACK4(ab
, (uint32_t)va
.va_nchildren
);
1414 if (al
.dirattr
& ATTR_DIR_MOUNTSTATUS
)
1415 ATTR_PACK_CAST(&ab
, uint32_t, (vp
->v_flag
& VROOT
) ? DIR_MNTSTATUS_MNTPOINT
: 0);
1418 /* file attributes **************************************************/
1419 if (!vnode_isdir(vp
)) {
1420 if (al
.fileattr
& ATTR_FILE_LINKCOUNT
)
1421 ATTR_PACK4(ab
, (uint32_t)va
.va_nlink
);
1422 if (al
.fileattr
& ATTR_FILE_TOTALSIZE
)
1423 ATTR_PACK8(ab
, va
.va_total_size
);
1424 if (al
.fileattr
& ATTR_FILE_ALLOCSIZE
)
1425 ATTR_PACK8(ab
, va
.va_total_alloc
);
1426 if (al
.fileattr
& ATTR_FILE_IOBLOCKSIZE
)
1427 ATTR_PACK4(ab
, va
.va_iosize
);
1428 if (al
.fileattr
& ATTR_FILE_CLUMPSIZE
)
1429 ATTR_PACK4(ab
, 0); /* XXX value is deprecated */
1430 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1431 if ((vp
->v_type
== VCHR
) || (vp
->v_type
== VBLK
)) {
1432 ATTR_PACK(&ab
, vp
->v_specinfo
->si_rdev
);
1434 ATTR_PACK_CAST(&ab
, uint32_t, 0);
1437 if (al
.fileattr
& ATTR_FILE_DATALENGTH
) {
1438 if (VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
1439 ATTR_PACK8(ab
, va
.va_data_size
);
1441 ATTR_PACK8(ab
, va
.va_total_size
);
1444 if (al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) {
1445 if (VATTR_IS_SUPPORTED(&va
, va_data_alloc
)) {
1446 ATTR_PACK8(ab
, va
.va_data_alloc
);
1448 ATTR_PACK8(ab
, va
.va_total_alloc
);
1451 /* fetch resource fork size/allocation via xattr interface */
1452 if (al
.fileattr
& (ATTR_FILE_RSRCLENGTH
| ATTR_FILE_RSRCALLOCSIZE
)) {
1456 if ((error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &rsize
, XATTR_NOSECURITY
, ctx
)) != 0) {
1457 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1464 if (al
.fileattr
& ATTR_FILE_RSRCLENGTH
) {
1466 ATTR_PACK8(ab
, rlength
);
1468 if (al
.fileattr
& ATTR_FILE_RSRCALLOCSIZE
) {
1469 uint32_t blksize
= vp
->v_mount
->mnt_vfsstat
.f_bsize
;
1472 rlength
= roundup(rsize
, blksize
);
1473 ATTR_PACK8(ab
, rlength
);
1479 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
1480 panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
1481 fixedsize
, ab
.fixedcursor
- ab
.base
, al
.commonattr
, al
.volattr
);
1482 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
1483 panic("packed variable field size mismatch; used %d but expected %ld", ab
.varcursor
- ab
.base
, ab
.needed
);
1486 * In the compatible case, we report the smaller of the required and returned sizes.
1487 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1488 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1489 * they gave us, so they can always check for truncation themselves.
1491 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
1493 /* Only actually copyout as much out as the user buffer can hold */
1494 error
= copyout(ab
.base
, uap
->attributeBuffer
, imin(uap
->bufferSize
, ab
.allocated
));
1498 kfree(va
.va_name
, MAXPATHLEN
);
1500 vnode_putname(vname
);
1503 if (ab
.base
!= NULL
)
1504 FREE(ab
.base
, M_TEMP
);
1505 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
))
1506 kauth_acl_free(va
.va_acl
);
1508 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
1513 attrlist_unpack_fixed(char **cursor
, char *end
, void *buf
, ssize_t size
)
1515 /* make sure we have enough source data */
1516 if ((*cursor
) + size
> end
)
1519 bcopy(*cursor
, buf
, size
);
1524 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1525 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1526 #define ATTR_UNPACK_TIME(v, is64) \
1529 struct user_timespec us; \
1531 v.tv_sec = us.tv_sec; \
1532 v.tv_nsec = us.tv_nsec; \
1543 setattrlist(proc_t p
, struct setattrlist_args
*uap
, __unused register_t
*retval
)
1546 struct vfs_context context
, *ctx
;
1547 struct vnode_attr va
;
1548 struct attrreference ar
;
1549 struct nameidata nd
;
1552 kauth_action_t action
;
1553 char *user_buf
, *cursor
, *bufend
, *fndrinfo
, *cp
, *volname
;
1554 int proc_is64
, error
;
1556 kauth_filesec_t rfsec
;
1558 context
.vc_thread
= current_thread();
1559 context
.vc_ucred
= kauth_cred_get();
1566 proc_is64
= proc_is64bit(p
);
1574 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0)
1575 nameiflags
|= FOLLOW
;
1576 NDINIT(&nd
, LOOKUP
, nameiflags
| AUDITVNPATH1
, UIO_USERSPACE
, uap
->path
, &context
);
1577 if ((error
= namei(&nd
)) != 0)
1583 * Fetch the attribute set and validate.
1585 if ((error
= copyin(uap
->alist
, (caddr_t
) &al
, sizeof (al
))))
1587 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
1592 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1593 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
1594 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
1597 if ((al
.volattr
& ~ATTR_VOL_SETMASK
) ||
1598 (al
.commonattr
& ~ATTR_CMN_VOLSETMASK
) ||
1602 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1606 if ((al
.commonattr
& ~ATTR_CMN_SETMASK
) ||
1607 (al
.fileattr
& ~ATTR_FILE_SETMASK
) ||
1608 (al
.dirattr
& ~ATTR_DIR_SETMASK
) ||
1609 (al
.forkattr
& ~ATTR_FORK_SETMASK
)) {
1611 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1617 * Make the naive assumption that the caller has supplied a reasonable buffer
1618 * size. We could be more careful by pulling in the fixed-size region, checking
1619 * the attrref structures, then pulling in the variable section.
1620 * We need to reconsider this for handling large ACLs, as they should probably be
1621 * brought directly into a buffer. Multiple copyins will make this slower though.
1623 * We could also map the user buffer if it is larger than some sensible mimimum.
1625 if (uap
->bufferSize
> ATTR_MAX_BUFFER
) {
1626 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size %d too large", uap
->bufferSize
);
1630 MALLOC(user_buf
, char *, uap
->bufferSize
, M_TEMP
, M_WAITOK
);
1631 if (user_buf
== NULL
) {
1632 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap
->bufferSize
);
1636 if ((error
= copyin(uap
->attributeBuffer
, user_buf
, uap
->bufferSize
)) != 0) {
1637 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer copyin failed");
1640 VFS_DEBUG(ctx
, vp
, "ATTRLIST - copied in %d bytes of user attributes to %p", uap
->bufferSize
, user_buf
);
1643 error
= mac_vnode_check_setattrlist(&context
, vp
, &al
);
1649 * Unpack the argument buffer.
1652 bufend
= cursor
+ uap
->bufferSize
;
1655 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
1656 ATTR_UNPACK(va
.va_encoding
);
1657 VATTR_SET_ACTIVE(&va
, va_encoding
);
1659 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
1660 ATTR_UNPACK_TIME(va
.va_create_time
, proc_is64
);
1661 VATTR_SET_ACTIVE(&va
, va_create_time
);
1663 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
1664 ATTR_UNPACK_TIME(va
.va_modify_time
, proc_is64
);
1665 VATTR_SET_ACTIVE(&va
, va_modify_time
);
1667 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
1668 ATTR_UNPACK_TIME(va
.va_change_time
, proc_is64
);
1669 VATTR_SET_ACTIVE(&va
, va_change_time
);
1671 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
1672 ATTR_UNPACK_TIME(va
.va_access_time
, proc_is64
);
1673 VATTR_SET_ACTIVE(&va
, va_access_time
);
1675 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
1676 ATTR_UNPACK_TIME(va
.va_backup_time
, proc_is64
);
1677 VATTR_SET_ACTIVE(&va
, va_backup_time
);
1679 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1680 if ((cursor
+ 32) > bufend
) {
1682 VFS_DEBUG(ctx
, vp
, "ATTRLIST - not enough data supplied for FINDERINFO");
1688 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
1689 ATTR_UNPACK(va
.va_uid
);
1690 VATTR_SET_ACTIVE(&va
, va_uid
);
1692 if (al
.commonattr
& ATTR_CMN_GRPID
) {
1693 ATTR_UNPACK(va
.va_gid
);
1694 VATTR_SET_ACTIVE(&va
, va_gid
);
1696 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1697 ATTR_UNPACK_CAST(uint32_t, va
.va_mode
);
1698 VATTR_SET_ACTIVE(&va
, va_mode
);
1700 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
1701 ATTR_UNPACK(va
.va_flags
);
1702 VATTR_SET_ACTIVE(&va
, va_flags
);
1704 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1707 * We are (for now) passed a kauth_filesec_t, but all we want from
1712 cp
+= ar
.attr_dataoffset
;
1713 rfsec
= (kauth_filesec_t
)cp
;
1714 if (((char *)(rfsec
+ 1) > bufend
) || /* no space for acl */
1715 (rfsec
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) || /* bad magic */
1716 (KAUTH_FILESEC_COPYSIZE(rfsec
) != ar
.attr_length
) || /* size does not match */
1717 ((cp
+ KAUTH_FILESEC_COPYSIZE(rfsec
)) > bufend
)) { /* ACEs overrun buffer */
1719 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied", ar
.attr_length
);
1722 nace
= rfsec
->fsec_entrycount
;
1723 if (nace
== KAUTH_FILESEC_NOACL
)
1725 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1727 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied");
1730 nace
= rfsec
->fsec_acl
.acl_entrycount
;
1731 if (nace
== KAUTH_FILESEC_NOACL
) {
1733 VATTR_SET(&va
, va_acl
, NULL
);
1736 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1738 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: supplied ACL is too large");
1741 VATTR_SET(&va
, va_acl
, &rfsec
->fsec_acl
);
1744 if (al
.commonattr
& ATTR_CMN_UUID
) {
1745 ATTR_UNPACK(va
.va_uuuid
);
1746 VATTR_SET_ACTIVE(&va
, va_uuuid
);
1748 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1749 ATTR_UNPACK(va
.va_guuid
);
1750 VATTR_SET_ACTIVE(&va
, va_guuid
);
1754 if (al
.volattr
& ATTR_VOL_INFO
) {
1755 if (al
.volattr
& ATTR_VOL_NAME
) {
1758 volname
+= ar
.attr_dataoffset
;
1759 if ((volname
+ ar
.attr_length
) > bufend
) {
1761 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume name too big for caller buffer");
1764 /* guarantee NUL termination */
1765 volname
[ar
.attr_length
- 1] = 0;
1770 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1771 /* XXX does it actually make any sense to change this? */
1773 VFS_DEBUG(ctx
, vp
, "ATTRLIST - XXX device type change not implemented");
1778 * Validate and authorize.
1781 if ((va
.va_active
!= 0LL) && ((error
= vnode_authattr(vp
, &va
, &action
, &context
)) != 0)) {
1782 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attribute changes refused: %d", error
);
1786 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1787 * and will be auth'ed by the FS.
1789 if (fndrinfo
!= NULL
) {
1790 if (al
.volattr
& ATTR_VOL_INFO
) {
1791 if (vp
->v_tag
!= VT_HFS
) {
1796 action
|= KAUTH_VNODE_WRITE_ATTRIBUTES
;
1800 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0)) {
1801 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorization failed");
1806 * When we're setting both the access mask and the finder info, then
1807 * check if were about to remove write access for the owner. Since
1808 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
1809 * to consider their ordering.
1811 * If were about to remove write access for the owner we'll set the
1812 * Finder Info here before vnode_setattr. Otherwise we'll set it
1813 * after vnode_setattr since it may be adding owner write access.
1815 if ((fndrinfo
!= NULL
) && !(al
.volattr
& ATTR_VOL_INFO
) &&
1816 (al
.commonattr
& ATTR_CMN_ACCESSMASK
) && !(va
.va_mode
& S_IWUSR
)) {
1817 if ((error
= setattrlist_setfinderinfo(vp
, fndrinfo
, ctx
)) != 0) {
1820 fndrinfo
= NULL
; /* it was set here so skip setting below */
1824 * Write the attributes if we have any.
1826 if ((va
.va_active
!= 0LL) && ((error
= vnode_setattr(vp
, &va
, &context
)) != 0)) {
1827 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1832 * Write the Finder Info if we have any.
1834 if (fndrinfo
!= NULL
) {
1835 if (al
.volattr
& ATTR_VOL_INFO
) {
1836 if (vp
->v_tag
== VT_HFS
) {
1837 error
= VNOP_IOCTL(vp
, HFS_SET_BOOT_INFO
, (caddr_t
)fndrinfo
, 0, &context
);
1841 /* XXX should never get here */
1843 } else if ((error
= setattrlist_setfinderinfo(vp
, fndrinfo
, ctx
)) != 0) {
1849 * Set the volume name, if we have one
1851 if (volname
!= NULL
)
1857 vs
.f_vol_name
= volname
; /* References the setattrlist buffer directly */
1858 VFSATTR_WANTED(&vs
, f_vol_name
);
1861 error
= mac_mount_check_setattr(ctx
, vp
->v_mount
, &vs
);
1866 if ((error
= vfs_setattr(vp
->v_mount
, &vs
, ctx
)) != 0) {
1867 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setting volume name failed");
1871 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
1873 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not set volume name");
1878 /* all done and successful */
1883 if (user_buf
!= NULL
)
1884 FREE(user_buf
, M_TEMP
);
1885 VFS_DEBUG(ctx
, vp
, "ATTRLIST - set returning %d", error
);