2 * Copyright (c) 1995-2014 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
63 #define FSOPT_ATTRLIST_EXTENDED 0x00000020
65 /* Valid only if FSOPT_ATTRLIST_EXTENDED is set */
66 #define ATTR_CMN_GEN_COUNT 0x00080000 /* same as ATTR_CMN_NAMEDATTRCOUNT */
67 #define ATTR_CMN_DOCUMENT_ID 0x00100000 /* same as ATTR_CMN_NAMEDATTRLIST */
69 #define ATTR_CMN_ERROR 0x20000000
72 * Structure describing the state of an in-progress attrlist operation.
74 struct _attrlist_buf
{
80 attribute_set_t actual
;
81 attribute_set_t valid
;
86 * Attempt to pack a fixed width attribute of size (count) bytes from
87 * source to our attrlist buffer.
90 attrlist_pack_fixed(struct _attrlist_buf
*ab
, void *source
, ssize_t count
)
93 * Use ssize_t for pointer math purposes,
94 * since a ssize_t is a signed long
99 * Compute the amount of remaining space in the attrlist buffer
100 * based on how much we've used for fixed width fields vs. the
101 * start of the attributes.
103 * If we've still got room, then 'fit' will contain the amount of
106 * Note that this math is safe because, in the event that the
107 * fixed-width cursor has moved beyond the end of the buffer,
108 * then, the second input into lmin() below will be negative, and
109 * we will fail the (fit > 0) check below.
111 fit
= lmin(count
, ab
->allocated
- (ab
->fixedcursor
- ab
->base
));
113 /* Copy in as much as we can */
114 bcopy(source
, ab
->fixedcursor
, fit
);
117 /* always move in increments of 4, even if we didn't pack an attribute. */
118 ab
->fixedcursor
+= roundup(count
, 4);
122 * Attempt to pack one (or two) variable width attributes into the attrlist
123 * buffer. If we are trying to pack two variable width attributes, they are treated
124 * as a single variable-width attribute from the POV of the system call caller.
126 * Recall that a variable-width attribute has two components: the fixed-width
127 * attribute that tells the caller where to look, and the actual variable width data.
130 attrlist_pack_variable2(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
,
131 const void *ext
, ssize_t extcount
)
134 /* Use ssize_t's for pointer math ease */
135 struct attrreference ar
;
139 * Pack the fixed-width component to the variable object.
140 * Note that we may be able to pack the fixed width attref, but not
141 * the variable (if there's no room).
143 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
144 ar
.attr_length
= count
+ extcount
;
145 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
148 * Use an lmin() to do a signed comparison. We use a signed comparison
149 * to detect the 'out of memory' conditions as described above in the
150 * fixed width check above.
152 * Then pack the first variable attribute as space allows. Note that we advance
153 * the variable cursor only if we we had some available space.
155 fit
= lmin(count
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
157 if (source
!= NULL
) {
158 bcopy(source
, ab
->varcursor
, fit
);
160 ab
->varcursor
+= fit
;
163 /* Compute the available space for the second attribute */
164 fit
= lmin(extcount
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
166 /* Copy in data for the second attribute (if needed) if there is room */
168 bcopy(ext
, ab
->varcursor
, fit
);
170 ab
->varcursor
+= fit
;
172 /* always move in increments of 4 */
173 ab
->varcursor
= (char *)roundup((uintptr_t)ab
->varcursor
, 4);
177 * Packing a single variable-width attribute is the same as calling the two, but with
178 * an invalid 2nd attribute.
181 attrlist_pack_variable(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
)
183 attrlist_pack_variable2(ab
, source
, count
, NULL
, 0);
187 * Attempt to pack a string. This is a special case of a variable width attribute.
189 * If "source" is NULL, then an empty string ("") will be packed. If "source" is
190 * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated
191 * C-string. If "source" is not NULL and "count" is not zero, then only the first
192 * "count" bytes of "source" will be copied, and a NUL terminator will be added.
194 * If the attrlist buffer doesn't have enough room to hold the entire string (including
195 * NUL terminator), then copy as much as will fit. The attrlist buffer's "varcursor"
196 * will always be updated based on the entire length of the string (including NUL
197 * terminator); this means "varcursor" may end up pointing beyond the end of the
198 * allocated buffer space.
201 attrlist_pack_string(struct _attrlist_buf
*ab
, const char *source
, ssize_t count
)
203 struct attrreference ar
;
207 * Supplied count is character count of string text, excluding trailing nul
208 * which we always supply here.
210 if (source
== NULL
) {
212 } else if (count
== 0) {
213 count
= strlen(source
);
217 * Construct the fixed-width attribute that refers to this string.
219 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
220 ar
.attr_length
= count
+ 1;
221 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
224 * Now compute how much available memory we have to copy the string text.
226 * space = the number of bytes available in the attribute buffer to hold the
229 * fit = the number of bytes to copy from the start of the string into the
230 * attribute buffer, NOT including the NUL terminator. If the attribute
231 * buffer is large enough, this will be the string's length; otherwise, it
232 * will be equal to "space".
234 space
= ab
->allocated
- (ab
->varcursor
- ab
->base
);
235 fit
= lmin(count
, space
);
238 * If there is space remaining, copy data in, and
239 * accommodate the trailing NUL terminator.
241 * NOTE: if "space" is too small to hold the string and its NUL
242 * terminator (space < fit + 1), then the string value in the attribute
243 * buffer will NOT be NUL terminated!
245 * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
246 * Therefore, we don't bother checking for that here.
248 bcopy(source
, ab
->varcursor
, fit
);
249 /* is there room for our trailing nul? */
251 ab
->varcursor
[fit
++] = '\0';
252 /* 'fit' now the number of bytes AFTER adding in the NUL */
256 * always move in increments of 4 (including the trailing NUL)
258 ab
->varcursor
+= roundup((count
+1), 4);
262 #define ATTR_PACK4(AB, V) \
264 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
265 *(uint32_t *)AB.fixedcursor = V; \
266 AB.fixedcursor += 4; \
270 #define ATTR_PACK8(AB, V) \
272 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
273 *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V; \
274 AB.fixedcursor += 8; \
278 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
279 #define ATTR_PACK_CAST(b, t, v) \
285 #define ATTR_PACK_TIME(b, v, is64) \
288 struct user64_timespec us = {v.tv_sec, v.tv_nsec}; \
291 struct user32_timespec us = {v.tv_sec, v.tv_nsec}; \
298 * Table-driven setup for all valid common/volume attributes.
300 struct getvolattrlist_attrtab
{
303 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
306 static struct getvolattrlist_attrtab getvolattrlist_common_tab
[] = {
307 {ATTR_CMN_NAME
, 0, sizeof(struct attrreference
)},
308 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
)},
309 {ATTR_CMN_FSID
, 0, sizeof(fsid_t
)},
310 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
)},
311 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
)},
312 {ATTR_CMN_OBJID
, 0, sizeof(fsobj_id_t
)},
313 {ATTR_CMN_OBJPERMANENTID
, 0, sizeof(fsobj_id_t
)},
314 {ATTR_CMN_PAROBJID
, 0, sizeof(fsobj_id_t
)},
315 {ATTR_CMN_SCRIPT
, 0, sizeof(text_encoding_t
)},
316 {ATTR_CMN_CRTIME
, VFSATTR_BIT(f_create_time
), ATTR_TIME_SIZE
},
317 {ATTR_CMN_MODTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
318 {ATTR_CMN_CHGTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
319 {ATTR_CMN_ACCTIME
, VFSATTR_BIT(f_access_time
), ATTR_TIME_SIZE
},
320 {ATTR_CMN_BKUPTIME
, VFSATTR_BIT(f_backup_time
), ATTR_TIME_SIZE
},
321 {ATTR_CMN_FNDRINFO
, 0, 32},
322 {ATTR_CMN_OWNERID
, 0, sizeof(uid_t
)},
323 {ATTR_CMN_GRPID
, 0, sizeof(gid_t
)},
324 {ATTR_CMN_ACCESSMASK
, 0, sizeof(uint32_t)},
325 {ATTR_CMN_FLAGS
, 0, sizeof(uint32_t)},
326 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t)},
327 {ATTR_CMN_EXTENDED_SECURITY
, 0, sizeof(struct attrreference
)},
328 {ATTR_CMN_UUID
, 0, sizeof(guid_t
)},
329 {ATTR_CMN_GRPUUID
, 0, sizeof(guid_t
)},
330 {ATTR_CMN_FILEID
, 0, sizeof(uint64_t)},
331 {ATTR_CMN_PARENTID
, 0, sizeof(uint64_t)},
332 {ATTR_CMN_RETURNED_ATTRS
, 0, sizeof(attribute_set_t
)},
333 {ATTR_CMN_ERROR
, 0, sizeof(uint32_t)},
336 #define ATTR_CMN_VOL_INVALID \
337 (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
338 ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
340 static struct getvolattrlist_attrtab getvolattrlist_vol_tab
[] = {
341 {ATTR_VOL_FSTYPE
, 0, sizeof(uint32_t)},
342 {ATTR_VOL_SIGNATURE
, VFSATTR_BIT(f_signature
), sizeof(uint32_t)},
343 {ATTR_VOL_SIZE
, VFSATTR_BIT(f_blocks
), sizeof(off_t
)},
344 {ATTR_VOL_SPACEFREE
, VFSATTR_BIT(f_bfree
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
345 {ATTR_VOL_SPACEAVAIL
, VFSATTR_BIT(f_bavail
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
346 {ATTR_VOL_MINALLOCATION
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
347 {ATTR_VOL_ALLOCATIONCLUMP
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
348 {ATTR_VOL_IOBLOCKSIZE
, VFSATTR_BIT(f_iosize
), sizeof(uint32_t)},
349 {ATTR_VOL_OBJCOUNT
, VFSATTR_BIT(f_objcount
), sizeof(uint32_t)},
350 {ATTR_VOL_FILECOUNT
, VFSATTR_BIT(f_filecount
), sizeof(uint32_t)},
351 {ATTR_VOL_DIRCOUNT
, VFSATTR_BIT(f_dircount
), sizeof(uint32_t)},
352 {ATTR_VOL_MAXOBJCOUNT
, VFSATTR_BIT(f_maxobjcount
), sizeof(uint32_t)},
353 {ATTR_VOL_MOUNTPOINT
, 0, sizeof(struct attrreference
)},
354 {ATTR_VOL_NAME
, VFSATTR_BIT(f_vol_name
), sizeof(struct attrreference
)},
355 {ATTR_VOL_MOUNTFLAGS
, 0, sizeof(uint32_t)},
356 {ATTR_VOL_MOUNTEDDEVICE
, 0, sizeof(struct attrreference
)},
357 {ATTR_VOL_ENCODINGSUSED
, 0, sizeof(uint64_t)},
358 {ATTR_VOL_CAPABILITIES
, VFSATTR_BIT(f_capabilities
), sizeof(vol_capabilities_attr_t
)},
359 {ATTR_VOL_UUID
, VFSATTR_BIT(f_uuid
), sizeof(uuid_t
)},
360 {ATTR_VOL_ATTRIBUTES
, VFSATTR_BIT(f_attributes
), sizeof(vol_attributes_attr_t
)},
361 {ATTR_VOL_INFO
, 0, 0},
366 getvolattrlist_parsetab(struct getvolattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vfs_attr
*vsp
,
367 ssize_t
*sizep
, int is_64bit
)
369 attrgroup_t recognised
;
373 /* is this attribute set? */
374 if (tab
->attr
& attrs
) {
375 recognised
|= tab
->attr
;
376 vsp
->f_active
|= tab
->bits
;
377 if (tab
->size
== ATTR_TIME_SIZE
) {
379 *sizep
+= sizeof(struct user64_timespec
);
381 *sizep
+= sizeof(struct user32_timespec
);
387 } while ((++tab
)->attr
!= 0);
389 /* check to make sure that we recognised all of the passed-in attributes */
390 if (attrs
& ~recognised
)
396 * Given the attributes listed in alp, configure vap to request
397 * the data from a filesystem.
400 getvolattrlist_setupvfsattr(struct attrlist
*alp
, struct vfs_attr
*vsp
, ssize_t
*sizep
, int is_64bit
)
405 * Parse the above tables.
407 *sizep
= sizeof(uint32_t); /* length count */
408 if (alp
->commonattr
) {
409 if ((alp
->commonattr
& ATTR_CMN_VOL_INVALID
) &&
410 (alp
->commonattr
& ATTR_CMN_RETURNED_ATTRS
) == 0) {
413 if ((error
= getvolattrlist_parsetab(getvolattrlist_common_tab
,
414 alp
->commonattr
, vsp
, sizep
,
420 (error
= getvolattrlist_parsetab(getvolattrlist_vol_tab
, alp
->volattr
, vsp
, sizep
, is_64bit
)) != 0)
427 * Given the attributes listed in asp and those supported
428 * in the vsp, fixup the asp attributes to reflect any
429 * missing attributes from the file system
432 getvolattrlist_fixupattrs(attribute_set_t
*asp
, struct vfs_attr
*vsp
)
434 struct getvolattrlist_attrtab
*tab
;
436 if (asp
->commonattr
) {
437 tab
= getvolattrlist_common_tab
;
439 if ((tab
->attr
& asp
->commonattr
) &&
441 ((tab
->bits
& vsp
->f_supported
) == 0)) {
442 asp
->commonattr
&= ~tab
->attr
;
444 } while ((++tab
)->attr
!= 0);
447 tab
= getvolattrlist_vol_tab
;
449 if ((tab
->attr
& asp
->volattr
) &&
451 ((tab
->bits
& vsp
->f_supported
) == 0)) {
452 asp
->volattr
&= ~tab
->attr
;
454 } while ((++tab
)->attr
!= 0);
459 * Table-driven setup for all valid common/dir/file/fork attributes against files.
461 struct getattrlist_attrtab
{
464 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
466 kauth_action_t action
;
470 * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
471 * information, and we will synthesize it at the VFS level.
473 static struct getattrlist_attrtab getattrlist_common_tab
[] = {
474 {ATTR_CMN_NAME
, VATTR_BIT(va_name
), sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
475 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
476 {ATTR_CMN_FSID
, VATTR_BIT(va_fsid
), sizeof(fsid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
477 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
478 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
479 {ATTR_CMN_OBJID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
480 {ATTR_CMN_OBJPERMANENTID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
481 {ATTR_CMN_PAROBJID
, VATTR_BIT(va_parentid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
482 {ATTR_CMN_SCRIPT
, VATTR_BIT(va_encoding
), sizeof(text_encoding_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
483 {ATTR_CMN_CRTIME
, VATTR_BIT(va_create_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
484 {ATTR_CMN_MODTIME
, VATTR_BIT(va_modify_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
485 {ATTR_CMN_CHGTIME
, VATTR_BIT(va_change_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
486 {ATTR_CMN_ACCTIME
, VATTR_BIT(va_access_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
487 {ATTR_CMN_BKUPTIME
, VATTR_BIT(va_backup_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
488 {ATTR_CMN_FNDRINFO
, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES
},
489 {ATTR_CMN_OWNERID
, VATTR_BIT(va_uid
), sizeof(uid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
490 {ATTR_CMN_GRPID
, VATTR_BIT(va_gid
), sizeof(gid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
491 {ATTR_CMN_ACCESSMASK
, VATTR_BIT(va_mode
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
492 {ATTR_CMN_FLAGS
, VATTR_BIT(va_flags
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
493 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
494 {ATTR_CMN_EXTENDED_SECURITY
, VATTR_BIT(va_acl
), sizeof(struct attrreference
), KAUTH_VNODE_READ_SECURITY
},
495 {ATTR_CMN_UUID
, VATTR_BIT(va_uuuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
496 {ATTR_CMN_GRPUUID
, VATTR_BIT(va_guuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
497 {ATTR_CMN_FILEID
, VATTR_BIT(va_fileid
), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES
},
498 {ATTR_CMN_PARENTID
, VATTR_BIT(va_parentid
), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES
},
499 {ATTR_CMN_FULLPATH
, 0, sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
500 {ATTR_CMN_ADDEDTIME
, VATTR_BIT(va_addedtime
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
501 {ATTR_CMN_RETURNED_ATTRS
, 0, sizeof(attribute_set_t
), 0},
502 {ATTR_CMN_ERROR
, 0, sizeof(uint32_t), 0},
506 static struct getattrlist_attrtab getattrlist_common_tab_extended
[] = {
507 {ATTR_CMN_NAME
, VATTR_BIT(va_name
), sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
508 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
509 {ATTR_CMN_FSID
, VATTR_BIT(va_fsid
), sizeof(fsid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
510 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
511 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
512 {ATTR_CMN_OBJID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
513 {ATTR_CMN_OBJPERMANENTID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
514 {ATTR_CMN_PAROBJID
, VATTR_BIT(va_parentid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
515 {ATTR_CMN_SCRIPT
, VATTR_BIT(va_encoding
), sizeof(text_encoding_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
516 {ATTR_CMN_CRTIME
, VATTR_BIT(va_create_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
517 {ATTR_CMN_MODTIME
, VATTR_BIT(va_modify_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
518 {ATTR_CMN_CHGTIME
, VATTR_BIT(va_change_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
519 {ATTR_CMN_ACCTIME
, VATTR_BIT(va_access_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
520 {ATTR_CMN_BKUPTIME
, VATTR_BIT(va_backup_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
521 {ATTR_CMN_FNDRINFO
, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES
},
522 {ATTR_CMN_OWNERID
, VATTR_BIT(va_uid
), sizeof(uid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
523 {ATTR_CMN_GRPID
, VATTR_BIT(va_gid
), sizeof(gid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
524 {ATTR_CMN_ACCESSMASK
, VATTR_BIT(va_mode
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
525 {ATTR_CMN_FLAGS
, VATTR_BIT(va_flags
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
526 {ATTR_CMN_GEN_COUNT
, VATTR_BIT(va_gen
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
527 {ATTR_CMN_DOCUMENT_ID
, VATTR_BIT(va_document_id
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
528 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
529 {ATTR_CMN_EXTENDED_SECURITY
, VATTR_BIT(va_acl
), sizeof(struct attrreference
), KAUTH_VNODE_READ_SECURITY
},
530 {ATTR_CMN_UUID
, VATTR_BIT(va_uuuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
531 {ATTR_CMN_GRPUUID
, VATTR_BIT(va_guuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
532 {ATTR_CMN_FILEID
, VATTR_BIT(va_fileid
), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES
},
533 {ATTR_CMN_PARENTID
, VATTR_BIT(va_parentid
), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES
},
534 {ATTR_CMN_FULLPATH
, 0, sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
535 {ATTR_CMN_ADDEDTIME
, VATTR_BIT(va_addedtime
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
536 {ATTR_CMN_RETURNED_ATTRS
, 0, sizeof(attribute_set_t
), 0},
537 {ATTR_CMN_ERROR
, 0, sizeof(uint32_t), 0},
541 static struct getattrlist_attrtab getattrlist_dir_tab
[] = {
542 {ATTR_DIR_LINKCOUNT
, VATTR_BIT(va_dirlinkcount
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
543 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nchildren
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
544 {ATTR_DIR_MOUNTSTATUS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
547 static struct getattrlist_attrtab getattrlist_file_tab
[] = {
548 {ATTR_FILE_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
549 {ATTR_FILE_TOTALSIZE
, VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
550 {ATTR_FILE_ALLOCSIZE
, VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
551 {ATTR_FILE_IOBLOCKSIZE
, VATTR_BIT(va_iosize
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
552 {ATTR_FILE_DEVTYPE
, VATTR_BIT(va_rdev
), sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
553 {ATTR_FILE_DATALENGTH
, VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
554 {ATTR_FILE_DATAALLOCSIZE
, VATTR_BIT(va_total_alloc
)| VATTR_BIT(va_data_alloc
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
555 {ATTR_FILE_RSRCLENGTH
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
556 {ATTR_FILE_RSRCALLOCSIZE
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
561 * The following are attributes that VFS can derive.
563 * A majority of them are the same attributes that are required for stat(2) and statfs(2).
565 #define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
566 ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
567 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
568 ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
569 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
570 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
571 ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
573 #define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
574 ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
575 ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
576 ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
577 ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
578 ATTR_CMN_FNDRINFO | \
579 ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
580 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
581 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
582 ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
583 ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT)
585 #define VFS_DFLT_ATTR_CMN_EXT (ATTR_CMN_EXT_GEN_COUNT | ATTR_CMN_EXT_DOCUMENT_ID)
587 #define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
589 #define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
590 ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
591 ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
592 ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
593 ATTR_FILE_RSRCALLOCSIZE)
596 getattrlist_parsetab(struct getattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vnode_attr
*vap
,
597 ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
)
599 attrgroup_t recognised
;
603 /* is this attribute set? */
604 if (tab
->attr
& attrs
) {
605 recognised
|= tab
->attr
;
606 vap
->va_active
|= tab
->bits
;
607 if (tab
->size
== ATTR_TIME_SIZE
) {
609 *sizep
+= sizeof(struct user64_timespec
);
611 *sizep
+= sizeof(struct user32_timespec
);
616 *actionp
|= tab
->action
;
617 if (attrs
== recognised
)
618 break; /* all done, get out */
620 } while ((++tab
)->attr
!= 0);
622 /* check to make sure that we recognised all of the passed-in attributes */
623 if (attrs
& ~recognised
)
629 * Given the attributes listed in alp, configure vap to request
630 * the data from a filesystem.
633 getattrlist_setupvattr(struct attrlist
*alp
, int attr_cmn_extended
, struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
, int isdir
)
636 struct getattrlist_attrtab
*cmn_tab
;
639 if (attr_cmn_extended
)
640 cmn_tab
= getattrlist_common_tab_extended
;
642 cmn_tab
= getattrlist_common_tab
;
644 * Parse the above tables.
646 *sizep
= sizeof(uint32_t); /* length count */
648 if (alp
->commonattr
&&
649 (error
= getattrlist_parsetab(cmn_tab
, alp
->commonattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
651 if (isdir
&& alp
->dirattr
&&
652 (error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
654 if (!isdir
&& alp
->fileattr
&&
655 (error
= getattrlist_parsetab(getattrlist_file_tab
, alp
->fileattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
662 * Given the attributes listed in asp and those supported
663 * in the vap, fixup the asp attributes to reflect any
664 * missing attributes from the file system
667 getattrlist_fixupattrs(attribute_set_t
*asp
, struct vnode_attr
*vap
)
669 struct getattrlist_attrtab
*tab
;
671 if (asp
->commonattr
) {
672 tab
= getattrlist_common_tab
;
675 * This if() statement is slightly confusing. We're trying to
676 * iterate through all of the bits listed in the array
677 * getattr_common_tab, and see if the filesystem was expected
678 * to support it, and whether or not we need to do anything about this.
680 * This array is full of structs that have 4 fields (attr, bits, size, action).
681 * The first is used to store the ATTR_CMN_* bit that was being requested
682 * from userland. The second stores the VATTR_BIT corresponding to the field
683 * filled in vnode_attr struct. If it is 0, then we don't typically expect
684 * the filesystem to fill in this field. The third is the size of the field,
685 * and the fourth is the type of kauth actions needed.
687 * So, for all of the ATTR_CMN bits listed in this array, we iterate through
688 * them, and check to see if it was both passed down to the filesystem via the
689 * va_active bitfield, and whether or not we expect it to be emitted from
690 * the filesystem. If it wasn't supported, then we un-twiddle the bit and move
691 * on. This is done so that we can uncheck those bits and re-request
692 * a vnode_getattr from the filesystem again.
694 if ((tab
->attr
& asp
->commonattr
) &&
695 (tab
->bits
& vap
->va_active
) &&
696 (tab
->bits
& vap
->va_supported
) == 0) {
697 asp
->commonattr
&= ~tab
->attr
;
699 } while ((++tab
)->attr
!= 0);
702 tab
= getattrlist_dir_tab
;
704 if ((tab
->attr
& asp
->dirattr
) &&
705 (tab
->bits
& vap
->va_active
) &&
706 (vap
->va_supported
& tab
->bits
) == 0) {
707 asp
->dirattr
&= ~tab
->attr
;
709 } while ((++tab
)->attr
!= 0);
712 tab
= getattrlist_file_tab
;
714 if ((tab
->attr
& asp
->fileattr
) &&
715 (tab
->bits
& vap
->va_active
) &&
716 (vap
->va_supported
& tab
->bits
) == 0) {
717 asp
->fileattr
&= ~tab
->attr
;
719 } while ((++tab
)->attr
!= 0);
724 setattrlist_setfinderinfo(vnode_t vp
, char *fndrinfo
, struct vfs_context
*ctx
)
727 char uio_buf
[UIO_SIZEOF(1)];
730 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
733 uio_addiov(auio
, CAST_USER_ADDR_T(fndrinfo
), 32);
734 error
= vn_setxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, XATTR_NOSECURITY
, ctx
);
739 if (error
== 0 && need_fsevent(FSE_FINDER_INFO_CHANGED
, vp
)) {
740 add_fsevent(FSE_FINDER_INFO_CHANGED
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
748 * Find something resembling a terminal component name in the mountedonname for vp
752 getattrlist_findnamecomp(const char *mn
, const char **np
, ssize_t
*nl
)
758 * We're looking for the last sequence of non / characters, but
759 * not including any trailing / characters.
764 for (cp
= mn
; *cp
!= 0; cp
++) {
766 /* start of run of chars */
772 /* end of run of chars */
779 /* need to close run? */
786 getvolattrlist(vnode_t vp
, struct getattrlist_args
*uap
, struct attrlist
*alp
,
787 vfs_context_t ctx
, int is_64bit
)
790 struct vnode_attr va
;
791 struct _attrlist_buf ab
;
793 ssize_t fixedsize
, varsize
;
794 const char *cnp
= NULL
; /* protected by ATTR_CMN_NAME */
795 ssize_t cnl
= 0; /* protected by ATTR_CMN_NAME */
804 vs
.f_vol_name
= NULL
;
807 /* Check for special packing semantics */
808 return_valid
= (alp
->commonattr
& ATTR_CMN_RETURNED_ATTRS
);
809 pack_invalid
= (uap
->options
& FSOPT_PACK_INVAL_ATTRS
);
811 /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
816 /* Keep invalid attrs from being uninitialized */
817 bzero(&vs
, sizeof (vs
));
818 /* Generate a valid mask for post processing */
819 bcopy(&alp
->commonattr
, &ab
.valid
, sizeof (attribute_set_t
));
823 * For now, the vnode must be the root of its filesystem.
824 * To relax this, we need to be able to find the root vnode of a filesystem
825 * from any vnode in the filesystem.
827 if (!vnode_isvroot(vp
)) {
829 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
834 * Set up the vfs_attr structure and call the filesystem.
836 if ((error
= getvolattrlist_setupvfsattr(alp
, &vs
, &fixedsize
, is_64bit
)) != 0) {
837 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
840 if (vs
.f_active
!= 0) {
841 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
842 if (VFSATTR_IS_ACTIVE(&vs
, f_vol_name
)) {
843 vs
.f_vol_name
= (char *) kalloc(MAXPATHLEN
);
844 if (vs
.f_vol_name
== NULL
) {
846 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
852 error
= mac_mount_check_getattr(ctx
, mnt
, &vs
);
856 VFS_DEBUG(ctx
, vp
, "ATTRLIST - calling to get %016llx with supported %016llx", vs
.f_active
, vs
.f_supported
);
857 if ((error
= vfs_getattr(mnt
, &vs
, ctx
)) != 0) {
858 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
863 * Did we ask for something the filesystem doesn't support?
865 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
866 /* default value for volume subtype */
867 if (VFSATTR_IS_ACTIVE(&vs
, f_fssubtype
)
868 && !VFSATTR_IS_SUPPORTED(&vs
, f_fssubtype
))
869 VFSATTR_RETURN(&vs
, f_fssubtype
, 0);
872 * If the file system didn't supply f_signature, then
873 * default it to 'BD', which is the generic signature
874 * that most Carbon file systems should return.
876 if (VFSATTR_IS_ACTIVE(&vs
, f_signature
)
877 && !VFSATTR_IS_SUPPORTED(&vs
, f_signature
))
878 VFSATTR_RETURN(&vs
, f_signature
, 0x4244);
880 /* default for block size */
881 if (VFSATTR_IS_ACTIVE(&vs
, f_bsize
)
882 && !VFSATTR_IS_SUPPORTED(&vs
, f_bsize
))
883 VFSATTR_RETURN(&vs
, f_bsize
, mnt
->mnt_devblocksize
);
885 /* default value for volume f_attributes */
886 if (VFSATTR_IS_ACTIVE(&vs
, f_attributes
)
887 && !VFSATTR_IS_SUPPORTED(&vs
, f_attributes
)) {
888 vol_attributes_attr_t
*attrp
= &vs
.f_attributes
;
890 attrp
->validattr
.commonattr
= VFS_DFLT_ATTR_CMN
;
891 attrp
->validattr
.volattr
= VFS_DFLT_ATTR_VOL
;
892 attrp
->validattr
.dirattr
= VFS_DFLT_ATTR_DIR
;
893 attrp
->validattr
.fileattr
= VFS_DFLT_ATTR_FILE
;
894 attrp
->validattr
.forkattr
= 0;
896 attrp
->nativeattr
.commonattr
= 0;
897 attrp
->nativeattr
.volattr
= 0;
898 attrp
->nativeattr
.dirattr
= 0;
899 attrp
->nativeattr
.fileattr
= 0;
900 attrp
->nativeattr
.forkattr
= 0;
901 VFSATTR_SET_SUPPORTED(&vs
, f_attributes
);
904 /* default value for volume f_capabilities */
905 if (VFSATTR_IS_ACTIVE(&vs
, f_capabilities
)) {
906 /* getattrlist is always supported now. */
907 if (!VFSATTR_IS_SUPPORTED(&vs
, f_capabilities
)) {
908 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] = 0;
909 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] = VOL_CAP_INT_ATTRLIST
;
910 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_RESERVED1
] = 0;
911 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_RESERVED2
] = 0;
913 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] = 0;
914 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] = VOL_CAP_INT_ATTRLIST
;
915 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_RESERVED1
] = 0;
916 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_RESERVED2
] = 0;
917 VFSATTR_SET_SUPPORTED(&vs
, f_capabilities
);
920 /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
921 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_ATTRLIST
;
922 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_ATTRLIST
;
926 /* check to see if our fixups were enough */
927 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
930 /* Fix up valid mask for post processing */
931 getvolattrlist_fixupattrs(&ab
.valid
, &vs
);
933 /* Force packing of everything asked for */
934 vs
.f_supported
= vs
.f_active
;
936 /* Adjust the requested attributes */
937 getvolattrlist_fixupattrs((attribute_set_t
*)&alp
->commonattr
, &vs
);
948 * Some fields require data from the root vp
950 if (alp
->commonattr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
| ATTR_CMN_ACCESSMASK
| ATTR_CMN_FLAGS
| ATTR_CMN_SCRIPT
)) {
951 VATTR_WANTED(&va
, va_uid
);
952 VATTR_WANTED(&va
, va_gid
);
953 VATTR_WANTED(&va
, va_mode
);
954 VATTR_WANTED(&va
, va_flags
);
955 VATTR_WANTED(&va
, va_encoding
);
957 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
958 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp
);
962 if (VATTR_IS_ACTIVE(&va
, va_encoding
) &&
963 !VATTR_IS_SUPPORTED(&va
, va_encoding
)) {
964 if (!return_valid
|| pack_invalid
)
965 /* use kTextEncodingMacUnicode */
966 VATTR_RETURN(&va
, va_encoding
, 0x7e);
968 /* don't use a default */
969 alp
->commonattr
&= ~ATTR_CMN_SCRIPT
;
974 * Compute variable-size buffer requirements.
977 if (alp
->commonattr
& ATTR_CMN_NAME
) {
978 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
979 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
980 /* special case for boot volume. Use root name when it's
981 * available (which is the volume name) or just the mount on
982 * name of "/". we must do this for binary compatibility with
983 * pre Tiger code. returning nothing for the boot volume name
984 * breaks installers - 3961058
986 cnp
= vnode_getname(vp
);
988 /* just use "/" as name */
989 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
997 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
999 if (alp
->commonattr
& ATTR_CMN_NAME
)
1000 varsize
+= roundup(cnl
+ 1, 4);
1002 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
1003 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntonname
) + 1, 4);
1004 if (alp
->volattr
& ATTR_VOL_NAME
) {
1005 vs
.f_vol_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
1006 varsize
+= roundup(strlen(vs
.f_vol_name
) + 1, 4);
1008 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
1009 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntfromname
) + 1, 4);
1012 * Allocate a target buffer for attribute results.
1013 * Note that since we won't ever copy out more than the caller requested,
1014 * we never need to allocate more than they offer.
1016 ab
.allocated
= ulmin(uap
->bufferSize
, fixedsize
+ varsize
);
1017 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
1019 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
1022 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
1023 if (ab
.base
== NULL
) {
1025 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
1030 * Pack results into the destination buffer.
1032 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
1034 ab
.fixedcursor
+= sizeof (attribute_set_t
);
1035 bzero(&ab
.actual
, sizeof (ab
.actual
));
1037 ab
.varcursor
= ab
.base
+ fixedsize
;
1038 ab
.needed
= fixedsize
+ varsize
;
1040 /* common attributes **************************************************/
1041 if (alp
->commonattr
& ATTR_CMN_NAME
) {
1042 attrlist_pack_string(&ab
, cnp
, cnl
);
1043 ab
.actual
.commonattr
|= ATTR_CMN_NAME
;
1045 if ((alp
->commonattr
& ATTR_CMN_ERROR
) &&
1046 (!return_valid
|| pack_invalid
)) {
1048 ab
.actual
.commonattr
|= ATTR_CMN_ERROR
;
1050 if (alp
->commonattr
& ATTR_CMN_DEVID
) {
1051 ATTR_PACK4(ab
, mnt
->mnt_vfsstat
.f_fsid
.val
[0]);
1052 ab
.actual
.commonattr
|= ATTR_CMN_DEVID
;
1054 if (alp
->commonattr
& ATTR_CMN_FSID
) {
1055 ATTR_PACK8(ab
, mnt
->mnt_vfsstat
.f_fsid
);
1056 ab
.actual
.commonattr
|= ATTR_CMN_FSID
;
1058 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
) {
1059 if (!return_valid
|| pack_invalid
)
1062 if (alp
->commonattr
& ATTR_CMN_OBJTAG
) {
1063 ATTR_PACK4(ab
, vp
->v_tag
);
1064 ab
.actual
.commonattr
|= ATTR_CMN_OBJTAG
;
1066 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
1067 if (!return_valid
|| pack_invalid
) {
1068 fsobj_id_t f
= {0, 0};
1072 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1073 if (!return_valid
|| pack_invalid
) {
1074 fsobj_id_t f
= {0, 0};
1078 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
1079 if (!return_valid
|| pack_invalid
) {
1080 fsobj_id_t f
= {0, 0};
1084 /* note that this returns the encoding for the volume name, not the node name */
1085 if (alp
->commonattr
& ATTR_CMN_SCRIPT
) {
1086 ATTR_PACK4(ab
, va
.va_encoding
);
1087 ab
.actual
.commonattr
|= ATTR_CMN_SCRIPT
;
1089 if (alp
->commonattr
& ATTR_CMN_CRTIME
) {
1090 ATTR_PACK_TIME(ab
, vs
.f_create_time
, is_64bit
);
1091 ab
.actual
.commonattr
|= ATTR_CMN_CRTIME
;
1093 if (alp
->commonattr
& ATTR_CMN_MODTIME
) {
1094 ATTR_PACK_TIME(ab
, vs
.f_modify_time
, is_64bit
);
1095 ab
.actual
.commonattr
|= ATTR_CMN_MODTIME
;
1097 if (alp
->commonattr
& ATTR_CMN_CHGTIME
) {
1098 if (!return_valid
|| pack_invalid
)
1099 ATTR_PACK_TIME(ab
, vs
.f_modify_time
, is_64bit
);
1101 if (alp
->commonattr
& ATTR_CMN_ACCTIME
) {
1102 ATTR_PACK_TIME(ab
, vs
.f_access_time
, is_64bit
);
1103 ab
.actual
.commonattr
|= ATTR_CMN_ACCTIME
;
1105 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
) {
1106 ATTR_PACK_TIME(ab
, vs
.f_backup_time
, is_64bit
);
1107 ab
.actual
.commonattr
|= ATTR_CMN_BKUPTIME
;
1109 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
1112 * This attribute isn't really Finder Info, at least for HFS.
1114 if (vp
->v_tag
== VT_HFS
) {
1115 error
= VNOP_IOCTL(vp
, HFS_GET_BOOT_INFO
, (caddr_t
)&f
, 0, ctx
);
1117 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
1118 ab
.actual
.commonattr
|= ATTR_CMN_FNDRINFO
;
1119 } else if (!return_valid
) {
1122 } else if (!return_valid
|| pack_invalid
) {
1123 /* XXX we could at least pass out the volume UUID here */
1124 bzero(&f
, sizeof(f
));
1125 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
1128 if (alp
->commonattr
& ATTR_CMN_OWNERID
) {
1129 ATTR_PACK4(ab
, va
.va_uid
);
1130 ab
.actual
.commonattr
|= ATTR_CMN_OWNERID
;
1132 if (alp
->commonattr
& ATTR_CMN_GRPID
) {
1133 ATTR_PACK4(ab
, va
.va_gid
);
1134 ab
.actual
.commonattr
|= ATTR_CMN_GRPID
;
1136 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
) {
1137 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
1138 ab
.actual
.commonattr
|= ATTR_CMN_ACCESSMASK
;
1140 if (alp
->commonattr
& ATTR_CMN_FLAGS
) {
1141 ATTR_PACK4(ab
, va
.va_flags
);
1142 ab
.actual
.commonattr
|= ATTR_CMN_FLAGS
;
1144 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* XXX this is expensive and also duplicate work */
1146 if (vnode_isdir(vp
)) {
1147 if (vnode_authorize(vp
, NULL
,
1148 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
1150 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
1152 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
1155 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
1157 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
1159 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
1164 * Rather than MAC preceding DAC, in this case we want
1165 * the smallest set of permissions granted by both MAC & DAC
1166 * checks. We won't add back any permissions.
1169 if (mac_vnode_check_access(ctx
, vp
, W_OK
) != 0)
1172 if (mac_vnode_check_access(ctx
, vp
, R_OK
) != 0)
1175 if (mac_vnode_check_access(ctx
, vp
, X_OK
) != 0)
1178 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms
);
1179 ATTR_PACK4(ab
, perms
);
1180 ab
.actual
.commonattr
|= ATTR_CMN_USERACCESS
;
1183 * The following common volume attributes are only
1184 * packed when the pack_invalid mode is enabled.
1189 if (alp
->commonattr
& ATTR_CMN_EXTENDED_SECURITY
)
1190 attrlist_pack_variable(&ab
, NULL
, 0);
1191 if (alp
->commonattr
& ATTR_CMN_UUID
)
1192 ATTR_PACK(&ab
, kauth_null_guid
);
1193 if (alp
->commonattr
& ATTR_CMN_GRPUUID
)
1194 ATTR_PACK(&ab
, kauth_null_guid
);
1195 if (alp
->commonattr
& ATTR_CMN_FILEID
)
1196 ATTR_PACK8(ab
, fid
);
1197 if (alp
->commonattr
& ATTR_CMN_PARENTID
)
1198 ATTR_PACK8(ab
, fid
);
1201 /* volume attributes **************************************************/
1203 if (alp
->volattr
& ATTR_VOL_FSTYPE
) {
1204 ATTR_PACK_CAST(&ab
, uint32_t, vfs_typenum(mnt
));
1205 ab
.actual
.volattr
|= ATTR_VOL_FSTYPE
;
1207 if (alp
->volattr
& ATTR_VOL_SIGNATURE
) {
1208 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_signature
);
1209 ab
.actual
.volattr
|= ATTR_VOL_SIGNATURE
;
1211 if (alp
->volattr
& ATTR_VOL_SIZE
) {
1212 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_blocks
);
1213 ab
.actual
.volattr
|= ATTR_VOL_SIZE
;
1215 if (alp
->volattr
& ATTR_VOL_SPACEFREE
) {
1216 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bfree
);
1217 ab
.actual
.volattr
|= ATTR_VOL_SPACEFREE
;
1219 if (alp
->volattr
& ATTR_VOL_SPACEAVAIL
) {
1220 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bavail
);
1221 ab
.actual
.volattr
|= ATTR_VOL_SPACEAVAIL
;
1223 if (alp
->volattr
& ATTR_VOL_MINALLOCATION
) {
1224 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
);
1225 ab
.actual
.volattr
|= ATTR_VOL_MINALLOCATION
;
1227 if (alp
->volattr
& ATTR_VOL_ALLOCATIONCLUMP
) {
1228 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
); /* not strictly true */
1229 ab
.actual
.volattr
|= ATTR_VOL_ALLOCATIONCLUMP
;
1231 if (alp
->volattr
& ATTR_VOL_IOBLOCKSIZE
) {
1232 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_iosize
);
1233 ab
.actual
.volattr
|= ATTR_VOL_IOBLOCKSIZE
;
1235 if (alp
->volattr
& ATTR_VOL_OBJCOUNT
) {
1236 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_objcount
);
1237 ab
.actual
.volattr
|= ATTR_VOL_OBJCOUNT
;
1239 if (alp
->volattr
& ATTR_VOL_FILECOUNT
) {
1240 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_filecount
);
1241 ab
.actual
.volattr
|= ATTR_VOL_FILECOUNT
;
1243 if (alp
->volattr
& ATTR_VOL_DIRCOUNT
) {
1244 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_dircount
);
1245 ab
.actual
.volattr
|= ATTR_VOL_DIRCOUNT
;
1247 if (alp
->volattr
& ATTR_VOL_MAXOBJCOUNT
) {
1248 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_maxobjcount
);
1249 ab
.actual
.volattr
|= ATTR_VOL_MAXOBJCOUNT
;
1251 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
) {
1252 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntonname
, 0);
1253 ab
.actual
.volattr
|= ATTR_VOL_MOUNTPOINT
;
1255 if (alp
->volattr
& ATTR_VOL_NAME
) {
1256 attrlist_pack_string(&ab
, vs
.f_vol_name
, 0);
1257 ab
.actual
.volattr
|= ATTR_VOL_NAME
;
1259 if (alp
->volattr
& ATTR_VOL_MOUNTFLAGS
) {
1260 ATTR_PACK_CAST(&ab
, uint32_t, mnt
->mnt_flag
);
1261 ab
.actual
.volattr
|= ATTR_VOL_MOUNTFLAGS
;
1263 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
) {
1264 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntfromname
, 0);
1265 ab
.actual
.volattr
|= ATTR_VOL_MOUNTEDDEVICE
;
1267 if (alp
->volattr
& ATTR_VOL_ENCODINGSUSED
) {
1268 if (!return_valid
|| pack_invalid
)
1269 ATTR_PACK_CAST(&ab
, uint64_t, ~0LL); /* return all encodings */
1271 if (alp
->volattr
& ATTR_VOL_CAPABILITIES
) {
1272 /* fix up volume capabilities */
1273 if (vfs_extendedsecurity(mnt
)) {
1274 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
1276 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] &= ~VOL_CAP_INT_EXTENDED_SECURITY
;
1278 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
1279 ATTR_PACK(&ab
, vs
.f_capabilities
);
1280 ab
.actual
.volattr
|= ATTR_VOL_CAPABILITIES
;
1282 if (alp
->volattr
& ATTR_VOL_UUID
) {
1283 ATTR_PACK(&ab
, vs
.f_uuid
);
1284 ab
.actual
.volattr
|= ATTR_VOL_UUID
;
1286 if (alp
->volattr
& ATTR_VOL_ATTRIBUTES
) {
1287 /* fix up volume attribute information */
1289 vs
.f_attributes
.validattr
.commonattr
|= VFS_DFLT_ATTR_CMN
;
1290 vs
.f_attributes
.validattr
.volattr
|= VFS_DFLT_ATTR_VOL
;
1291 vs
.f_attributes
.validattr
.dirattr
|= VFS_DFLT_ATTR_DIR
;
1292 vs
.f_attributes
.validattr
.fileattr
|= VFS_DFLT_ATTR_FILE
;
1294 if (vfs_extendedsecurity(mnt
)) {
1295 vs
.f_attributes
.validattr
.commonattr
|= (ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
1297 vs
.f_attributes
.validattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
1298 vs
.f_attributes
.nativeattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
1300 ATTR_PACK(&ab
, vs
.f_attributes
);
1301 ab
.actual
.volattr
|= ATTR_VOL_ATTRIBUTES
;
1305 if (!return_valid
&& (ab
.fixedcursor
- ab
.base
) != fixedsize
)
1306 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
1307 fixedsize
, (long) (ab
.fixedcursor
- ab
.base
), alp
->commonattr
, alp
->volattr
);
1308 if (!return_valid
&& ab
.varcursor
!= (ab
.base
+ ab
.needed
))
1309 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab
.varcursor
- ab
.base
), ab
.needed
);
1312 * In the compatible case, we report the smaller of the required and returned sizes.
1313 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1314 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1315 * they gave us, so they can always check for truncation themselves.
1317 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
1319 /* Return attribute set output if requested. */
1321 ab
.actual
.commonattr
|= ATTR_CMN_RETURNED_ATTRS
;
1323 /* Only report the attributes that are valid */
1324 ab
.actual
.commonattr
&= ab
.valid
.commonattr
;
1325 ab
.actual
.volattr
&= ab
.valid
.volattr
;
1327 bcopy(&ab
.actual
, ab
.base
+ sizeof(uint32_t), sizeof (ab
.actual
));
1329 error
= copyout(ab
.base
, uap
->attributeBuffer
, ab
.allocated
);
1332 if (vs
.f_vol_name
!= NULL
)
1333 kfree(vs
.f_vol_name
, MAXPATHLEN
);
1337 if (ab
.base
!= NULL
)
1338 FREE(ab
.base
, M_TEMP
);
1339 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
1344 * Obtain attribute information about a filesystem object.
1348 getattrlist_internal(vnode_t vp
, struct getattrlist_args
*uap
,
1349 __unused
struct componentname
*getattr_name
, proc_t p
, vfs_context_t ctx
)
1352 struct vnode_attr va
;
1353 struct _attrlist_buf ab
;
1354 kauth_action_t action
;
1355 ssize_t fixedsize
, varsize
;
1357 const char *vname
= NULL
;
1359 ssize_t fullpathlen
;
1369 proc_is64
= proc_is64bit(p
);
1379 * Fetch the attribute request.
1381 if ((error
= copyin(uap
->alist
, &al
, sizeof(al
))) != 0)
1383 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
1388 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1389 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
1390 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
1393 error
= mac_vnode_check_getattrlist(ctx
, vp
, &al
);
1399 * It is legal to request volume or file attributes,
1403 if (al
.fileattr
|| al
.dirattr
|| al
.forkattr
) {
1405 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
1408 /* handle volume attribute request */
1409 error
= getvolattrlist(vp
, uap
, &al
, ctx
, proc_is64
);
1413 /* Check for special packing semantics */
1414 return_valid
= (al
.commonattr
& ATTR_CMN_RETURNED_ATTRS
) ? 1 : 0;
1415 pack_invalid
= (uap
->options
& FSOPT_PACK_INVAL_ATTRS
) ? 1 : 0;
1416 attr_extended
= (uap
->options
& FSOPT_ATTRLIST_EXTENDED
) ? 1 : 0;
1418 /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
1419 if (!return_valid
|| al
.forkattr
) {
1423 /* Keep invalid attrs from being uninitialized */
1424 bzero(&va
, sizeof (va
));
1425 /* Generate a valid mask for post processing */
1426 bcopy(&al
.commonattr
, &ab
.valid
, sizeof (attribute_set_t
));
1429 /* Pick up the vnode type. If the FS is bad and changes vnode types on us, we
1430 * will have a valid snapshot that we can work from here.
1436 * Set up the vnode_attr structure and authorise.
1438 if ((error
= getattrlist_setupvattr(&al
, attr_extended
, &va
, &fixedsize
, &action
, proc_is64
, (vtype
== VDIR
))) != 0) {
1439 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
1442 if ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0) {
1443 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorisation failed/denied");
1448 * If we're asking for the full path, allocate a buffer for that.
1450 if (al
.commonattr
& (ATTR_CMN_FULLPATH
)) {
1451 fullpathptr
= (char*) kalloc(MAXPATHLEN
);
1452 if (fullpathptr
== NULL
) {
1454 VFS_DEBUG(ctx
,vp
, "ATTRLIST - ERROR: cannot allocate fullpath buffer");
1460 if (va
.va_active
!= 0) {
1462 * If we're going to ask for va_name, allocate a buffer to point it at
1464 if (VATTR_IS_ACTIVE(&va
, va_name
)) {
1465 va
.va_name
= (char *) kalloc(MAXPATHLEN
);
1466 if (va
.va_name
== NULL
) {
1468 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: cannot allocate va_name buffer");
1474 * Call the filesystem.
1476 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
1477 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1481 /* did we ask for something the filesystem doesn't support? */
1482 if (!VATTR_ALL_SUPPORTED(&va
)) {
1485 * There are a couple of special cases. If we are after object IDs,
1486 * we can make do with va_fileid.
1488 if ((al
.commonattr
& (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
| ATTR_CMN_FILEID
)) && !VATTR_IS_SUPPORTED(&va
, va_linkid
))
1489 VATTR_CLEAR_ACTIVE(&va
, va_linkid
); /* forget we wanted this */
1492 * Many filesystems don't know their parent object id.
1493 * If necessary, attempt to derive it from the vnode.
1495 if ((al
.commonattr
& (ATTR_CMN_PAROBJID
| ATTR_CMN_PARENTID
)) &&
1496 !VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
1499 if ((dvp
= vnode_getparent(vp
)) != NULLVP
) {
1500 struct vnode_attr lva
;
1503 VATTR_WANTED(&lva
, va_fileid
);
1504 if (vnode_getattr(dvp
, &lva
, ctx
) == 0 &&
1505 VATTR_IS_SUPPORTED(&va
, va_fileid
)) {
1506 va
.va_parentid
= lva
.va_fileid
;
1507 VATTR_SET_SUPPORTED(&va
, va_parentid
);
1513 * And we can report datasize/alloc from total.
1515 if ((al
.fileattr
& ATTR_FILE_DATALENGTH
) && !VATTR_IS_SUPPORTED(&va
, va_data_size
))
1516 VATTR_CLEAR_ACTIVE(&va
, va_data_size
);
1517 if ((al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) && !VATTR_IS_SUPPORTED(&va
, va_data_alloc
))
1518 VATTR_CLEAR_ACTIVE(&va
, va_data_alloc
);
1521 * If we don't have an encoding, go with UTF-8
1523 if ((al
.commonattr
& ATTR_CMN_SCRIPT
) &&
1524 !VATTR_IS_SUPPORTED(&va
, va_encoding
) && !return_valid
)
1525 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
1528 * If we don't have a name, we'll get one from the vnode or mount point.
1530 if ((al
.commonattr
& ATTR_CMN_NAME
) && !VATTR_IS_SUPPORTED(&va
, va_name
)) {
1531 VATTR_CLEAR_ACTIVE(&va
, va_name
);
1534 /* If va_dirlinkcount isn't supported use a default of 1. */
1535 if ((al
.dirattr
& ATTR_DIR_LINKCOUNT
) && !VATTR_IS_SUPPORTED(&va
, va_dirlinkcount
)) {
1536 VATTR_RETURN(&va
, va_dirlinkcount
, 1);
1540 if (!VATTR_ALL_SUPPORTED(&va
)) {
1543 /* Fix up valid mask for post processing */
1544 getattrlist_fixupattrs(&ab
.valid
, &va
);
1546 /* Force packing of everything asked for */
1547 va
.va_supported
= va
.va_active
;
1549 /* Adjust the requested attributes */
1550 getattrlist_fixupattrs((attribute_set_t
*)&al
.commonattr
, &va
);
1561 * Compute variable-space requirements.
1563 varsize
= 0; /* length count */
1565 /* We may need to fix up the name attribute if requested */
1566 if (al
.commonattr
& ATTR_CMN_NAME
) {
1567 if (VATTR_IS_SUPPORTED(&va
, va_name
)) {
1568 va
.va_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
1573 /* Filesystem did not support getting the name */
1574 if (vnode_isvroot(vp
)) {
1575 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
1576 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
1577 /* special case for boot volume. Use root name when it's
1578 * available (which is the volume name) or just the mount on
1579 * name of "/". we must do this for binary compatibility with
1580 * pre Tiger code. returning nothing for the boot volume name
1581 * breaks installers - 3961058
1583 cnp
= vname
= vnode_getname(vp
);
1585 /* just use "/" as name */
1586 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
1591 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
1595 cnp
= vname
= vnode_getname(vp
);
1602 varsize
+= roundup(cnl
+ 1, 4);
1606 * Compute the full path to this vnode, if necessary. This attribute is almost certainly
1607 * not supported by any filesystem, so build the path to this vnode at this time.
1609 if (al
.commonattr
& ATTR_CMN_FULLPATH
) {
1610 int len
= MAXPATHLEN
;
1612 /* call build_path making sure NOT to use the cache-only behavior */
1613 err
= build_path(vp
, fullpathptr
, len
, &len
, 0, vfs_context_current());
1620 fullpathlen
= strlen(fullpathptr
);
1622 varsize
+= roundup(fullpathlen
+1, 4);
1626 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
1628 * XXX This needs to change at some point; since the blob is opaque in
1629 * user-space this is OK.
1631 if ((al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) &&
1632 VATTR_IS_SUPPORTED(&va
, va_acl
) &&
1633 (va
.va_acl
!= NULL
)) {
1636 * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
1637 * KAUTH_FILESEC_NOACL ourselves
1639 if (va
.va_acl
->acl_entrycount
== KAUTH_FILESEC_NOACL
) {
1640 varsize
+= roundup((KAUTH_FILESEC_SIZE(0)), 4);
1643 varsize
+= roundup ((KAUTH_FILESEC_SIZE(va
.va_acl
->acl_entrycount
)), 4);
1648 * Allocate a target buffer for attribute results.
1650 * Note that we won't ever copy out more than the caller requested, even though
1651 * we might have to allocate more than they offer so that the diagnostic checks
1652 * don't result in a panic if the caller's buffer is too small..
1654 ab
.allocated
= fixedsize
+ varsize
;
1655 /* Cast 'allocated' to an unsigned to verify allocation size */
1656 if ( ((size_t)ab
.allocated
) > ATTR_MAX_BUFFER
) {
1658 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
1661 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
1662 if (ab
.base
== NULL
) {
1664 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
1668 /* set the S_IFMT bits for the mode */
1669 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1670 switch (vp
->v_type
) {
1672 va
.va_mode
|= S_IFREG
;
1675 va
.va_mode
|= S_IFDIR
;
1678 va
.va_mode
|= S_IFBLK
;
1681 va
.va_mode
|= S_IFCHR
;
1684 va
.va_mode
|= S_IFLNK
;
1687 va
.va_mode
|= S_IFSOCK
;
1690 va
.va_mode
|= S_IFIFO
;
1699 * Pack results into the destination buffer.
1701 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
1703 ab
.fixedcursor
+= sizeof (attribute_set_t
);
1704 bzero(&ab
.actual
, sizeof (ab
.actual
));
1706 ab
.varcursor
= ab
.base
+ fixedsize
;
1707 ab
.needed
= ab
.allocated
;
1709 /* common attributes **************************************************/
1710 if (al
.commonattr
& ATTR_CMN_NAME
) {
1711 attrlist_pack_string(&ab
, cnp
, cnl
);
1712 ab
.actual
.commonattr
|= ATTR_CMN_NAME
;
1714 if ((al
.commonattr
& ATTR_CMN_ERROR
) &&
1715 (!return_valid
|| pack_invalid
)) {
1717 ab
.actual
.commonattr
|= ATTR_CMN_ERROR
;
1719 if (al
.commonattr
& ATTR_CMN_DEVID
) {
1720 ATTR_PACK4(ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
1721 ab
.actual
.commonattr
|= ATTR_CMN_DEVID
;
1723 if (al
.commonattr
& ATTR_CMN_FSID
) {
1724 ATTR_PACK8(ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
);
1725 ab
.actual
.commonattr
|= ATTR_CMN_FSID
;
1727 if (al
.commonattr
& ATTR_CMN_OBJTYPE
) {
1728 ATTR_PACK4(ab
, vtype
);
1729 ab
.actual
.commonattr
|= ATTR_CMN_OBJTYPE
;
1731 if (al
.commonattr
& ATTR_CMN_OBJTAG
) {
1732 ATTR_PACK4(ab
, vp
->v_tag
);
1733 ab
.actual
.commonattr
|= ATTR_CMN_OBJTAG
;
1735 if (al
.commonattr
& ATTR_CMN_OBJID
) {
1738 * Carbon can't deal with us reporting the target ID
1739 * for links. So we ask the filesystem to give us the
1740 * source ID as well, and if it gives us one, we use
1743 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1744 f
.fid_objno
= va
.va_linkid
;
1746 f
.fid_objno
= va
.va_fileid
;
1748 f
.fid_generation
= 0;
1750 ab
.actual
.commonattr
|= ATTR_CMN_OBJID
;
1752 if (al
.commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1755 * Carbon can't deal with us reporting the target ID
1756 * for links. So we ask the filesystem to give us the
1757 * source ID as well, and if it gives us one, we use
1760 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1761 f
.fid_objno
= va
.va_linkid
;
1763 f
.fid_objno
= va
.va_fileid
;
1765 f
.fid_generation
= 0;
1767 ab
.actual
.commonattr
|= ATTR_CMN_OBJPERMANENTID
;
1769 if (al
.commonattr
& ATTR_CMN_PAROBJID
) {
1772 f
.fid_objno
= va
.va_parentid
; /* could be lossy here! */
1773 f
.fid_generation
= 0;
1775 ab
.actual
.commonattr
|= ATTR_CMN_PAROBJID
;
1777 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
1778 if (VATTR_IS_SUPPORTED(&va
, va_encoding
)) {
1779 ATTR_PACK4(ab
, va
.va_encoding
);
1780 ab
.actual
.commonattr
|= ATTR_CMN_SCRIPT
;
1781 } else if (!return_valid
|| pack_invalid
) {
1782 ATTR_PACK4(ab
, 0x7e);
1785 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
1786 ATTR_PACK_TIME(ab
, va
.va_create_time
, proc_is64
);
1787 ab
.actual
.commonattr
|= ATTR_CMN_CRTIME
;
1789 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
1790 ATTR_PACK_TIME(ab
, va
.va_modify_time
, proc_is64
);
1791 ab
.actual
.commonattr
|= ATTR_CMN_MODTIME
;
1793 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
1794 ATTR_PACK_TIME(ab
, va
.va_change_time
, proc_is64
);
1795 ab
.actual
.commonattr
|= ATTR_CMN_CHGTIME
;
1797 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
1798 ATTR_PACK_TIME(ab
, va
.va_access_time
, proc_is64
);
1799 ab
.actual
.commonattr
|= ATTR_CMN_ACCTIME
;
1801 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
1802 ATTR_PACK_TIME(ab
, va
.va_backup_time
, proc_is64
);
1803 ab
.actual
.commonattr
|= ATTR_CMN_BKUPTIME
;
1806 * They are requesting user access, we should obtain this before getting
1807 * the finder info. For some network file systems this is a performance
1810 if (al
.commonattr
& ATTR_CMN_USERACCESS
) { /* this is expensive */
1811 if (vtype
== VDIR
) {
1812 if (vnode_authorize(vp
, NULL
,
1813 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
1815 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
1817 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
1820 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
1822 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
1824 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
1829 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1832 char uio_buf
[UIO_SIZEOF(1)];
1834 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
1835 uio_buf
, sizeof(uio_buf
))) == NULL
) {
1839 uio_addiov(auio
, CAST_USER_ADDR_T(ab
.fixedcursor
), fisize
);
1840 error
= vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, auio
,
1841 &fisize
, XATTR_NOSECURITY
, ctx
);
1844 * Default to zeros if its not available,
1845 * unless ATTR_CMN_RETURNED_ATTRS was requested.
1848 (!return_valid
|| pack_invalid
) &&
1849 ((error
== ENOATTR
) || (error
== ENOENT
) ||
1850 (error
== ENOTSUP
) || (error
== EPERM
))) {
1851 VFS_DEBUG(ctx
, vp
, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1852 bzero(ab
.fixedcursor
, 32);
1856 ab
.fixedcursor
+= 32;
1857 ab
.actual
.commonattr
|= ATTR_CMN_FNDRINFO
;
1858 } else if (!return_valid
) {
1859 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1863 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
1864 ATTR_PACK4(ab
, va
.va_uid
);
1865 ab
.actual
.commonattr
|= ATTR_CMN_OWNERID
;
1867 if (al
.commonattr
& ATTR_CMN_GRPID
) {
1868 ATTR_PACK4(ab
, va
.va_gid
);
1869 ab
.actual
.commonattr
|= ATTR_CMN_GRPID
;
1871 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1872 ATTR_PACK4(ab
, va
.va_mode
);
1873 ab
.actual
.commonattr
|= ATTR_CMN_ACCESSMASK
;
1875 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
1876 ATTR_PACK4(ab
, va
.va_flags
);
1877 ab
.actual
.commonattr
|= ATTR_CMN_FLAGS
;
1879 if (attr_extended
) {
1880 if (al
.commonattr
& ATTR_CMN_GEN_COUNT
) {
1881 if (VATTR_IS_SUPPORTED(&va
, va_gen
)) {
1882 ATTR_PACK4(ab
, va
.va_gen
);
1883 ab
.actual
.commonattr
|= ATTR_CMN_GEN_COUNT
;
1884 } else if (!return_valid
|| pack_invalid
) {
1889 if (al
.commonattr
& ATTR_CMN_DOCUMENT_ID
) {
1890 if (VATTR_IS_SUPPORTED(&va
, va_document_id
)) {
1891 ATTR_PACK4(ab
, va
.va_document_id
);
1892 ab
.actual
.commonattr
|= ATTR_CMN_DOCUMENT_ID
;
1893 } else if (!return_valid
|| pack_invalid
) {
1898 /* We already obtain the user access, so just fill in the buffer here */
1899 if (al
.commonattr
& ATTR_CMN_USERACCESS
) {
1902 * Rather than MAC preceding DAC, in this case we want
1903 * the smallest set of permissions granted by both MAC & DAC
1904 * checks. We won't add back any permissions.
1907 if (mac_vnode_check_access(ctx
, vp
, W_OK
) != 0)
1910 if (mac_vnode_check_access(ctx
, vp
, R_OK
) != 0)
1913 if (mac_vnode_check_access(ctx
, vp
, X_OK
) != 0)
1916 VFS_DEBUG(ctx
, vp
, "ATTRLIST - granting perms %d", perms
);
1917 ATTR_PACK4(ab
, perms
);
1918 ab
.actual
.commonattr
|= ATTR_CMN_USERACCESS
;
1920 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1921 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
)) {
1922 struct kauth_filesec fsec
;
1924 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1926 fsec
.fsec_magic
= KAUTH_FILESEC_MAGIC
;
1927 fsec
.fsec_owner
= kauth_null_guid
;
1928 fsec
.fsec_group
= kauth_null_guid
;
1929 attrlist_pack_variable2(&ab
, &fsec
, __offsetof(struct kauth_filesec
, fsec_acl
), va
.va_acl
, KAUTH_ACL_COPYSIZE(va
.va_acl
));
1930 ab
.actual
.commonattr
|= ATTR_CMN_EXTENDED_SECURITY
;
1931 } else if (!return_valid
|| pack_invalid
) {
1932 attrlist_pack_variable(&ab
, NULL
, 0);
1935 if (al
.commonattr
& ATTR_CMN_UUID
) {
1936 if (VATTR_IS_SUPPORTED(&va
, va_uuuid
)) {
1937 ATTR_PACK(&ab
, va
.va_uuuid
);
1938 ab
.actual
.commonattr
|= ATTR_CMN_UUID
;
1939 } else if (!return_valid
|| pack_invalid
) {
1940 ATTR_PACK(&ab
, kauth_null_guid
);
1943 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1944 if (VATTR_IS_SUPPORTED(&va
, va_guuid
)) {
1945 ATTR_PACK(&ab
, va
.va_guuid
);
1946 ab
.actual
.commonattr
|= ATTR_CMN_GRPUUID
;
1947 } else if (!return_valid
|| pack_invalid
) {
1948 ATTR_PACK(&ab
, kauth_null_guid
);
1951 if (al
.commonattr
& ATTR_CMN_FILEID
) {
1952 ATTR_PACK8(ab
, va
.va_fileid
);
1953 ab
.actual
.commonattr
|= ATTR_CMN_FILEID
;
1955 if (al
.commonattr
& ATTR_CMN_PARENTID
) {
1956 ATTR_PACK8(ab
, va
.va_parentid
);
1957 ab
.actual
.commonattr
|= ATTR_CMN_PARENTID
;
1960 if (al
.commonattr
& ATTR_CMN_FULLPATH
) {
1961 attrlist_pack_string (&ab
, fullpathptr
, fullpathlen
);
1962 ab
.actual
.commonattr
|= ATTR_CMN_FULLPATH
;
1965 if (al
.commonattr
& ATTR_CMN_ADDEDTIME
) {
1966 ATTR_PACK_TIME(ab
, va
.va_addedtime
, proc_is64
);
1967 ab
.actual
.commonattr
|= ATTR_CMN_ADDEDTIME
;
1970 /* directory attributes *********************************************/
1971 if (al
.dirattr
&& (vtype
== VDIR
)) {
1972 if (al
.dirattr
& ATTR_DIR_LINKCOUNT
) { /* full count of entries */
1973 ATTR_PACK4(ab
, (uint32_t)va
.va_dirlinkcount
);
1974 ab
.actual
.dirattr
|= ATTR_DIR_LINKCOUNT
;
1976 if (al
.dirattr
& ATTR_DIR_ENTRYCOUNT
) {
1977 ATTR_PACK4(ab
, (uint32_t)va
.va_nchildren
);
1978 ab
.actual
.dirattr
|= ATTR_DIR_ENTRYCOUNT
;
1980 if (al
.dirattr
& ATTR_DIR_MOUNTSTATUS
) {
1983 mntstat
= (vp
->v_flag
& VROOT
) ? DIR_MNTSTATUS_MNTPOINT
: 0;
1986 * Report back on active vnode triggers
1987 * that can directly trigger a mount
1989 if (vp
->v_resolve
&&
1990 !(vp
->v_resolve
->vr_flags
& VNT_NO_DIRECT_MOUNT
)) {
1991 mntstat
|= DIR_MNTSTATUS_TRIGGER
;
1994 ATTR_PACK4(ab
, mntstat
);
1995 ab
.actual
.dirattr
|= ATTR_DIR_MOUNTSTATUS
;
1999 /* file attributes **************************************************/
2000 if (al
.fileattr
&& (vtype
!= VDIR
)) {
2003 uint64_t rlength
= 0;
2004 uint64_t ralloc
= 0;
2006 * Pre-fetch the rsrc attributes now so we only get them once.
2007 * Fetch the resource fork size/allocation via xattr interface
2009 if (al
.fileattr
& (ATTR_FILE_TOTALSIZE
| ATTR_FILE_ALLOCSIZE
| ATTR_FILE_RSRCLENGTH
| ATTR_FILE_RSRCALLOCSIZE
)) {
2010 if ((error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &rsize
, XATTR_NOSECURITY
, ctx
)) != 0) {
2011 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)|| (error
== EACCES
)) {
2020 if (al
.fileattr
& (ATTR_FILE_RSRCALLOCSIZE
| ATTR_FILE_ALLOCSIZE
)) {
2021 uint32_t blksize
= vp
->v_mount
->mnt_vfsstat
.f_bsize
;
2025 ralloc
= roundup(rsize
, blksize
);
2029 if (al
.fileattr
& ATTR_FILE_LINKCOUNT
) {
2030 ATTR_PACK4(ab
, (uint32_t)va
.va_nlink
);
2031 ab
.actual
.fileattr
|= ATTR_FILE_LINKCOUNT
;
2034 * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
2035 * We infer that if the filesystem does not support va_data_size or va_data_alloc
2036 * it must not know about alternate forks. So when we need to gather
2037 * the total size or total alloc, it's OK to substitute the total size for
2038 * the data size below. This is because it is likely a flat filesystem and we must
2039 * be using AD files to store the rsrc fork and EAs.
2041 * Additionally, note that getattrlist is barred from being called on
2042 * resource fork paths. (Search for CN_ALLOWRSRCFORK). So if the filesystem does
2043 * support va_data_size, it is guaranteed to represent the data fork's size. This
2044 * is an important distinction to make because when we call vnode_getattr on
2045 * an HFS resource fork vnode, to get the size, it will vend out the resource
2046 * fork's size (it only gets the size of the passed-in vnode).
2048 if (al
.fileattr
& ATTR_FILE_TOTALSIZE
) {
2049 uint64_t totalsize
= rlength
;
2051 if (VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
2052 totalsize
+= va
.va_data_size
;
2054 totalsize
+= va
.va_total_size
;
2057 ATTR_PACK8(ab
, totalsize
);
2058 ab
.actual
.fileattr
|= ATTR_FILE_TOTALSIZE
;
2060 if (al
.fileattr
& ATTR_FILE_ALLOCSIZE
) {
2061 uint64_t totalalloc
= ralloc
;
2064 * If data_alloc is supported, then it must represent the
2067 if (VATTR_IS_SUPPORTED(&va
, va_data_alloc
)) {
2068 totalalloc
+= va
.va_data_alloc
;
2071 totalalloc
+= va
.va_total_alloc
;
2074 ATTR_PACK8(ab
, totalalloc
);
2075 ab
.actual
.fileattr
|= ATTR_FILE_ALLOCSIZE
;
2077 if (al
.fileattr
& ATTR_FILE_IOBLOCKSIZE
) {
2078 ATTR_PACK4(ab
, va
.va_iosize
);
2079 ab
.actual
.fileattr
|= ATTR_FILE_IOBLOCKSIZE
;
2081 if (al
.fileattr
& ATTR_FILE_CLUMPSIZE
) {
2082 if (!return_valid
|| pack_invalid
) {
2083 ATTR_PACK4(ab
, 0); /* this value is deprecated */
2084 ab
.actual
.fileattr
|= ATTR_FILE_CLUMPSIZE
;
2087 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
2090 if ((vp
->v_type
== VCHR
) || (vp
->v_type
== VBLK
)) {
2091 if (vp
->v_specinfo
!= NULL
)
2092 dev
= vp
->v_specinfo
->si_rdev
;
2098 ATTR_PACK4(ab
, dev
);
2099 ab
.actual
.fileattr
|= ATTR_FILE_DEVTYPE
;
2103 * If the filesystem does not support datalength
2104 * or dataallocsize, then we infer that totalsize and
2105 * totalalloc are substitutes.
2107 if (al
.fileattr
& ATTR_FILE_DATALENGTH
) {
2108 if (VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
2109 ATTR_PACK8(ab
, va
.va_data_size
);
2111 ATTR_PACK8(ab
, va
.va_total_size
);
2113 ab
.actual
.fileattr
|= ATTR_FILE_DATALENGTH
;
2115 if (al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) {
2116 if (VATTR_IS_SUPPORTED(&va
, va_data_alloc
)) {
2117 ATTR_PACK8(ab
, va
.va_data_alloc
);
2119 ATTR_PACK8(ab
, va
.va_total_alloc
);
2121 ab
.actual
.fileattr
|= ATTR_FILE_DATAALLOCSIZE
;
2123 /* already got the resource fork size/allocation above */
2124 if (al
.fileattr
& ATTR_FILE_RSRCLENGTH
) {
2125 ATTR_PACK8(ab
, rlength
);
2126 ab
.actual
.fileattr
|= ATTR_FILE_RSRCLENGTH
;
2128 if (al
.fileattr
& ATTR_FILE_RSRCALLOCSIZE
) {
2129 ATTR_PACK8(ab
, ralloc
);
2130 ab
.actual
.fileattr
|= ATTR_FILE_RSRCALLOCSIZE
;
2135 if (!return_valid
&& (ab
.fixedcursor
- ab
.base
) != fixedsize
)
2136 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
2137 fixedsize
, (long) (ab
.fixedcursor
- ab
.base
), al
.commonattr
, al
.volattr
);
2138 if (!return_valid
&& ab
.varcursor
!= (ab
.base
+ ab
.needed
))
2139 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab
.varcursor
- ab
.base
), ab
.needed
);
2142 * In the compatible case, we report the smaller of the required and returned sizes.
2143 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
2144 * of the result buffer, even if we copied less out. The caller knows how big a buffer
2145 * they gave us, so they can always check for truncation themselves.
2147 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
2149 /* Return attribute set output if requested. */
2151 ab
.actual
.commonattr
|= ATTR_CMN_RETURNED_ATTRS
;
2153 /* Only report the attributes that are valid */
2154 ab
.actual
.commonattr
&= ab
.valid
.commonattr
;
2155 ab
.actual
.dirattr
&= ab
.valid
.dirattr
;
2156 ab
.actual
.fileattr
&= ab
.valid
.fileattr
;
2158 bcopy(&ab
.actual
, ab
.base
+ sizeof(uint32_t), sizeof (ab
.actual
));
2161 /* Only actually copyout as much out as the user buffer can hold */
2162 error
= copyout(ab
.base
, uap
->attributeBuffer
, imin(uap
->bufferSize
, ab
.allocated
));
2166 kfree(va
.va_name
, MAXPATHLEN
);
2168 kfree(fullpathptr
, MAXPATHLEN
);
2170 vnode_putname(vname
);
2171 if (ab
.base
!= NULL
)
2172 FREE(ab
.base
, M_TEMP
);
2173 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
))
2174 kauth_acl_free(va
.va_acl
);
2176 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
2181 fgetattrlist(proc_t p
, struct fgetattrlist_args
*uap
, __unused
int32_t *retval
)
2183 struct vfs_context
*ctx
;
2186 struct getattrlist_args ap
;
2188 ctx
= vfs_context_current();
2191 if ((error
= file_vnode(uap
->fd
, &vp
)) != 0)
2194 if ((error
= vnode_getwithref(vp
)) != 0) {
2200 ap
.alist
= uap
->alist
;
2201 ap
.attributeBuffer
= uap
->attributeBuffer
;
2202 ap
.bufferSize
= uap
->bufferSize
;
2203 ap
.options
= uap
->options
;
2205 /* Default to using the vnode's name. */
2206 error
= getattrlist_internal(vp
, &ap
, NULL
, p
, ctx
);
2216 getattrlist(proc_t p
, struct getattrlist_args
*uap
, __unused
int32_t *retval
)
2218 struct vfs_context
*ctx
;
2219 struct nameidata nd
;
2224 ctx
= vfs_context_current();
2230 nameiflags
= NOTRIGGER
| AUDITVNPATH1
;
2231 if (!(uap
->options
& FSOPT_NOFOLLOW
))
2232 nameiflags
|= FOLLOW
;
2233 NDINIT(&nd
, LOOKUP
, OP_GETATTR
, nameiflags
, UIO_USERSPACE
, uap
->path
, ctx
);
2235 if ((error
= namei(&nd
)) != 0) {
2236 /* vp is still uninitialized */
2241 /* Pass along our componentname to getattrlist_internal */
2242 error
= getattrlist_internal(vp
, uap
, &(nd
.ni_cnd
), p
, ctx
);
2244 /* Retain the namei reference until the getattrlist completes. */
2253 attrlist_unpack_fixed(char **cursor
, char *end
, void *buf
, ssize_t size
)
2255 /* make sure we have enough source data */
2256 if ((*cursor
) + size
> end
)
2259 bcopy(*cursor
, buf
, size
);
2264 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
2265 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
2266 #define ATTR_UNPACK_TIME(v, is64) \
2269 struct user64_timespec us; \
2271 v.tv_sec = us.tv_sec; \
2272 v.tv_nsec = us.tv_nsec; \
2274 struct user32_timespec us; \
2276 v.tv_sec = us.tv_sec; \
2277 v.tv_nsec = us.tv_nsec; \
2286 setattrlist_internal(vnode_t vp
, struct setattrlist_args
*uap
, proc_t p
, vfs_context_t ctx
)
2289 struct vnode_attr va
;
2290 struct attrreference ar
;
2291 kauth_action_t action
;
2292 char *user_buf
, *cursor
, *bufend
, *fndrinfo
, *cp
, *volname
;
2293 int proc_is64
, error
;
2295 kauth_filesec_t rfsec
;
2301 proc_is64
= proc_is64bit(p
);
2305 * Fetch the attribute set and validate.
2307 if ((error
= copyin(uap
->alist
, (caddr_t
) &al
, sizeof (al
))))
2309 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
2314 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
2315 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
2316 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
2319 if ((al
.volattr
& ~ATTR_VOL_SETMASK
) ||
2320 (al
.commonattr
& ~ATTR_CMN_VOLSETMASK
) ||
2324 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
2328 if ((al
.commonattr
& ~ATTR_CMN_SETMASK
) ||
2329 (al
.fileattr
& ~ATTR_FILE_SETMASK
) ||
2330 (al
.dirattr
& ~ATTR_DIR_SETMASK
) ||
2331 (al
.forkattr
& ~ATTR_FORK_SETMASK
)) {
2333 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
2339 * If the caller's bitmaps indicate that there are no attributes to set,
2340 * then exit early. In particular, we want to avoid the MALLOC below
2341 * since the caller's bufferSize could be zero, and MALLOC of zero bytes
2342 * returns a NULL pointer, which would cause setattrlist to return ENOMEM.
2344 if (al
.commonattr
== 0 &&
2345 (al
.volattr
& ~ATTR_VOL_INFO
) == 0 &&
2354 * Make the naive assumption that the caller has supplied a reasonable buffer
2355 * size. We could be more careful by pulling in the fixed-size region, checking
2356 * the attrref structures, then pulling in the variable section.
2357 * We need to reconsider this for handling large ACLs, as they should probably be
2358 * brought directly into a buffer. Multiple copyins will make this slower though.
2360 * We could also map the user buffer if it is larger than some sensible mimimum.
2362 if (uap
->bufferSize
> ATTR_MAX_BUFFER
) {
2363 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size %d too large", uap
->bufferSize
);
2367 MALLOC(user_buf
, char *, uap
->bufferSize
, M_TEMP
, M_WAITOK
);
2368 if (user_buf
== NULL
) {
2369 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap
->bufferSize
);
2373 if ((error
= copyin(uap
->attributeBuffer
, user_buf
, uap
->bufferSize
)) != 0) {
2374 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer copyin failed");
2377 VFS_DEBUG(ctx
, vp
, "ATTRLIST - copied in %d bytes of user attributes to %p", uap
->bufferSize
, user_buf
);
2380 error
= mac_vnode_check_setattrlist(ctx
, vp
, &al
);
2386 * Unpack the argument buffer.
2389 bufend
= cursor
+ uap
->bufferSize
;
2392 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
2393 ATTR_UNPACK(va
.va_encoding
);
2394 VATTR_SET_ACTIVE(&va
, va_encoding
);
2396 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
2397 ATTR_UNPACK_TIME(va
.va_create_time
, proc_is64
);
2398 VATTR_SET_ACTIVE(&va
, va_create_time
);
2400 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
2401 ATTR_UNPACK_TIME(va
.va_modify_time
, proc_is64
);
2402 VATTR_SET_ACTIVE(&va
, va_modify_time
);
2404 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
2405 ATTR_UNPACK_TIME(va
.va_change_time
, proc_is64
);
2406 VATTR_SET_ACTIVE(&va
, va_change_time
);
2408 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
2409 ATTR_UNPACK_TIME(va
.va_access_time
, proc_is64
);
2410 VATTR_SET_ACTIVE(&va
, va_access_time
);
2412 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
2413 ATTR_UNPACK_TIME(va
.va_backup_time
, proc_is64
);
2414 VATTR_SET_ACTIVE(&va
, va_backup_time
);
2416 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
2417 if ((cursor
+ 32) > bufend
) {
2419 VFS_DEBUG(ctx
, vp
, "ATTRLIST - not enough data supplied for FINDERINFO");
2425 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
2426 ATTR_UNPACK(va
.va_uid
);
2427 VATTR_SET_ACTIVE(&va
, va_uid
);
2429 if (al
.commonattr
& ATTR_CMN_GRPID
) {
2430 ATTR_UNPACK(va
.va_gid
);
2431 VATTR_SET_ACTIVE(&va
, va_gid
);
2433 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
2434 ATTR_UNPACK_CAST(uint32_t, va
.va_mode
);
2435 VATTR_SET_ACTIVE(&va
, va_mode
);
2437 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
2438 ATTR_UNPACK(va
.va_flags
);
2439 VATTR_SET_ACTIVE(&va
, va_flags
);
2441 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
2444 * We are (for now) passed a kauth_filesec_t, but all we want from
2449 if (ar
.attr_dataoffset
< 0) {
2450 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad offset supplied", ar
.attr_dataoffset
);
2455 cp
+= ar
.attr_dataoffset
;
2456 rfsec
= (kauth_filesec_t
)cp
;
2457 if (((((char *)rfsec
) + KAUTH_FILESEC_SIZE(0)) > bufend
) || /* no space for acl */
2458 (rfsec
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) || /* bad magic */
2459 (KAUTH_FILESEC_COPYSIZE(rfsec
) != ar
.attr_length
) || /* size does not match */
2460 ((cp
+ KAUTH_FILESEC_COPYSIZE(rfsec
)) > bufend
)) { /* ACEs overrun buffer */
2462 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied", ar
.attr_length
);
2465 nace
= rfsec
->fsec_entrycount
;
2466 if (nace
== KAUTH_FILESEC_NOACL
)
2468 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
2470 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied");
2473 nace
= rfsec
->fsec_acl
.acl_entrycount
;
2474 if (nace
== KAUTH_FILESEC_NOACL
) {
2476 VATTR_SET(&va
, va_acl
, NULL
);
2479 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
2481 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: supplied ACL is too large");
2484 VATTR_SET(&va
, va_acl
, &rfsec
->fsec_acl
);
2487 if (al
.commonattr
& ATTR_CMN_UUID
) {
2488 ATTR_UNPACK(va
.va_uuuid
);
2489 VATTR_SET_ACTIVE(&va
, va_uuuid
);
2491 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
2492 ATTR_UNPACK(va
.va_guuid
);
2493 VATTR_SET_ACTIVE(&va
, va_guuid
);
2497 if (al
.volattr
& ATTR_VOL_INFO
) {
2498 if (al
.volattr
& ATTR_VOL_NAME
) {
2501 /* attr_length cannot be 0! */
2502 if ((ar
.attr_dataoffset
< 0) || (ar
.attr_length
== 0)) {
2503 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad offset supplied (2) ", ar
.attr_dataoffset
);
2508 volname
+= ar
.attr_dataoffset
;
2509 if ((volname
+ ar
.attr_length
) > bufend
) {
2511 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume name too big for caller buffer");
2514 /* guarantee NUL termination */
2515 volname
[ar
.attr_length
- 1] = 0;
2520 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
2521 /* XXX does it actually make any sense to change this? */
2523 VFS_DEBUG(ctx
, vp
, "ATTRLIST - XXX device type change not implemented");
2528 * Validate and authorize.
2531 if ((va
.va_active
!= 0LL) && ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0)) {
2532 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attribute changes refused: %d", error
);
2536 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
2537 * and will be auth'ed by the FS.
2539 if (fndrinfo
!= NULL
) {
2540 if (al
.volattr
& ATTR_VOL_INFO
) {
2541 if (vp
->v_tag
!= VT_HFS
) {
2546 action
|= KAUTH_VNODE_WRITE_EXTATTRIBUTES
;
2550 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0)) {
2551 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorization failed");
2556 * When we're setting both the access mask and the finder info, then
2557 * check if were about to remove write access for the owner. Since
2558 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
2559 * to consider their ordering.
2561 * If were about to remove write access for the owner we'll set the
2562 * Finder Info here before vnode_setattr. Otherwise we'll set it
2563 * after vnode_setattr since it may be adding owner write access.
2565 if ((fndrinfo
!= NULL
) && !(al
.volattr
& ATTR_VOL_INFO
) &&
2566 (al
.commonattr
& ATTR_CMN_ACCESSMASK
) && !(va
.va_mode
& S_IWUSR
)) {
2567 if ((error
= setattrlist_setfinderinfo(vp
, fndrinfo
, ctx
)) != 0) {
2570 fndrinfo
= NULL
; /* it was set here so skip setting below */
2574 * Write the attributes if we have any.
2576 if ((va
.va_active
!= 0LL) && ((error
= vnode_setattr(vp
, &va
, ctx
)) != 0)) {
2577 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
2582 * Write the Finder Info if we have any.
2584 if (fndrinfo
!= NULL
) {
2585 if (al
.volattr
& ATTR_VOL_INFO
) {
2586 if (vp
->v_tag
== VT_HFS
) {
2587 error
= VNOP_IOCTL(vp
, HFS_SET_BOOT_INFO
, (caddr_t
)fndrinfo
, 0, ctx
);
2591 /* XXX should never get here */
2593 } else if ((error
= setattrlist_setfinderinfo(vp
, fndrinfo
, ctx
)) != 0) {
2599 * Set the volume name, if we have one
2601 if (volname
!= NULL
)
2607 vs
.f_vol_name
= volname
; /* References the setattrlist buffer directly */
2608 VFSATTR_WANTED(&vs
, f_vol_name
);
2611 error
= mac_mount_check_setattr(ctx
, vp
->v_mount
, &vs
);
2616 if ((error
= vfs_setattr(vp
->v_mount
, &vs
, ctx
)) != 0) {
2617 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setting volume name failed");
2621 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
2623 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not set volume name");
2628 /* all done and successful */
2631 if (user_buf
!= NULL
)
2632 FREE(user_buf
, M_TEMP
);
2633 VFS_DEBUG(ctx
, vp
, "ATTRLIST - set returning %d", error
);
2638 setattrlist(proc_t p
, struct setattrlist_args
*uap
, __unused
int32_t *retval
)
2640 struct vfs_context
*ctx
;
2641 struct nameidata nd
;
2646 ctx
= vfs_context_current();
2651 nameiflags
= AUDITVNPATH1
;
2652 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0)
2653 nameiflags
|= FOLLOW
;
2654 NDINIT(&nd
, LOOKUP
, OP_SETATTR
, nameiflags
, UIO_USERSPACE
, uap
->path
, ctx
);
2655 if ((error
= namei(&nd
)) != 0)
2660 error
= setattrlist_internal(vp
, uap
, p
, ctx
);
2668 fsetattrlist(proc_t p
, struct fsetattrlist_args
*uap
, __unused
int32_t *retval
)
2670 struct vfs_context
*ctx
;
2673 struct setattrlist_args ap
;
2675 ctx
= vfs_context_current();
2677 if ((error
= file_vnode(uap
->fd
, &vp
)) != 0)
2680 if ((error
= vnode_getwithref(vp
)) != 0) {
2686 ap
.alist
= uap
->alist
;
2687 ap
.attributeBuffer
= uap
->attributeBuffer
;
2688 ap
.bufferSize
= uap
->bufferSize
;
2689 ap
.options
= uap
->options
;
2691 error
= setattrlist_internal(vp
, &ap
, p
, ctx
);