2 * Copyright (c) 1995-2019 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/syslog.h>
41 #include <sys/vnode_internal.h>
42 #include <sys/mount_internal.h>
43 #include <sys/proc_internal.h>
44 #include <sys/file_internal.h>
45 #include <sys/kauth.h>
46 #include <sys/uio_internal.h>
47 #include <kern/kalloc.h>
49 #include <sys/sysproto.h>
50 #include <sys/xattr.h>
51 #include <sys/fsevents.h>
52 #include <kern/zalloc.h>
53 #include <miscfs/specfs/specdev.h>
54 #include <security/audit/audit.h>
57 #include <security/mac_framework.h>
60 #define ATTR_TIME_SIZE -1
62 static int readdirattr(vnode_t
, struct fd_vn_data
*, uio_t
, struct attrlist
*,
63 uint64_t, int *, int *, vfs_context_t ctx
) __attribute__((noinline
));
66 vattr_get_alt_data(vnode_t
, struct attrlist
*, struct vnode_attr
*, int, int,
67 int, vfs_context_t
) __attribute__((noinline
));
69 static void get_error_attributes(vnode_t
, struct attrlist
*, uint64_t, user_addr_t
,
70 size_t, int, caddr_t
, vfs_context_t
) __attribute__((noinline
));
72 static int getvolattrlist(vfs_context_t
, vnode_t
, struct attrlist
*, user_addr_t
,
73 size_t, uint64_t, enum uio_seg
, int) __attribute__((noinline
));
75 static int get_direntry(vfs_context_t
, vnode_t
, struct fd_vn_data
*, int *,
76 struct direntry
**) __attribute__((noinline
));
79 * Structure describing the state of an in-progress attrlist operation.
81 struct _attrlist_buf
{
87 attribute_set_t actual
;
88 attribute_set_t valid
;
93 * Attempt to pack a fixed width attribute of size (count) bytes from
94 * source to our attrlist buffer.
97 attrlist_pack_fixed(struct _attrlist_buf
*ab
, void *source
, ssize_t count
)
100 * Use ssize_t for pointer math purposes,
101 * since a ssize_t is a signed long
106 * Compute the amount of remaining space in the attrlist buffer
107 * based on how much we've used for fixed width fields vs. the
108 * start of the attributes.
110 * If we've still got room, then 'fit' will contain the amount of
113 * Note that this math is safe because, in the event that the
114 * fixed-width cursor has moved beyond the end of the buffer,
115 * then, the second input into lmin() below will be negative, and
116 * we will fail the (fit > 0) check below.
118 fit
= lmin(count
, ab
->allocated
- (ab
->fixedcursor
- ab
->base
));
120 /* Copy in as much as we can */
121 bcopy(source
, ab
->fixedcursor
, fit
);
124 /* always move in increments of 4, even if we didn't pack an attribute. */
125 ab
->fixedcursor
+= roundup(count
, 4);
129 * Attempt to pack one (or two) variable width attributes into the attrlist
130 * buffer. If we are trying to pack two variable width attributes, they are treated
131 * as a single variable-width attribute from the POV of the system call caller.
133 * Recall that a variable-width attribute has two components: the fixed-width
134 * attribute that tells the caller where to look, and the actual variable width data.
137 attrlist_pack_variable2(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
,
138 const void *ext
, ssize_t extcount
)
140 /* Use ssize_t's for pointer math ease */
141 struct attrreference ar
;
145 * Pack the fixed-width component to the variable object.
146 * Note that we may be able to pack the fixed width attref, but not
147 * the variable (if there's no room).
149 ar
.attr_dataoffset
= (int32_t)(ab
->varcursor
- ab
->fixedcursor
);
150 ar
.attr_length
= (u_int32_t
)(count
+ extcount
);
151 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
154 * Use an lmin() to do a signed comparison. We use a signed comparison
155 * to detect the 'out of memory' conditions as described above in the
156 * fixed width check above.
158 * Then pack the first variable attribute as space allows. Note that we advance
159 * the variable cursor only if we we had some available space.
161 fit
= lmin(count
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
163 if (source
!= NULL
) {
164 bcopy(source
, ab
->varcursor
, fit
);
166 ab
->varcursor
+= fit
;
169 /* Compute the available space for the second attribute */
170 fit
= lmin(extcount
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
172 /* Copy in data for the second attribute (if needed) if there is room */
174 bcopy(ext
, ab
->varcursor
, fit
);
176 ab
->varcursor
+= fit
;
178 /* always move in increments of 4 */
179 ab
->varcursor
= (char *)roundup((uintptr_t)ab
->varcursor
, 4);
183 * Packing a single variable-width attribute is the same as calling the two, but with
184 * an invalid 2nd attribute.
187 attrlist_pack_variable(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
)
189 attrlist_pack_variable2(ab
, source
, count
, NULL
, 0);
193 * Attempt to pack a string. This is a special case of a variable width attribute.
195 * If "source" is NULL, then an empty string ("") will be packed. If "source" is
196 * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated
197 * C-string. If "source" is not NULL and "count" is not zero, then only the first
198 * "count" bytes of "source" will be copied, and a NUL terminator will be added.
200 * If the attrlist buffer doesn't have enough room to hold the entire string (including
201 * NUL terminator), then copy as much as will fit. The attrlist buffer's "varcursor"
202 * will always be updated based on the entire length of the string (including NUL
203 * terminator); this means "varcursor" may end up pointing beyond the end of the
204 * allocated buffer space.
207 attrlist_pack_string(struct _attrlist_buf
*ab
, const char *source
, size_t count
)
209 struct attrreference ar
;
213 * Supplied count is character count of string text, excluding trailing nul
214 * which we always supply here.
216 if (source
== NULL
) {
218 } else if (count
== 0) {
219 count
= strlen(source
);
223 * Construct the fixed-width attribute that refers to this string.
225 ar
.attr_dataoffset
= (int32_t)(ab
->varcursor
- ab
->fixedcursor
);
226 ar
.attr_length
= (u_int32_t
)count
+ 1;
227 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
230 * Now compute how much available memory we have to copy the string text.
232 * space = the number of bytes available in the attribute buffer to hold the
235 * fit = the number of bytes to copy from the start of the string into the
236 * attribute buffer, NOT including the NUL terminator. If the attribute
237 * buffer is large enough, this will be the string's length; otherwise, it
238 * will be equal to "space".
240 space
= ab
->allocated
- (ab
->varcursor
- ab
->base
);
241 fit
= lmin(count
, space
);
246 * If there is space remaining, copy data in, and
247 * accommodate the trailing NUL terminator.
249 * NOTE: if "space" is too small to hold the string and its NUL
250 * terminator (space < fit + 1), then the string value in the attribute
251 * buffer will NOT be NUL terminated!
253 * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
254 * Therefore, we don't bother checking for that here.
256 bcopy(source
, ab
->varcursor
, fit
);
257 /* is there room for our trailing nul? */
259 ab
->varcursor
[fit
++] = '\0';
260 /* 'fit' now the number of bytes AFTER adding in the NUL */
262 * Zero out any additional bytes we might have as a
263 * result of rounding up.
265 bytes_to_zero
= lmin((roundup(fit
, 4) - fit
),
268 bzero(&(ab
->varcursor
[fit
]), bytes_to_zero
);
273 * always move in increments of 4 (including the trailing NUL)
275 ab
->varcursor
+= roundup((count
+ 1), 4);
278 #define ATTR_PACK4(AB, V) \
280 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
281 *(uint32_t *)AB.fixedcursor = V; \
282 AB.fixedcursor += 4; \
286 #define ATTR_PACK8(AB, V) \
288 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
289 memcpy(AB.fixedcursor, &V, 8); \
290 AB.fixedcursor += 8; \
294 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
295 #define ATTR_PACK_CAST(b, t, v) \
301 #define ATTR_PACK_TIME(b, v, is64) \
304 struct user64_timespec us = {.tv_sec = v.tv_sec, .tv_nsec = v.tv_nsec}; \
307 struct user32_timespec us = {.tv_sec = (user32_time_t)v.tv_sec, .tv_nsec = (user32_long_t)v.tv_nsec}; \
314 * Table-driven setup for all valid common/volume attributes.
316 struct getvolattrlist_attrtab
{
319 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
322 static struct getvolattrlist_attrtab getvolattrlist_common_tab
[] = {
323 {.attr
= ATTR_CMN_NAME
, .bits
= 0, .size
= sizeof(struct attrreference
)},
324 {.attr
= ATTR_CMN_DEVID
, .bits
= 0, .size
= sizeof(dev_t
)},
325 {.attr
= ATTR_CMN_FSID
, .bits
= 0, .size
= sizeof(fsid_t
)},
326 {.attr
= ATTR_CMN_OBJTYPE
, .bits
= 0, .size
= sizeof(fsobj_type_t
)},
327 {.attr
= ATTR_CMN_OBJTAG
, .bits
= 0, .size
= sizeof(fsobj_tag_t
)},
328 {.attr
= ATTR_CMN_OBJID
, .bits
= 0, .size
= sizeof(fsobj_id_t
)},
329 {.attr
= ATTR_CMN_OBJPERMANENTID
, .bits
= 0, .size
= sizeof(fsobj_id_t
)},
330 {.attr
= ATTR_CMN_PAROBJID
, .bits
= 0, .size
= sizeof(fsobj_id_t
)},
331 {.attr
= ATTR_CMN_SCRIPT
, .bits
= 0, .size
= sizeof(text_encoding_t
)},
332 {.attr
= ATTR_CMN_CRTIME
, .bits
= VFSATTR_BIT(f_create_time
), .size
= ATTR_TIME_SIZE
},
333 {.attr
= ATTR_CMN_MODTIME
, .bits
= VFSATTR_BIT(f_modify_time
), .size
= ATTR_TIME_SIZE
},
334 {.attr
= ATTR_CMN_CHGTIME
, .bits
= VFSATTR_BIT(f_modify_time
), .size
= ATTR_TIME_SIZE
},
335 {.attr
= ATTR_CMN_ACCTIME
, .bits
= VFSATTR_BIT(f_access_time
), .size
= ATTR_TIME_SIZE
},
336 {.attr
= ATTR_CMN_BKUPTIME
, .bits
= VFSATTR_BIT(f_backup_time
), .size
= ATTR_TIME_SIZE
},
337 {.attr
= ATTR_CMN_FNDRINFO
, .bits
= 0, .size
= 32},
338 {.attr
= ATTR_CMN_OWNERID
, .bits
= 0, .size
= sizeof(uid_t
)},
339 {.attr
= ATTR_CMN_GRPID
, .bits
= 0, .size
= sizeof(gid_t
)},
340 {.attr
= ATTR_CMN_ACCESSMASK
, .bits
= 0, .size
= sizeof(uint32_t)},
341 {.attr
= ATTR_CMN_FLAGS
, .bits
= 0, .size
= sizeof(uint32_t)},
342 {.attr
= ATTR_CMN_USERACCESS
, .bits
= 0, .size
= sizeof(uint32_t)},
343 {.attr
= ATTR_CMN_EXTENDED_SECURITY
, .bits
= 0, .size
= sizeof(struct attrreference
)},
344 {.attr
= ATTR_CMN_UUID
, .bits
= 0, .size
= sizeof(guid_t
)},
345 {.attr
= ATTR_CMN_GRPUUID
, .bits
= 0, .size
= sizeof(guid_t
)},
346 {.attr
= ATTR_CMN_FILEID
, .bits
= 0, .size
= sizeof(uint64_t)},
347 {.attr
= ATTR_CMN_PARENTID
, .bits
= 0, .size
= sizeof(uint64_t)},
348 {.attr
= ATTR_CMN_RETURNED_ATTRS
, .bits
= 0, .size
= sizeof(attribute_set_t
)},
349 {.attr
= ATTR_CMN_ERROR
, .bits
= 0, .size
= sizeof(uint32_t)},
350 {.attr
= 0, .bits
= 0, .size
= 0}
352 #define ATTR_CMN_VOL_INVALID \
353 (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
354 ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
356 static struct getvolattrlist_attrtab getvolattrlist_vol_tab
[] = {
357 {.attr
= ATTR_VOL_FSTYPE
, .bits
= 0, .size
= sizeof(uint32_t)},
358 {.attr
= ATTR_VOL_SIGNATURE
, .bits
= VFSATTR_BIT(f_signature
), .size
= sizeof(uint32_t)},
359 {.attr
= ATTR_VOL_SIZE
, .bits
= VFSATTR_BIT(f_blocks
) | VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
360 {.attr
= ATTR_VOL_SPACEFREE
, .bits
= VFSATTR_BIT(f_bfree
) | VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
361 {.attr
= ATTR_VOL_SPACEAVAIL
, .bits
= VFSATTR_BIT(f_bavail
) | VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
362 {.attr
= ATTR_VOL_MINALLOCATION
, .bits
= VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
363 {.attr
= ATTR_VOL_ALLOCATIONCLUMP
, .bits
= VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
364 {.attr
= ATTR_VOL_IOBLOCKSIZE
, .bits
= VFSATTR_BIT(f_iosize
), .size
= sizeof(uint32_t)},
365 {.attr
= ATTR_VOL_OBJCOUNT
, .bits
= VFSATTR_BIT(f_objcount
), .size
= sizeof(uint32_t)},
366 {.attr
= ATTR_VOL_FILECOUNT
, .bits
= VFSATTR_BIT(f_filecount
), .size
= sizeof(uint32_t)},
367 {.attr
= ATTR_VOL_DIRCOUNT
, .bits
= VFSATTR_BIT(f_dircount
), .size
= sizeof(uint32_t)},
368 {.attr
= ATTR_VOL_MAXOBJCOUNT
, .bits
= VFSATTR_BIT(f_maxobjcount
), .size
= sizeof(uint32_t)},
369 {.attr
= ATTR_VOL_MOUNTPOINT
, .bits
= 0, .size
= sizeof(struct attrreference
)},
370 {.attr
= ATTR_VOL_NAME
, .bits
= VFSATTR_BIT(f_vol_name
), .size
= sizeof(struct attrreference
)},
371 {.attr
= ATTR_VOL_MOUNTFLAGS
, .bits
= 0, .size
= sizeof(uint32_t)},
372 {.attr
= ATTR_VOL_MOUNTEDDEVICE
, .bits
= 0, .size
= sizeof(struct attrreference
)},
373 {.attr
= ATTR_VOL_ENCODINGSUSED
, .bits
= 0, .size
= sizeof(uint64_t)},
374 {.attr
= ATTR_VOL_CAPABILITIES
, .bits
= VFSATTR_BIT(f_capabilities
), .size
= sizeof(vol_capabilities_attr_t
)},
375 {.attr
= ATTR_VOL_UUID
, .bits
= VFSATTR_BIT(f_uuid
), .size
= sizeof(uuid_t
)},
376 {.attr
= ATTR_VOL_QUOTA_SIZE
, .bits
= VFSATTR_BIT(f_quota
) | VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
377 {.attr
= ATTR_VOL_RESERVED_SIZE
, .bits
= VFSATTR_BIT(f_reserved
) | VFSATTR_BIT(f_bsize
), .size
= sizeof(off_t
)},
378 {.attr
= ATTR_VOL_ATTRIBUTES
, .bits
= VFSATTR_BIT(f_attributes
), .size
= sizeof(vol_attributes_attr_t
)},
379 {.attr
= ATTR_VOL_INFO
, .bits
= 0, .size
= 0},
380 {.attr
= 0, .bits
= 0, .size
= 0}
384 getvolattrlist_parsetab(struct getvolattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vfs_attr
*vsp
,
385 ssize_t
*sizep
, int is_64bit
, unsigned int maxiter
)
387 attrgroup_t recognised
;
391 /* is this attribute set? */
392 if (tab
->attr
& attrs
) {
393 recognised
|= tab
->attr
;
394 vsp
->f_active
|= tab
->bits
;
395 if (tab
->size
== ATTR_TIME_SIZE
) {
397 *sizep
+= sizeof(struct user64_timespec
);
399 *sizep
+= sizeof(struct user32_timespec
);
405 } while (((++tab
)->attr
!= 0) && (--maxiter
> 0));
407 /* check to make sure that we recognised all of the passed-in attributes */
408 if (attrs
& ~recognised
) {
415 * Given the attributes listed in alp, configure vap to request
416 * the data from a filesystem.
419 getvolattrlist_setupvfsattr(struct attrlist
*alp
, struct vfs_attr
*vsp
, ssize_t
*sizep
, int is_64bit
)
427 * Parse the above tables.
429 *sizep
= sizeof(uint32_t); /* length count */
430 if (alp
->commonattr
) {
431 if ((alp
->commonattr
& ATTR_CMN_VOL_INVALID
) &&
432 (alp
->commonattr
& ATTR_CMN_RETURNED_ATTRS
) == 0) {
435 if ((error
= getvolattrlist_parsetab(getvolattrlist_common_tab
,
436 alp
->commonattr
, vsp
, sizep
,
438 sizeof(getvolattrlist_common_tab
) / sizeof(getvolattrlist_common_tab
[0]))) != 0) {
443 (error
= getvolattrlist_parsetab(getvolattrlist_vol_tab
, alp
->volattr
, vsp
, sizep
, is_64bit
, sizeof(getvolattrlist_vol_tab
) / sizeof(getvolattrlist_vol_tab
[0]))) != 0) {
451 * Given the attributes listed in asp and those supported
452 * in the vsp, fixup the asp attributes to reflect any
453 * missing attributes from the file system
456 getvolattrlist_fixupattrs(attribute_set_t
*asp
, struct vfs_attr
*vsp
)
458 struct getvolattrlist_attrtab
*tab
;
460 if (asp
->commonattr
) {
461 tab
= getvolattrlist_common_tab
;
463 if ((tab
->attr
& asp
->commonattr
) &&
465 ((tab
->bits
& vsp
->f_supported
) == 0)) {
466 asp
->commonattr
&= ~tab
->attr
;
468 } while ((++tab
)->attr
!= 0);
471 tab
= getvolattrlist_vol_tab
;
473 if ((tab
->attr
& asp
->volattr
) &&
475 ((tab
->bits
& vsp
->f_supported
) == 0)) {
476 asp
->volattr
&= ~tab
->attr
;
478 } while ((++tab
)->attr
!= 0);
483 * Table-driven setup for all valid common/dir/file/fork attributes against files.
485 struct getattrlist_attrtab
{
488 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
490 kauth_action_t action
;
494 * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
495 * information, and we will synthesize it at the VFS level.
497 static struct getattrlist_attrtab getattrlist_common_tab
[] = {
498 {.attr
= ATTR_CMN_NAME
, .bits
= VATTR_BIT(va_name
), .size
= sizeof(struct attrreference
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
499 {.attr
= ATTR_CMN_DEVID
, .bits
= VATTR_BIT(va_fsid
), .size
= sizeof(dev_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
500 {.attr
= ATTR_CMN_FSID
, .bits
= VATTR_BIT(va_fsid64
), .size
= sizeof(fsid_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
501 {.attr
= ATTR_CMN_OBJTYPE
, .bits
= 0, .size
= sizeof(fsobj_type_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
502 {.attr
= ATTR_CMN_OBJTAG
, .bits
= 0, .size
= sizeof(fsobj_tag_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
503 {.attr
= ATTR_CMN_OBJID
, .bits
= VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), .size
= sizeof(fsobj_id_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
504 {.attr
= ATTR_CMN_OBJPERMANENTID
, .bits
= VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), .size
= sizeof(fsobj_id_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
505 {.attr
= ATTR_CMN_PAROBJID
, .bits
= VATTR_BIT(va_parentid
), .size
= sizeof(fsobj_id_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
506 {.attr
= ATTR_CMN_SCRIPT
, .bits
= VATTR_BIT(va_encoding
), .size
= sizeof(text_encoding_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
507 {.attr
= ATTR_CMN_CRTIME
, .bits
= VATTR_BIT(va_create_time
), .size
= ATTR_TIME_SIZE
, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
508 {.attr
= ATTR_CMN_MODTIME
, .bits
= VATTR_BIT(va_modify_time
), .size
= ATTR_TIME_SIZE
, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
509 {.attr
= ATTR_CMN_CHGTIME
, .bits
= VATTR_BIT(va_change_time
), .size
= ATTR_TIME_SIZE
, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
510 {.attr
= ATTR_CMN_ACCTIME
, .bits
= VATTR_BIT(va_access_time
), .size
= ATTR_TIME_SIZE
, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
511 {.attr
= ATTR_CMN_BKUPTIME
, .bits
= VATTR_BIT(va_backup_time
), .size
= ATTR_TIME_SIZE
, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
512 {.attr
= ATTR_CMN_FNDRINFO
, .bits
= 0, .size
= 32, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
513 {.attr
= ATTR_CMN_OWNERID
, .bits
= VATTR_BIT(va_uid
), .size
= sizeof(uid_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
514 {.attr
= ATTR_CMN_GRPID
, .bits
= VATTR_BIT(va_gid
), .size
= sizeof(gid_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
515 {.attr
= ATTR_CMN_ACCESSMASK
, .bits
= VATTR_BIT(va_mode
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
516 {.attr
= ATTR_CMN_FLAGS
, .bits
= VATTR_BIT(va_flags
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
517 {.attr
= ATTR_CMN_GEN_COUNT
, .bits
= VATTR_BIT(va_write_gencount
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
518 {.attr
= ATTR_CMN_DOCUMENT_ID
, .bits
= VATTR_BIT(va_document_id
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
519 {.attr
= ATTR_CMN_USERACCESS
, .bits
= 0, .size
= sizeof(uint32_t), .action
= 0},
520 {.attr
= ATTR_CMN_EXTENDED_SECURITY
, .bits
= VATTR_BIT(va_acl
), .size
= sizeof(struct attrreference
), .action
= KAUTH_VNODE_READ_SECURITY
},
521 {.attr
= ATTR_CMN_UUID
, .bits
= VATTR_BIT(va_uuuid
), .size
= sizeof(guid_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
522 {.attr
= ATTR_CMN_GRPUUID
, .bits
= VATTR_BIT(va_guuid
), .size
= sizeof(guid_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
523 {.attr
= ATTR_CMN_FILEID
, .bits
= VATTR_BIT(va_fileid
), .size
= sizeof(uint64_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
524 {.attr
= ATTR_CMN_PARENTID
, .bits
= VATTR_BIT(va_parentid
), .size
= sizeof(uint64_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
525 {.attr
= ATTR_CMN_FULLPATH
, .bits
= 0, .size
= sizeof(struct attrreference
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
526 {.attr
= ATTR_CMN_ADDEDTIME
, .bits
= VATTR_BIT(va_addedtime
), .size
= ATTR_TIME_SIZE
, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
527 {.attr
= ATTR_CMN_RETURNED_ATTRS
, .bits
= 0, .size
= sizeof(attribute_set_t
), .action
= 0},
528 {.attr
= ATTR_CMN_ERROR
, .bits
= 0, .size
= sizeof(uint32_t), .action
= 0},
529 {.attr
= ATTR_CMN_DATA_PROTECT_FLAGS
, .bits
= VATTR_BIT(va_dataprotect_class
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
530 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
533 static struct getattrlist_attrtab getattrlist_dir_tab
[] = {
534 {.attr
= ATTR_DIR_LINKCOUNT
, .bits
= VATTR_BIT(va_dirlinkcount
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
535 {.attr
= ATTR_DIR_ENTRYCOUNT
, .bits
= VATTR_BIT(va_nchildren
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
536 {.attr
= ATTR_DIR_MOUNTSTATUS
, .bits
= 0, .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
537 {.attr
= ATTR_DIR_ALLOCSIZE
, .bits
= VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
538 {.attr
= ATTR_DIR_IOBLOCKSIZE
, .bits
= VATTR_BIT(va_iosize
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
539 {.attr
= ATTR_DIR_DATALENGTH
, .bits
= VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
540 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
542 static struct getattrlist_attrtab getattrlist_file_tab
[] = {
543 {.attr
= ATTR_FILE_LINKCOUNT
, .bits
= VATTR_BIT(va_nlink
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
544 {.attr
= ATTR_FILE_TOTALSIZE
, .bits
= VATTR_BIT(va_total_size
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
545 {.attr
= ATTR_FILE_ALLOCSIZE
, .bits
= VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
546 {.attr
= ATTR_FILE_IOBLOCKSIZE
, .bits
= VATTR_BIT(va_iosize
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
547 {.attr
= ATTR_FILE_CLUMPSIZE
, .bits
= 0, .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
548 {.attr
= ATTR_FILE_DEVTYPE
, .bits
= VATTR_BIT(va_rdev
), .size
= sizeof(dev_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
549 {.attr
= ATTR_FILE_DATALENGTH
, .bits
= VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
550 {.attr
= ATTR_FILE_DATAALLOCSIZE
, .bits
= VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_data_alloc
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
551 {.attr
= ATTR_FILE_RSRCLENGTH
, .bits
= 0, .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
552 {.attr
= ATTR_FILE_RSRCALLOCSIZE
, .bits
= 0, .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
553 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
556 //for forkattr bits repurposed as new common attributes
557 static struct getattrlist_attrtab getattrlist_common_extended_tab
[] = {
558 {.attr
= ATTR_CMNEXT_RELPATH
, .bits
= 0, .size
= sizeof(struct attrreference
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
559 {.attr
= ATTR_CMNEXT_PRIVATESIZE
, .bits
= VATTR_BIT(va_private_size
), .size
= sizeof(off_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
560 {.attr
= ATTR_CMNEXT_LINKID
, .bits
= VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), .size
= sizeof(uint64_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
561 {.attr
= ATTR_CMNEXT_NOFIRMLINKPATH
, .bits
= 0, .size
= sizeof(struct attrreference
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
562 {.attr
= ATTR_CMNEXT_REALDEVID
, .bits
= VATTR_BIT(va_devid
), .size
= sizeof(uint32_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
563 {.attr
= ATTR_CMNEXT_REALFSID
, .bits
= VATTR_BIT(va_fsid64
), .size
= sizeof(fsid_t
), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
564 {.attr
= ATTR_CMNEXT_CLONEID
, .bits
= VATTR_BIT(va_clone_id
), .size
= sizeof(uint64_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
565 {.attr
= ATTR_CMNEXT_EXT_FLAGS
, .bits
= VATTR_BIT(va_extflags
), .size
= sizeof(uint64_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
566 {.attr
= ATTR_CMNEXT_RECURSIVE_GENCOUNT
, .bits
= VATTR_BIT(va_recursive_gencount
), .size
= sizeof(uint64_t), .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
567 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
571 * This table is for attributes which are only set from the getattrlistbulk(2)
572 * call. These attributes have already been set from the common, file and
573 * directory tables but the vattr bits have not been recorded. Since these
574 * vattr bits are only used from the bulk call, we have a seperate table for
576 * The sizes are not returned from here since the sizes have already been
577 * accounted from the common, file and directory tables.
579 static struct getattrlist_attrtab getattrlistbulk_common_tab
[] = {
580 {.attr
= ATTR_CMN_DEVID
, .bits
= VATTR_BIT(va_devid
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
581 {.attr
= ATTR_CMN_FSID
, .bits
= VATTR_BIT(va_fsid64
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
582 {.attr
= ATTR_CMN_OBJTYPE
, .bits
= VATTR_BIT(va_objtype
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
583 {.attr
= ATTR_CMN_OBJTAG
, .bits
= VATTR_BIT(va_objtag
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
584 {.attr
= ATTR_CMN_USERACCESS
, .bits
= VATTR_BIT(va_user_access
), .size
= 0, .action
= 0},
585 {.attr
= ATTR_CMN_FNDRINFO
, .bits
= VATTR_BIT(va_finderinfo
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
586 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
589 static struct getattrlist_attrtab getattrlistbulk_file_tab
[] = {
590 {.attr
= ATTR_FILE_RSRCLENGTH
, .bits
= VATTR_BIT(va_rsrc_length
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
591 {.attr
= ATTR_FILE_RSRCALLOCSIZE
, .bits
= VATTR_BIT(va_rsrc_alloc
), .size
= 0, .action
= KAUTH_VNODE_READ_ATTRIBUTES
},
592 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
595 static struct getattrlist_attrtab getattrlistbulk_common_extended_tab
[] = {
596 /* getattrlist_parsetab() expects > 1 entries */
597 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0},
598 {.attr
= 0, .bits
= 0, .size
= 0, .action
= 0}
602 * The following are attributes that VFS can derive.
604 * A majority of them are the same attributes that are required for stat(2) and statfs(2).
606 #define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
607 ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | ATTR_VOL_QUOTA_SIZE | ATTR_VOL_RESERVED_SIZE | \
608 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
609 ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
610 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
611 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
612 ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
614 #define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
615 ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
616 ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
617 ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
618 ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
619 ATTR_CMN_FNDRINFO | \
620 ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
621 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
622 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
623 ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
624 ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
625 ATTR_CMN_DATA_PROTECT_FLAGS)
627 #define VFS_DFLT_ATTR_CMN_EXT (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID | \
628 ATTR_CMNEXT_NOFIRMLINKPATH | ATTR_CMNEXT_REALDEVID | \
629 ATTR_CMNEXT_REALFSID | ATTR_CMNEXT_CLONEID | \
630 ATTR_CMNEXT_EXT_FLAGS)
632 #define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
634 #define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
635 ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
636 ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
637 ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
638 ATTR_FILE_RSRCALLOCSIZE)
641 getattrlist_parsetab(struct getattrlist_attrtab
*tab
, attrgroup_t attrs
,
642 struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
,
643 int is_64bit
, unsigned int maxiter
)
645 attrgroup_t recognised
;
652 /* is this attribute set? */
653 if (tab
->attr
& attrs
) {
654 recognised
|= tab
->attr
;
656 vap
->va_active
|= tab
->bits
;
659 if (tab
->size
== ATTR_TIME_SIZE
) {
662 struct user64_timespec
);
665 struct user32_timespec
);
672 *actionp
|= tab
->action
;
674 if (attrs
== recognised
) {
675 break; /* all done, get out */
678 } while (((++tab
)->attr
!= 0) && (--maxiter
> 0));
680 /* check to make sure that we recognised all of the passed-in attributes */
681 if (attrs
& ~recognised
) {
688 * Given the attributes listed in alp, configure vap to request
689 * the data from a filesystem.
692 getattrlist_setupvattr(struct attrlist
*alp
, struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
, int isdir
, int use_fork
)
697 * Parse the above tables.
699 *sizep
= sizeof(uint32_t); /* length count */
701 if (alp
->commonattr
&&
702 (error
= getattrlist_parsetab(getattrlist_common_tab
, alp
->commonattr
, vap
, sizep
, actionp
, is_64bit
, sizeof(getattrlist_common_tab
) / sizeof(getattrlist_common_tab
[0]))) != 0) {
705 if (isdir
&& alp
->dirattr
&&
706 (error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
, vap
, sizep
, actionp
, is_64bit
, sizeof(getattrlist_dir_tab
) / sizeof(getattrlist_dir_tab
[0]))) != 0) {
709 if (!isdir
&& alp
->fileattr
&&
710 (error
= getattrlist_parsetab(getattrlist_file_tab
, alp
->fileattr
, vap
, sizep
, actionp
, is_64bit
, sizeof(getattrlist_file_tab
) / sizeof(getattrlist_file_tab
[0]))) != 0) {
713 if (use_fork
&& alp
->forkattr
&&
714 (error
= getattrlist_parsetab(getattrlist_common_extended_tab
, alp
->forkattr
, vap
, sizep
, actionp
, is_64bit
, sizeof(getattrlist_common_extended_tab
) / sizeof(getattrlist_common_extended_tab
[0]))) != 0) {
722 * Given the attributes listed in alp, configure vap to request
723 * the data from a filesystem.
726 getattrlist_setupvattr_all(struct attrlist
*alp
, struct vnode_attr
*vap
,
727 enum vtype obj_type
, ssize_t
*fixedsize
, int is_64bit
, int use_fork
)
732 * Parse the above tables.
735 *fixedsize
= sizeof(uint32_t);
737 if (alp
->commonattr
) {
738 error
= getattrlist_parsetab(getattrlist_common_tab
,
739 alp
->commonattr
, vap
, fixedsize
, NULL
, is_64bit
,
740 sizeof(getattrlist_common_tab
) / sizeof(getattrlist_common_tab
[0]));
743 /* Ignore any errrors from the bulk table */
744 (void)getattrlist_parsetab(getattrlistbulk_common_tab
,
745 alp
->commonattr
, vap
, fixedsize
, NULL
, is_64bit
,
746 sizeof(getattrlistbulk_common_tab
) / sizeof(getattrlistbulk_common_tab
[0]));
750 if (!error
&& (obj_type
== VNON
|| obj_type
== VDIR
) && alp
->dirattr
) {
751 error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
,
752 vap
, fixedsize
, NULL
, is_64bit
,
753 sizeof(getattrlist_dir_tab
) / sizeof(getattrlist_dir_tab
[0]));
756 if (!error
&& (obj_type
!= VDIR
) && alp
->fileattr
) {
757 error
= getattrlist_parsetab(getattrlist_file_tab
,
758 alp
->fileattr
, vap
, fixedsize
, NULL
, is_64bit
,
759 sizeof(getattrlist_file_tab
) / sizeof(getattrlist_file_tab
[0]));
762 /*Ignore any errors from the bulk table */
763 (void)getattrlist_parsetab(getattrlistbulk_file_tab
,
764 alp
->fileattr
, vap
, fixedsize
, NULL
, is_64bit
,
765 sizeof(getattrlistbulk_file_tab
) / sizeof(getattrlistbulk_file_tab
[0]));
769 /* fork attributes are like extended common attributes if enabled*/
770 if (!error
&& use_fork
&& alp
->forkattr
) {
771 error
= getattrlist_parsetab(getattrlist_common_extended_tab
,
772 alp
->forkattr
, vap
, fixedsize
, NULL
, is_64bit
,
773 sizeof(getattrlist_common_extended_tab
) / sizeof(getattrlist_common_extended_tab
[0]));
776 (void)getattrlist_parsetab(getattrlistbulk_common_extended_tab
,
777 alp
->forkattr
, vap
, fixedsize
, NULL
, is_64bit
,
778 sizeof(getattrlistbulk_common_extended_tab
) / sizeof(getattrlistbulk_common_extended_tab
[0]));
786 vfs_setup_vattr_from_attrlist(struct attrlist
*alp
, struct vnode_attr
*vap
,
787 enum vtype obj_vtype
, ssize_t
*attrs_fixed_sizep
, vfs_context_t ctx
)
791 // the caller passes us no options, we assume the caller wants the new fork
792 // attr behavior, hence the hardcoded 1
793 return getattrlist_setupvattr_all(alp
, vap
, obj_vtype
,
794 attrs_fixed_sizep
, IS_64BIT_PROCESS(vfs_context_proc(ctx
)), 1);
801 * Given the attributes listed in asp and those supported
802 * in the vap, fixup the asp attributes to reflect any
803 * missing attributes from the file system
806 getattrlist_fixupattrs(attribute_set_t
*asp
, struct vnode_attr
*vap
, int use_fork
)
808 struct getattrlist_attrtab
*tab
;
810 if (asp
->commonattr
) {
811 tab
= getattrlist_common_tab
;
814 * This if() statement is slightly confusing. We're trying to
815 * iterate through all of the bits listed in the array
816 * getattr_common_tab, and see if the filesystem was expected
817 * to support it, and whether or not we need to do anything about this.
819 * This array is full of structs that have 4 fields (attr, bits, size, action).
820 * The first is used to store the ATTR_CMN_* bit that was being requested
821 * from userland. The second stores the VATTR_BIT corresponding to the field
822 * filled in vnode_attr struct. If it is 0, then we don't typically expect
823 * the filesystem to fill in this field. The third is the size of the field,
824 * and the fourth is the type of kauth actions needed.
826 * So, for all of the ATTR_CMN bits listed in this array, we iterate through
827 * them, and check to see if it was both passed down to the filesystem via the
828 * va_active bitfield, and whether or not we expect it to be emitted from
829 * the filesystem. If it wasn't supported, then we un-twiddle the bit and move
830 * on. This is done so that we can uncheck those bits and re-request
831 * a vnode_getattr from the filesystem again.
833 if ((tab
->attr
& asp
->commonattr
) &&
834 (tab
->bits
& vap
->va_active
) &&
835 (tab
->bits
& vap
->va_supported
) == 0) {
836 asp
->commonattr
&= ~tab
->attr
;
838 } while ((++tab
)->attr
!= 0);
841 tab
= getattrlist_dir_tab
;
843 if ((tab
->attr
& asp
->dirattr
) &&
844 (tab
->bits
& vap
->va_active
) &&
845 (vap
->va_supported
& tab
->bits
) == 0) {
846 asp
->dirattr
&= ~tab
->attr
;
848 } while ((++tab
)->attr
!= 0);
851 tab
= getattrlist_file_tab
;
853 if ((tab
->attr
& asp
->fileattr
) &&
854 (tab
->bits
& vap
->va_active
) &&
855 (vap
->va_supported
& tab
->bits
) == 0) {
856 asp
->fileattr
&= ~tab
->attr
;
858 } while ((++tab
)->attr
!= 0);
860 if (use_fork
&& asp
->forkattr
) {
861 tab
= getattrlist_common_extended_tab
;
863 if ((tab
->attr
& asp
->forkattr
) &&
864 (tab
->bits
& vap
->va_active
) &&
865 (vap
->va_supported
& tab
->bits
) == 0) {
866 asp
->forkattr
&= ~tab
->attr
;
868 } while ((++tab
)->attr
!= 0);
873 setattrlist_setfinderinfo(vnode_t vp
, char *fndrinfo
, struct vfs_context
*ctx
)
876 char uio_buf
[UIO_SIZEOF(1)];
879 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
882 uio_addiov(auio
, CAST_USER_ADDR_T(fndrinfo
), 32);
883 error
= vn_setxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, XATTR_NOSECURITY
, ctx
);
888 if (error
== 0 && need_fsevent(FSE_FINDER_INFO_CHANGED
, vp
)) {
889 add_fsevent(FSE_FINDER_INFO_CHANGED
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
897 * Find something resembling a terminal component name in the mountedonname for vp
901 getattrlist_findnamecomp(const char *mn
, const char **np
, ssize_t
*nl
)
907 * We're looking for the last sequence of non / characters, but
908 * not including any trailing / characters.
913 for (cp
= mn
; *cp
!= 0; cp
++) {
915 /* start of run of chars */
921 /* end of run of chars */
928 /* need to close run? */
936 getvolattrlist(vfs_context_t ctx
, vnode_t vp
, struct attrlist
*alp
,
937 user_addr_t attributeBuffer
, size_t bufferSize
, uint64_t options
,
938 enum uio_seg segflg
, int is_64bit
)
941 struct vnode_attr va
;
942 struct _attrlist_buf ab
;
944 ssize_t fixedsize
, varsize
;
945 const char *cnp
= NULL
; /* protected by ATTR_CMN_NAME */
946 ssize_t cnl
= 0; /* protected by ATTR_CMN_NAME */
951 vnode_t root_vp
= NULL
;
956 vs
.f_vol_name
= NULL
;
960 /* Check for special packing semantics */
961 return_valid
= (alp
->commonattr
& ATTR_CMN_RETURNED_ATTRS
);
962 pack_invalid
= (options
& FSOPT_PACK_INVAL_ATTRS
);
964 /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
969 /* Keep invalid attrs from being uninitialized */
970 bzero(&vs
, sizeof(vs
));
971 /* Generate a valid mask for post processing */
972 bcopy(&alp
->commonattr
, &ab
.valid
, sizeof(attribute_set_t
));
975 /* If we do not have root vnode, look it up and substitute it in */
976 if (!vnode_isvroot(vp
)) {
978 error
= VFS_ROOT(mnt
, &root_vp
, ctx
);
980 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but got an error getting root.");
986 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but no backpointer to mount.");
992 * Set up the vfs_attr structure and call the filesystem.
994 if ((error
= getvolattrlist_setupvfsattr(alp
, &vs
, &fixedsize
, is_64bit
)) != 0) {
995 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
998 if (vs
.f_active
!= 0) {
999 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
1000 if (VFSATTR_IS_ACTIVE(&vs
, f_vol_name
)) {
1001 vs
.f_vol_name
= (char *) zalloc(ZV_NAMEI
);
1002 vs
.f_vol_name
[0] = '\0';
1005 VFS_DEBUG(ctx
, vp
, "ATTRLIST - calling to get %016llx with supported %016llx", vs
.f_active
, vs
.f_supported
);
1006 if ((error
= vfs_getattr(mnt
, &vs
, ctx
)) != 0) {
1007 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1011 error
= mac_mount_check_getattr(ctx
, mnt
, &vs
);
1013 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: MAC framework returned %d", error
);
1018 * Did we ask for something the filesystem doesn't support?
1020 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
1021 /* default value for volume subtype */
1022 if (VFSATTR_IS_ACTIVE(&vs
, f_fssubtype
)
1023 && !VFSATTR_IS_SUPPORTED(&vs
, f_fssubtype
)) {
1024 VFSATTR_RETURN(&vs
, f_fssubtype
, 0);
1028 * If the file system didn't supply f_signature, then
1029 * default it to 'BD', which is the generic signature
1030 * that most Carbon file systems should return.
1032 if (VFSATTR_IS_ACTIVE(&vs
, f_signature
)
1033 && !VFSATTR_IS_SUPPORTED(&vs
, f_signature
)) {
1034 VFSATTR_RETURN(&vs
, f_signature
, 0x4244);
1037 /* default for block size */
1038 if (VFSATTR_IS_ACTIVE(&vs
, f_bsize
)
1039 && !VFSATTR_IS_SUPPORTED(&vs
, f_bsize
)) {
1040 VFSATTR_RETURN(&vs
, f_bsize
, mnt
->mnt_devblocksize
);
1043 /* default value for volume f_attributes */
1044 if (VFSATTR_IS_ACTIVE(&vs
, f_attributes
)
1045 && !VFSATTR_IS_SUPPORTED(&vs
, f_attributes
)) {
1046 vol_attributes_attr_t
*attrp
= &vs
.f_attributes
;
1048 attrp
->validattr
.commonattr
= VFS_DFLT_ATTR_CMN
;
1049 attrp
->validattr
.volattr
= VFS_DFLT_ATTR_VOL
;
1050 attrp
->validattr
.dirattr
= VFS_DFLT_ATTR_DIR
;
1051 attrp
->validattr
.fileattr
= VFS_DFLT_ATTR_FILE
;
1052 attrp
->validattr
.forkattr
= VFS_DFLT_ATTR_CMN_EXT
;
1054 attrp
->nativeattr
.commonattr
= 0;
1055 attrp
->nativeattr
.volattr
= 0;
1056 attrp
->nativeattr
.dirattr
= 0;
1057 attrp
->nativeattr
.fileattr
= 0;
1058 attrp
->nativeattr
.forkattr
= 0;
1059 VFSATTR_SET_SUPPORTED(&vs
, f_attributes
);
1062 /* default value for volume f_capabilities */
1063 if (VFSATTR_IS_ACTIVE(&vs
, f_capabilities
)) {
1064 /* getattrlist is always supported now. */
1065 if (!VFSATTR_IS_SUPPORTED(&vs
, f_capabilities
)) {
1066 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] = 0;
1067 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] = VOL_CAP_INT_ATTRLIST
;
1068 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_RESERVED1
] = 0;
1069 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_RESERVED2
] = 0;
1071 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] = 0;
1072 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] = VOL_CAP_INT_ATTRLIST
;
1073 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_RESERVED1
] = 0;
1074 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_RESERVED2
] = 0;
1075 VFSATTR_SET_SUPPORTED(&vs
, f_capabilities
);
1077 /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
1078 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_ATTRLIST
;
1079 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_ATTRLIST
;
1083 /* check to see if our fixups were enough */
1084 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
1087 /* Fix up valid mask for post processing */
1088 getvolattrlist_fixupattrs(&ab
.valid
, &vs
);
1090 /* Force packing of everything asked for */
1091 vs
.f_supported
= vs
.f_active
;
1093 /* Adjust the requested attributes */
1094 getvolattrlist_fixupattrs((attribute_set_t
*)&alp
->commonattr
, &vs
);
1105 * Some fields require data from the root vp
1107 if (alp
->commonattr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
| ATTR_CMN_ACCESSMASK
| ATTR_CMN_FLAGS
| ATTR_CMN_SCRIPT
)) {
1108 VATTR_WANTED(&va
, va_uid
);
1109 VATTR_WANTED(&va
, va_gid
);
1110 VATTR_WANTED(&va
, va_mode
);
1111 VATTR_WANTED(&va
, va_flags
);
1112 VATTR_WANTED(&va
, va_encoding
);
1114 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
1115 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp
);
1119 error
= mac_vnode_check_getattr(ctx
, NOCRED
, vp
, &va
);
1121 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: MAC framework returned %d for root vnode", error
);
1125 if (VATTR_IS_ACTIVE(&va
, va_encoding
) &&
1126 !VATTR_IS_SUPPORTED(&va
, va_encoding
)) {
1127 if (!return_valid
|| pack_invalid
) {
1128 /* use kTextEncodingMacUnicode */
1129 VATTR_RETURN(&va
, va_encoding
, 0x7e);
1131 /* don't use a default */
1132 alp
->commonattr
&= ~ATTR_CMN_SCRIPT
;
1138 * Compute variable-size buffer requirements.
1141 if (alp
->commonattr
& ATTR_CMN_NAME
) {
1142 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
1143 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
1144 /* special case for boot volume. Use root name when it's
1145 * available (which is the volume name) or just the mount on
1146 * name of "/". we must do this for binary compatibility with
1147 * pre Tiger code. returning nothing for the boot volume name
1148 * breaks installers - 3961058
1150 cnp
= vnode_getname(vp
);
1152 /* just use "/" as name */
1153 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
1159 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
1161 if (alp
->commonattr
& ATTR_CMN_NAME
) {
1162 varsize
+= roundup(cnl
+ 1, 4);
1165 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
) {
1166 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntonname
) + 1, 4);
1168 if (alp
->volattr
& ATTR_VOL_NAME
) {
1169 vs
.f_vol_name
[MAXPATHLEN
- 1] = '\0'; /* Ensure nul-termination */
1170 varsize
+= roundup(strlen(vs
.f_vol_name
) + 1, 4);
1172 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
) {
1173 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntfromname
) + 1, 4);
1177 * Allocate a target buffer for attribute results.
1178 * Note that since we won't ever copy out more than the caller requested,
1179 * we never need to allocate more than they offer.
1181 ab
.allocated
= fixedsize
+ varsize
;
1182 if (((size_t)ab
.allocated
) > ATTR_MAX_BUFFER
) {
1184 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
1189 (ab
.allocated
< (ssize_t
)(sizeof(uint32_t) + sizeof(attribute_set_t
))) &&
1190 !(options
& FSOPT_REPORT_FULLSIZE
)) {
1191 uint32_t num_bytes_valid
= sizeof(uint32_t);
1193 * Not enough to return anything and we don't have to report
1194 * how much space is needed. Get out now.
1195 * N.B. - We have only been called after having verified that
1196 * attributeBuffer is at least sizeof(uint32_t);
1198 if (UIO_SEG_IS_USER_SPACE(segflg
)) {
1199 error
= copyout(&num_bytes_valid
,
1200 CAST_USER_ADDR_T(attributeBuffer
), num_bytes_valid
);
1202 bcopy(&num_bytes_valid
, (void *)attributeBuffer
,
1203 (size_t)num_bytes_valid
);
1208 ab
.base
= kheap_alloc(KHEAP_TEMP
, ab
.allocated
, Z_ZERO
| Z_WAITOK
);
1209 if (ab
.base
== NULL
) {
1211 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
1216 * Pack results into the destination buffer.
1218 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
1220 ab
.fixedcursor
+= sizeof(attribute_set_t
);
1221 bzero(&ab
.actual
, sizeof(ab
.actual
));
1223 ab
.varcursor
= ab
.base
+ fixedsize
;
1224 ab
.needed
= fixedsize
+ varsize
;
1226 /* common attributes **************************************************/
1227 if (alp
->commonattr
& ATTR_CMN_ERROR
) {
1229 ab
.actual
.commonattr
|= ATTR_CMN_ERROR
;
1231 if (alp
->commonattr
& ATTR_CMN_NAME
) {
1232 attrlist_pack_string(&ab
, cnp
, cnl
);
1233 ab
.actual
.commonattr
|= ATTR_CMN_NAME
;
1235 if (alp
->commonattr
& ATTR_CMN_DEVID
) {
1236 ATTR_PACK4(ab
, mnt
->mnt_vfsstat
.f_fsid
.val
[0]);
1237 ab
.actual
.commonattr
|= ATTR_CMN_DEVID
;
1239 if (alp
->commonattr
& ATTR_CMN_FSID
) {
1240 ATTR_PACK8(ab
, mnt
->mnt_vfsstat
.f_fsid
);
1241 ab
.actual
.commonattr
|= ATTR_CMN_FSID
;
1243 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
) {
1244 if (!return_valid
|| pack_invalid
) {
1248 if (alp
->commonattr
& ATTR_CMN_OBJTAG
) {
1249 ATTR_PACK4(ab
, vp
->v_tag
);
1250 ab
.actual
.commonattr
|= ATTR_CMN_OBJTAG
;
1252 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
1253 if (!return_valid
|| pack_invalid
) {
1254 fsobj_id_t f
= {0, 0};
1258 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1259 if (!return_valid
|| pack_invalid
) {
1260 fsobj_id_t f
= {0, 0};
1264 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
1265 if (!return_valid
|| pack_invalid
) {
1266 fsobj_id_t f
= {0, 0};
1270 /* note that this returns the encoding for the volume name, not the node name */
1271 if (alp
->commonattr
& ATTR_CMN_SCRIPT
) {
1272 ATTR_PACK4(ab
, va
.va_encoding
);
1273 ab
.actual
.commonattr
|= ATTR_CMN_SCRIPT
;
1275 if (alp
->commonattr
& ATTR_CMN_CRTIME
) {
1276 ATTR_PACK_TIME(ab
, vs
.f_create_time
, is_64bit
);
1277 ab
.actual
.commonattr
|= ATTR_CMN_CRTIME
;
1279 if (alp
->commonattr
& ATTR_CMN_MODTIME
) {
1280 ATTR_PACK_TIME(ab
, vs
.f_modify_time
, is_64bit
);
1281 ab
.actual
.commonattr
|= ATTR_CMN_MODTIME
;
1283 if (alp
->commonattr
& ATTR_CMN_CHGTIME
) {
1284 if (!return_valid
|| pack_invalid
) {
1285 ATTR_PACK_TIME(ab
, vs
.f_modify_time
, is_64bit
);
1288 if (alp
->commonattr
& ATTR_CMN_ACCTIME
) {
1289 ATTR_PACK_TIME(ab
, vs
.f_access_time
, is_64bit
);
1290 ab
.actual
.commonattr
|= ATTR_CMN_ACCTIME
;
1292 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
) {
1293 ATTR_PACK_TIME(ab
, vs
.f_backup_time
, is_64bit
);
1294 ab
.actual
.commonattr
|= ATTR_CMN_BKUPTIME
;
1296 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
1299 * This attribute isn't really Finder Info, at least for HFS.
1301 if (vp
->v_tag
== VT_HFS
) {
1302 #define HFS_GET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00004)
1303 error
= VNOP_IOCTL(vp
, HFS_GET_BOOT_INFO
, (caddr_t
)&f
, 0, ctx
);
1305 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
1306 ab
.actual
.commonattr
|= ATTR_CMN_FNDRINFO
;
1307 } else if (!return_valid
) {
1310 } else if (!return_valid
|| pack_invalid
) {
1311 /* XXX we could at least pass out the volume UUID here */
1312 bzero(&f
, sizeof(f
));
1313 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
1316 if (alp
->commonattr
& ATTR_CMN_OWNERID
) {
1317 ATTR_PACK4(ab
, va
.va_uid
);
1318 ab
.actual
.commonattr
|= ATTR_CMN_OWNERID
;
1320 if (alp
->commonattr
& ATTR_CMN_GRPID
) {
1321 ATTR_PACK4(ab
, va
.va_gid
);
1322 ab
.actual
.commonattr
|= ATTR_CMN_GRPID
;
1324 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
) {
1325 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
1326 ab
.actual
.commonattr
|= ATTR_CMN_ACCESSMASK
;
1328 if (alp
->commonattr
& ATTR_CMN_FLAGS
) {
1329 ATTR_PACK4(ab
, va
.va_flags
);
1330 ab
.actual
.commonattr
|= ATTR_CMN_FLAGS
;
1332 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* XXX this is expensive and also duplicate work */
1334 if (vnode_isdir(vp
)) {
1335 if (vnode_authorize(vp
, NULL
,
1336 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0) {
1339 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0) {
1342 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0) {
1346 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0) {
1349 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0) {
1352 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0) {
1358 * Rather than MAC preceding DAC, in this case we want
1359 * the smallest set of permissions granted by both MAC & DAC
1360 * checks. We won't add back any permissions.
1363 if (mac_vnode_check_access(ctx
, vp
, W_OK
) != 0) {
1368 if (mac_vnode_check_access(ctx
, vp
, R_OK
) != 0) {
1373 if (mac_vnode_check_access(ctx
, vp
, X_OK
) != 0) {
1378 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms
);
1379 ATTR_PACK4(ab
, perms
);
1380 ab
.actual
.commonattr
|= ATTR_CMN_USERACCESS
;
1383 * The following common volume attributes are only
1384 * packed when the pack_invalid mode is enabled.
1389 if (alp
->commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1390 attrlist_pack_variable(&ab
, NULL
, 0);
1392 if (alp
->commonattr
& ATTR_CMN_UUID
) {
1393 ATTR_PACK(&ab
, kauth_null_guid
);
1395 if (alp
->commonattr
& ATTR_CMN_GRPUUID
) {
1396 ATTR_PACK(&ab
, kauth_null_guid
);
1398 if (alp
->commonattr
& ATTR_CMN_FILEID
) {
1399 ATTR_PACK8(ab
, fid
);
1401 if (alp
->commonattr
& ATTR_CMN_PARENTID
) {
1402 ATTR_PACK8(ab
, fid
);
1406 /* volume attributes **************************************************/
1408 if (alp
->volattr
& ATTR_VOL_FSTYPE
) {
1409 ATTR_PACK_CAST(&ab
, uint32_t, vfs_typenum(mnt
));
1410 ab
.actual
.volattr
|= ATTR_VOL_FSTYPE
;
1412 if (alp
->volattr
& ATTR_VOL_SIGNATURE
) {
1413 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_signature
);
1414 ab
.actual
.volattr
|= ATTR_VOL_SIGNATURE
;
1416 if (alp
->volattr
& ATTR_VOL_SIZE
) {
1417 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_blocks
);
1418 ab
.actual
.volattr
|= ATTR_VOL_SIZE
;
1420 if (alp
->volattr
& ATTR_VOL_SPACEFREE
) {
1421 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bfree
);
1422 ab
.actual
.volattr
|= ATTR_VOL_SPACEFREE
;
1424 if (alp
->volattr
& ATTR_VOL_SPACEAVAIL
) {
1425 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bavail
);
1426 ab
.actual
.volattr
|= ATTR_VOL_SPACEAVAIL
;
1428 if (alp
->volattr
& ATTR_VOL_MINALLOCATION
) {
1429 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
);
1430 ab
.actual
.volattr
|= ATTR_VOL_MINALLOCATION
;
1432 if (alp
->volattr
& ATTR_VOL_ALLOCATIONCLUMP
) {
1433 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
); /* not strictly true */
1434 ab
.actual
.volattr
|= ATTR_VOL_ALLOCATIONCLUMP
;
1436 if (alp
->volattr
& ATTR_VOL_IOBLOCKSIZE
) {
1437 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_iosize
);
1438 ab
.actual
.volattr
|= ATTR_VOL_IOBLOCKSIZE
;
1440 if (alp
->volattr
& ATTR_VOL_OBJCOUNT
) {
1441 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_objcount
);
1442 ab
.actual
.volattr
|= ATTR_VOL_OBJCOUNT
;
1444 if (alp
->volattr
& ATTR_VOL_FILECOUNT
) {
1445 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_filecount
);
1446 ab
.actual
.volattr
|= ATTR_VOL_FILECOUNT
;
1448 if (alp
->volattr
& ATTR_VOL_DIRCOUNT
) {
1449 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_dircount
);
1450 ab
.actual
.volattr
|= ATTR_VOL_DIRCOUNT
;
1452 if (alp
->volattr
& ATTR_VOL_MAXOBJCOUNT
) {
1453 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_maxobjcount
);
1454 ab
.actual
.volattr
|= ATTR_VOL_MAXOBJCOUNT
;
1456 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
) {
1457 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntonname
, 0);
1458 ab
.actual
.volattr
|= ATTR_VOL_MOUNTPOINT
;
1460 if (alp
->volattr
& ATTR_VOL_NAME
) {
1461 attrlist_pack_string(&ab
, vs
.f_vol_name
, 0);
1462 ab
.actual
.volattr
|= ATTR_VOL_NAME
;
1464 if (alp
->volattr
& ATTR_VOL_MOUNTFLAGS
) {
1465 ATTR_PACK_CAST(&ab
, uint32_t, mnt
->mnt_flag
);
1466 ab
.actual
.volattr
|= ATTR_VOL_MOUNTFLAGS
;
1468 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
) {
1469 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntfromname
, 0);
1470 ab
.actual
.volattr
|= ATTR_VOL_MOUNTEDDEVICE
;
1472 if (alp
->volattr
& ATTR_VOL_ENCODINGSUSED
) {
1473 if (!return_valid
|| pack_invalid
) {
1474 ATTR_PACK_CAST(&ab
, uint64_t, ~0LL); /* return all encodings */
1477 if (alp
->volattr
& ATTR_VOL_CAPABILITIES
) {
1478 /* fix up volume capabilities */
1479 if (vfs_extendedsecurity(mnt
)) {
1480 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
1482 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] &= ~VOL_CAP_INT_EXTENDED_SECURITY
;
1484 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
1487 * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES
1488 * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported
1490 if (!(vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] & VOL_CAP_FMT_NO_IMMUTABLE_FILES
)) {
1491 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES
;
1492 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES
;
1495 if (!(vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] & VOL_CAP_FMT_NO_PERMISSIONS
)) {
1496 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] &= ~VOL_CAP_FMT_NO_PERMISSIONS
;
1497 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] |= VOL_CAP_FMT_NO_PERMISSIONS
;
1501 * ATTR_CMN_USERACCESS attribute was previously set by file-system drivers, thus volume capabilitiy
1502 * VOL_CAP_INT_USERACCESS was conditionally enabled. ATTR_CMN_USERACCESS is now set inside VFS,
1503 * regardless of underlying volume type thus we always set VOL_CAP_INT_USERACCESS.
1505 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_USERACCESS
;
1506 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_USERACCESS
;
1508 ATTR_PACK(&ab
, vs
.f_capabilities
);
1509 ab
.actual
.volattr
|= ATTR_VOL_CAPABILITIES
;
1511 if (alp
->volattr
& ATTR_VOL_UUID
) {
1512 ATTR_PACK(&ab
, vs
.f_uuid
);
1513 ab
.actual
.volattr
|= ATTR_VOL_UUID
;
1515 if (alp
->volattr
& ATTR_VOL_QUOTA_SIZE
) {
1516 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_quota
);
1517 ab
.actual
.volattr
|= ATTR_VOL_QUOTA_SIZE
;
1519 if (alp
->volattr
& ATTR_VOL_RESERVED_SIZE
) {
1520 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_reserved
);
1521 ab
.actual
.volattr
|= ATTR_VOL_RESERVED_SIZE
;
1523 if (alp
->volattr
& ATTR_VOL_ATTRIBUTES
) {
1524 /* fix up volume attribute information */
1526 vs
.f_attributes
.validattr
.commonattr
|= VFS_DFLT_ATTR_CMN
;
1527 vs
.f_attributes
.validattr
.volattr
|= VFS_DFLT_ATTR_VOL
;
1528 vs
.f_attributes
.validattr
.dirattr
|= VFS_DFLT_ATTR_DIR
;
1529 vs
.f_attributes
.validattr
.fileattr
|= VFS_DFLT_ATTR_FILE
;
1531 if (vfs_extendedsecurity(mnt
)) {
1532 vs
.f_attributes
.validattr
.commonattr
|= (ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
1534 vs
.f_attributes
.validattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
1535 vs
.f_attributes
.nativeattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
1537 ATTR_PACK(&ab
, vs
.f_attributes
);
1538 ab
.actual
.volattr
|= ATTR_VOL_ATTRIBUTES
;
1542 if (!return_valid
&& (ab
.fixedcursor
- ab
.base
) != fixedsize
) {
1543 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
1544 fixedsize
, (long) (ab
.fixedcursor
- ab
.base
), alp
->commonattr
, alp
->volattr
);
1546 if (!return_valid
&& ab
.varcursor
!= (ab
.base
+ ab
.needed
)) {
1547 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab
.varcursor
- ab
.base
), ab
.needed
);
1551 * In the compatible case, we report the smaller of the required and returned sizes.
1552 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1553 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1554 * they gave us, so they can always check for truncation themselves.
1556 *(uint32_t *)ab
.base
= (options
& FSOPT_REPORT_FULLSIZE
) ? (uint32_t)ab
.needed
: (uint32_t)lmin(bufferSize
, ab
.needed
);
1558 /* Return attribute set output if requested. */
1560 (ab
.allocated
>= (ssize_t
)(sizeof(uint32_t) + sizeof(ab
.actual
)))) {
1561 ab
.actual
.commonattr
|= ATTR_CMN_RETURNED_ATTRS
;
1563 /* Only report the attributes that are valid */
1564 ab
.actual
.commonattr
&= ab
.valid
.commonattr
;
1565 ab
.actual
.volattr
&= ab
.valid
.volattr
;
1567 bcopy(&ab
.actual
, ab
.base
+ sizeof(uint32_t), sizeof(ab
.actual
));
1570 if (UIO_SEG_IS_USER_SPACE(segflg
)) {
1571 error
= copyout(ab
.base
, CAST_USER_ADDR_T(attributeBuffer
),
1572 ulmin((uint32_t)bufferSize
, (uint32_t)ab
.needed
));
1574 bcopy(ab
.base
, (void *)attributeBuffer
, (size_t)ulmin((uint32_t)bufferSize
, (uint32_t)ab
.needed
));
1578 if (vs
.f_vol_name
!= NULL
) {
1579 zfree(ZV_NAMEI
, vs
.f_vol_name
);
1584 kheap_free(KHEAP_TEMP
, ab
.base
, ab
.allocated
);
1585 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
1587 if (root_vp
!= NULL
) {
1594 * Pack ATTR_COMMON attributes into a user buffer.
1595 * alp is a pointer to the bitmap of attributes required.
1596 * abp is the state of the attribute filling operation.
1597 * The attribute data (along with some other fields that are required
1601 attr_pack_common(vfs_context_t ctx
, mount_t mp
, vnode_t vp
, struct attrlist
*alp
,
1602 struct _attrlist_buf
*abp
, struct vnode_attr
*vap
, int proc_is64
,
1603 const char *cnp
, ssize_t cnl
, const char *fullpathptr
,
1604 ssize_t fullpathlen
, int return_valid
, int pack_invalid
, int vtype
,
1610 if ((alp
->commonattr
& ATTR_CMN_ERROR
) &&
1611 (!return_valid
|| pack_invalid
)) {
1612 ATTR_PACK4((*abp
), 0);
1613 abp
->actual
.commonattr
|= ATTR_CMN_ERROR
;
1615 if (alp
->commonattr
& ATTR_CMN_NAME
) {
1616 attrlist_pack_string(abp
, cnp
, cnl
);
1617 abp
->actual
.commonattr
|= ATTR_CMN_NAME
;
1619 if (alp
->commonattr
& ATTR_CMN_DEVID
) {
1620 if (mp
) { /* caller needs real devid */
1622 mp
->mnt_vfsstat
.f_fsid
.val
[0]);
1623 abp
->actual
.commonattr
|= ATTR_CMN_DEVID
;
1624 } else if (VATTR_IS_ACTIVE(vap
, va_fsid
) && VATTR_IS_SUPPORTED(vap
, va_fsid
)) {
1625 ATTR_PACK4((*abp
), vap
->va_fsid
);
1626 abp
->actual
.commonattr
|= ATTR_CMN_DEVID
;
1629 vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
1630 abp
->actual
.commonattr
|= ATTR_CMN_DEVID
;
1631 } else if (VATTR_IS_SUPPORTED(vap
, va_devid
)) {
1632 ATTR_PACK4((*abp
), vap
->va_devid
);
1633 abp
->actual
.commonattr
|= ATTR_CMN_DEVID
;
1634 } else if (!return_valid
|| pack_invalid
) {
1635 ATTR_PACK4((*abp
), 0);
1638 if (alp
->commonattr
& ATTR_CMN_FSID
) {
1639 if (mp
) { /* caller needs real fsid */
1641 mp
->mnt_vfsstat
.f_fsid
);
1642 abp
->actual
.commonattr
|= ATTR_CMN_FSID
;
1643 } else if (VATTR_IS_SUPPORTED(vap
, va_fsid64
)) {
1644 ATTR_PACK8((*abp
), vap
->va_fsid64
);
1645 abp
->actual
.commonattr
|= ATTR_CMN_FSID
;
1648 vp
->v_mount
->mnt_vfsstat
.f_fsid
);
1649 abp
->actual
.commonattr
|= ATTR_CMN_FSID
;
1650 } else if (!return_valid
|| pack_invalid
) {
1651 fsid_t fsid
= {{0}};
1652 ATTR_PACK8((*abp
), fsid
);
1655 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
) {
1657 ATTR_PACK4((*abp
), vtype
);
1658 abp
->actual
.commonattr
|= ATTR_CMN_OBJTYPE
;
1659 } else if (VATTR_IS_SUPPORTED(vap
, va_objtype
)) {
1660 ATTR_PACK4((*abp
), vap
->va_objtype
);
1661 abp
->actual
.commonattr
|= ATTR_CMN_OBJTYPE
;
1662 } else if (!return_valid
|| pack_invalid
) {
1663 ATTR_PACK4((*abp
), 0);
1666 if (alp
->commonattr
& ATTR_CMN_OBJTAG
) {
1668 ATTR_PACK4((*abp
), vp
->v_tag
);
1669 abp
->actual
.commonattr
|= ATTR_CMN_OBJTAG
;
1670 } else if (VATTR_IS_SUPPORTED(vap
, va_objtag
)) {
1671 ATTR_PACK4((*abp
), vap
->va_objtag
);
1672 abp
->actual
.commonattr
|= ATTR_CMN_OBJTAG
;
1673 } else if (!return_valid
|| pack_invalid
) {
1674 ATTR_PACK4((*abp
), 0);
1677 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
1679 * Carbon can't deal with us reporting the target ID
1680 * for links. So we ask the filesystem to give us the
1681 * source ID as well, and if it gives us one, we use
1684 if (vap
->va_vaflags
& VA_64BITOBJIDS
) {
1685 if (VATTR_IS_SUPPORTED(vap
, va_linkid
)) {
1686 ATTR_PACK8((*abp
), vap
->va_linkid
);
1688 ATTR_PACK8((*abp
), vap
->va_fileid
);
1692 if (VATTR_IS_SUPPORTED(vap
, va_linkid
)) {
1693 f
.fid_objno
= (uint32_t)vap
->va_linkid
;
1695 f
.fid_objno
= (uint32_t)vap
->va_fileid
;
1697 f
.fid_generation
= 0;
1698 ATTR_PACK8((*abp
), f
);
1700 abp
->actual
.commonattr
|= ATTR_CMN_OBJID
;
1702 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1704 * Carbon can't deal with us reporting the target ID
1705 * for links. So we ask the filesystem to give us the
1706 * source ID as well, and if it gives us one, we use
1709 if (vap
->va_vaflags
& VA_64BITOBJIDS
) {
1710 if (VATTR_IS_SUPPORTED(vap
, va_linkid
)) {
1711 ATTR_PACK8((*abp
), vap
->va_linkid
);
1713 ATTR_PACK8((*abp
), vap
->va_fileid
);
1717 if (VATTR_IS_SUPPORTED(vap
, va_linkid
)) {
1718 f
.fid_objno
= (uint32_t)vap
->va_linkid
;
1720 f
.fid_objno
= (uint32_t)vap
->va_fileid
;
1722 f
.fid_generation
= 0;
1723 ATTR_PACK8((*abp
), f
);
1725 abp
->actual
.commonattr
|= ATTR_CMN_OBJPERMANENTID
;
1727 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
1728 if (vap
->va_vaflags
& VA_64BITOBJIDS
) {
1729 ATTR_PACK8((*abp
), vap
->va_parentid
);
1732 f
.fid_objno
= (uint32_t)vap
->va_parentid
;
1733 f
.fid_generation
= 0;
1734 ATTR_PACK8((*abp
), f
);
1736 abp
->actual
.commonattr
|= ATTR_CMN_PAROBJID
;
1738 if (alp
->commonattr
& ATTR_CMN_SCRIPT
) {
1739 if (VATTR_IS_SUPPORTED(vap
, va_encoding
)) {
1740 ATTR_PACK4((*abp
), vap
->va_encoding
);
1741 abp
->actual
.commonattr
|= ATTR_CMN_SCRIPT
;
1742 } else if (!return_valid
|| pack_invalid
) {
1743 ATTR_PACK4((*abp
), 0x7e);
1746 if (alp
->commonattr
& ATTR_CMN_CRTIME
) {
1747 ATTR_PACK_TIME((*abp
), vap
->va_create_time
, proc_is64
);
1748 abp
->actual
.commonattr
|= ATTR_CMN_CRTIME
;
1750 if (alp
->commonattr
& ATTR_CMN_MODTIME
) {
1751 ATTR_PACK_TIME((*abp
), vap
->va_modify_time
, proc_is64
);
1752 abp
->actual
.commonattr
|= ATTR_CMN_MODTIME
;
1754 if (alp
->commonattr
& ATTR_CMN_CHGTIME
) {
1755 ATTR_PACK_TIME((*abp
), vap
->va_change_time
, proc_is64
);
1756 abp
->actual
.commonattr
|= ATTR_CMN_CHGTIME
;
1758 if (alp
->commonattr
& ATTR_CMN_ACCTIME
) {
1759 ATTR_PACK_TIME((*abp
), vap
->va_access_time
, proc_is64
);
1760 abp
->actual
.commonattr
|= ATTR_CMN_ACCTIME
;
1762 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
) {
1763 ATTR_PACK_TIME((*abp
), vap
->va_backup_time
, proc_is64
);
1764 abp
->actual
.commonattr
|= ATTR_CMN_BKUPTIME
;
1767 * They are requesting user access, we should obtain this before getting
1768 * the finder info. For some network file systems this is a performance
1771 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* this is expensive */
1772 if (vp
&& !is_bulk
) {
1773 if (vtype
== VDIR
) {
1774 if (vnode_authorize(vp
, NULL
,
1775 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
|
1776 KAUTH_VNODE_ADD_SUBDIRECTORY
|
1777 KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0) {
1781 if (vnode_authorize(vp
, NULL
,
1782 KAUTH_VNODE_ACCESS
|
1783 KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0) {
1787 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
|
1788 KAUTH_VNODE_SEARCH
, ctx
) == 0) {
1792 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
|
1793 KAUTH_VNODE_WRITE_DATA
, ctx
) == 0) {
1797 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0) {
1800 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0) {
1804 } else if (is_bulk
&&
1805 VATTR_IS_SUPPORTED(vap
, va_user_access
)) {
1806 perms
= vap
->va_user_access
;
1809 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
1813 if (vp
&& !is_bulk
) {
1815 char uio_buf
[UIO_SIZEOF(1)];
1817 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
,
1818 UIO_READ
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1822 uio_addiov(auio
, CAST_USER_ADDR_T(abp
->fixedcursor
),
1824 /* fisize may be reset to 0 after this call */
1825 error
= vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, auio
,
1826 &fisize
, XATTR_NOSECURITY
, ctx
);
1830 * Default to zeros if its not available,
1831 * unless ATTR_CMN_RETURNED_ATTRS was requested.
1834 (!return_valid
|| pack_invalid
) &&
1835 ((error
== ENOATTR
) || (error
== ENOENT
) ||
1836 (error
== ENOTSUP
) || (error
== EPERM
))) {
1837 VFS_DEBUG(ctx
, vp
, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1838 bzero(abp
->fixedcursor
, 32);
1843 abp
->fixedcursor
+= 32;
1844 abp
->actual
.commonattr
|= ATTR_CMN_FNDRINFO
;
1845 } else if (!return_valid
) {
1849 * If we can inform the caller that we can't
1850 * return this attribute, reset error and
1851 * continue with the rest of the attributes.
1855 } else if (VATTR_IS_SUPPORTED(vap
, va_finderinfo
)) {
1856 bcopy(&vap
->va_finderinfo
[0], abp
->fixedcursor
, fisize
);
1857 abp
->fixedcursor
+= fisize
;
1858 abp
->actual
.commonattr
|= ATTR_CMN_FNDRINFO
;
1859 } else if (!return_valid
|| pack_invalid
) {
1860 bzero(abp
->fixedcursor
, fisize
);
1861 abp
->fixedcursor
+= fisize
;
1864 if (alp
->commonattr
& ATTR_CMN_OWNERID
) {
1865 ATTR_PACK4((*abp
), vap
->va_uid
);
1866 abp
->actual
.commonattr
|= ATTR_CMN_OWNERID
;
1868 if (alp
->commonattr
& ATTR_CMN_GRPID
) {
1869 ATTR_PACK4((*abp
), vap
->va_gid
);
1870 abp
->actual
.commonattr
|= ATTR_CMN_GRPID
;
1872 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
) {
1873 ATTR_PACK4((*abp
), vap
->va_mode
);
1874 abp
->actual
.commonattr
|= ATTR_CMN_ACCESSMASK
;
1876 if (alp
->commonattr
& ATTR_CMN_FLAGS
) {
1877 ATTR_PACK4((*abp
), vap
->va_flags
);
1878 abp
->actual
.commonattr
|= ATTR_CMN_FLAGS
;
1880 if (alp
->commonattr
& ATTR_CMN_GEN_COUNT
) {
1881 if (VATTR_IS_SUPPORTED(vap
, va_write_gencount
)) {
1882 ATTR_PACK4((*abp
), vap
->va_write_gencount
);
1883 abp
->actual
.commonattr
|= ATTR_CMN_GEN_COUNT
;
1884 } else if (!return_valid
|| pack_invalid
) {
1885 ATTR_PACK4((*abp
), 0);
1889 if (alp
->commonattr
& ATTR_CMN_DOCUMENT_ID
) {
1890 if (VATTR_IS_SUPPORTED(vap
, va_document_id
)) {
1891 ATTR_PACK4((*abp
), vap
->va_document_id
);
1892 abp
->actual
.commonattr
|= ATTR_CMN_DOCUMENT_ID
;
1893 } else if (!return_valid
|| pack_invalid
) {
1894 ATTR_PACK4((*abp
), 0);
1897 /* We already obtain the user access, so just fill in the buffer here */
1898 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) {
1900 if (!is_bulk
&& vp
) {
1902 * Rather than MAC preceding DAC, in this case we want
1903 * the smallest set of permissions granted by both MAC &
1904 * DAC checks. We won't add back any permissions.
1907 if (mac_vnode_check_access(ctx
, vp
, W_OK
) != 0) {
1912 if (mac_vnode_check_access(ctx
, vp
, R_OK
) != 0) {
1917 if (mac_vnode_check_access(ctx
, vp
, X_OK
) != 0) {
1923 VFS_DEBUG(ctx
, vp
, "ATTRLIST - granting perms %d", perms
);
1924 if (!is_bulk
&& vp
) {
1925 ATTR_PACK4((*abp
), perms
);
1926 abp
->actual
.commonattr
|= ATTR_CMN_USERACCESS
;
1927 } else if (is_bulk
&& VATTR_IS_SUPPORTED(vap
, va_user_access
)) {
1928 ATTR_PACK4((*abp
), perms
);
1929 abp
->actual
.commonattr
|= ATTR_CMN_USERACCESS
;
1930 } else if (!return_valid
|| pack_invalid
) {
1931 ATTR_PACK4((*abp
), 0);
1934 if (alp
->commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1935 if (VATTR_IS_SUPPORTED(vap
, va_acl
) && (vap
->va_acl
!= NULL
)) {
1936 struct kauth_filesec fsec
;
1938 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1940 fsec
.fsec_magic
= KAUTH_FILESEC_MAGIC
;
1941 fsec
.fsec_owner
= kauth_null_guid
;
1942 fsec
.fsec_group
= kauth_null_guid
;
1943 attrlist_pack_variable2(abp
, &fsec
, __offsetof(struct kauth_filesec
, fsec_acl
), vap
->va_acl
, KAUTH_ACL_COPYSIZE(vap
->va_acl
));
1944 abp
->actual
.commonattr
|= ATTR_CMN_EXTENDED_SECURITY
;
1945 } else if (!return_valid
|| pack_invalid
) {
1946 attrlist_pack_variable(abp
, NULL
, 0);
1949 if (alp
->commonattr
& ATTR_CMN_UUID
) {
1950 if (VATTR_IS_SUPPORTED(vap
, va_uuuid
)) {
1951 ATTR_PACK(abp
, vap
->va_uuuid
);
1952 abp
->actual
.commonattr
|= ATTR_CMN_UUID
;
1953 } else if (!return_valid
|| pack_invalid
) {
1954 ATTR_PACK(abp
, kauth_null_guid
);
1957 if (alp
->commonattr
& ATTR_CMN_GRPUUID
) {
1958 if (VATTR_IS_SUPPORTED(vap
, va_guuid
)) {
1959 ATTR_PACK(abp
, vap
->va_guuid
);
1960 abp
->actual
.commonattr
|= ATTR_CMN_GRPUUID
;
1961 } else if (!return_valid
|| pack_invalid
) {
1962 ATTR_PACK(abp
, kauth_null_guid
);
1965 if (alp
->commonattr
& ATTR_CMN_FILEID
) {
1966 ATTR_PACK8((*abp
), vap
->va_fileid
);
1967 abp
->actual
.commonattr
|= ATTR_CMN_FILEID
;
1969 if (alp
->commonattr
& ATTR_CMN_PARENTID
) {
1970 ATTR_PACK8((*abp
), vap
->va_parentid
);
1971 abp
->actual
.commonattr
|= ATTR_CMN_PARENTID
;
1974 if (alp
->commonattr
& ATTR_CMN_FULLPATH
) {
1976 attrlist_pack_string(abp
, fullpathptr
, fullpathlen
);
1977 abp
->actual
.commonattr
|= ATTR_CMN_FULLPATH
;
1981 if (alp
->commonattr
& ATTR_CMN_ADDEDTIME
) {
1982 if (VATTR_IS_SUPPORTED(vap
, va_addedtime
)) {
1983 ATTR_PACK_TIME((*abp
), vap
->va_addedtime
, proc_is64
);
1984 abp
->actual
.commonattr
|= ATTR_CMN_ADDEDTIME
;
1985 } else if (!return_valid
|| pack_invalid
) {
1986 struct timespec zerotime
= {.tv_sec
= 0, .tv_nsec
= 0};
1988 ATTR_PACK_TIME((*abp
), zerotime
, proc_is64
);
1991 if (alp
->commonattr
& ATTR_CMN_DATA_PROTECT_FLAGS
) {
1992 if (VATTR_IS_SUPPORTED(vap
, va_dataprotect_class
)) {
1993 ATTR_PACK4((*abp
), vap
->va_dataprotect_class
);
1994 abp
->actual
.commonattr
|= ATTR_CMN_DATA_PROTECT_FLAGS
;
1995 } else if (!return_valid
|| pack_invalid
) {
1996 ATTR_PACK4((*abp
), 0);
2004 attr_pack_dir(struct vnode
*vp
, struct attrlist
*alp
, struct _attrlist_buf
*abp
,
2005 struct vnode_attr
*vap
, int return_valid
, int pack_invalid
)
2007 if (alp
->dirattr
& ATTR_DIR_LINKCOUNT
) { /* full count of entries */
2008 ATTR_PACK4((*abp
), (uint32_t)vap
->va_dirlinkcount
);
2009 abp
->actual
.dirattr
|= ATTR_DIR_LINKCOUNT
;
2011 if (alp
->dirattr
& ATTR_DIR_ENTRYCOUNT
) {
2012 ATTR_PACK4((*abp
), (uint32_t)vap
->va_nchildren
);
2013 abp
->actual
.dirattr
|= ATTR_DIR_ENTRYCOUNT
;
2015 if (alp
->dirattr
& ATTR_DIR_MOUNTSTATUS
) {
2020 * The vnode that is passed down may either be a
2021 * top level vnode of a mount stack or a mounted
2022 * on vnode. In either case, the directory should
2023 * be reported as a mount point.
2025 if ((vp
->v_flag
& VROOT
) || vnode_mountedhere(vp
)) {
2026 mntstat
= DIR_MNTSTATUS_MNTPOINT
;
2032 * Report back on active vnode triggers
2033 * that can directly trigger a mount
2035 if (vp
->v_resolve
&&
2036 !(vp
->v_resolve
->vr_flags
& VNT_NO_DIRECT_MOUNT
)) {
2037 mntstat
|= DIR_MNTSTATUS_TRIGGER
;
2044 ATTR_PACK4((*abp
), mntstat
);
2045 abp
->actual
.dirattr
|= ATTR_DIR_MOUNTSTATUS
;
2047 if (alp
->dirattr
& ATTR_DIR_ALLOCSIZE
) {
2048 if (VATTR_IS_SUPPORTED(vap
, va_data_alloc
)) {
2049 ATTR_PACK8((*abp
), vap
->va_data_alloc
);
2050 abp
->actual
.dirattr
|= ATTR_DIR_ALLOCSIZE
;
2051 } else if (VATTR_IS_SUPPORTED(vap
, va_total_alloc
)) {
2052 ATTR_PACK8((*abp
), vap
->va_total_alloc
);
2053 abp
->actual
.dirattr
|= ATTR_DIR_ALLOCSIZE
;
2054 } else if (!return_valid
|| pack_invalid
) {
2055 uint64_t zero_val
= 0;
2056 ATTR_PACK8((*abp
), zero_val
);
2059 if (alp
->dirattr
& ATTR_DIR_IOBLOCKSIZE
) {
2060 if (VATTR_IS_SUPPORTED(vap
, va_iosize
)) {
2061 ATTR_PACK4((*abp
), vap
->va_iosize
);
2062 abp
->actual
.dirattr
|= ATTR_DIR_IOBLOCKSIZE
;
2063 } else if (!return_valid
|| pack_invalid
) {
2064 ATTR_PACK4((*abp
), 0);
2068 * If the filesystem does not support datalength
2069 * or dataallocsize, then we infer that totalsize and
2070 * totalalloc are substitutes.
2072 if (alp
->dirattr
& ATTR_DIR_DATALENGTH
) {
2073 if (VATTR_IS_SUPPORTED(vap
, va_data_size
)) {
2074 ATTR_PACK8((*abp
), vap
->va_data_size
);
2075 abp
->actual
.dirattr
|= ATTR_DIR_DATALENGTH
;
2076 } else if (VATTR_IS_SUPPORTED(vap
, va_total_size
)) {
2077 ATTR_PACK8((*abp
), vap
->va_total_size
);
2078 abp
->actual
.dirattr
|= ATTR_DIR_DATALENGTH
;
2079 } else if (!return_valid
|| pack_invalid
) {
2080 uint64_t zero_val
= 0;
2081 ATTR_PACK8((*abp
), zero_val
);
2089 * The is_bulk parameter differentiates whether the function is called from
2090 * getattrlist or getattrlistbulk. When coming in from getattrlistbulk,
2091 * the corresponding va_* values are expected to be the values filled and no
2092 * attempt is made to retrieve them by calling back into the filesystem.
2095 attr_pack_file(vfs_context_t ctx
, struct vnode
*vp
, struct attrlist
*alp
,
2096 struct _attrlist_buf
*abp
, struct vnode_attr
*vap
, int return_valid
,
2097 int pack_invalid
, int is_bulk
)
2100 uint64_t rlength
= 0;
2101 uint64_t ralloc
= 0;
2105 * Pre-fetch the rsrc attributes now so we only get them once.
2106 * Fetch the resource fork size/allocation via xattr interface
2108 if (vp
&& !is_bulk
&&
2109 (alp
->fileattr
& (ATTR_FILE_TOTALSIZE
| ATTR_FILE_ALLOCSIZE
|
2110 ATTR_FILE_RSRCLENGTH
| ATTR_FILE_RSRCALLOCSIZE
))) {
2111 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
,
2112 &rsize
, XATTR_NOSECURITY
, ctx
);
2114 if ((error
== ENOENT
) || (error
== ENOATTR
) ||
2115 (error
== ENOTSUP
) || (error
== EPERM
) ||
2116 (error
== EACCES
)) {
2125 if (alp
->fileattr
& (ATTR_FILE_RSRCALLOCSIZE
|
2126 ATTR_FILE_ALLOCSIZE
)) {
2129 blksize
= vp
->v_mount
->mnt_vfsstat
.f_bsize
;
2134 ralloc
= roundup(rsize
, blksize
);
2138 if (alp
->fileattr
& ATTR_FILE_LINKCOUNT
) {
2139 ATTR_PACK4((*abp
), (uint32_t)vap
->va_nlink
);
2140 abp
->actual
.fileattr
|= ATTR_FILE_LINKCOUNT
;
2143 * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
2144 * We infer that if the filesystem does not support va_data_size or va_data_alloc
2145 * it must not know about alternate forks. So when we need to gather
2146 * the total size or total alloc, it's OK to substitute the total size for
2147 * the data size below. This is because it is likely a flat filesystem and we must
2148 * be using AD files to store the rsrc fork and EAs.
2150 * Additionally, note that getattrlist is barred from being called on
2151 * resource fork paths. (Search for CN_ALLOWRSRCFORK). So if the filesystem does
2152 * support va_data_size, it is guaranteed to represent the data fork's size. This
2153 * is an important distinction to make because when we call vnode_getattr on
2154 * an HFS resource fork vnode, to get the size, it will vend out the resource
2155 * fork's size (it only gets the size of the passed-in vnode).
2157 if (alp
->fileattr
& ATTR_FILE_TOTALSIZE
) {
2159 uint64_t totalsize
= rlength
;
2161 if (VATTR_IS_SUPPORTED(vap
, va_data_size
)) {
2162 totalsize
+= vap
->va_data_size
;
2163 } else if (VATTR_IS_SUPPORTED(vap
, va_total_size
)) {
2164 totalsize
+= vap
->va_total_size
;
2167 ATTR_PACK8((*abp
), totalsize
);
2168 abp
->actual
.fileattr
|= ATTR_FILE_TOTALSIZE
;
2169 } else if (VATTR_IS_SUPPORTED(vap
, va_total_size
)) {
2170 ATTR_PACK8((*abp
), vap
->va_total_size
);
2171 abp
->actual
.fileattr
|= ATTR_FILE_TOTALSIZE
;
2172 } else if (!return_valid
|| pack_invalid
) {
2173 uint64_t zero_val
= 0;
2175 ATTR_PACK8((*abp
), zero_val
);
2178 if (alp
->fileattr
& ATTR_FILE_ALLOCSIZE
) {
2180 uint64_t totalalloc
= ralloc
;
2183 * If data_alloc is supported, then it must represent the
2186 if (VATTR_IS_SUPPORTED(vap
, va_data_alloc
)) {
2187 totalalloc
+= vap
->va_data_alloc
;
2188 } else if (VATTR_IS_SUPPORTED(vap
, va_total_alloc
)) {
2189 totalalloc
+= vap
->va_total_alloc
;
2192 ATTR_PACK8((*abp
), totalalloc
);
2193 abp
->actual
.fileattr
|= ATTR_FILE_ALLOCSIZE
;
2194 } else if (VATTR_IS_SUPPORTED(vap
, va_total_alloc
)) {
2195 ATTR_PACK8((*abp
), vap
->va_total_alloc
);
2196 abp
->actual
.fileattr
|= ATTR_FILE_ALLOCSIZE
;
2197 } else if (!return_valid
|| pack_invalid
) {
2198 uint64_t zero_val
= 0;
2200 ATTR_PACK8((*abp
), zero_val
);
2203 if (alp
->fileattr
& ATTR_FILE_IOBLOCKSIZE
) {
2204 if (VATTR_IS_SUPPORTED(vap
, va_iosize
)) {
2205 ATTR_PACK4((*abp
), vap
->va_iosize
);
2206 abp
->actual
.fileattr
|= ATTR_FILE_IOBLOCKSIZE
;
2207 } else if (!return_valid
|| pack_invalid
) {
2208 ATTR_PACK4((*abp
), 0);
2211 if (alp
->fileattr
& ATTR_FILE_CLUMPSIZE
) {
2212 if (!return_valid
|| pack_invalid
) {
2213 ATTR_PACK4((*abp
), 0); /* this value is deprecated */
2214 abp
->actual
.fileattr
|= ATTR_FILE_CLUMPSIZE
;
2217 if (alp
->fileattr
& ATTR_FILE_DEVTYPE
) {
2218 if (vp
&& (vp
->v_type
== VCHR
|| vp
->v_type
== VBLK
)) {
2221 if (vp
->v_specinfo
!= NULL
) {
2222 dev
= vp
->v_specinfo
->si_rdev
;
2223 } else if (VATTR_IS_SUPPORTED(vap
, va_rdev
)) {
2228 ATTR_PACK4((*abp
), dev
);
2229 abp
->actual
.fileattr
|= ATTR_FILE_DEVTYPE
;
2231 ATTR_PACK4((*abp
), 0);
2232 abp
->actual
.fileattr
|= ATTR_FILE_DEVTYPE
;
2233 } else if (VATTR_IS_SUPPORTED(vap
, va_rdev
)) {
2234 ATTR_PACK4((*abp
), vap
->va_rdev
);
2235 abp
->actual
.fileattr
|= ATTR_FILE_DEVTYPE
;
2236 } else if (!return_valid
|| pack_invalid
) {
2237 ATTR_PACK4((*abp
), 0);
2241 * If the filesystem does not support datalength
2242 * or dataallocsize, then we infer that totalsize and
2243 * totalalloc are substitutes.
2245 if (alp
->fileattr
& ATTR_FILE_DATALENGTH
) {
2246 if (VATTR_IS_SUPPORTED(vap
, va_data_size
)) {
2247 ATTR_PACK8((*abp
), vap
->va_data_size
);
2248 abp
->actual
.fileattr
|= ATTR_FILE_DATALENGTH
;
2249 } else if (VATTR_IS_SUPPORTED(vap
, va_total_size
)) {
2250 ATTR_PACK8((*abp
), vap
->va_total_size
);
2251 abp
->actual
.fileattr
|= ATTR_FILE_DATALENGTH
;
2252 } else if (!return_valid
|| pack_invalid
) {
2253 uint64_t zero_val
= 0;
2254 ATTR_PACK8((*abp
), zero_val
);
2257 if (alp
->fileattr
& ATTR_FILE_DATAALLOCSIZE
) {
2258 if (VATTR_IS_SUPPORTED(vap
, va_data_alloc
)) {
2259 ATTR_PACK8((*abp
), vap
->va_data_alloc
);
2260 abp
->actual
.fileattr
|= ATTR_FILE_DATAALLOCSIZE
;
2261 } else if (VATTR_IS_SUPPORTED(vap
, va_total_alloc
)) {
2262 ATTR_PACK8((*abp
), vap
->va_total_alloc
);
2263 abp
->actual
.fileattr
|= ATTR_FILE_DATAALLOCSIZE
;
2264 } else if (!return_valid
|| pack_invalid
) {
2265 uint64_t zero_val
= 0;
2266 ATTR_PACK8((*abp
), zero_val
);
2269 /* already got the resource fork size/allocation above */
2270 if (alp
->fileattr
& ATTR_FILE_RSRCLENGTH
) {
2272 ATTR_PACK8((*abp
), rlength
);
2273 abp
->actual
.fileattr
|= ATTR_FILE_RSRCLENGTH
;
2274 } else if (VATTR_IS_SUPPORTED(vap
, va_rsrc_length
)) {
2275 ATTR_PACK8((*abp
), vap
->va_rsrc_length
);
2276 abp
->actual
.fileattr
|= ATTR_FILE_RSRCLENGTH
;
2277 } else if (!return_valid
|| pack_invalid
) {
2278 uint64_t zero_val
= 0;
2280 ATTR_PACK8((*abp
), zero_val
);
2283 if (alp
->fileattr
& ATTR_FILE_RSRCALLOCSIZE
) {
2285 ATTR_PACK8((*abp
), ralloc
);
2286 abp
->actual
.fileattr
|= ATTR_FILE_RSRCALLOCSIZE
;
2287 } else if (VATTR_IS_SUPPORTED(vap
, va_rsrc_alloc
)) {
2288 ATTR_PACK8((*abp
), vap
->va_rsrc_alloc
);
2289 abp
->actual
.fileattr
|= ATTR_FILE_RSRCALLOCSIZE
;
2290 } else if (!return_valid
|| pack_invalid
) {
2291 uint64_t zero_val
= 0;
2293 ATTR_PACK8((*abp
), zero_val
);
2301 * Pack FORKATTR attributes into a user buffer.
2302 * alp is a pointer to the bitmap of attributes required.
2303 * abp is the state of the attribute filling operation.
2304 * The attribute data (along with some other fields that are required
2308 attr_pack_common_extended(mount_t mp
, struct vnode
*vp
, struct attrlist
*alp
,
2309 struct _attrlist_buf
*abp
, const char *relpathptr
, ssize_t relpathlen
,
2310 const char *REALpathptr
, ssize_t REALpathlen
,
2311 struct vnode_attr
*vap
, int return_valid
, int pack_invalid
)
2313 if (vp
&& (alp
->forkattr
& ATTR_CMNEXT_RELPATH
)) {
2314 attrlist_pack_string(abp
, relpathptr
, relpathlen
);
2315 abp
->actual
.forkattr
|= ATTR_CMNEXT_RELPATH
;
2318 if (alp
->forkattr
& ATTR_CMNEXT_PRIVATESIZE
) {
2319 if (VATTR_IS_SUPPORTED(vap
, va_private_size
)) {
2320 ATTR_PACK8((*abp
), vap
->va_private_size
);
2321 abp
->actual
.forkattr
|= ATTR_CMNEXT_PRIVATESIZE
;
2322 } else if (!return_valid
|| pack_invalid
) {
2323 uint64_t zero_val
= 0;
2324 ATTR_PACK8((*abp
), zero_val
);
2328 if (alp
->forkattr
& ATTR_CMNEXT_LINKID
) {
2331 if (VATTR_IS_SUPPORTED(vap
, va_linkid
)) {
2332 linkid
= vap
->va_linkid
;
2334 linkid
= vap
->va_fileid
;
2337 ATTR_PACK8((*abp
), linkid
);
2338 abp
->actual
.forkattr
|= ATTR_CMNEXT_LINKID
;
2341 if (vp
&& (alp
->forkattr
& ATTR_CMNEXT_NOFIRMLINKPATH
)) {
2342 attrlist_pack_string(abp
, REALpathptr
, REALpathlen
);
2343 abp
->actual
.forkattr
|= ATTR_CMNEXT_NOFIRMLINKPATH
;
2346 if (alp
->forkattr
& ATTR_CMNEXT_REALDEVID
) {
2349 mp
->mnt_vfsstat
.f_fsid
.val
[0]);
2350 abp
->actual
.forkattr
|= ATTR_CMNEXT_REALDEVID
;
2353 vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
2354 abp
->actual
.forkattr
|= ATTR_CMNEXT_REALDEVID
;
2355 } else if (VATTR_IS_SUPPORTED(vap
, va_fsid
)) {
2356 ATTR_PACK4((*abp
), vap
->va_fsid
);
2357 abp
->actual
.forkattr
|= ATTR_CMN_DEVID
;
2358 } else if (!return_valid
|| pack_invalid
) {
2359 ATTR_PACK4((*abp
), 0);
2363 if (alp
->forkattr
& ATTR_CMNEXT_REALFSID
) {
2366 mp
->mnt_vfsstat
.f_fsid
);
2367 abp
->actual
.forkattr
|= ATTR_CMNEXT_REALFSID
;
2370 vp
->v_mount
->mnt_vfsstat
.f_fsid
);
2371 abp
->actual
.forkattr
|= ATTR_CMNEXT_REALFSID
;
2372 } else if (VATTR_IS_SUPPORTED(vap
, va_fsid64
)) {
2373 ATTR_PACK8((*abp
), vap
->va_fsid64
);
2374 abp
->actual
.forkattr
|= ATTR_CMN_FSID
;
2375 } else if (!return_valid
|| pack_invalid
) {
2376 fsid_t fsid
= {{0}};
2378 ATTR_PACK8((*abp
), fsid
);
2382 if (alp
->forkattr
& ATTR_CMNEXT_CLONEID
) {
2383 if (VATTR_IS_SUPPORTED(vap
, va_clone_id
)) {
2384 ATTR_PACK8((*abp
), vap
->va_clone_id
);
2385 abp
->actual
.forkattr
|= ATTR_CMNEXT_CLONEID
;
2386 } else if (!return_valid
|| pack_invalid
) {
2387 uint64_t zero_val
= 0;
2388 ATTR_PACK8((*abp
), zero_val
);
2392 if (alp
->forkattr
& ATTR_CMNEXT_EXT_FLAGS
) {
2393 if (VATTR_IS_SUPPORTED(vap
, va_extflags
)) {
2394 ATTR_PACK8((*abp
), vap
->va_extflags
);
2395 abp
->actual
.forkattr
|= ATTR_CMNEXT_EXT_FLAGS
;
2396 } else if (!return_valid
|| pack_invalid
) {
2397 uint64_t zero_val
= 0;
2398 ATTR_PACK8((*abp
), zero_val
);
2402 if (alp
->forkattr
& ATTR_CMNEXT_RECURSIVE_GENCOUNT
) {
2403 if (VATTR_IS_SUPPORTED(vap
, va_recursive_gencount
)) {
2404 ATTR_PACK8((*abp
), vap
->va_recursive_gencount
);
2405 abp
->actual
.forkattr
|= ATTR_CMNEXT_RECURSIVE_GENCOUNT
;
2406 } else if (!return_valid
|| pack_invalid
) {
2407 uint64_t zero_val
= 0;
2408 ATTR_PACK8((*abp
), zero_val
);
2416 vattr_get_alt_data(vnode_t vp
, struct attrlist
*alp
, struct vnode_attr
*vap
,
2417 int return_valid
, int is_bulk
,
2418 #if !CONFIG_FIRMLINKS
2421 int is_realdev
, vfs_context_t ctx
)
2424 * There are a couple of special cases.
2425 * If we are after object IDs, we can make do with va_fileid.
2427 if ((alp
->commonattr
&
2428 (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
| ATTR_CMN_FILEID
)) &&
2429 !VATTR_IS_SUPPORTED(vap
, va_linkid
)) {
2430 /* forget we wanted this */
2431 VATTR_CLEAR_ACTIVE(vap
, va_linkid
);
2435 * A filesystem may not support va_fsid64. If it is not available, then we'll
2436 * synthesize it from the mount.
2438 if ((alp
->commonattr
& ATTR_CMN_FSID
) && !VATTR_IS_SUPPORTED(vap
, va_fsid64
)) {
2439 VATTR_CLEAR_ACTIVE(vap
, va_fsid64
);
2443 if ((alp
->commonattr
& ATTR_CMN_FSID
) && !VATTR_IS_SUPPORTED(vap
, va_fsid
)) {
2444 VATTR_CLEAR_ACTIVE(vap
, va_fsid
);
2447 /* We request the fsid64 for the devid */
2448 if ((alp
->commonattr
& ATTR_CMN_DEVID
) && !VATTR_IS_SUPPORTED(vap
, va_fsid
)) {
2449 VATTR_CLEAR_ACTIVE(vap
, va_fsid
);
2454 * Many filesystems don't know their parent object id.
2455 * If necessary, attempt to derive it from the vnode.
2457 if ((alp
->commonattr
& (ATTR_CMN_PAROBJID
| ATTR_CMN_PARENTID
)) && vp
) {
2460 #if CONFIG_FIRMLINKS
2461 /* If this is a firmlink target, we get the fileid of the firmlink parent. */
2462 if (!is_realdev
&& (vp
->v_flag
& VFMLINKTARGET
) && ((dvp
= vp
->v_fmlink
) != NULL
) && (vnode_get(dvp
) == 0)) {
2463 struct vnode_attr lva
;
2466 VATTR_WANTED(&lva
, va_parentid
);
2467 VATTR_WANTED(&lva
, va_fsid
);
2468 if (vnode_getattr(dvp
, &lva
, ctx
) == 0 &&
2469 VATTR_IS_SUPPORTED(&lva
, va_parentid
) &&
2470 VATTR_IS_SUPPORTED(&lva
, va_fsid
) &&
2471 (lva
.va_fsid
== (uint32_t)vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0])) {
2472 vap
->va_parentid
= lva
.va_parentid
;
2473 VATTR_SET_SUPPORTED(vap
, va_parentid
);
2477 #endif /* CONFIG_FIRMLINKS */
2478 if (!VATTR_IS_SUPPORTED(vap
, va_parentid
) && !is_bulk
) {
2479 if ((dvp
= vnode_getparent(vp
)) != NULLVP
) {
2480 struct vnode_attr lva
;
2483 VATTR_WANTED(&lva
, va_fileid
);
2484 if (vnode_getattr(dvp
, &lva
, ctx
) == 0 &&
2485 VATTR_IS_SUPPORTED(vap
, va_fileid
)) {
2486 vap
->va_parentid
= lva
.va_fileid
;
2487 VATTR_SET_SUPPORTED(vap
, va_parentid
);
2495 * And we can report datasize/alloc from total.
2497 if ((alp
->fileattr
& ATTR_FILE_DATALENGTH
) &&
2498 !VATTR_IS_SUPPORTED(vap
, va_data_size
)) {
2499 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2502 if ((alp
->fileattr
& ATTR_FILE_DATAALLOCSIZE
) &&
2503 !VATTR_IS_SUPPORTED(vap
, va_data_alloc
)) {
2504 VATTR_CLEAR_ACTIVE(vap
, va_data_alloc
);
2508 * If we don't have an encoding, go with UTF-8
2510 if ((alp
->commonattr
& ATTR_CMN_SCRIPT
) &&
2511 !VATTR_IS_SUPPORTED(vap
, va_encoding
) && !return_valid
) {
2512 VATTR_RETURN(vap
, va_encoding
,
2513 0x7e /* kTextEncodingMacUnicode */);
2517 * If we don't have a name, we'll get one from the vnode or
2520 if ((alp
->commonattr
& ATTR_CMN_NAME
) &&
2521 !VATTR_IS_SUPPORTED(vap
, va_name
)) {
2522 VATTR_CLEAR_ACTIVE(vap
, va_name
);
2525 /* If va_dirlinkcount isn't supported use a default of 1. */
2526 if ((alp
->dirattr
& ATTR_DIR_LINKCOUNT
) &&
2527 !VATTR_IS_SUPPORTED(vap
, va_dirlinkcount
)) {
2528 VATTR_RETURN(vap
, va_dirlinkcount
, 1);
2532 struct _attrlist_paths
{
2534 ssize_t
*fullpathlenp
;
2536 ssize_t
*relpathlenp
;
2538 ssize_t
*REALpathlenp
;
2542 calc_varsize(vnode_t vp
, struct attrlist
*alp
, struct vnode_attr
*vap
,
2543 ssize_t
*varsizep
, struct _attrlist_paths
*pathsp
, const char **vnamep
,
2544 const char **cnpp
, ssize_t
*cnlp
)
2548 *varsizep
= 0; /* length count */
2549 /* We may need to fix up the name attribute if requested */
2550 if (alp
->commonattr
& ATTR_CMN_NAME
) {
2551 if (VATTR_IS_SUPPORTED(vap
, va_name
)) {
2552 vap
->va_name
[MAXPATHLEN
- 1] = '\0'; /* Ensure nul-termination */
2553 *cnpp
= vap
->va_name
;
2554 *cnlp
= strlen(*cnpp
);
2556 /* Filesystem did not support getting the name */
2557 if (vnode_isvroot(vp
)) {
2558 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
2559 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
2560 /* special case for boot volume. Use root name when it's
2561 * available (which is the volume name) or just the mount on
2562 * name of "/". we must do this for binary compatibility with
2563 * pre Tiger code. returning nothing for the boot volume name
2564 * breaks installers - 3961058
2566 *cnpp
= *vnamep
= vnode_getname(vp
);
2567 if (*cnpp
== NULL
) {
2568 /* just use "/" as name */
2569 *cnpp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
2571 *cnlp
= strlen(*cnpp
);
2573 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, cnpp
, cnlp
);
2576 *cnpp
= *vnamep
= vnode_getname(vp
);
2578 if (*cnpp
!= NULL
) {
2579 *cnlp
= strlen(*cnpp
);
2585 *varsizep
+= roundup(*cnlp
+ 1, 4);
2589 * Compute the full path to this vnode, if necessary. This attribute is almost certainly
2590 * not supported by any filesystem, so build the path to this vnode at this time.
2592 if (vp
&& (alp
->commonattr
& ATTR_CMN_FULLPATH
)) {
2593 int len
= MAXPATHLEN
;
2596 /* call build_path making sure NOT to use the cache-only behavior */
2597 err
= build_path(vp
, pathsp
->fullpathptr
, len
, &len
, 0, vfs_context_current());
2602 if (pathsp
->fullpathptr
) {
2603 *(pathsp
->fullpathlenp
) = strlen(pathsp
->fullpathptr
);
2605 *(pathsp
->fullpathlenp
) = 0;
2607 *varsizep
+= roundup(((*(pathsp
->fullpathlenp
)) + 1), 4);
2611 * Compute this vnode's volume relative path.
2613 if (vp
&& (alp
->forkattr
& ATTR_CMNEXT_RELPATH
)) {
2617 /* call build_path making sure NOT to use the cache-only behavior */
2618 err
= build_path(vp
, pathsp
->relpathptr
, MAXPATHLEN
, &len
, BUILDPATH_VOLUME_RELATIVE
, vfs_context_current());
2624 //`len' includes trailing null
2625 *(pathsp
->relpathlenp
) = len
- 1;
2626 *varsizep
+= roundup(len
, 4);
2630 * Compute this vnode's real (firmlink free) path.
2632 if (vp
&& (alp
->forkattr
& ATTR_CMNEXT_NOFIRMLINKPATH
)) {
2636 /* call build_path making sure NOT to use the cache-only behavior */
2637 err
= build_path(vp
, pathsp
->REALpathptr
, MAXPATHLEN
, &len
, BUILDPATH_NO_FIRMLINK
, vfs_context_current());
2643 //`len' includes trailing null
2644 *(pathsp
->REALpathlenp
) = len
- 1;
2645 *varsizep
+= roundup(len
, 4);
2649 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
2651 * XXX This needs to change at some point; since the blob is opaque in
2652 * user-space this is OK.
2654 if ((alp
->commonattr
& ATTR_CMN_EXTENDED_SECURITY
) &&
2655 VATTR_IS_SUPPORTED(vap
, va_acl
) &&
2656 (vap
->va_acl
!= NULL
)) {
2658 * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
2659 * KAUTH_FILESEC_NOACL ourselves
2661 if (vap
->va_acl
->acl_entrycount
== KAUTH_FILESEC_NOACL
) {
2662 *varsizep
+= roundup((KAUTH_FILESEC_SIZE(0)), 4);
2664 *varsizep
+= roundup((KAUTH_FILESEC_SIZE(vap
->va_acl
->acl_entrycount
)), 4);
2673 vfs_attr_pack_internal(mount_t mp
, vnode_t vp
, uio_t auio
, struct attrlist
*alp
,
2674 uint64_t options
, struct vnode_attr
*vap
, __unused
void *fndesc
,
2675 vfs_context_t ctx
, int is_bulk
, enum vtype vtype
, ssize_t fixedsize
)
2677 struct _attrlist_buf ab
;
2678 struct _attrlist_paths apaths
= {.fullpathptr
= NULL
, .fullpathlenp
= NULL
,
2679 .relpathptr
= NULL
, .relpathlenp
= NULL
,
2680 .REALpathptr
= NULL
, .REALpathlenp
= NULL
};
2684 const char *vname
= NULL
;
2688 ssize_t fullpathlen
;
2692 ssize_t REALpathlen
;
2698 int alloc_local_buf
;
2699 const int use_fork
= options
& FSOPT_ATTR_CMN_EXTENDED
;
2701 proc_is64
= proc_is64bit(vfs_context_proc(ctx
));
2712 alloc_local_buf
= 0;
2714 buf_size
= (ssize_t
)uio_resid(auio
);
2715 if ((buf_size
<= 0) || (uio_iovcnt(auio
) > 1)) {
2720 /* Check for special packing semantics */
2721 return_valid
= (alp
->commonattr
& ATTR_CMN_RETURNED_ATTRS
) ? 1 : 0;
2722 pack_invalid
= (options
& FSOPT_PACK_INVAL_ATTRS
) ? 1 : 0;
2723 is_realdev
= options
& FSOPT_RETURN_REALDEV
? 1 : 0;
2726 /* Generate a valid mask for post processing */
2727 bcopy(&(alp
->commonattr
), &ab
.valid
, sizeof(attribute_set_t
));
2730 /* did we ask for something the filesystem doesn't support? */
2731 if (vap
->va_active
&&
2732 (!VATTR_ALL_SUPPORTED(vap
)
2733 #if CONFIG_FIRMLINKS
2734 /* For firmlink targets we have to overide what the FS returned for parentid */
2736 (!is_realdev
&& vp
&& (vp
->v_flag
& VFMLINKTARGET
) && vp
->v_fmlink
&&
2737 (alp
->commonattr
& (ATTR_CMN_PAROBJID
| ATTR_CMN_PARENTID
)))
2740 // this disables the selectors that were not supported by the filesystem
2741 vattr_get_alt_data(vp
, alp
, vap
, return_valid
, is_bulk
, is_realdev
,
2745 if (!VATTR_ALL_SUPPORTED(vap
)) {
2746 if (return_valid
&& pack_invalid
) {
2747 /* Fix up valid mask for post processing */
2748 getattrlist_fixupattrs(&ab
.valid
, vap
, use_fork
);
2750 /* Force packing of everything asked for */
2751 vap
->va_supported
= vap
->va_active
;
2752 } else if (return_valid
) {
2753 /* Adjust the requested attributes */
2754 getattrlist_fixupattrs(
2755 (attribute_set_t
*)&(alp
->commonattr
), vap
, use_fork
);
2766 //if a path is requested, allocate a temporary buffer to build it
2767 if (vp
&& (alp
->commonattr
& (ATTR_CMN_FULLPATH
))) {
2768 fullpathptr
= (char*) zalloc_flags(ZV_NAMEI
, Z_WAITOK
| Z_ZERO
);
2769 apaths
.fullpathptr
= fullpathptr
;
2770 apaths
.fullpathlenp
= &fullpathlen
;
2773 // only interpret fork attributes if they're used as new common attributes
2774 if (vp
&& use_fork
) {
2775 if (alp
->forkattr
& (ATTR_CMNEXT_RELPATH
)) {
2776 relpathptr
= (char*) zalloc_flags(ZV_NAMEI
, Z_WAITOK
| Z_ZERO
);
2777 apaths
.relpathptr
= relpathptr
;
2778 apaths
.relpathlenp
= &relpathlen
;
2781 if (alp
->forkattr
& (ATTR_CMNEXT_NOFIRMLINKPATH
)) {
2782 REALpathptr
= (char*) zalloc_flags(ZV_NAMEI
, Z_WAITOK
| Z_ZERO
);
2783 apaths
.REALpathptr
= REALpathptr
;
2784 apaths
.REALpathlenp
= &REALpathlen
;
2789 * Compute variable-space requirements.
2791 error
= calc_varsize(vp
, alp
, vap
, &varsize
, &apaths
, &vname
, &cnp
, &cnl
);
2797 * Allocate a target buffer for attribute results.
2799 * Note that we won't ever copy out more than the caller requested, even though
2800 * we might have to allocate more than they offer so that the diagnostic checks
2801 * don't result in a panic if the caller's buffer is too small.
2803 ab
.allocated
= fixedsize
+ varsize
;
2804 /* Cast 'allocated' to an unsigned to verify allocation size */
2805 if (((size_t)ab
.allocated
) > ATTR_MAX_BUFFER
) {
2807 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
2812 * Special handling for bulk calls, align to 8 (and only if enough
2816 if (buf_size
< ab
.allocated
) {
2821 newlen
= (ab
.allocated
+ 7) & ~0x07;
2822 /* Align only if enough space for alignment */
2823 if (newlen
<= (uint32_t)buf_size
) {
2824 ab
.allocated
= newlen
;
2830 * See if we can reuse buffer passed in i.e. it is a kernel buffer
2833 if (uio_isuserspace(auio
) || (buf_size
< ab
.allocated
)) {
2834 ab
.base
= kheap_alloc(KHEAP_TEMP
, ab
.allocated
, Z_ZERO
| Z_WAITOK
);
2835 alloc_local_buf
= 1;
2838 * In case this is a kernel buffer and sufficiently
2839 * big, this function will try to use that buffer
2840 * instead of allocating another buffer and bcopy'ing
2843 * The calculation below figures out where to start
2844 * writing in the buffer and once all the data has been
2845 * filled in, uio_resid is updated to reflect the usage
2848 * uio_offset cannot be used here to determine the
2849 * starting location as uio_offset could be set to a
2850 * value which has nothing to do the location
2853 ab
.base
= (char *)uio_curriovbase(auio
) +
2854 ((ssize_t
)uio_curriovlen(auio
) - buf_size
);
2855 bzero(ab
.base
, ab
.allocated
);
2858 if (ab
.base
== NULL
) {
2860 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
2865 /* set the S_IFMT bits for the mode */
2866 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
) {
2868 switch (vp
->v_type
) {
2870 vap
->va_mode
|= S_IFREG
;
2873 vap
->va_mode
|= S_IFDIR
;
2876 vap
->va_mode
|= S_IFBLK
;
2879 vap
->va_mode
|= S_IFCHR
;
2882 vap
->va_mode
|= S_IFLNK
;
2885 vap
->va_mode
|= S_IFSOCK
;
2888 vap
->va_mode
|= S_IFIFO
;
2898 * Pack results into the destination buffer.
2900 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
2902 ab
.fixedcursor
+= sizeof(attribute_set_t
);
2903 bzero(&ab
.actual
, sizeof(ab
.actual
));
2905 ab
.varcursor
= ab
.base
+ fixedsize
;
2906 ab
.needed
= ab
.allocated
;
2908 /* common attributes ************************************************/
2909 error
= attr_pack_common(ctx
, (options
& FSOPT_RETURN_REALDEV
? mp
: NULL
),
2910 vp
, alp
, &ab
, vap
, proc_is64
, cnp
, cnl
, fullpathptr
, fullpathlen
,
2911 return_valid
, pack_invalid
, vtype
, is_bulk
);
2913 /* directory attributes *********************************************/
2914 if (!error
&& alp
->dirattr
&& (vtype
== VDIR
)) {
2915 error
= attr_pack_dir(vp
, alp
, &ab
, vap
, return_valid
, pack_invalid
);
2918 /* file attributes **************************************************/
2919 if (!error
&& alp
->fileattr
&& (vtype
!= VDIR
)) {
2920 error
= attr_pack_file(ctx
, vp
, alp
, &ab
, vap
, return_valid
,
2921 pack_invalid
, is_bulk
);
2924 /* common extended attributes *****************************************/
2925 if (!error
&& use_fork
) {
2926 error
= attr_pack_common_extended(mp
, vp
, alp
, &ab
, relpathptr
, relpathlen
,
2927 REALpathptr
, REALpathlen
, vap
, return_valid
, pack_invalid
);
2935 if (!return_valid
&& (ab
.fixedcursor
- ab
.base
) != fixedsize
) {
2936 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
2937 fixedsize
, (long) (ab
.fixedcursor
- ab
.base
), alp
->commonattr
, alp
->volattr
);
2939 if (!return_valid
&& ab
.varcursor
!= (ab
.base
+ ab
.needed
)) {
2940 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab
.varcursor
- ab
.base
), ab
.needed
);
2944 * In the compatible case, we report the smaller of the required and returned sizes.
2945 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
2946 * of the result buffer, even if we copied less out. The caller knows how big a buffer
2947 * they gave us, so they can always check for truncation themselves.
2949 *(uint32_t *)ab
.base
= (options
& FSOPT_REPORT_FULLSIZE
) ? (uint32_t)ab
.needed
: (uint32_t)lmin(ab
.allocated
, ab
.needed
);
2951 /* Return attribute set output if requested. */
2953 ab
.actual
.commonattr
|= ATTR_CMN_RETURNED_ATTRS
;
2955 /* Only report the attributes that are valid */
2956 ab
.actual
.commonattr
&= ab
.valid
.commonattr
;
2957 ab
.actual
.dirattr
&= ab
.valid
.dirattr
;
2958 ab
.actual
.fileattr
&= ab
.valid
.fileattr
;
2960 bcopy(&ab
.actual
, ab
.base
+ sizeof(uint32_t), sizeof(ab
.actual
));
2963 copy_size
= lmin(buf_size
, ab
.allocated
);
2965 /* Only actually copyout as much out as the user buffer can hold */
2966 if (alloc_local_buf
) {
2967 error
= uiomove(ab
.base
, (int)copy_size
, auio
);
2969 off_t orig_offset
= uio_offset(auio
);
2972 * The buffer in the uio struct was used directly
2973 * (i.e. it was a kernel buffer and big enough
2974 * to hold the data required) in order to avoid
2975 * un-needed allocation and copies.
2977 * At this point, update the resid value to what it
2978 * would be if this was the result of a uiomove. The
2979 * offset is also incremented, though it may not
2980 * mean anything to the caller but that is what
2981 * uiomove does as well.
2983 uio_setresid(auio
, buf_size
- copy_size
);
2984 uio_setoffset(auio
, orig_offset
+ (off_t
)copy_size
);
2989 vnode_putname(vname
);
2992 zfree(ZV_NAMEI
, fullpathptr
);
2995 zfree(ZV_NAMEI
, relpathptr
);
2998 zfree(ZV_NAMEI
, REALpathptr
);
3000 if (alloc_local_buf
) {
3001 kheap_free(KHEAP_TEMP
, ab
.base
, ab
.allocated
);
3007 vfs_attr_pack_ext(mount_t mp
, vnode_t vp
, uio_t uio
, struct attrlist
*alp
, uint64_t options
,
3008 struct vnode_attr
*vap
, __unused
void *fndesc
, vfs_context_t ctx
)
3012 uint64_t orig_active
;
3013 struct attrlist orig_al
;
3017 v_type
= vnode_vtype(vp
);
3019 v_type
= vap
->va_objtype
;
3023 orig_active
= vap
->va_active
;
3026 error
= getattrlist_setupvattr_all(alp
, vap
, v_type
, &fixedsize
,
3027 proc_is64bit(vfs_context_proc(ctx
)), options
& FSOPT_ATTR_CMN_EXTENDED
);
3031 "ATTRLIST - ERROR: setup for request failed");
3035 error
= vfs_attr_pack_internal(mp
, vp
, uio
, alp
,
3036 options
| FSOPT_REPORT_FULLSIZE
, vap
, NULL
, ctx
, 1, v_type
,
3039 VATTR_CLEAR_SUPPORTED_ALL(vap
);
3040 vap
->va_active
= orig_active
;
3047 vfs_attr_pack(vnode_t vp
, uio_t uio
, struct attrlist
*alp
, uint64_t options
,
3048 struct vnode_attr
*vap
, __unused
void *fndesc
, vfs_context_t ctx
)
3050 return vfs_attr_pack_ext(NULL
, vp
, uio
, alp
, options
, vap
, fndesc
, ctx
);
3054 * Obtain attribute information about a filesystem object.
3056 * Note: The alt_name parameter can be used by the caller to pass in the vnode
3057 * name obtained from some authoritative source (eg. readdir vnop); where
3058 * filesystems' getattr vnops do not support ATTR_CMN_NAME, the alt_name will be
3059 * used as the ATTR_CMN_NAME attribute returned in vnode_attr.va_name.
3063 getattrlist_internal(vfs_context_t ctx
, vnode_t vp
, struct attrlist
*alp
,
3064 user_addr_t attributeBuffer
, size_t bufferSize
, uint64_t options
,
3065 enum uio_seg segflg
, char* authoritative_name
, struct ucred
*file_cred
)
3067 struct vnode_attr
*va
;
3068 kauth_action_t action
;
3077 char uio_buf
[UIO_SIZEOF(1)];
3078 // must be true for fork attributes to be used as new common attributes
3079 const int use_fork
= (options
& FSOPT_ATTR_CMN_EXTENDED
) != 0;
3081 if (bufferSize
< sizeof(uint32_t)) {
3085 proc_is64
= proc_is64bit(vfs_context_proc(ctx
));
3087 if (segflg
== UIO_USERSPACE
) {
3089 segflg
= UIO_USERSPACE64
;
3091 segflg
= UIO_USERSPACE32
;
3094 auio
= uio_createwithbuffer(1, 0, segflg
, UIO_READ
,
3095 &uio_buf
[0], sizeof(uio_buf
));
3096 uio_addiov(auio
, attributeBuffer
, bufferSize
);
3098 va
= kheap_alloc(KHEAP_TEMP
, sizeof(struct vnode_attr
), Z_WAITOK
);
3102 if (alp
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
3107 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
3108 vp
, vfs_context_proc(ctx
)->p_comm
, alp
->commonattr
, alp
->volattr
, alp
->fileattr
, alp
->dirattr
, alp
->forkattr
,
3109 (options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
3112 error
= mac_vnode_check_getattrlist(ctx
, vp
, alp
);
3119 * It is legal to request volume or file attributes, but not both.
3121 * 26903449 fork attributes can also be requested, but only if they're
3122 * interpreted as new, common attributes
3125 if (alp
->fileattr
|| alp
->dirattr
|| (alp
->forkattr
&& !use_fork
)) {
3127 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: mixed volume/file/directory attributes");
3130 /* handle volume attribute request */
3131 error
= getvolattrlist(ctx
, vp
, alp
, attributeBuffer
,
3132 bufferSize
, options
, segflg
, proc_is64
);
3137 * ATTR_CMN_GEN_COUNT and ATTR_CMN_DOCUMENT_ID reuse the bits
3138 * originally allocated to ATTR_CMN_NAMEDATTRCOUNT and
3139 * ATTR_CMN_NAMEDATTRLIST.
3141 if ((alp
->commonattr
& (ATTR_CMN_GEN_COUNT
| ATTR_CMN_DOCUMENT_ID
)) &&
3142 !(options
& FSOPT_ATTR_CMN_EXTENDED
)) {
3147 /* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */
3148 if (!(use_fork
) && (alp
->forkattr
& ATTR_CMNEXT_VALIDMASK
)) {
3153 /* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */
3154 if ((options
& FSOPT_ATTR_CMN_EXTENDED
) && (alp
->forkattr
& (ATTR_FORK_VALIDMASK
))) {
3159 /* Check for special packing semantics */
3160 return_valid
= (alp
->commonattr
& ATTR_CMN_RETURNED_ATTRS
) ? 1 : 0;
3161 pack_invalid
= (options
& FSOPT_PACK_INVAL_ATTRS
) ? 1 : 0;
3163 /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
3164 if (!return_valid
|| (alp
->forkattr
&& !use_fork
)) {
3168 /* Keep invalid attrs from being uninitialized */
3169 bzero(va
, sizeof(*va
));
3172 /* Pick up the vnode type. If the FS is bad and changes vnode types on us, we
3173 * will have a valid snapshot that we can work from here.
3178 * Set up the vnode_attr structure and authorise.
3180 if ((error
= getattrlist_setupvattr(alp
, va
, &fixedsize
, &action
, proc_is64
, (vtype
== VDIR
), use_fork
)) != 0) {
3181 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
3184 if ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0) {
3185 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorisation failed/denied");
3190 if (va
->va_active
!= 0) {
3191 uint64_t va_active
= va
->va_active
;
3194 * If we're going to ask for va_name, allocate a buffer to point it at
3196 if (VATTR_IS_ACTIVE(va
, va_name
)) {
3197 va_name
= zalloc(ZV_NAMEI
);
3199 * If we have an authoritative_name, prefer that name.
3201 * N.B. Since authoritative_name implies this is coming from getattrlistbulk,
3202 * we know the name is authoritative. For /dev/fd, we want to use the file
3203 * descriptor as the name not the underlying name of the associate vnode in a
3204 * particular file system.
3206 if (authoritative_name
) {
3207 /* Don't ask the file system */
3208 VATTR_CLEAR_ACTIVE(va
, va_name
);
3209 strlcpy(va_name
, authoritative_name
, MAXPATHLEN
);
3213 va
->va_name
= authoritative_name
? NULL
: va_name
;
3215 if (options
& FSOPT_RETURN_REALDEV
) {
3216 va
->va_vaflags
|= VA_REALFSID
;
3220 * Call the filesystem.
3222 if ((error
= vnode_getattr(vp
, va
, ctx
)) != 0) {
3223 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
3228 * Give MAC polices a chance to reject or filter the
3229 * attributes returned by the filesystem. Note that MAC
3230 * policies are consulted *after* calling the filesystem
3231 * because filesystems can return more attributes than
3232 * were requested so policies wouldn't be authoritative
3233 * is consulted beforehand. This also gives policies an
3234 * opportunity to change the values of attributes
3237 error
= mac_vnode_check_getattr(ctx
, file_cred
, vp
, va
);
3239 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: MAC framework returned %d", error
);
3247 * It we ask for the name, i.e., vname is non null and
3248 * we have an authoritative name, then reset va_name is
3249 * active and if needed set va_name is supported.
3251 * A (buggy) filesystem may change fields which belong
3252 * to us. We try to deal with that here as well.
3254 va
->va_active
= va_active
;
3255 if (authoritative_name
&& va_name
) {
3256 VATTR_SET_ACTIVE(va
, va_name
);
3257 if (!(VATTR_IS_SUPPORTED(va
, va_name
))) {
3258 VATTR_SET_SUPPORTED(va
, va_name
);
3261 va
->va_name
= va_name
;
3264 error
= vfs_attr_pack_internal(vp
->v_mount
, vp
, auio
, alp
, options
, va
, NULL
, ctx
,
3265 0, vtype
, fixedsize
);
3269 zfree(ZV_NAMEI
, va_name
);
3271 if (VATTR_IS_SUPPORTED(va
, va_acl
) && (va
->va_acl
!= NULL
)) {
3272 kauth_acl_free(va
->va_acl
);
3274 kheap_free(KHEAP_TEMP
, va
, sizeof(struct vnode_attr
));
3276 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
3281 fgetattrlist(proc_t p
, struct fgetattrlist_args
*uap
, __unused
int32_t *retval
)
3287 struct fileproc
*fp
;
3289 ctx
= vfs_context_current();
3294 if ((error
= fp_get_ftype(p
, uap
->fd
, DTYPE_VNODE
, EINVAL
, &fp
)) != 0) {
3297 vp
= (struct vnode
*)fp
->fp_glob
->fg_data
;
3299 if ((error
= vnode_getwithref(vp
)) != 0) {
3304 * Fetch the attribute request.
3306 error
= copyin(uap
->alist
, &al
, sizeof(al
));
3311 /* Default to using the vnode's name. */
3312 error
= getattrlist_internal(ctx
, vp
, &al
, uap
->attributeBuffer
,
3313 uap
->bufferSize
, uap
->options
,
3314 (IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: \
3315 UIO_USERSPACE32
), NULL
,
3316 fp
->fp_glob
->fg_cred
);
3321 fp_drop(p
, uap
->fd
, fp
, 0);
3327 getattrlistat_internal(vfs_context_t ctx
, user_addr_t path
,
3328 struct attrlist
*alp
, user_addr_t attributeBuffer
, size_t bufferSize
,
3329 uint64_t options
, enum uio_seg segflg
, enum uio_seg pathsegflg
, int fd
)
3331 struct nameidata nd
;
3340 if (!(options
& FSOPT_NOFOLLOW
)) {
3341 nameiflags
|= FOLLOW
;
3344 nameiflags
|= AUDITVNPATH1
;
3345 NDINIT(&nd
, LOOKUP
, OP_GETATTR
, nameiflags
, pathsegflg
,
3348 error
= nameiat(&nd
, fd
);
3356 error
= getattrlist_internal(ctx
, vp
, alp
, attributeBuffer
,
3357 bufferSize
, options
, segflg
, NULL
, NOCRED
);
3359 /* Retain the namei reference until the getattrlist completes. */
3366 getattrlist(proc_t p
, struct getattrlist_args
*uap
, __unused
int32_t *retval
)
3368 enum uio_seg segflg
;
3372 segflg
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
3375 * Fetch the attribute request.
3377 error
= copyin(uap
->alist
, &al
, sizeof(al
));
3382 return getattrlistat_internal(vfs_context_current(),
3383 CAST_USER_ADDR_T(uap
->path
), &al
,
3384 CAST_USER_ADDR_T(uap
->attributeBuffer
), uap
->bufferSize
,
3385 (uint64_t)uap
->options
, segflg
, segflg
, AT_FDCWD
);
3389 getattrlistat(proc_t p
, struct getattrlistat_args
*uap
, __unused
int32_t *retval
)
3391 enum uio_seg segflg
;
3395 segflg
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
3398 * Fetch the attribute request.
3400 error
= copyin(uap
->alist
, &al
, sizeof(al
));
3405 return getattrlistat_internal(vfs_context_current(),
3406 CAST_USER_ADDR_T(uap
->path
), &al
,
3407 CAST_USER_ADDR_T(uap
->attributeBuffer
), uap
->bufferSize
,
3408 (uint64_t)uap
->options
, segflg
, segflg
, uap
->fd
);
3412 * This refills the per-fd direntries cache by issuing a VNOP_READDIR.
3413 * It attempts to try and find a size the filesystem responds to, so
3414 * it first tries 1 direntry sized buffer and going from 1 to 2 to 4
3415 * direntry sized buffers to readdir. If the filesystem does not respond
3416 * to 4 * direntry it returns the error by the filesystem (if any) and sets
3419 * This function also tries again if the last "refill" returned an EOF
3420 * to try and get any additional entries if they were added after the last
3424 refill_fd_direntries(vfs_context_t ctx
, vnode_t dvp
, struct fd_vn_data
*fvd
,
3428 char uio_buf
[UIO_SIZEOF(1)];
3436 * If the last readdir returned EOF, don't try again.
3438 if (fvd
->fv_eofflag
) {
3441 kheap_free(KHEAP_DATA_BUFFERS
, fvd
->fv_buf
, fvd
->fv_bufallocsiz
);
3450 * If there is a cached allocation size of the dirbuf that should be
3451 * allocated, use that. Otherwise start with a allocation size of
3452 * FV_DIRBUF_START_SIZ. This start size may need to be increased if the
3453 * filesystem doesn't respond to the initial size.
3456 if (fvd
->fv_offset
&& fvd
->fv_bufallocsiz
) {
3457 rdirbufsiz
= fvd
->fv_bufallocsiz
;
3459 rdirbufsiz
= FV_DIRBUF_START_SIZ
;
3464 rdir_uio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3465 &uio_buf
[0], sizeof(uio_buf
));
3469 * Don't explicitly zero out this buffer since this is
3470 * not copied out to user space.
3473 fvd
->fv_buf
= kheap_alloc(KHEAP_DATA_BUFFERS
, rdirbufsiz
, Z_WAITOK
);
3474 fvd
->fv_bufallocsiz
= rdirbufsiz
;
3475 fvd
->fv_bufdone
= 0;
3478 uio_reset(rdir_uio
, fvd
->fv_eoff
, UIO_SYSSPACE
, UIO_READ
);
3479 uio_addiov(rdir_uio
, CAST_USER_ADDR_T(fvd
->fv_buf
), rdirbufsiz
);
3482 * Some filesystems do not set nentries or eofflag...
3486 error
= vnode_readdir64(dvp
, rdir_uio
, VNODE_READDIR_EXTENDED
,
3487 &eofflag
, &nentries
, ctx
);
3489 rdirbufused
= rdirbufsiz
- (size_t)uio_resid(rdir_uio
);
3491 if (!error
&& (rdirbufused
> 0) && (rdirbufused
<= rdirbufsiz
)) {
3493 fvd
->fv_soff
= fvd
->fv_eoff
;
3494 fvd
->fv_eoff
= uio_offset(rdir_uio
);
3495 /* Save eofflag state but don't return EOF for this time.*/
3496 fvd
->fv_eofflag
= eofflag
;
3498 /* Reset buffer parameters */
3499 fvd
->fv_bufsiz
= rdirbufused
;
3500 fvd
->fv_bufdone
= 0;
3501 bzero(fvd
->fv_buf
+ rdirbufused
, rdirbufsiz
- rdirbufused
);
3502 } else if (!eofflag
&& (rdirbufsiz
< FV_DIRBUF_MAX_SIZ
)) {
3504 * Some Filesystems have higher requirements for the
3505 * smallest buffer size they will respond to for a
3506 * directory listing. Start (relatively) small but increase
3507 * it upto FV_DIRBUF_MAX_SIZ. Most should be good with
3508 * 1*direntry. Cache the size found so that this does not need
3509 * need to be done every time. This also means that an error
3510 * from VNOP_READDIR is ignored until at least FV_DIRBUF_MAX_SIZ
3511 * has been attempted.
3513 kheap_free(KHEAP_DATA_BUFFERS
, fvd
->fv_buf
, fvd
->fv_bufallocsiz
);
3514 rdirbufsiz
= 2 * rdirbufsiz
;
3515 fvd
->fv_bufallocsiz
= 0;
3517 } else if (!error
) {
3519 * The Filesystem did not set eofflag but also did not
3520 * return any entries (or an error). It is presumed that
3521 * EOF has been reached.
3523 fvd
->fv_eofflag
= eofflag
= 1;
3527 * If the filesystem returned an error and it had previously returned
3528 * EOF, ignore the error and set EOF.
3530 if (error
&& fvd
->fv_eofflag
) {
3536 * If either the directory has either hit EOF or an error, now is a good
3537 * time to free up directory entry buffer.
3539 if ((error
|| eofflag
) && fvd
->fv_buf
) {
3540 kheap_free(KHEAP_DATA_BUFFERS
, fvd
->fv_buf
, fvd
->fv_bufallocsiz
);
3542 fvd
->fv_bufallocsiz
= 0;
3546 *eofflagp
= eofflag
;
3552 * gets the current direntry. To advance to the next direntry this has to be
3553 * paired with a direntry_done.
3555 * Since directories have restrictions on where directory enumeration
3556 * can restart from, entries are first read into* a per fd diectory entry
3557 * "cache" and entries provided from that cache.
3560 get_direntry(vfs_context_t ctx
, vnode_t dvp
, struct fd_vn_data
*fvd
,
3561 int *eofflagp
, struct direntry
**dpp
)
3569 if (!fvd
->fv_bufsiz
) {
3570 error
= refill_fd_direntries(ctx
, dvp
, fvd
, &eofflag
);
3575 *eofflagp
= eofflag
;
3580 *dpp
= (struct direntry
*)(fvd
->fv_buf
+ fvd
->fv_bufdone
);
3585 * Advances to the next direntry.
3588 direntry_done(struct fd_vn_data
*fvd
)
3590 struct direntry
*dp
;
3592 dp
= (struct direntry
*)(fvd
->fv_buf
+ fvd
->fv_bufdone
);
3594 fvd
->fv_bufdone
+= dp
->d_reclen
;
3595 if (fvd
->fv_bufdone
> fvd
->fv_bufsiz
) {
3596 fvd
->fv_bufdone
= fvd
->fv_bufsiz
;
3599 fvd
->fv_bufdone
= fvd
->fv_bufsiz
;
3603 * If we're at the end the fd direntries cache, reset the
3606 if (fvd
->fv_bufdone
== fvd
->fv_bufsiz
) {
3607 fvd
->fv_bufdone
= 0;
3613 * A stripped down version of getattrlist_internal to fill in only select
3614 * attributes in case of an error from getattrlist_internal.
3616 * It always returns at least ATTR_BULK_REQUIRED i.e. the name (but may also
3617 * return some other attributes which can be obtained from the vnode).
3619 * It does not change the value of the passed in attrlist.
3621 * The objective of this function is to fill in an "error entry", i.e.
3622 * an entry with ATTR_CMN_RETURNED_ATTRS & ATTR_CMN_NAME. If the caller
3623 * has also asked for ATTR_CMN_ERROR, it is filled in as well.
3626 * vp - vnode pointer
3627 * alp - pointer to attrlist struct.
3628 * options - options passed to getattrlistbulk(2)
3629 * kern_attr_buf - Kernel buffer to fill data (assumes offset 0 in
3631 * kern_attr_buf_siz - Size of buffer.
3632 * needs_error_attr - Whether the caller asked for ATTR_CMN_ERROR
3633 * error_attr - This value is used to fill ATTR_CMN_ERROR (if the user
3634 * has requested it in the attribute list.
3635 * namebuf - This is used to fill in the name.
3636 * ctx - vfs context of caller.
3639 get_error_attributes(vnode_t vp
, struct attrlist
*alp
, uint64_t options
,
3640 user_addr_t kern_attr_buf
, size_t kern_attr_buf_siz
, int error_attr
,
3641 caddr_t namebuf
, vfs_context_t ctx
)
3644 struct _attrlist_buf ab
;
3646 kauth_action_t action
;
3648 int needs_error_attr
= (alp
->commonattr
& ATTR_CMN_ERROR
);
3651 * To calculate fixed size required, in the FSOPT_PACK_INVAL_ATTRS case,
3652 * the fixedsize should include space for all the attributes asked by
3653 * the user. Only ATTR_BULK_REQUIRED (and ATTR_CMN_ERROR) will be filled
3654 * and will be valid. All other attributes are zeroed out later.
3656 * ATTR_CMN_RETURNED_ATTRS, ATTR_CMN_ERROR and ATTR_CMN_NAME
3657 * (the only valid ones being returned from here) happen to be
3658 * the first three attributes by order as well.
3661 if (!(options
& FSOPT_PACK_INVAL_ATTRS
)) {
3663 * In this case the fixedsize only needs to be only for the
3664 * attributes being actually returned.
3666 al
.commonattr
= ATTR_BULK_REQUIRED
;
3667 if (needs_error_attr
) {
3668 al
.commonattr
|= ATTR_CMN_ERROR
;
3675 * Passing NULL for the vnode_attr pointer is valid for
3676 * getattrlist_setupvattr. All that is required is the size.
3679 (void)getattrlist_setupvattr(&al
, NULL
, (ssize_t
*)&fsiz
,
3680 &action
, proc_is64bit(vfs_context_proc(ctx
)),
3681 (vnode_vtype(vp
) == VDIR
), (options
& FSOPT_ATTR_CMN_EXTENDED
));
3683 namelen
= strlen(namebuf
);
3685 vsiz
= ((vsiz
+ 3) & ~0x03);
3687 bzero(&ab
, sizeof(ab
));
3688 ab
.base
= (char *)kern_attr_buf
;
3689 ab
.needed
= fsiz
+ vsiz
;
3691 /* Fill in the size needed */
3692 *((uint32_t *)ab
.base
) = (uint32_t)ab
.needed
;
3693 if (ab
.needed
> (ssize_t
)kern_attr_buf_siz
) {
3698 * Setup to pack results into the destination buffer.
3700 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
3702 * Zero out buffer, ab.fixedbuffer starts after the first uint32_t
3703 * which gives the length. This ensures everything that we don't
3704 * fill in explicitly later is zeroed out correctly.
3706 bzero(ab
.fixedcursor
, fsiz
);
3708 * variable size data should start after all the fixed
3711 ab
.varcursor
= ab
.base
+ fsiz
;
3713 * Initialise the value for ATTR_CMN_RETURNED_ATTRS and leave space
3714 * Leave space for filling in its value here at the end.
3716 bzero(&ab
.actual
, sizeof(ab
.actual
));
3717 ab
.fixedcursor
+= sizeof(attribute_set_t
);
3719 ab
.allocated
= ab
.needed
;
3721 /* Fill ATTR_CMN_ERROR (if asked for) */
3722 if (needs_error_attr
) {
3723 ATTR_PACK4(ab
, error_attr
);
3724 ab
.actual
.commonattr
|= ATTR_CMN_ERROR
;
3728 * Fill ATTR_CMN_NAME, The attrrefrence is packed at this location
3729 * but the actual string itself is packed after fixedsize which set
3730 * to different lengths based on whether FSOPT_PACK_INVAL_ATTRS
3733 attrlist_pack_string(&ab
, namebuf
, namelen
);
3736 * Now Fill in ATTR_CMN_RETURNED_ATTR. This copies to a
3737 * location after the count i.e. before ATTR_CMN_ERROR and
3740 ab
.actual
.commonattr
|= ATTR_CMN_NAME
| ATTR_CMN_RETURNED_ATTRS
;
3741 bcopy(&ab
.actual
, ab
.base
+ sizeof(uint32_t), sizeof(ab
.actual
));
3747 * This is the buffer size required to return at least 1 entry. We need space
3748 * for the length, for ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_NAME. Assuming the
3749 * smallest filename of a single byte we get
3752 #define MIN_BUF_SIZE_REQUIRED (sizeof(uint32_t) + sizeof(attribute_set_t) +\
3753 sizeof(attrreference_t))
3756 * Read directory entries and get attributes filled in for each directory
3759 readdirattr(vnode_t dvp
, struct fd_vn_data
*fvd
, uio_t auio
,
3760 struct attrlist
*alp
, uint64_t options
, int *count
, int *eofflagp
,
3763 caddr_t kern_attr_buf
;
3764 size_t kern_attr_buf_siz
;
3765 caddr_t max_path_name_buf
= NULL
;
3771 if (uio_iovcnt(auio
) > 1) {
3776 * We fill in a kernel buffer for the attributes and uiomove each
3777 * entry's attributes (as returned by getattrlist_internal)
3779 kern_attr_buf_siz
= uio_resid(auio
);
3780 if (kern_attr_buf_siz
> ATTR_MAX_BUFFER
) {
3781 kern_attr_buf_siz
= ATTR_MAX_BUFFER
;
3782 } else if (kern_attr_buf_siz
== 0) {
3787 kern_attr_buf
= kheap_alloc(KHEAP_TEMP
, kern_attr_buf_siz
, Z_WAITOK
);
3789 while (uio_resid(auio
) > (user_ssize_t
)MIN_BUF_SIZE_REQUIRED
) {
3790 struct direntry
*dp
;
3791 user_addr_t name_buffer
;
3792 struct nameidata nd
;
3801 * get_direntry returns the current direntry and does not
3802 * advance. A move to the next direntry only happens if
3803 * direntry_done is called.
3805 error
= get_direntry(ctx
, dvp
, fvd
, eofflagp
, &dp
);
3806 if (error
|| (*eofflagp
) || !dp
) {
3811 * skip "." and ".." (and a bunch of other invalid conditions.)
3813 if (!dp
->d_reclen
|| dp
->d_ino
== 0 || dp
->d_namlen
== 0 ||
3814 (dp
->d_namlen
== 1 && dp
->d_name
[0] == '.') ||
3815 (dp
->d_namlen
== 2 && dp
->d_name
[0] == '.' &&
3816 dp
->d_name
[1] == '.')) {
3822 * try to deal with not-null terminated filenames.
3824 if (dp
->d_name
[dp
->d_namlen
] != '\0') {
3825 if (!max_path_name_buf
) {
3826 max_path_name_buf
= zalloc_flags(ZV_NAMEI
, Z_WAITOK
);
3828 bcopy(dp
->d_name
, max_path_name_buf
, dp
->d_namlen
);
3829 max_path_name_buf
[dp
->d_namlen
] = '\0';
3830 name_buffer
= CAST_USER_ADDR_T(max_path_name_buf
);
3832 name_buffer
= CAST_USER_ADDR_T(&(dp
->d_name
));
3836 * We have an iocount on the directory already.
3838 * Note that we supply NOCROSSMOUNT to the namei call as we attempt to acquire
3839 * a vnode for this particular entry. This is because the native call will
3840 * (likely) attempt to emit attributes based on its own metadata in order to avoid
3841 * creating vnodes where posssible. If the native call is not going to walk
3842 * up the vnode mounted-on chain in order to find the top-most mount point, then we
3843 * should not either in this emulated readdir+getattrlist() approach. We
3844 * will be responsible for setting DIR_MNTSTATUS_MNTPOINT on that directory that
3845 * contains a mount point.
3847 NDINIT(&nd
, LOOKUP
, OP_GETATTR
, (AUDITVNPATH1
| USEDVP
| NOCROSSMOUNT
),
3848 UIO_SYSSPACE
, CAST_USER_ADDR_T(name_buffer
), ctx
);
3862 * getattrlist_internal can change the values of the
3863 * the required attribute list. Copy the current values
3864 * and use that one instead.
3868 error
= getattrlist_internal(ctx
, vp
, &al
,
3869 CAST_USER_ADDR_T(kern_attr_buf
), kern_attr_buf_siz
,
3870 options
| FSOPT_REPORT_FULLSIZE
, UIO_SYSSPACE
,
3871 CAST_DOWN_EXPLICIT(char *, name_buffer
),
3877 get_error_attributes(vp
, alp
, options
,
3878 CAST_USER_ADDR_T(kern_attr_buf
),
3879 kern_attr_buf_siz
, error
, (caddr_t
)name_buffer
,
3884 /* Done with vnode now */
3888 * Because FSOPT_REPORT_FULLSIZE was set, the first 4 bytes
3889 * of the buffer returned by getattrlist contains the size
3890 * (even if the provided buffer isn't sufficiently big). Use
3891 * that to check if we've run out of buffer space.
3893 * resid is a signed type, and the size of the buffer etc
3894 * are unsigned types. It is theoretically possible for
3895 * resid to be < 0 and in which case we would be assigning
3896 * an out of bounds value to bytes_left (which is unsigned)
3897 * uiomove takes care to not ever set resid to < 0, so it
3898 * is safe to do this here.
3900 bytes_left
= (size_t)((user_size_t
)uio_resid(auio
));
3901 entlen
= (size_t)(*((uint32_t *)(kern_attr_buf
)));
3902 if (!entlen
|| (entlen
> bytes_left
)) {
3907 * Will the pad bytes fit as well ? If they can't be, still use
3908 * this entry but this will be the last entry returned.
3910 pad_bytes
= ((entlen
+ 7) & ~0x07) - entlen
;
3912 if (pad_bytes
&& (entlen
+ pad_bytes
<= bytes_left
)) {
3914 * While entlen can never be > ATTR_MAX_BUFFER,
3915 * (entlen + pad_bytes) can be, handle that and
3916 * zero out the pad bytes. N.B. - Only zero
3917 * out information in the kernel buffer that is
3918 * going to be uiomove'ed out.
3920 if (entlen
+ pad_bytes
<= kern_attr_buf_siz
) {
3921 /* This is the normal case. */
3922 bzero(kern_attr_buf
+ entlen
, pad_bytes
);
3924 bzero(kern_attr_buf
+ entlen
,
3925 kern_attr_buf_siz
- entlen
);
3927 * Pad bytes left over, change the resid value
3928 * manually. We only got in here because
3929 * bytes_left >= entlen + pad_bytes so
3930 * new_resid (which is a signed type) is
3933 new_resid
= (ssize_t
)(bytes_left
-
3934 (entlen
+ pad_bytes
));
3936 entlen
+= pad_bytes
;
3938 *((uint32_t *)kern_attr_buf
) = (uint32_t)entlen
;
3939 error
= uiomove(kern_attr_buf
, min((int)entlen
, (int)kern_attr_buf_siz
),
3947 uio_setresid(auio
, (user_ssize_t
)new_resid
);
3951 * At this point, the directory entry has been consumed, proceed
3958 if (max_path_name_buf
) {
3959 zfree(ZV_NAMEI
, max_path_name_buf
);
3963 * At this point, kern_attr_buf is always allocated
3965 kheap_free(KHEAP_TEMP
, kern_attr_buf
, kern_attr_buf_siz
);
3968 * Always set the offset to the last succesful offset
3969 * returned by VNOP_READDIR.
3971 uio_setoffset(auio
, fvd
->fv_eoff
);
3976 /* common attributes that only require KAUTH_VNODE_LIST_DIRECTORY */
3977 #define LIST_DIR_ATTRS (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE | \
3978 ATTR_CMN_FILEID | ATTR_CMN_RETURNED_ATTRS | \
3982 * int getattrlistbulk(int dirfd, struct attrlist *alist, void *attributeBuffer,
3983 * size_t bufferSize, uint64_t options)
3985 * Gets directory entries alongwith their attributes in the same way
3986 * getattrlist does for a single file system object.
3988 * On non error returns, retval will hold the count of entries returned.
3991 getattrlistbulk(proc_t p
, struct getattrlistbulk_args
*uap
, int32_t *retval
)
3994 vnode_t dvp
= NULLVP
;
3995 struct fileproc
*fp
;
3996 struct fd_vn_data
*fvdata
;
3999 enum uio_seg segflg
;
4002 char uio_buf
[UIO_SIZEOF(1)];
4003 kauth_action_t action
;
4010 error
= fp_getfvp(p
, uap
->dirfd
, &fp
, &dvp
);
4018 ctx
= vfs_context_current();
4019 ut
= get_bsdthread_info(current_thread());
4020 segflg
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
4022 if ((fp
->fp_glob
->fg_flag
& FREAD
) == 0) {
4024 * AUDIT_ARG(vnpath_withref, dvp, ARG_VNODE1);
4031 if ((error
= vnode_getwithref(dvp
))) {
4036 if (uap
->options
& FSOPT_LIST_SNAPSHOT
) {
4039 if (!vnode_isvroot(dvp
)) {
4044 /* switch directory to snapshot directory */
4045 error
= vnode_get_snapdir(dvp
, &snapdvp
, ctx
);
4053 if (dvp
->v_type
!= VDIR
) {
4059 error
= mac_file_check_change_offset(vfs_context_ucred(ctx
),
4066 * XXX : Audit Support
4067 * AUDIT_ARG(vnpath, dvp, ARG_VNODE1);
4070 options
= uap
->options
| FSOPT_ATTR_CMN_EXTENDED
;
4072 if ((error
= copyin(CAST_USER_ADDR_T(uap
->alist
), &al
,
4073 sizeof(struct attrlist
)))) {
4078 ((al
.commonattr
& ATTR_BULK_REQUIRED
) != ATTR_BULK_REQUIRED
)) {
4084 error
= mac_vnode_check_readdir(ctx
, dvp
);
4091 * Requested attributes that are available in the direntry struct, with the addition
4092 * of ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_ERROR, can be let past with just LIST_DIRECTORY.
4093 * Any other requested attributes require SEARCH as well.
4095 action
= KAUTH_VNODE_LIST_DIRECTORY
;
4096 if ((al
.commonattr
& ~LIST_DIR_ATTRS
) || al
.fileattr
|| al
.dirattr
) {
4097 action
|= KAUTH_VNODE_SEARCH
;
4100 error
= vnode_authorize(dvp
, NULL
, action
, ctx
);
4105 fvdata
= (struct fd_vn_data
*)fp
->fp_glob
->fg_vn_data
;
4107 panic("Directory expected to have fg_vn_data");
4113 * getattrlistbulk(2) maintains its offset in fv_offset. However
4114 * if the offset in the file glob is set (or reset) to 0, the directory
4115 * traversal needs to be restarted (Any existing state in the
4116 * directory buffer is removed as well).
4118 if (!fp
->fp_glob
->fg_offset
) {
4119 fvdata
->fv_offset
= 0;
4120 kheap_free(KHEAP_DATA_BUFFERS
, fvdata
->fv_buf
,
4121 fvdata
->fv_bufallocsiz
);
4122 fvdata
->fv_bufsiz
= 0;
4123 fvdata
->fv_bufdone
= 0;
4124 fvdata
->fv_soff
= 0;
4125 fvdata
->fv_eoff
= 0;
4126 fvdata
->fv_eofflag
= 0;
4129 auio
= uio_createwithbuffer(1, fvdata
->fv_offset
, segflg
, UIO_READ
,
4130 &uio_buf
[0], sizeof(uio_buf
));
4131 uio_addiov(auio
, uap
->attributeBuffer
, (user_size_t
)uap
->bufferSize
);
4134 * For "expensive" operations in which the native VNOP implementations
4135 * end up having to do just as much (if not more) work than the default
4136 * implementation, fall back to the default implementation.
4137 * The VNOP helper functions depend on the filesystem providing the
4138 * object type, if the caller has not requested ATTR_CMN_OBJTYPE, fall
4139 * back to the default implementation.
4141 if ((al
.commonattr
&
4142 (ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
| ATTR_CMN_EXTENDED_SECURITY
)) ||
4143 !(al
.commonattr
& ATTR_CMN_OBJTYPE
)) {
4146 struct vnode_attr
*va
;
4149 if (fvdata
->fv_eofflag
&& !fvdata
->fv_buf
) {
4151 * If the last successful VNOP_GETATTRLISTBULK or
4152 * VNOP_READDIR returned EOF, don't try again.
4161 va
= kheap_alloc(KHEAP_TEMP
, sizeof(struct vnode_attr
), Z_WAITOK
);
4164 va_name
= zalloc_flags(ZV_NAMEI
, Z_WAITOK
| Z_ZERO
);
4165 va
->va_name
= va_name
;
4167 (void)getattrlist_setupvattr_all(&al
, va
, VNON
, NULL
,
4168 IS_64BIT_PROCESS(p
), (uap
->options
& FSOPT_ATTR_CMN_EXTENDED
));
4171 * Set UT_KERN_RAGE_VNODES to cause all vnodes created by the
4172 * filesystem to be rapidly aged.
4174 ut
->uu_flag
|= UT_KERN_RAGE_VNODES
;
4175 error
= VNOP_GETATTRLISTBULK(dvp
, &al
, va
, auio
, NULL
,
4176 options
, &eofflag
, &count
, ctx
);
4177 ut
->uu_flag
&= ~UT_KERN_RAGE_VNODES
;
4179 zfree(ZV_NAMEI
, va_name
);
4180 kheap_free(KHEAP_TEMP
, va
, sizeof(struct vnode_attr
));
4183 * cache state of eofflag.
4186 fvdata
->fv_eofflag
= eofflag
;
4192 * If the Filessytem does not natively support getattrlistbulk,
4193 * do the default implementation.
4195 if (error
== ENOTSUP
) {
4199 ut
->uu_flag
|= UT_KERN_RAGE_VNODES
;
4200 error
= readdirattr(dvp
, fvdata
, auio
, &al
, options
,
4201 &count
, &eofflag
, ctx
);
4202 ut
->uu_flag
&= ~UT_KERN_RAGE_VNODES
;
4206 fvdata
->fv_offset
= uio_offset(auio
);
4207 fp
->fp_glob
->fg_offset
= fvdata
->fv_offset
;
4210 } else if (!error
&& !eofflag
) {
4212 * This just means the buffer was too small to fit even a
4224 file_drop(uap
->dirfd
);
4230 attrlist_unpack_fixed(char **cursor
, char *end
, void *buf
, ssize_t size
)
4232 /* make sure we have enough source data */
4233 if ((*cursor
) + size
> end
) {
4237 bcopy(*cursor
, buf
, size
);
4242 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
4243 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = (typeof(v))_f;} while(0)
4244 #define ATTR_UNPACK_TIME(v, is64) \
4247 struct user64_timespec us; \
4249 v.tv_sec = (unsigned long)us.tv_sec; \
4250 v.tv_nsec = (long)us.tv_nsec; \
4252 struct user32_timespec us; \
4254 v.tv_sec = us.tv_sec; \
4255 v.tv_nsec = us.tv_nsec; \
4264 setattrlist_internal(vnode_t vp
, struct setattrlist_args
*uap
, proc_t p
, vfs_context_t ctx
)
4267 struct vnode_attr va
;
4268 struct attrreference ar
;
4269 kauth_action_t action
;
4270 char *user_buf
, *cursor
, *bufend
, *fndrinfo
, *cp
, *volname
;
4271 int proc_is64
, error
;
4273 kauth_filesec_t rfsec
;
4279 proc_is64
= proc_is64bit(p
);
4283 * Fetch the attribute set and validate.
4285 if ((error
= copyin(uap
->alist
, (caddr_t
) &al
, sizeof(al
)))) {
4288 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
4293 #if DEVELOPMENT || DEBUG
4295 * XXX VSWAP: Check for entitlements or special flag here
4296 * so we can restrict access appropriately.
4298 #else /* DEVELOPMENT || DEBUG */
4300 if (vnode_isswap(vp
) && (ctx
!= vfs_context_kernel())) {
4304 #endif /* DEVELOPMENT || DEBUG */
4306 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
4307 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
4308 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
4311 if ((al
.volattr
& ~ATTR_VOL_SETMASK
) ||
4312 (al
.commonattr
& ~ATTR_CMN_VOLSETMASK
) ||
4316 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
4320 if ((al
.commonattr
& ~ATTR_CMN_SETMASK
) ||
4321 (al
.fileattr
& ~ATTR_FILE_SETMASK
) ||
4322 (al
.dirattr
& ~ATTR_DIR_SETMASK
) ||
4323 (al
.forkattr
& ~ATTR_FORK_SETMASK
)) {
4325 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
4331 * If the caller's bitmaps indicate that there are no attributes to set,
4334 if (al
.commonattr
== 0 &&
4335 (al
.volattr
& ~ATTR_VOL_INFO
) == 0 &&
4344 * Make the naive assumption that the caller has supplied a reasonable buffer
4345 * size. We could be more careful by pulling in the fixed-size region, checking
4346 * the attrref structures, then pulling in the variable section.
4347 * We need to reconsider this for handling large ACLs, as they should probably be
4348 * brought directly into a buffer. Multiple copyins will make this slower though.
4350 * We could also map the user buffer if it is larger than some sensible mimimum.
4352 if (uap
->bufferSize
> ATTR_MAX_BUFFER
) {
4353 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size %d too large", uap
->bufferSize
);
4357 user_buf
= kheap_alloc(KHEAP_DATA_BUFFERS
, uap
->bufferSize
, Z_WAITOK
);
4358 if (user_buf
== NULL
) {
4359 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap
->bufferSize
);
4363 if ((error
= copyin(uap
->attributeBuffer
, user_buf
, uap
->bufferSize
)) != 0) {
4364 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer copyin failed");
4367 VFS_DEBUG(ctx
, vp
, "ATTRLIST - copied in %d bytes of user attributes to %p", uap
->bufferSize
, user_buf
);
4370 error
= mac_vnode_check_setattrlist(ctx
, vp
, &al
);
4377 * Unpack the argument buffer.
4380 bufend
= cursor
+ uap
->bufferSize
;
4383 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
4384 ATTR_UNPACK(va
.va_encoding
);
4385 VATTR_SET_ACTIVE(&va
, va_encoding
);
4387 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
4388 ATTR_UNPACK_TIME(va
.va_create_time
, proc_is64
);
4389 VATTR_SET_ACTIVE(&va
, va_create_time
);
4391 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
4392 ATTR_UNPACK_TIME(va
.va_modify_time
, proc_is64
);
4393 VATTR_SET_ACTIVE(&va
, va_modify_time
);
4395 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
4396 ATTR_UNPACK_TIME(va
.va_change_time
, proc_is64
);
4397 al
.commonattr
&= ~ATTR_CMN_CHGTIME
;
4398 /*quietly ignore change time; advisory in man page*/
4400 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
4401 ATTR_UNPACK_TIME(va
.va_access_time
, proc_is64
);
4402 VATTR_SET_ACTIVE(&va
, va_access_time
);
4404 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
4405 ATTR_UNPACK_TIME(va
.va_backup_time
, proc_is64
);
4406 VATTR_SET_ACTIVE(&va
, va_backup_time
);
4408 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
4409 if ((cursor
+ 32) > bufend
) {
4411 VFS_DEBUG(ctx
, vp
, "ATTRLIST - not enough data supplied for FINDERINFO");
4417 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
4418 ATTR_UNPACK(va
.va_uid
);
4419 VATTR_SET_ACTIVE(&va
, va_uid
);
4421 if (al
.commonattr
& ATTR_CMN_GRPID
) {
4422 ATTR_UNPACK(va
.va_gid
);
4423 VATTR_SET_ACTIVE(&va
, va_gid
);
4425 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
4426 ATTR_UNPACK_CAST(uint32_t, va
.va_mode
);
4427 VATTR_SET_ACTIVE(&va
, va_mode
);
4429 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
4430 ATTR_UNPACK(va
.va_flags
);
4431 VATTR_SET_ACTIVE(&va
, va_flags
);
4433 if ((error
= mac_vnode_check_setflags(ctx
, vp
, va
.va_flags
)) != 0) {
4438 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
4440 * We are (for now) passed a kauth_filesec_t, but all we want from
4445 if (ar
.attr_dataoffset
< 0) {
4446 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad offset supplied", ar
.attr_dataoffset
);
4451 cp
+= ar
.attr_dataoffset
;
4452 rfsec
= (kauth_filesec_t
)cp
;
4453 if (((((char *)rfsec
) + KAUTH_FILESEC_SIZE(0)) > bufend
) || /* no space for acl */
4454 (rfsec
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) || /* bad magic */
4455 (KAUTH_FILESEC_COPYSIZE(rfsec
) != ar
.attr_length
) || /* size does not match */
4456 ((cp
+ KAUTH_FILESEC_COPYSIZE(rfsec
)) > bufend
)) { /* ACEs overrun buffer */
4458 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied", ar
.attr_length
);
4461 nace
= rfsec
->fsec_entrycount
;
4462 if (nace
== KAUTH_FILESEC_NOACL
) {
4465 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
4467 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied");
4470 nace
= rfsec
->fsec_acl
.acl_entrycount
;
4471 if (nace
== KAUTH_FILESEC_NOACL
) {
4473 VATTR_SET(&va
, va_acl
, NULL
);
4475 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
4477 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: supplied ACL is too large");
4480 VATTR_SET(&va
, va_acl
, &rfsec
->fsec_acl
);
4483 if (al
.commonattr
& ATTR_CMN_UUID
) {
4484 ATTR_UNPACK(va
.va_uuuid
);
4485 VATTR_SET_ACTIVE(&va
, va_uuuid
);
4487 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
4488 ATTR_UNPACK(va
.va_guuid
);
4489 VATTR_SET_ACTIVE(&va
, va_guuid
);
4491 if (al
.commonattr
& ATTR_CMN_ADDEDTIME
) {
4492 ATTR_UNPACK_TIME(va
.va_addedtime
, proc_is64
);
4493 VATTR_SET_ACTIVE(&va
, va_addedtime
);
4495 /* Support setattrlist of data protection class */
4496 if (al
.commonattr
& ATTR_CMN_DATA_PROTECT_FLAGS
) {
4497 ATTR_UNPACK(va
.va_dataprotect_class
);
4498 VATTR_SET_ACTIVE(&va
, va_dataprotect_class
);
4502 if (al
.volattr
& ATTR_VOL_INFO
) {
4503 if (al
.volattr
& ATTR_VOL_NAME
) {
4506 /* attr_length cannot be 0! */
4507 if ((ar
.attr_dataoffset
< 0) || (ar
.attr_length
== 0) ||
4508 (ar
.attr_length
> uap
->bufferSize
) ||
4509 (uap
->bufferSize
- ar
.attr_length
< (unsigned)ar
.attr_dataoffset
)) {
4510 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad offset supplied (2) ", ar
.attr_dataoffset
);
4515 if (volname
>= bufend
- ar
.attr_dataoffset
- ar
.attr_length
) {
4517 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume name too big for caller buffer");
4520 volname
+= ar
.attr_dataoffset
;
4521 /* guarantee NUL termination */
4522 volname
[ar
.attr_length
- 1] = 0;
4527 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
4528 /* XXX does it actually make any sense to change this? */
4530 VFS_DEBUG(ctx
, vp
, "ATTRLIST - XXX device type change not implemented");
4535 * Validate and authorize.
4538 if ((va
.va_active
!= 0LL) && ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0)) {
4539 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attribute changes refused: %d", error
);
4543 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
4544 * and will be auth'ed by the FS.
4546 if (fndrinfo
!= NULL
) {
4547 if (al
.volattr
& ATTR_VOL_INFO
) {
4548 if (vp
->v_tag
!= VT_HFS
) {
4553 action
|= KAUTH_VNODE_WRITE_EXTATTRIBUTES
;
4557 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0)) {
4558 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorization failed");
4563 * When we're setting both the access mask and the finder info, then
4564 * check if were about to remove write access for the owner. Since
4565 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
4566 * to consider their ordering.
4568 * If were about to remove write access for the owner we'll set the
4569 * Finder Info here before vnode_setattr. Otherwise we'll set it
4570 * after vnode_setattr since it may be adding owner write access.
4572 if ((fndrinfo
!= NULL
) && !(al
.volattr
& ATTR_VOL_INFO
) &&
4573 (al
.commonattr
& ATTR_CMN_ACCESSMASK
) && !(va
.va_mode
& S_IWUSR
)) {
4574 if ((error
= setattrlist_setfinderinfo(vp
, fndrinfo
, ctx
)) != 0) {
4577 fndrinfo
= NULL
; /* it was set here so skip setting below */
4581 * Write the attributes if we have any.
4583 if ((va
.va_active
!= 0LL) && ((error
= vnode_setattr(vp
, &va
, ctx
)) != 0)) {
4584 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
4589 mac_vnode_notify_setattrlist(ctx
, vp
, &al
);
4590 if (VATTR_IS_ACTIVE(&va
, va_flags
)) {
4591 mac_vnode_notify_setflags(ctx
, vp
, va
.va_flags
);
4596 * Write the Finder Info if we have any.
4598 if (fndrinfo
!= NULL
) {
4599 if (al
.volattr
& ATTR_VOL_INFO
) {
4600 if (vp
->v_tag
== VT_HFS
) {
4601 #define HFS_SET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00005)
4602 error
= VNOP_IOCTL(vp
, HFS_SET_BOOT_INFO
, (caddr_t
)fndrinfo
, 0, ctx
);
4607 /* XXX should never get here */
4609 } else if ((error
= setattrlist_setfinderinfo(vp
, fndrinfo
, ctx
)) != 0) {
4615 * Set the volume name, if we have one
4617 if (volname
!= NULL
) {
4622 vs
.f_vol_name
= volname
; /* References the setattrlist buffer directly */
4623 VFSATTR_WANTED(&vs
, f_vol_name
);
4626 error
= mac_mount_check_setattr(ctx
, vp
->v_mount
, &vs
);
4632 if ((error
= vfs_setattr(vp
->v_mount
, &vs
, ctx
)) != 0) {
4633 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setting volume name failed");
4637 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
4639 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not set volume name");
4644 /* all done and successful */
4647 kheap_free(KHEAP_DATA_BUFFERS
, user_buf
, uap
->bufferSize
);
4648 VFS_DEBUG(ctx
, vp
, "ATTRLIST - set returning %d", error
);
4653 setattrlist(proc_t p
, struct setattrlist_args
*uap
, __unused
int32_t *retval
)
4655 struct vfs_context
*ctx
;
4656 struct nameidata nd
;
4658 uint32_t nameiflags
;
4661 ctx
= vfs_context_current();
4666 nameiflags
= AUDITVNPATH1
;
4667 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0) {
4668 nameiflags
|= FOLLOW
;
4670 NDINIT(&nd
, LOOKUP
, OP_SETATTR
, nameiflags
, UIO_USERSPACE
, uap
->path
, ctx
);
4671 if ((error
= namei(&nd
)) != 0) {
4677 error
= setattrlist_internal(vp
, uap
, p
, ctx
);
4686 setattrlistat(proc_t p
, struct setattrlistat_args
*uap
, __unused
int32_t *retval
)
4688 struct setattrlist_args ap
;
4689 struct vfs_context
*ctx
;
4690 struct nameidata nd
;
4691 vnode_t vp
= NULLVP
;
4692 uint32_t nameiflags
;
4695 ctx
= vfs_context_current();
4697 AUDIT_ARG(fd
, uap
->fd
);
4701 nameiflags
= AUDITVNPATH1
;
4702 if (!(uap
->options
& FSOPT_NOFOLLOW
)) {
4703 nameiflags
|= FOLLOW
;
4705 NDINIT(&nd
, LOOKUP
, OP_SETATTR
, nameiflags
, UIO_USERSPACE
, uap
->path
, ctx
);
4706 if ((error
= nameiat(&nd
, uap
->fd
)) != 0) {
4713 ap
.alist
= uap
->alist
;
4714 ap
.attributeBuffer
= uap
->attributeBuffer
;
4715 ap
.bufferSize
= uap
->bufferSize
;
4716 ap
.options
= uap
->options
;
4718 error
= setattrlist_internal(vp
, &ap
, p
, ctx
);
4727 fsetattrlist(proc_t p
, struct fsetattrlist_args
*uap
, __unused
int32_t *retval
)
4729 struct vfs_context
*ctx
;
4732 struct setattrlist_args ap
;
4734 ctx
= vfs_context_current();
4736 if ((error
= file_vnode(uap
->fd
, &vp
)) != 0) {
4740 if ((error
= vnode_getwithref(vp
)) != 0) {
4746 ap
.alist
= uap
->alist
;
4747 ap
.attributeBuffer
= uap
->attributeBuffer
;
4748 ap
.bufferSize
= uap
->bufferSize
;
4749 ap
.options
= uap
->options
;
4751 error
= setattrlist_internal(vp
, &ap
, p
, ctx
);