2 * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/namei.h>
27 #include <sys/kernel.h>
29 #include <sys/vnode_internal.h>
30 #include <sys/mount_internal.h>
31 #include <sys/proc_internal.h>
32 #include <sys/kauth.h>
33 #include <sys/uio_internal.h>
34 #include <sys/malloc.h>
36 #include <sys/sysproto.h>
37 #include <sys/xattr.h>
38 #include <sys/fsevents.h>
39 #include <kern/kalloc.h>
40 #include <miscfs/specfs/specdev.h>
43 #define ATTR_TIME_SIZE -1
46 * Structure describing the state of an in-progress attrlist operation.
48 struct _attrlist_buf
{
58 * Pack (count) bytes from (source) into (buf).
61 attrlist_pack_fixed(struct _attrlist_buf
*ab
, void *source
, ssize_t count
)
65 /* how much room left in the buffer? */
66 fit
= imin(count
, ab
->allocated
- (ab
->fixedcursor
- ab
->base
));
68 bcopy(source
, ab
->fixedcursor
, fit
);
70 /* always move in increments of 4 */
71 ab
->fixedcursor
+= roundup(count
, 4);
74 attrlist_pack_variable2(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
, const void *ext
, ssize_t extcount
)
76 struct attrreference ar
;
79 /* pack the reference to the variable object */
80 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
81 ar
.attr_length
= count
+ extcount
;
82 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
84 /* calculate space and pack the variable object */
85 fit
= imin(count
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
88 bcopy(source
, ab
->varcursor
, fit
);
91 fit
= imin(extcount
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
94 bcopy(ext
, ab
->varcursor
, fit
);
97 /* always move in increments of 4 */
98 ab
->varcursor
= (char *)roundup((uintptr_t)ab
->varcursor
, 4);
101 attrlist_pack_variable(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
)
103 attrlist_pack_variable2(ab
, source
, count
, NULL
, 0);
106 attrlist_pack_string(struct _attrlist_buf
*ab
, const char *source
, ssize_t count
)
108 struct attrreference ar
;
113 * Supplied count is character count of string text, excluding trailing nul
114 * which we always supply here.
116 if (source
== NULL
) {
118 } else if (count
== 0) {
119 count
= strlen(source
);
123 * Make the reference and pack it.
124 * Note that this is entirely independent of how much we get into
127 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
128 ar
.attr_length
= count
+ 1;
129 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
131 /* calculate how much of the string text we can copy, and do that */
132 space
= ab
->allocated
- (ab
->varcursor
- ab
->base
);
133 fit
= imin(count
, space
);
135 bcopy(source
, ab
->varcursor
, fit
);
136 /* is there room for our trailing nul? */
138 ab
->varcursor
[fit
] = '\0';
140 /* always move in increments of 4 */
141 ab
->varcursor
+= roundup(count
+ 1, 4);
144 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
145 #define ATTR_PACK_CAST(b, t, v) \
151 #define ATTR_PACK_TIME(b, v, is64) \
154 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
163 * Table-driven setup for all valid common/volume attributes.
165 struct getvolattrlist_attrtab
{
168 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
171 static struct getvolattrlist_attrtab getvolattrlist_common_tab
[] = {
172 {ATTR_CMN_NAME
, 0, sizeof(struct attrreference
)},
173 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
)},
174 {ATTR_CMN_FSID
, 0, sizeof(fsid_t
)},
175 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
)},
176 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
)},
177 {ATTR_CMN_OBJID
, 0, sizeof(fsobj_id_t
)},
178 {ATTR_CMN_OBJPERMANENTID
, 0, sizeof(fsobj_id_t
)},
179 {ATTR_CMN_PAROBJID
, 0, sizeof(fsobj_id_t
)},
180 {ATTR_CMN_SCRIPT
, 0, sizeof(text_encoding_t
)},
181 {ATTR_CMN_CRTIME
, VFSATTR_BIT(f_create_time
), ATTR_TIME_SIZE
},
182 {ATTR_CMN_MODTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
183 {ATTR_CMN_CHGTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
184 {ATTR_CMN_ACCTIME
, VFSATTR_BIT(f_access_time
), ATTR_TIME_SIZE
},
185 {ATTR_CMN_BKUPTIME
, VFSATTR_BIT(f_backup_time
), ATTR_TIME_SIZE
},
186 {ATTR_CMN_FNDRINFO
, 0, 32},
187 {ATTR_CMN_OWNERID
, 0, sizeof(uid_t
)},
188 {ATTR_CMN_GRPID
, 0, sizeof(gid_t
)},
189 {ATTR_CMN_ACCESSMASK
, 0, sizeof(uint32_t)},
190 {ATTR_CMN_FLAGS
, 0, sizeof(uint32_t)},
191 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t)},
195 static struct getvolattrlist_attrtab getvolattrlist_vol_tab
[] = {
196 {ATTR_VOL_FSTYPE
, 0, sizeof(uint32_t)},
197 {ATTR_VOL_SIGNATURE
, VFSATTR_BIT(f_signature
), sizeof(uint32_t)},
198 {ATTR_VOL_SIZE
, VFSATTR_BIT(f_blocks
), sizeof(off_t
)},
199 {ATTR_VOL_SPACEFREE
, VFSATTR_BIT(f_bfree
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
200 {ATTR_VOL_SPACEAVAIL
, VFSATTR_BIT(f_bavail
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
201 {ATTR_VOL_MINALLOCATION
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
202 {ATTR_VOL_ALLOCATIONCLUMP
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
203 {ATTR_VOL_IOBLOCKSIZE
, VFSATTR_BIT(f_iosize
), sizeof(uint32_t)},
204 {ATTR_VOL_OBJCOUNT
, VFSATTR_BIT(f_objcount
), sizeof(uint32_t)},
205 {ATTR_VOL_FILECOUNT
, VFSATTR_BIT(f_filecount
), sizeof(uint32_t)},
206 {ATTR_VOL_DIRCOUNT
, VFSATTR_BIT(f_dircount
), sizeof(uint32_t)},
207 {ATTR_VOL_MAXOBJCOUNT
, VFSATTR_BIT(f_maxobjcount
), sizeof(uint32_t)},
208 {ATTR_VOL_MOUNTPOINT
, 0, sizeof(struct attrreference
)},
209 {ATTR_VOL_NAME
, VFSATTR_BIT(f_vol_name
), sizeof(struct attrreference
)},
210 {ATTR_VOL_MOUNTFLAGS
, 0, sizeof(uint32_t)},
211 {ATTR_VOL_MOUNTEDDEVICE
, 0, sizeof(struct attrreference
)},
212 {ATTR_VOL_ENCODINGSUSED
, 0, sizeof(uint64_t)},
213 {ATTR_VOL_CAPABILITIES
, VFSATTR_BIT(f_capabilities
), sizeof(vol_capabilities_attr_t
)},
214 {ATTR_VOL_ATTRIBUTES
, VFSATTR_BIT(f_attributes
), sizeof(vol_attributes_attr_t
)},
215 {ATTR_VOL_INFO
, 0, 0},
220 getvolattrlist_parsetab(struct getvolattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vfs_attr
*vsp
,
221 ssize_t
*sizep
, int is_64bit
)
223 attrgroup_t recognised
;
227 /* is this attribute set? */
228 if (tab
->attr
& attrs
) {
229 recognised
|= tab
->attr
;
230 vsp
->f_active
|= tab
->bits
;
231 if (tab
->size
== ATTR_TIME_SIZE
) {
233 *sizep
+= sizeof(struct user_timespec
);
235 *sizep
+= sizeof(struct timespec
);
241 } while ((++tab
)->attr
!= 0);
243 /* check to make sure that we recognised all of the passed-in attributes */
244 if (attrs
& ~recognised
)
250 * Given the attributes listed in alp, configure vap to request
251 * the data from a filesystem.
254 getvolattrlist_setupvfsattr(struct attrlist
*alp
, struct vfs_attr
*vsp
, ssize_t
*sizep
, int is_64bit
)
259 * Parse the above tables.
261 *sizep
= sizeof(uint32_t); /* length count */
262 if (alp
->commonattr
&&
263 (error
= getvolattrlist_parsetab(getvolattrlist_common_tab
, alp
->commonattr
, vsp
, sizep
, is_64bit
)) != 0)
266 (error
= getvolattrlist_parsetab(getvolattrlist_vol_tab
, alp
->volattr
, vsp
, sizep
, is_64bit
)) != 0)
273 * Table-driven setup for all valid common/dir/file/fork attributes against files.
275 struct getattrlist_attrtab
{
278 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
280 kauth_action_t action
;
282 static struct getattrlist_attrtab getattrlist_common_tab
[] = {
283 {ATTR_CMN_NAME
, VATTR_BIT(va_name
), sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
284 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
285 {ATTR_CMN_FSID
, VATTR_BIT(va_fsid
), sizeof(fsid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
286 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
287 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
288 {ATTR_CMN_OBJID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
289 {ATTR_CMN_OBJPERMANENTID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
290 {ATTR_CMN_PAROBJID
, VATTR_BIT(va_parentid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
291 {ATTR_CMN_SCRIPT
, VATTR_BIT(va_encoding
), sizeof(text_encoding_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
292 {ATTR_CMN_CRTIME
, VATTR_BIT(va_create_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
293 {ATTR_CMN_MODTIME
, VATTR_BIT(va_modify_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
294 {ATTR_CMN_CHGTIME
, VATTR_BIT(va_change_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
295 {ATTR_CMN_ACCTIME
, VATTR_BIT(va_access_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
296 {ATTR_CMN_BKUPTIME
, VATTR_BIT(va_backup_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
297 {ATTR_CMN_FNDRINFO
, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES
},
298 {ATTR_CMN_OWNERID
, VATTR_BIT(va_uid
), sizeof(uid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
299 {ATTR_CMN_GRPID
, VATTR_BIT(va_gid
), sizeof(gid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
300 {ATTR_CMN_ACCESSMASK
, VATTR_BIT(va_mode
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
301 {ATTR_CMN_FLAGS
, VATTR_BIT(va_flags
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
302 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
303 {ATTR_CMN_EXTENDED_SECURITY
, VATTR_BIT(va_acl
), sizeof(struct attrreference
), KAUTH_VNODE_READ_SECURITY
},
304 {ATTR_CMN_UUID
, VATTR_BIT(va_uuuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
305 {ATTR_CMN_GRPUUID
, VATTR_BIT(va_guuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
308 static struct getattrlist_attrtab getattrlist_dir_tab
[] = {
309 {ATTR_DIR_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
310 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nchildren
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
311 /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
312 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nlink
), 0, KAUTH_VNODE_READ_ATTRIBUTES
},
313 {ATTR_DIR_MOUNTSTATUS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
316 static struct getattrlist_attrtab getattrlist_file_tab
[] = {
317 {ATTR_FILE_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
318 {ATTR_FILE_TOTALSIZE
, VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
319 {ATTR_FILE_ALLOCSIZE
, VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
320 {ATTR_FILE_IOBLOCKSIZE
, VATTR_BIT(va_iosize
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
321 {ATTR_FILE_DEVTYPE
, VATTR_BIT(va_rdev
), sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
322 {ATTR_FILE_DATALENGTH
, VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
323 {ATTR_FILE_DATAALLOCSIZE
, VATTR_BIT(va_total_alloc
)| VATTR_BIT(va_data_alloc
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
324 {ATTR_FILE_RSRCLENGTH
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
325 {ATTR_FILE_RSRCALLOCSIZE
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
330 getattrlist_parsetab(struct getattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vnode_attr
*vap
,
331 ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
)
333 attrgroup_t recognised
;
337 /* is this attribute set? */
338 if (tab
->attr
& attrs
) {
339 recognised
|= tab
->attr
;
340 vap
->va_active
|= tab
->bits
;
341 if (tab
->size
== ATTR_TIME_SIZE
) {
343 *sizep
+= sizeof(struct user_timespec
);
345 *sizep
+= sizeof(struct timespec
);
350 *actionp
|= tab
->action
;
352 } while ((++tab
)->attr
!= 0);
354 /* check to make sure that we recognised all of the passed-in attributes */
355 if (attrs
& ~recognised
)
361 * Given the attributes listed in alp, configure vap to request
362 * the data from a filesystem.
365 getattrlist_setupvattr(struct attrlist
*alp
, struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
, int isdir
)
370 * Parse the above tables.
372 *sizep
= sizeof(uint32_t); /* length count */
374 if (alp
->commonattr
&&
375 (error
= getattrlist_parsetab(getattrlist_common_tab
, alp
->commonattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
377 if (isdir
&& alp
->dirattr
&&
378 (error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
380 if (!isdir
&& alp
->fileattr
&&
381 (error
= getattrlist_parsetab(getattrlist_file_tab
, alp
->fileattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
389 * Find something resembling a terminal component name in the mountedonname for vp
393 getattrlist_findnamecomp(const char *mn
, const char **np
, ssize_t
*nl
)
399 * We're looking for the last sequence of non / characters, but
400 * not including any trailing / characters.
405 for (cp
= mn
; *cp
!= 0; cp
++) {
407 /* start of run of chars */
413 /* end of run of chars */
420 /* need to close run? */
427 getvolattrlist(vnode_t vp
, struct getattrlist_args
*uap
, struct attrlist
*alp
, vfs_context_t ctx
, int is_64bit
)
430 struct vnode_attr va
;
431 struct _attrlist_buf ab
;
433 ssize_t fixedsize
, varsize
;
441 vs
.f_vol_name
= NULL
;
446 * For now, the vnode must be the root of its filesystem.
447 * To relax this, we need to be able to find the root vnode of a filesystem
448 * from any vnode in the filesystem.
450 if (!vnode_isvroot(vp
)) {
452 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
457 * Set up the vfs_attr structure and call the filesystem.
459 if ((error
= getvolattrlist_setupvfsattr(alp
, &vs
, &fixedsize
, is_64bit
)) != 0) {
460 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
463 if (vs
.f_active
!= 0) {
464 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
465 if (VFSATTR_IS_ACTIVE(&vs
, f_vol_name
)) {
466 vs
.f_vol_name
= (char *) kalloc(MAXPATHLEN
);
467 if (vs
.f_vol_name
== NULL
) {
469 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
474 VFS_DEBUG(ctx
, vp
, "ATTRLIST - calling to get %016llx with supported %016llx", vs
.f_active
, vs
.f_supported
);
475 if ((error
= vfs_getattr(mnt
, &vs
, ctx
)) != 0) {
476 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
481 * Did we ask for something the filesystem doesn't support?
483 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
484 /* default value for volume subtype */
485 if (VFSATTR_IS_ACTIVE(&vs
, f_fssubtype
)
486 && !VFSATTR_IS_SUPPORTED(&vs
, f_fssubtype
))
487 VFSATTR_RETURN(&vs
, f_fssubtype
, 0);
490 * If the file system didn't supply f_signature, then
491 * default it to 'BD', which is the generic signature
492 * that most Carbon file systems should return.
494 if (VFSATTR_IS_ACTIVE(&vs
, f_signature
)
495 && !VFSATTR_IS_SUPPORTED(&vs
, f_signature
))
496 VFSATTR_RETURN(&vs
, f_signature
, 0x4244);
498 /* default for block size */
499 if (VFSATTR_IS_ACTIVE(&vs
, f_bsize
)
500 && !VFSATTR_IS_SUPPORTED(&vs
, f_bsize
))
501 VFSATTR_RETURN(&vs
, f_bsize
, mnt
->mnt_devblocksize
);
503 /* check to see if our fixups were enough */
504 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
506 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested volume attributes");
507 VFS_DEBUG(ctx
, vp
, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
508 vs
.f_active
, vs
.f_supported
, vs
.f_active
& ~vs
.f_supported
);
515 * Some fields require data from the root vp
517 if (alp
->commonattr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
| ATTR_CMN_ACCESSMASK
| ATTR_CMN_FLAGS
| ATTR_CMN_SCRIPT
)) {
518 VATTR_WANTED(&va
, va_uid
);
519 VATTR_WANTED(&va
, va_gid
);
520 VATTR_WANTED(&va
, va_mode
);
521 VATTR_WANTED(&va
, va_flags
);
522 VATTR_WANTED(&va
, va_encoding
);
524 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
525 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp
);
529 if (VATTR_IS_ACTIVE(&va
, va_encoding
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
530 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
534 * Compute variable-size buffer requirements.
537 if (alp
->commonattr
& ATTR_CMN_NAME
) {
538 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
539 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
540 /* special case for boot volume. Use root name when it's
541 * available (which is the volume name) or just the mount on
542 * name of "/". we must do this for binary compatibility with
543 * pre Tiger code. returning nothing for the boot volume name
544 * breaks installers - 3961058
546 cnp
= vnode_getname(vp
);
548 /* just use "/" as name */
549 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
554 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
556 if (alp
->commonattr
& ATTR_CMN_NAME
)
557 varsize
+= roundup(cnl
+ 1, 4);
559 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
560 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntonname
) + 1, 4);
561 if (alp
->volattr
& ATTR_VOL_NAME
) {
562 vs
.f_vol_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
563 varsize
+= roundup(strlen(vs
.f_vol_name
) + 1, 4);
565 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
566 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntfromname
) + 1, 4);
569 * Allocate a target buffer for attribute results.
570 * Note that since we won't ever copy out more than the caller requested,
571 * we never need to allocate more than they offer.
573 ab
.allocated
= imin(uap
->bufferSize
, fixedsize
+ varsize
);
574 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
576 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
579 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
580 if (ab
.base
== NULL
) {
582 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
587 * Pack results into the destination buffer.
589 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
590 ab
.varcursor
= ab
.base
+ fixedsize
;
591 ab
.needed
= fixedsize
+ varsize
;
593 /* common attributes **************************************************/
594 if (alp
->commonattr
& ATTR_CMN_NAME
)
595 attrlist_pack_string(&ab
, cnp
, cnl
);
596 if (alp
->commonattr
& ATTR_CMN_DEVID
)
597 ATTR_PACK_CAST(&ab
, dev_t
, mnt
->mnt_vfsstat
.f_fsid
.val
[0]);
598 if (alp
->commonattr
& ATTR_CMN_FSID
)
599 ATTR_PACK(&ab
, mnt
->mnt_vfsstat
.f_fsid
);
600 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
)
601 ATTR_PACK_CAST(&ab
, fsobj_type_t
, 0);
602 if (alp
->commonattr
& ATTR_CMN_OBJTAG
)
603 ATTR_PACK_CAST(&ab
, fsobj_tag_t
, vp
->v_tag
);
604 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
605 fsobj_id_t f
= {0, 0};
608 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
609 fsobj_id_t f
= {0, 0};
612 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
613 fsobj_id_t f
= {0, 0};
616 /* note that this returns the encoding for the volume name, not the node name */
617 if (alp
->commonattr
& ATTR_CMN_SCRIPT
)
618 ATTR_PACK_CAST(&ab
, text_encoding_t
, va
.va_encoding
);
619 if (alp
->commonattr
& ATTR_CMN_CRTIME
)
620 ATTR_PACK_TIME(&ab
, vs
.f_create_time
, is_64bit
);
621 if (alp
->commonattr
& ATTR_CMN_MODTIME
)
622 ATTR_PACK_TIME(&ab
, vs
.f_modify_time
, is_64bit
);
623 if (alp
->commonattr
& ATTR_CMN_CHGTIME
)
624 ATTR_PACK_TIME(&ab
, vs
.f_modify_time
, is_64bit
);
625 if (alp
->commonattr
& ATTR_CMN_ACCTIME
)
626 ATTR_PACK_TIME(&ab
, vs
.f_access_time
, is_64bit
);
627 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
)
628 ATTR_PACK_TIME(&ab
, vs
.f_backup_time
, is_64bit
);
629 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
632 * This attribute isn't really Finder Info, at least for HFS.
634 if (vp
->v_tag
== VT_HFS
) {
635 if ((error
= VNOP_IOCTL(vp
, HFS_GET_BOOT_INFO
, (caddr_t
)&f
, 0, ctx
)) != 0)
638 /* XXX we could at least pass out the volume UUID here */
639 bzero(&f
, sizeof(f
));
641 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
643 if (alp
->commonattr
& ATTR_CMN_OWNERID
)
644 ATTR_PACK(&ab
, va
.va_uid
);
645 if (alp
->commonattr
& ATTR_CMN_GRPID
)
646 ATTR_PACK(&ab
, va
.va_gid
);
647 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
)
648 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
649 if (alp
->commonattr
& ATTR_CMN_FLAGS
)
650 ATTR_PACK(&ab
, va
.va_flags
);
651 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* XXX this is expensive and also duplicate work */
653 if (vnode_isdir(vp
)) {
654 if (vnode_authorize(vp
, NULL
,
655 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
657 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
659 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
662 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
664 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
666 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
669 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms
);
670 ATTR_PACK(&ab
, perms
);
673 /* volume attributes **************************************************/
675 if (alp
->volattr
& ATTR_VOL_FSTYPE
)
676 ATTR_PACK_CAST(&ab
, uint32_t, vfs_typenum(mnt
));
677 if (alp
->volattr
& ATTR_VOL_SIGNATURE
)
678 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_signature
);
679 if (alp
->volattr
& ATTR_VOL_SIZE
)
680 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_blocks
);
681 if (alp
->volattr
& ATTR_VOL_SPACEFREE
)
682 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bfree
);
683 if (alp
->volattr
& ATTR_VOL_SPACEAVAIL
)
684 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bavail
);
685 if (alp
->volattr
& ATTR_VOL_MINALLOCATION
)
686 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
);
687 if (alp
->volattr
& ATTR_VOL_ALLOCATIONCLUMP
)
688 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
); /* not strictly true */
689 if (alp
->volattr
& ATTR_VOL_IOBLOCKSIZE
)
690 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_iosize
);
691 if (alp
->volattr
& ATTR_VOL_OBJCOUNT
)
692 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_objcount
);
693 if (alp
->volattr
& ATTR_VOL_FILECOUNT
)
694 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_filecount
);
695 if (alp
->volattr
& ATTR_VOL_DIRCOUNT
)
696 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_dircount
);
697 if (alp
->volattr
& ATTR_VOL_MAXOBJCOUNT
)
698 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_maxobjcount
);
699 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
700 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntonname
, 0);
701 if (alp
->volattr
& ATTR_VOL_NAME
)
702 attrlist_pack_string(&ab
, vs
.f_vol_name
, 0);
703 if (alp
->volattr
& ATTR_VOL_MOUNTFLAGS
)
704 ATTR_PACK_CAST(&ab
, uint32_t, mnt
->mnt_flag
);
705 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
706 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntfromname
, 0);
707 if (alp
->volattr
& ATTR_VOL_ENCODINGSUSED
)
708 ATTR_PACK_CAST(&ab
, uint64_t, ~0LL); /* return all encodings */
709 if (alp
->volattr
& ATTR_VOL_CAPABILITIES
) {
710 /* fix up volume capabilities */
711 if (vfs_extendedsecurity(mnt
)) {
712 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
714 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] &= ~VOL_CAP_INT_EXTENDED_SECURITY
;
716 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
717 ATTR_PACK(&ab
, vs
.f_capabilities
);
719 if (alp
->volattr
& ATTR_VOL_ATTRIBUTES
) {
720 /* fix up volume attribute information */
721 if (vfs_extendedsecurity(mnt
)) {
722 vs
.f_attributes
.validattr
.commonattr
|= (ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
724 vs
.f_attributes
.validattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
725 vs
.f_attributes
.nativeattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
727 ATTR_PACK(&ab
, vs
.f_attributes
);
731 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
732 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
733 fixedsize
, ab
.fixedcursor
- ab
.base
, alp
->commonattr
, alp
->volattr
);
734 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
735 panic("packed variable field size mismatch; used %d but expected %d", ab
.varcursor
- ab
.base
, ab
.needed
);
738 * In the compatible case, we report the smaller of the required and returned sizes.
739 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
740 * of the result buffer, even if we copied less out. The caller knows how big a buffer
741 * they gave us, so they can always check for truncation themselves.
743 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
745 error
= copyout(ab
.base
, uap
->attributeBuffer
, ab
.allocated
);
748 if (vs
.f_vol_name
!= NULL
)
749 kfree(vs
.f_vol_name
, MAXPATHLEN
);
751 FREE(ab
.base
, M_TEMP
);
752 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
757 * Obtain attribute information about a filesystem object.
760 getattrlist(struct proc
*p
, struct getattrlist_args
*uap
, __unused register_t
*retval
)
763 struct vnode_attr va
;
764 struct vfs_context context
, *ctx
;
766 struct _attrlist_buf ab
;
769 kauth_action_t action
;
770 ssize_t fixedsize
, varsize
;
777 context
.vc_ucred
= kauth_cred_get();
790 nameiflags
= AUDITVNPATH1
;
791 if (!(uap
->options
& FSOPT_NOFOLLOW
))
792 nameiflags
|= FOLLOW
;
793 NDINIT(&nd
, LOOKUP
, nameiflags
, UIO_USERSPACE
, uap
->path
, &context
);
795 if ((error
= namei(&nd
)) != 0)
801 * Fetch the attribute request.
803 if ((error
= copyin(uap
->alist
, &al
, sizeof(al
))) != 0)
805 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
810 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
811 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
812 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
815 * It is legal to request volume or file attributes,
819 if (al
.fileattr
|| al
.dirattr
|| al
.forkattr
) {
821 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
824 /* handle volume attribute request */
825 error
= getvolattrlist(vp
, uap
, &al
, &context
, proc_is64bit(p
));
830 * Set up the vnode_attr structure and authorise.
832 if ((error
= getattrlist_setupvattr(&al
, &va
, &fixedsize
, &action
, proc_is64bit(p
), vnode_isdir(vp
))) != 0) {
833 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
836 if ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0) {
837 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorisation failed/denied");
841 if (va
.va_active
!= 0) {
843 * If we're going to ask for va_name, allocate a buffer to point it at
845 if (VATTR_IS_ACTIVE(&va
, va_name
)) {
846 va
.va_name
= (char *) kalloc(MAXPATHLEN
);
847 if (va
.va_name
== NULL
) {
849 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: cannot allocate va_name buffer");
855 * Call the filesystem.
857 if ((error
= vnode_getattr(vp
, &va
, &context
)) != 0) {
858 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
862 /* did we ask for something the filesystem doesn't support? */
863 if (!VATTR_ALL_SUPPORTED(&va
)) {
866 * There are a couple of special cases. If we are after object IDs,
867 * we can make do with va_fileid.
869 if ((al
.commonattr
& (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
)) && !VATTR_IS_SUPPORTED(&va
, va_linkid
))
870 VATTR_CLEAR_ACTIVE(&va
, va_linkid
); /* forget we wanted this */
872 * Many (most?) filesystems don't know their parent object id. We can get it the
875 if ((al
.commonattr
& ATTR_CMN_PAROBJID
) && !VATTR_IS_SUPPORTED(&va
, va_parentid
))
876 VATTR_CLEAR_ACTIVE(&va
, va_parentid
);
878 * And we can report datasize/alloc from total.
880 if ((al
.fileattr
& ATTR_FILE_DATALENGTH
) && !VATTR_IS_SUPPORTED(&va
, va_data_size
))
881 VATTR_CLEAR_ACTIVE(&va
, va_data_size
);
882 if ((al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) && !VATTR_IS_SUPPORTED(&va
, va_data_alloc
))
883 VATTR_CLEAR_ACTIVE(&va
, va_data_alloc
);
886 * If we don't have an encoding, go with UTF-8
888 if ((al
.commonattr
& ATTR_CMN_SCRIPT
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
889 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
892 * If we don't have a name, we'll get one from the vnode or mount point.
894 if ((al
.commonattr
& ATTR_CMN_NAME
) && !VATTR_IS_SUPPORTED(&va
, va_name
)) {
895 VATTR_CLEAR_ACTIVE(&va
, va_name
);
899 * We used to return va_nlink-2 for ATTR_DIR_ENTRYCOUNT. The va_nchildren
900 * field is preferred, but we'll fall back to va_nlink-2 for compatibility
901 * with file systems which haven't adopted va_nchildren. Note: the "- 2"
902 * reflects the "." and ".." entries which are reported via POSIX APIs, but
903 * not via Carbon (since they don't in fact exist in HFS).
905 if ((al
.dirattr
& ATTR_DIR_ENTRYCOUNT
) && !VATTR_IS_SUPPORTED(&va
, va_nchildren
) &&
906 VATTR_IS_SUPPORTED(&va
, va_nlink
)) {
907 VATTR_RETURN(&va
, va_nchildren
, va
.va_nlink
- 2);
911 if (!VATTR_ALL_SUPPORTED(&va
)) {
913 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested file attributes");
914 VFS_DEBUG(ctx
, vp
, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
915 va
.va_supported
, va
.va_active
, va
.va_active
& ~va
.va_supported
);
922 * Compute variable-space requirements.
924 varsize
= 0; /* length count */
925 if (al
.commonattr
& ATTR_CMN_NAME
) {
926 if (VATTR_IS_SUPPORTED(&va
, va_name
)) {
927 va
.va_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
931 if (vnode_isvroot(vp
)) {
932 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
933 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
934 /* special case for boot volume. Use root name when it's
935 * available (which is the volume name) or just the mount on
936 * name of "/". we must do this for binary compatibility with
937 * pre Tiger code. returning nothing for the boot volume name
938 * breaks installers - 3961058
940 cnp
= vname
= vnode_getname(vp
);
942 /* just use "/" as name */
943 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
948 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
951 cnp
= vname
= vnode_getname(vp
);
958 varsize
+= roundup(cnl
+ 1, 4);
962 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
964 * XXX This needs to change at some point; since the blob is opaque in
965 * user-space this is OK.
967 if ((al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) &&
968 VATTR_IS_SUPPORTED(&va
, va_acl
) &&
970 varsize
+= roundup(KAUTH_FILESEC_SIZE(va
.va_acl
->acl_entrycount
), 4);
973 * Allocate a target buffer for attribute results.
975 * Note that we won't ever copy out more than the caller requested, even though
976 * we might have to allocate more than they offer do that the diagnostic checks
977 * don't result in a panic if the caller's buffer is too small..
979 ab
.allocated
= fixedsize
+ varsize
;
980 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
982 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
985 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
986 if (ab
.base
== NULL
) {
988 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
993 * Pack results into the destination buffer.
995 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
996 ab
.varcursor
= ab
.base
+ fixedsize
;
997 ab
.needed
= ab
.allocated
;
999 /* common attributes **************************************************/
1000 if (al
.commonattr
& ATTR_CMN_NAME
)
1001 attrlist_pack_string(&ab
, cnp
, cnl
);
1002 if (al
.commonattr
& ATTR_CMN_DEVID
)
1003 ATTR_PACK_CAST(&ab
, dev_t
, vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
1004 if (al
.commonattr
& ATTR_CMN_FSID
)
1005 ATTR_PACK(&ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
);
1006 if (al
.commonattr
& ATTR_CMN_OBJTYPE
)
1007 ATTR_PACK_CAST(&ab
, fsobj_type_t
, vp
->v_type
);
1008 if (al
.commonattr
& ATTR_CMN_OBJTAG
)
1009 ATTR_PACK_CAST(&ab
, fsobj_tag_t
, vp
->v_tag
);
1010 if (al
.commonattr
& ATTR_CMN_OBJID
) {
1013 * Carbon can't deal with us reporting the target ID
1014 * for links. So we ask the filesystem to give us the
1015 * source ID as well, and if it gives us one, we use
1018 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1019 f
.fid_objno
= va
.va_linkid
;
1021 f
.fid_objno
= va
.va_fileid
;
1023 f
.fid_generation
= 0;
1026 if (al
.commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1029 * Carbon can't deal with us reporting the target ID
1030 * for links. So we ask the filesystem to give us the
1031 * source ID as well, and if it gives us one, we use
1034 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1035 f
.fid_objno
= va
.va_linkid
;
1037 f
.fid_objno
= va
.va_fileid
;
1039 f
.fid_generation
= 0;
1042 if (al
.commonattr
& ATTR_CMN_PAROBJID
) {
1045 * If the filesystem doesn't know the parent ID, we can
1046 * try to get it via v->v_parent. Don't need to worry
1047 * about links here, as we dont allow hardlinks to
1050 if (VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
1051 f
.fid_objno
= va
.va_parentid
;
1053 struct vnode_attr lva
;
1056 pvp
= vnode_getparent(vp
);
1058 if (pvp
== NULLVP
) {
1063 VATTR_WANTED(&lva
, va_fileid
);
1064 error
= vnode_getattr(pvp
, &lva
, &context
);
1069 f
.fid_objno
= lva
.va_fileid
;
1071 f
.fid_generation
= 0;
1074 if (al
.commonattr
& ATTR_CMN_SCRIPT
)
1075 ATTR_PACK_CAST(&ab
, text_encoding_t
, va
.va_encoding
);
1076 if (al
.commonattr
& ATTR_CMN_CRTIME
)
1077 ATTR_PACK_TIME(&ab
, va
.va_create_time
, proc_is64bit(p
));
1078 if (al
.commonattr
& ATTR_CMN_MODTIME
)
1079 ATTR_PACK_TIME(&ab
, va
.va_modify_time
, proc_is64bit(p
));
1080 if (al
.commonattr
& ATTR_CMN_CHGTIME
)
1081 ATTR_PACK_TIME(&ab
, va
.va_change_time
, proc_is64bit(p
));
1082 if (al
.commonattr
& ATTR_CMN_ACCTIME
)
1083 ATTR_PACK_TIME(&ab
, va
.va_access_time
, proc_is64bit(p
));
1084 if (al
.commonattr
& ATTR_CMN_BKUPTIME
)
1085 ATTR_PACK_TIME(&ab
, va
.va_backup_time
, proc_is64bit(p
));
1086 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1089 char uio_buf
[UIO_SIZEOF(1)];
1091 fisize
= imin(32, ab
.allocated
- (ab
.fixedcursor
- ab
.base
));
1093 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1097 uio_addiov(auio
, CAST_USER_ADDR_T(ab
.fixedcursor
), fisize
);
1098 error
= vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, &fisize
, XATTR_NOSECURITY
, &context
);
1102 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1103 VFS_DEBUG(ctx
, vp
, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1104 bzero(ab
.fixedcursor
, 32);
1107 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1112 VFS_DEBUG(ctx
, vp
, "ATTRLIST - no room in caller buffer for FINDERINFO");
1114 ab
.fixedcursor
+= 32;
1116 if (al
.commonattr
& ATTR_CMN_OWNERID
)
1117 ATTR_PACK(&ab
, va
.va_uid
);
1118 if (al
.commonattr
& ATTR_CMN_GRPID
)
1119 ATTR_PACK(&ab
, va
.va_gid
);
1120 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
)
1121 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
1122 if (al
.commonattr
& ATTR_CMN_FLAGS
)
1123 ATTR_PACK(&ab
, va
.va_flags
);
1124 if (al
.commonattr
& ATTR_CMN_USERACCESS
) { /* this is expensive */
1126 if (vnode_isdir(vp
)) {
1127 if (vnode_authorize(vp
, NULL
,
1128 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, &context
) == 0)
1130 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, &context
) == 0)
1132 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, &context
) == 0)
1135 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, &context
) == 0)
1137 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, &context
) == 0)
1139 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, &context
) == 0)
1142 VFS_DEBUG(ctx
, vp
, "ATTRLIST - granting perms %d", perms
);
1143 ATTR_PACK(&ab
, perms
);
1145 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1146 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
)) {
1147 struct kauth_filesec fsec
;
1149 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1151 fsec
.fsec_magic
= KAUTH_FILESEC_MAGIC
;
1152 fsec
.fsec_owner
= kauth_null_guid
;
1153 fsec
.fsec_group
= kauth_null_guid
;
1154 attrlist_pack_variable2(&ab
, &fsec
, ((char *)&fsec
.fsec_acl
- (char *)&fsec
), va
.va_acl
, KAUTH_ACL_COPYSIZE(va
.va_acl
));
1156 attrlist_pack_variable(&ab
, NULL
, 0);
1159 if (al
.commonattr
& ATTR_CMN_UUID
) {
1160 if (!VATTR_IS_SUPPORTED(&va
, va_uuuid
)) {
1161 ATTR_PACK(&ab
, kauth_null_guid
);
1163 ATTR_PACK(&ab
, va
.va_uuuid
);
1166 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1167 if (!VATTR_IS_SUPPORTED(&va
, va_guuid
)) {
1168 ATTR_PACK(&ab
, kauth_null_guid
);
1170 ATTR_PACK(&ab
, va
.va_guuid
);
1174 /* directory attributes **************************************************/
1175 if (vnode_isdir(vp
)) {
1176 if (al
.dirattr
& ATTR_DIR_LINKCOUNT
) /* full count of entries */
1177 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nlink
);
1178 if (al
.dirattr
& ATTR_DIR_ENTRYCOUNT
)
1179 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nchildren
);
1180 if (al
.dirattr
& ATTR_DIR_MOUNTSTATUS
)
1181 ATTR_PACK_CAST(&ab
, uint32_t, (vp
->v_flag
& VROOT
) ? DIR_MNTSTATUS_MNTPOINT
: 0);
1184 /* file attributes **************************************************/
1185 if (!vnode_isdir(vp
)) {
1186 if (al
.fileattr
& ATTR_FILE_LINKCOUNT
)
1187 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nlink
);
1188 if (al
.fileattr
& ATTR_FILE_TOTALSIZE
)
1189 ATTR_PACK(&ab
, va
.va_total_size
);
1190 if (al
.fileattr
& ATTR_FILE_ALLOCSIZE
)
1191 ATTR_PACK(&ab
, va
.va_total_alloc
);
1192 if (al
.fileattr
& ATTR_FILE_IOBLOCKSIZE
)
1193 ATTR_PACK(&ab
, va
.va_iosize
);
1194 if (al
.fileattr
& ATTR_FILE_CLUMPSIZE
)
1195 ATTR_PACK_CAST(&ab
, uint32_t, 0); /* XXX value is deprecated */
1196 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1197 if ((vp
->v_type
== VCHR
) || (vp
->v_type
== VBLK
)) {
1198 ATTR_PACK(&ab
, vp
->v_specinfo
->si_rdev
);
1200 ATTR_PACK_CAST(&ab
, uint32_t, 0);
1203 if (al
.fileattr
& ATTR_FILE_DATALENGTH
) {
1204 if (VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
1205 ATTR_PACK(&ab
, va
.va_data_size
);
1207 ATTR_PACK(&ab
, va
.va_total_size
);
1210 if (al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) {
1211 if (VATTR_IS_SUPPORTED(&va
, va_data_alloc
)) {
1212 ATTR_PACK(&ab
, va
.va_data_alloc
);
1214 ATTR_PACK(&ab
, va
.va_total_alloc
);
1217 /* fetch resource fork size/allocation via xattr interface */
1218 if (al
.fileattr
& (ATTR_FILE_RSRCLENGTH
| ATTR_FILE_RSRCALLOCSIZE
)) {
1220 if ((error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &rsize
, XATTR_NOSECURITY
, &context
)) != 0) {
1221 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1228 if (al
.fileattr
& ATTR_FILE_RSRCLENGTH
)
1229 ATTR_PACK_CAST(&ab
, off_t
, rsize
);
1230 if (al
.fileattr
& ATTR_FILE_RSRCALLOCSIZE
) {
1231 uint32_t blksize
= vp
->v_mount
->mnt_vfsstat
.f_bsize
;
1234 ATTR_PACK_CAST(&ab
, off_t
, (roundup(rsize
, blksize
)));
1240 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
1241 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
1242 fixedsize
, ab
.fixedcursor
- ab
.base
, al
.commonattr
, al
.volattr
);
1243 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
1244 panic("packed variable field size mismatch; used %d but expected %d", ab
.varcursor
- ab
.base
, ab
.needed
);
1247 * In the compatible case, we report the smaller of the required and returned sizes.
1248 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1249 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1250 * they gave us, so they can always check for truncation themselves.
1252 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
1254 /* Only actually copyout as much out as the user buffer can hold */
1255 error
= copyout(ab
.base
, uap
->attributeBuffer
, imin(uap
->bufferSize
, ab
.allocated
));
1259 kfree(va
.va_name
, MAXPATHLEN
);
1261 vnode_putname(vname
);
1264 if (ab
.base
!= NULL
)
1265 FREE(ab
.base
, M_TEMP
);
1266 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
))
1267 kauth_acl_free(va
.va_acl
);
1269 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
1274 attrlist_unpack_fixed(char **cursor
, char *end
, void *buf
, ssize_t size
)
1276 /* make sure we have enough source data */
1277 if ((*cursor
) + size
> end
)
1280 bcopy(*cursor
, buf
, size
);
1285 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1286 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1287 #define ATTR_UNPACK_TIME(v, is64) \
1290 struct user_timespec us; \
1292 v.tv_sec = us.tv_sec; \
1293 v.tv_nsec = us.tv_nsec; \
1304 setattrlist(struct proc
*p
, register struct setattrlist_args
*uap
, __unused register_t
*retval
)
1307 struct vfs_context context
, *ctx
;
1308 struct vnode_attr va
;
1309 struct attrreference ar
;
1310 struct nameidata nd
;
1313 kauth_action_t action
;
1314 char *user_buf
, *cursor
, *bufend
, *fndrinfo
, *cp
, *volname
;
1315 int proc_is64
, error
;
1317 kauth_filesec_t rfsec
;
1319 context
.vc_proc
= p
;
1320 context
.vc_ucred
= kauth_cred_get();
1327 proc_is64
= proc_is64bit(p
);
1335 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0)
1336 nameiflags
|= FOLLOW
;
1337 NDINIT(&nd
, LOOKUP
, nameiflags
| AUDITVNPATH1
, UIO_USERSPACE
, uap
->path
, &context
);
1338 if ((error
= namei(&nd
)) != 0)
1344 * Fetch the attribute set and validate.
1346 if ((error
= copyin(uap
->alist
, (caddr_t
) &al
, sizeof (al
))))
1348 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
1353 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1354 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
1355 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
1358 if ((al
.volattr
& ~ATTR_VOL_SETMASK
) ||
1359 (al
.commonattr
& ~ATTR_CMN_VOLSETMASK
) ||
1363 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1367 if ((al
.commonattr
& ~ATTR_CMN_SETMASK
) ||
1368 (al
.fileattr
& ~ATTR_FILE_SETMASK
) ||
1369 (al
.dirattr
& ~ATTR_DIR_SETMASK
) ||
1370 (al
.forkattr
& ~ATTR_FORK_SETMASK
)) {
1372 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1378 * Make the naive assumption that the caller has supplied a reasonable buffer
1379 * size. We could be more careful by pulling in the fixed-size region, checking
1380 * the attrref structures, then pulling in the variable section.
1381 * We need to reconsider this for handling large ACLs, as they should probably be
1382 * brought directly into a buffer. Multiple copyins will make this slower though.
1384 * We could also map the user buffer if it is larger than some sensible mimimum.
1386 if (uap
->bufferSize
> ATTR_MAX_BUFFER
) {
1387 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size %d too large", uap
->bufferSize
);
1391 MALLOC(user_buf
, char *, uap
->bufferSize
, M_TEMP
, M_WAITOK
);
1392 if (user_buf
== NULL
) {
1393 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap
->bufferSize
);
1397 if ((error
= copyin(uap
->attributeBuffer
, user_buf
, uap
->bufferSize
)) != 0) {
1398 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer copyin failed");
1401 VFS_DEBUG(ctx
, vp
, "ATTRLIST - copied in %d bytes of user attributes to %p", uap
->bufferSize
, user_buf
);
1404 * Unpack the argument buffer.
1407 bufend
= cursor
+ uap
->bufferSize
;
1410 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
1411 ATTR_UNPACK(va
.va_encoding
);
1412 VATTR_SET_ACTIVE(&va
, va_encoding
);
1414 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
1415 ATTR_UNPACK_TIME(va
.va_create_time
, proc_is64
);
1416 VATTR_SET_ACTIVE(&va
, va_create_time
);
1418 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
1419 ATTR_UNPACK_TIME(va
.va_modify_time
, proc_is64
);
1420 VATTR_SET_ACTIVE(&va
, va_modify_time
);
1422 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
1423 ATTR_UNPACK_TIME(va
.va_change_time
, proc_is64
);
1424 VATTR_SET_ACTIVE(&va
, va_change_time
);
1426 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
1427 ATTR_UNPACK_TIME(va
.va_access_time
, proc_is64
);
1428 VATTR_SET_ACTIVE(&va
, va_access_time
);
1430 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
1431 ATTR_UNPACK_TIME(va
.va_backup_time
, proc_is64
);
1432 VATTR_SET_ACTIVE(&va
, va_backup_time
);
1434 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1435 if ((cursor
+ 32) > bufend
) {
1437 VFS_DEBUG(ctx
, vp
, "ATTRLIST - not enough data supplied for FINDERINFO");
1443 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
1444 ATTR_UNPACK(va
.va_uid
);
1445 VATTR_SET_ACTIVE(&va
, va_uid
);
1447 if (al
.commonattr
& ATTR_CMN_GRPID
) {
1448 ATTR_UNPACK(va
.va_gid
);
1449 VATTR_SET_ACTIVE(&va
, va_gid
);
1451 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1452 ATTR_UNPACK_CAST(uint32_t, va
.va_mode
);
1453 VATTR_SET_ACTIVE(&va
, va_mode
);
1455 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
1456 ATTR_UNPACK(va
.va_flags
);
1457 VATTR_SET_ACTIVE(&va
, va_flags
);
1459 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1462 * We are (for now) passed a kauth_filesec_t, but all we want from
1467 cp
+= ar
.attr_dataoffset
;
1468 rfsec
= (kauth_filesec_t
)cp
;
1469 if (((char *)(rfsec
+ 1) > bufend
) || /* no space for acl */
1470 (rfsec
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) || /* bad magic */
1471 (KAUTH_FILESEC_COPYSIZE(rfsec
) != ar
.attr_length
) || /* size does not match */
1472 ((cp
+ KAUTH_FILESEC_COPYSIZE(rfsec
)) > bufend
)) { /* ACEs overrun buffer */
1474 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied", ar
.attr_length
);
1477 nace
= rfsec
->fsec_entrycount
;
1478 if (nace
== KAUTH_FILESEC_NOACL
)
1480 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1482 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied");
1485 nace
= rfsec
->fsec_acl
.acl_entrycount
;
1486 if (nace
== KAUTH_FILESEC_NOACL
) {
1488 VATTR_SET(&va
, va_acl
, NULL
);
1491 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1493 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: supplied ACL is too large");
1496 VATTR_SET(&va
, va_acl
, &rfsec
->fsec_acl
);
1499 if (al
.commonattr
& ATTR_CMN_UUID
) {
1500 ATTR_UNPACK(va
.va_uuuid
);
1501 VATTR_SET_ACTIVE(&va
, va_uuuid
);
1503 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1504 ATTR_UNPACK(va
.va_guuid
);
1505 VATTR_SET_ACTIVE(&va
, va_guuid
);
1509 if (al
.volattr
& ATTR_VOL_INFO
) {
1510 if (al
.volattr
& ATTR_VOL_NAME
) {
1513 volname
+= ar
.attr_dataoffset
;
1514 if ((volname
+ ar
.attr_length
) > bufend
) {
1516 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume name too big for caller buffer");
1519 /* guarantee NUL termination */
1520 volname
[ar
.attr_length
- 1] = 0;
1525 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1526 /* XXX does it actually make any sense to change this? */
1528 VFS_DEBUG(ctx
, vp
, "ATTRLIST - XXX device type change not implemented");
1533 * Validate and authorize.
1536 if ((va
.va_active
!= 0LL) && ((error
= vnode_authattr(vp
, &va
, &action
, &context
)) != 0)) {
1537 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attribute changes refused: %d", error
);
1541 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1542 * and will be auth'ed by the FS.
1544 if (fndrinfo
!= NULL
) {
1545 if (al
.volattr
& ATTR_VOL_INFO
) {
1546 if (vp
->v_tag
!= VT_HFS
) {
1551 action
|= KAUTH_VNODE_WRITE_ATTRIBUTES
;
1555 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0)) {
1556 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorization failed");
1561 * Write the attributes if we have any.
1563 if ((va
.va_active
!= 0LL) && ((error
= vnode_setattr(vp
, &va
, &context
)) != 0)) {
1564 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1569 * Write the Finder Info if we have any.
1571 if (fndrinfo
!= NULL
) {
1572 if (al
.volattr
& ATTR_VOL_INFO
) {
1573 if (vp
->v_tag
== VT_HFS
) {
1574 error
= VNOP_IOCTL(vp
, HFS_SET_BOOT_INFO
, (caddr_t
)fndrinfo
, 0, &context
);
1578 /* XXX should never get here */
1581 /* write Finder Info EA */
1583 char uio_buf
[UIO_SIZEOF(1)];
1585 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1588 uio_addiov(auio
, CAST_USER_ADDR_T(fndrinfo
), 32);
1589 error
= vn_setxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, XATTR_NOSECURITY
, &context
);
1593 if (error
== 0 && need_fsevent(FSE_FINDER_INFO_CHANGED
, vp
)) {
1594 add_fsevent(FSE_FINDER_INFO_CHANGED
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
1604 * Set the volume name, if we have one
1606 if (volname
!= NULL
)
1612 vs
.f_vol_name
= volname
; /* References the setattrlist buffer directly */
1613 VFSATTR_WANTED(&vs
, f_vol_name
);
1615 if ((error
= vfs_setattr(vp
->v_mount
, &vs
, ctx
)) != 0) {
1616 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setting volume name failed");
1620 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
1622 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not set volume name");
1627 /* all done and successful */
1632 if (user_buf
!= NULL
)
1633 FREE(user_buf
, M_TEMP
);
1634 VFS_DEBUG(ctx
, vp
, "ATTRLIST - set returning %d", error
);