2 * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/namei.h>
26 #include <sys/kernel.h>
28 #include <sys/vnode_internal.h>
29 #include <sys/mount_internal.h>
30 #include <sys/proc_internal.h>
31 #include <sys/kauth.h>
32 #include <sys/uio_internal.h>
33 #include <sys/malloc.h>
35 #include <sys/sysproto.h>
36 #include <sys/xattr.h>
37 #include <sys/fsevents.h>
38 #include <kern/kalloc.h>
39 #include <miscfs/specfs/specdev.h>
42 #define ATTR_TIME_SIZE -1
45 * Structure describing the state of an in-progress attrlist operation.
47 struct _attrlist_buf
{
57 * Pack (count) bytes from (source) into (buf).
60 attrlist_pack_fixed(struct _attrlist_buf
*ab
, void *source
, ssize_t count
)
64 /* how much room left in the buffer? */
65 fit
= imin(count
, ab
->allocated
- (ab
->fixedcursor
- ab
->base
));
67 bcopy(source
, ab
->fixedcursor
, fit
);
69 /* always move in increments of 4 */
70 ab
->fixedcursor
+= roundup(count
, 4);
73 attrlist_pack_variable2(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
, const void *ext
, ssize_t extcount
)
75 struct attrreference ar
;
78 /* pack the reference to the variable object */
79 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
80 ar
.attr_length
= count
+ extcount
;
81 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
83 /* calculate space and pack the variable object */
84 fit
= imin(count
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
87 bcopy(source
, ab
->varcursor
, fit
);
90 fit
= imin(extcount
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
93 bcopy(ext
, ab
->varcursor
, fit
);
96 /* always move in increments of 4 */
97 ab
->varcursor
= (char *)roundup((uintptr_t)ab
->varcursor
, 4);
100 attrlist_pack_variable(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
)
102 attrlist_pack_variable2(ab
, source
, count
, NULL
, 0);
105 attrlist_pack_string(struct _attrlist_buf
*ab
, const char *source
, ssize_t count
)
107 struct attrreference ar
;
112 * Supplied count is character count of string text, excluding trailing nul
113 * which we always supply here.
115 if (source
== NULL
) {
117 } else if (count
== 0) {
118 count
= strlen(source
);
122 * Make the reference and pack it.
123 * Note that this is entirely independent of how much we get into
126 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
127 ar
.attr_length
= count
+ 1;
128 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
130 /* calculate how much of the string text we can copy, and do that */
131 space
= ab
->allocated
- (ab
->varcursor
- ab
->base
);
132 fit
= imin(count
, space
);
134 bcopy(source
, ab
->varcursor
, fit
);
135 /* is there room for our trailing nul? */
137 ab
->varcursor
[fit
] = '\0';
139 /* always move in increments of 4 */
140 ab
->varcursor
+= roundup(count
+ 1, 4);
143 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
144 #define ATTR_PACK_CAST(b, t, v) \
150 #define ATTR_PACK_TIME(b, v, is64) \
153 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
162 * Table-driven setup for all valid common/volume attributes.
164 struct getvolattrlist_attrtab
{
167 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
170 static struct getvolattrlist_attrtab getvolattrlist_common_tab
[] = {
171 {ATTR_CMN_NAME
, 0, sizeof(struct attrreference
)},
172 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
)},
173 {ATTR_CMN_FSID
, 0, sizeof(fsid_t
)},
174 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
)},
175 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
)},
176 {ATTR_CMN_OBJID
, 0, sizeof(fsobj_id_t
)},
177 {ATTR_CMN_OBJPERMANENTID
, 0, sizeof(fsobj_id_t
)},
178 {ATTR_CMN_PAROBJID
, 0, sizeof(fsobj_id_t
)},
179 {ATTR_CMN_SCRIPT
, 0, sizeof(text_encoding_t
)},
180 {ATTR_CMN_CRTIME
, VFSATTR_BIT(f_create_time
), ATTR_TIME_SIZE
},
181 {ATTR_CMN_MODTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
182 {ATTR_CMN_CHGTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
183 {ATTR_CMN_ACCTIME
, VFSATTR_BIT(f_access_time
), ATTR_TIME_SIZE
},
184 {ATTR_CMN_BKUPTIME
, VFSATTR_BIT(f_backup_time
), ATTR_TIME_SIZE
},
185 {ATTR_CMN_FNDRINFO
, 0, 32},
186 {ATTR_CMN_OWNERID
, 0, sizeof(uid_t
)},
187 {ATTR_CMN_GRPID
, 0, sizeof(gid_t
)},
188 {ATTR_CMN_ACCESSMASK
, 0, sizeof(uint32_t)},
189 {ATTR_CMN_FLAGS
, 0, sizeof(uint32_t)},
190 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t)},
194 static struct getvolattrlist_attrtab getvolattrlist_vol_tab
[] = {
195 {ATTR_VOL_FSTYPE
, 0, sizeof(uint32_t)},
196 {ATTR_VOL_SIGNATURE
, VFSATTR_BIT(f_signature
), sizeof(uint32_t)},
197 {ATTR_VOL_SIZE
, VFSATTR_BIT(f_blocks
), sizeof(off_t
)},
198 {ATTR_VOL_SPACEFREE
, VFSATTR_BIT(f_bfree
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
199 {ATTR_VOL_SPACEAVAIL
, VFSATTR_BIT(f_bavail
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
200 {ATTR_VOL_MINALLOCATION
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
201 {ATTR_VOL_ALLOCATIONCLUMP
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
202 {ATTR_VOL_IOBLOCKSIZE
, VFSATTR_BIT(f_iosize
), sizeof(uint32_t)},
203 {ATTR_VOL_OBJCOUNT
, VFSATTR_BIT(f_objcount
), sizeof(uint32_t)},
204 {ATTR_VOL_FILECOUNT
, VFSATTR_BIT(f_filecount
), sizeof(uint32_t)},
205 {ATTR_VOL_DIRCOUNT
, VFSATTR_BIT(f_dircount
), sizeof(uint32_t)},
206 {ATTR_VOL_MAXOBJCOUNT
, VFSATTR_BIT(f_maxobjcount
), sizeof(uint32_t)},
207 {ATTR_VOL_MOUNTPOINT
, 0, sizeof(struct attrreference
)},
208 {ATTR_VOL_NAME
, VFSATTR_BIT(f_vol_name
), sizeof(struct attrreference
)},
209 {ATTR_VOL_MOUNTFLAGS
, 0, sizeof(uint32_t)},
210 {ATTR_VOL_MOUNTEDDEVICE
, 0, sizeof(struct attrreference
)},
211 {ATTR_VOL_ENCODINGSUSED
, 0, sizeof(uint64_t)},
212 {ATTR_VOL_CAPABILITIES
, VFSATTR_BIT(f_capabilities
), sizeof(vol_capabilities_attr_t
)},
213 {ATTR_VOL_ATTRIBUTES
, VFSATTR_BIT(f_attributes
), sizeof(vol_attributes_attr_t
)},
214 {ATTR_VOL_INFO
, 0, 0},
219 getvolattrlist_parsetab(struct getvolattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vfs_attr
*vsp
,
220 ssize_t
*sizep
, int is_64bit
)
222 attrgroup_t recognised
;
226 /* is this attribute set? */
227 if (tab
->attr
& attrs
) {
228 recognised
|= tab
->attr
;
229 vsp
->f_active
|= tab
->bits
;
230 if (tab
->size
== ATTR_TIME_SIZE
) {
232 *sizep
+= sizeof(struct user_timespec
);
234 *sizep
+= sizeof(struct timespec
);
240 } while ((++tab
)->attr
!= 0);
242 /* check to make sure that we recognised all of the passed-in attributes */
243 if (attrs
& ~recognised
)
249 * Given the attributes listed in alp, configure vap to request
250 * the data from a filesystem.
253 getvolattrlist_setupvfsattr(struct attrlist
*alp
, struct vfs_attr
*vsp
, ssize_t
*sizep
, int is_64bit
)
258 * Parse the above tables.
260 *sizep
= sizeof(uint32_t); /* length count */
261 if (alp
->commonattr
&&
262 (error
= getvolattrlist_parsetab(getvolattrlist_common_tab
, alp
->commonattr
, vsp
, sizep
, is_64bit
)) != 0)
265 (error
= getvolattrlist_parsetab(getvolattrlist_vol_tab
, alp
->volattr
, vsp
, sizep
, is_64bit
)) != 0)
272 * Table-driven setup for all valid common/dir/file/fork attributes against files.
274 struct getattrlist_attrtab
{
277 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
279 kauth_action_t action
;
281 static struct getattrlist_attrtab getattrlist_common_tab
[] = {
282 {ATTR_CMN_NAME
, VATTR_BIT(va_name
), sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
283 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
284 {ATTR_CMN_FSID
, VATTR_BIT(va_fsid
), sizeof(fsid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
285 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
286 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
287 {ATTR_CMN_OBJID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
288 {ATTR_CMN_OBJPERMANENTID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
289 {ATTR_CMN_PAROBJID
, VATTR_BIT(va_parentid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
290 {ATTR_CMN_SCRIPT
, VATTR_BIT(va_encoding
), sizeof(text_encoding_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
291 {ATTR_CMN_CRTIME
, VATTR_BIT(va_create_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
292 {ATTR_CMN_MODTIME
, VATTR_BIT(va_modify_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
293 {ATTR_CMN_CHGTIME
, VATTR_BIT(va_change_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
294 {ATTR_CMN_ACCTIME
, VATTR_BIT(va_access_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
295 {ATTR_CMN_BKUPTIME
, VATTR_BIT(va_backup_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
296 {ATTR_CMN_FNDRINFO
, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES
},
297 {ATTR_CMN_OWNERID
, VATTR_BIT(va_uid
), sizeof(uid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
298 {ATTR_CMN_GRPID
, VATTR_BIT(va_gid
), sizeof(gid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
299 {ATTR_CMN_ACCESSMASK
, VATTR_BIT(va_mode
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
300 {ATTR_CMN_FLAGS
, VATTR_BIT(va_flags
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
301 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
302 {ATTR_CMN_EXTENDED_SECURITY
, VATTR_BIT(va_acl
), sizeof(struct attrreference
), KAUTH_VNODE_READ_SECURITY
},
303 {ATTR_CMN_UUID
, VATTR_BIT(va_uuuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
304 {ATTR_CMN_GRPUUID
, VATTR_BIT(va_guuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
307 static struct getattrlist_attrtab getattrlist_dir_tab
[] = {
308 {ATTR_DIR_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
309 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nchildren
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
310 /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
311 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nlink
), 0, KAUTH_VNODE_READ_ATTRIBUTES
},
312 {ATTR_DIR_MOUNTSTATUS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
315 static struct getattrlist_attrtab getattrlist_file_tab
[] = {
316 {ATTR_FILE_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
317 {ATTR_FILE_TOTALSIZE
, VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
318 {ATTR_FILE_ALLOCSIZE
, VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
319 {ATTR_FILE_IOBLOCKSIZE
, VATTR_BIT(va_iosize
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
320 {ATTR_FILE_DEVTYPE
, VATTR_BIT(va_rdev
), sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
321 {ATTR_FILE_DATALENGTH
, VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
322 {ATTR_FILE_DATAALLOCSIZE
, VATTR_BIT(va_total_alloc
)| VATTR_BIT(va_data_alloc
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
323 {ATTR_FILE_RSRCLENGTH
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
324 {ATTR_FILE_RSRCALLOCSIZE
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
329 getattrlist_parsetab(struct getattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vnode_attr
*vap
,
330 ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
)
332 attrgroup_t recognised
;
336 /* is this attribute set? */
337 if (tab
->attr
& attrs
) {
338 recognised
|= tab
->attr
;
339 vap
->va_active
|= tab
->bits
;
340 if (tab
->size
== ATTR_TIME_SIZE
) {
342 *sizep
+= sizeof(struct user_timespec
);
344 *sizep
+= sizeof(struct timespec
);
349 *actionp
|= tab
->action
;
351 } while ((++tab
)->attr
!= 0);
353 /* check to make sure that we recognised all of the passed-in attributes */
354 if (attrs
& ~recognised
)
360 * Given the attributes listed in alp, configure vap to request
361 * the data from a filesystem.
364 getattrlist_setupvattr(struct attrlist
*alp
, struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
, int isdir
)
369 * Parse the above tables.
371 *sizep
= sizeof(uint32_t); /* length count */
373 if (alp
->commonattr
&&
374 (error
= getattrlist_parsetab(getattrlist_common_tab
, alp
->commonattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
376 if (isdir
&& alp
->dirattr
&&
377 (error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
379 if (!isdir
&& alp
->fileattr
&&
380 (error
= getattrlist_parsetab(getattrlist_file_tab
, alp
->fileattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
388 * Find something resembling a terminal component name in the mountedonname for vp
392 getattrlist_findnamecomp(const char *mn
, const char **np
, ssize_t
*nl
)
398 * We're looking for the last sequence of non / characters, but
399 * not including any trailing / characters.
404 for (cp
= mn
; *cp
!= 0; cp
++) {
406 /* start of run of chars */
412 /* end of run of chars */
419 /* need to close run? */
426 getvolattrlist(vnode_t vp
, struct getattrlist_args
*uap
, struct attrlist
*alp
, vfs_context_t ctx
, int is_64bit
)
429 struct vnode_attr va
;
430 struct _attrlist_buf ab
;
432 ssize_t fixedsize
, varsize
;
440 vs
.f_vol_name
= NULL
;
445 * For now, the vnode must be the root of its filesystem.
446 * To relax this, we need to be able to find the root vnode of a filesystem
447 * from any vnode in the filesystem.
449 if (!vnode_isvroot(vp
)) {
451 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
456 * Set up the vfs_attr structure and call the filesystem.
458 if ((error
= getvolattrlist_setupvfsattr(alp
, &vs
, &fixedsize
, is_64bit
)) != 0) {
459 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
462 if (vs
.f_active
!= 0) {
463 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
464 if (VFSATTR_IS_ACTIVE(&vs
, f_vol_name
)) {
465 vs
.f_vol_name
= (char *) kalloc(MAXPATHLEN
);
466 if (vs
.f_vol_name
== NULL
) {
468 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
473 VFS_DEBUG(ctx
, vp
, "ATTRLIST - calling to get %016llx with supported %016llx", vs
.f_active
, vs
.f_supported
);
474 if ((error
= vfs_getattr(mnt
, &vs
, ctx
)) != 0) {
475 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
480 * Did we ask for something the filesystem doesn't support?
482 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
483 /* default value for volume subtype */
484 if (VFSATTR_IS_ACTIVE(&vs
, f_fssubtype
)
485 && !VFSATTR_IS_SUPPORTED(&vs
, f_fssubtype
))
486 VFSATTR_RETURN(&vs
, f_fssubtype
, 0);
489 * If the file system didn't supply f_signature, then
490 * default it to 'BD', which is the generic signature
491 * that most Carbon file systems should return.
493 if (VFSATTR_IS_ACTIVE(&vs
, f_signature
)
494 && !VFSATTR_IS_SUPPORTED(&vs
, f_signature
))
495 VFSATTR_RETURN(&vs
, f_signature
, 0x4244);
497 /* default for block size */
498 if (VFSATTR_IS_ACTIVE(&vs
, f_bsize
)
499 && !VFSATTR_IS_SUPPORTED(&vs
, f_bsize
))
500 VFSATTR_RETURN(&vs
, f_bsize
, mnt
->mnt_devblocksize
);
502 /* check to see if our fixups were enough */
503 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
505 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested volume attributes");
506 VFS_DEBUG(ctx
, vp
, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
507 vs
.f_active
, vs
.f_supported
, vs
.f_active
& ~vs
.f_supported
);
514 * Some fields require data from the root vp
516 if (alp
->commonattr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
| ATTR_CMN_ACCESSMASK
| ATTR_CMN_FLAGS
| ATTR_CMN_SCRIPT
)) {
517 VATTR_WANTED(&va
, va_uid
);
518 VATTR_WANTED(&va
, va_gid
);
519 VATTR_WANTED(&va
, va_mode
);
520 VATTR_WANTED(&va
, va_flags
);
521 VATTR_WANTED(&va
, va_encoding
);
523 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
524 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp
);
528 if (VATTR_IS_ACTIVE(&va
, va_encoding
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
529 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
533 * Compute variable-size buffer requirements.
536 if (alp
->commonattr
& ATTR_CMN_NAME
) {
537 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
538 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
539 /* special case for boot volume. Use root name when it's
540 * available (which is the volume name) or just the mount on
541 * name of "/". we must do this for binary compatibility with
542 * pre Tiger code. returning nothing for the boot volume name
543 * breaks installers - 3961058
545 cnp
= vnode_getname(vp
);
547 /* just use "/" as name */
548 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
553 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
555 if (alp
->commonattr
& ATTR_CMN_NAME
)
556 varsize
+= roundup(cnl
+ 1, 4);
558 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
559 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntonname
) + 1, 4);
560 if (alp
->volattr
& ATTR_VOL_NAME
) {
561 vs
.f_vol_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
562 varsize
+= roundup(strlen(vs
.f_vol_name
) + 1, 4);
564 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
565 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntfromname
) + 1, 4);
568 * Allocate a target buffer for attribute results.
569 * Note that since we won't ever copy out more than the caller requested,
570 * we never need to allocate more than they offer.
572 ab
.allocated
= imin(uap
->bufferSize
, fixedsize
+ varsize
);
573 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
575 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
578 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
579 if (ab
.base
== NULL
) {
581 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
586 * Pack results into the destination buffer.
588 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
589 ab
.varcursor
= ab
.base
+ fixedsize
;
590 ab
.needed
= fixedsize
+ varsize
;
592 /* common attributes **************************************************/
593 if (alp
->commonattr
& ATTR_CMN_NAME
)
594 attrlist_pack_string(&ab
, cnp
, cnl
);
595 if (alp
->commonattr
& ATTR_CMN_DEVID
)
596 ATTR_PACK_CAST(&ab
, dev_t
, mnt
->mnt_vfsstat
.f_fsid
.val
[0]);
597 if (alp
->commonattr
& ATTR_CMN_FSID
)
598 ATTR_PACK(&ab
, mnt
->mnt_vfsstat
.f_fsid
);
599 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
)
600 ATTR_PACK_CAST(&ab
, fsobj_type_t
, 0);
601 if (alp
->commonattr
& ATTR_CMN_OBJTAG
)
602 ATTR_PACK_CAST(&ab
, fsobj_tag_t
, vp
->v_tag
);
603 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
604 fsobj_id_t f
= {0, 0};
607 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
608 fsobj_id_t f
= {0, 0};
611 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
612 fsobj_id_t f
= {0, 0};
615 /* note that this returns the encoding for the volume name, not the node name */
616 if (alp
->commonattr
& ATTR_CMN_SCRIPT
)
617 ATTR_PACK_CAST(&ab
, text_encoding_t
, va
.va_encoding
);
618 if (alp
->commonattr
& ATTR_CMN_CRTIME
)
619 ATTR_PACK_TIME(&ab
, vs
.f_create_time
, is_64bit
);
620 if (alp
->commonattr
& ATTR_CMN_MODTIME
)
621 ATTR_PACK_TIME(&ab
, vs
.f_modify_time
, is_64bit
);
622 if (alp
->commonattr
& ATTR_CMN_CHGTIME
)
623 ATTR_PACK_TIME(&ab
, vs
.f_modify_time
, is_64bit
);
624 if (alp
->commonattr
& ATTR_CMN_ACCTIME
)
625 ATTR_PACK_TIME(&ab
, vs
.f_access_time
, is_64bit
);
626 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
)
627 ATTR_PACK_TIME(&ab
, vs
.f_backup_time
, is_64bit
);
628 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
631 * This attribute isn't really Finder Info, at least for HFS.
633 if (vp
->v_tag
== VT_HFS
) {
634 if ((error
= VNOP_IOCTL(vp
, HFS_GET_BOOT_INFO
, (caddr_t
)&f
, 0, ctx
)) != 0)
637 /* XXX we could at least pass out the volume UUID here */
638 bzero(&f
, sizeof(f
));
640 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
642 if (alp
->commonattr
& ATTR_CMN_OWNERID
)
643 ATTR_PACK(&ab
, va
.va_uid
);
644 if (alp
->commonattr
& ATTR_CMN_GRPID
)
645 ATTR_PACK(&ab
, va
.va_gid
);
646 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
)
647 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
648 if (alp
->commonattr
& ATTR_CMN_FLAGS
)
649 ATTR_PACK(&ab
, va
.va_flags
);
650 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* XXX this is expensive and also duplicate work */
652 if (vnode_isdir(vp
)) {
653 if (vnode_authorize(vp
, NULL
,
654 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
656 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
658 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
661 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
663 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
665 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
668 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms
);
669 ATTR_PACK(&ab
, perms
);
672 /* volume attributes **************************************************/
674 if (alp
->volattr
& ATTR_VOL_FSTYPE
)
675 ATTR_PACK_CAST(&ab
, uint32_t, vfs_typenum(mnt
));
676 if (alp
->volattr
& ATTR_VOL_SIGNATURE
)
677 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_signature
);
678 if (alp
->volattr
& ATTR_VOL_SIZE
)
679 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_blocks
);
680 if (alp
->volattr
& ATTR_VOL_SPACEFREE
)
681 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bfree
);
682 if (alp
->volattr
& ATTR_VOL_SPACEAVAIL
)
683 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bavail
);
684 if (alp
->volattr
& ATTR_VOL_MINALLOCATION
)
685 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
);
686 if (alp
->volattr
& ATTR_VOL_ALLOCATIONCLUMP
)
687 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
); /* not strictly true */
688 if (alp
->volattr
& ATTR_VOL_IOBLOCKSIZE
)
689 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_iosize
);
690 if (alp
->volattr
& ATTR_VOL_OBJCOUNT
)
691 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_objcount
);
692 if (alp
->volattr
& ATTR_VOL_FILECOUNT
)
693 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_filecount
);
694 if (alp
->volattr
& ATTR_VOL_DIRCOUNT
)
695 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_dircount
);
696 if (alp
->volattr
& ATTR_VOL_MAXOBJCOUNT
)
697 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_maxobjcount
);
698 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
699 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntonname
, 0);
700 if (alp
->volattr
& ATTR_VOL_NAME
)
701 attrlist_pack_string(&ab
, vs
.f_vol_name
, 0);
702 if (alp
->volattr
& ATTR_VOL_MOUNTFLAGS
)
703 ATTR_PACK_CAST(&ab
, uint32_t, mnt
->mnt_flag
);
704 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
705 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntfromname
, 0);
706 if (alp
->volattr
& ATTR_VOL_ENCODINGSUSED
)
707 ATTR_PACK_CAST(&ab
, uint64_t, ~0LL); /* return all encodings */
708 if (alp
->volattr
& ATTR_VOL_CAPABILITIES
) {
709 /* fix up volume capabilities */
710 if (vfs_extendedsecurity(mnt
)) {
711 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
713 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] &= ~VOL_CAP_INT_EXTENDED_SECURITY
;
715 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
716 ATTR_PACK(&ab
, vs
.f_capabilities
);
718 if (alp
->volattr
& ATTR_VOL_ATTRIBUTES
) {
719 /* fix up volume attribute information */
720 if (vfs_extendedsecurity(mnt
)) {
721 vs
.f_attributes
.validattr
.commonattr
|= (ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
723 vs
.f_attributes
.validattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
724 vs
.f_attributes
.nativeattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
726 ATTR_PACK(&ab
, vs
.f_attributes
);
730 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
731 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
732 fixedsize
, ab
.fixedcursor
- ab
.base
, alp
->commonattr
, alp
->volattr
);
733 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
734 panic("packed variable field size mismatch; used %d but expected %d", ab
.varcursor
- ab
.base
, ab
.needed
);
737 * In the compatible case, we report the smaller of the required and returned sizes.
738 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
739 * of the result buffer, even if we copied less out. The caller knows how big a buffer
740 * they gave us, so they can always check for truncation themselves.
742 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
744 error
= copyout(ab
.base
, uap
->attributeBuffer
, ab
.allocated
);
747 if (vs
.f_vol_name
!= NULL
)
748 kfree(vs
.f_vol_name
, MAXPATHLEN
);
750 FREE(ab
.base
, M_TEMP
);
751 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
756 * Obtain attribute information about a filesystem object.
759 getattrlist(struct proc
*p
, struct getattrlist_args
*uap
, __unused register_t
*retval
)
762 struct vnode_attr va
;
763 struct vfs_context context
, *ctx
;
765 struct _attrlist_buf ab
;
768 kauth_action_t action
;
769 ssize_t fixedsize
, varsize
;
776 context
.vc_ucred
= kauth_cred_get();
789 nameiflags
= AUDITVNPATH1
;
790 if (!(uap
->options
& FSOPT_NOFOLLOW
))
791 nameiflags
|= FOLLOW
;
792 NDINIT(&nd
, LOOKUP
, nameiflags
, UIO_USERSPACE
, uap
->path
, &context
);
794 if ((error
= namei(&nd
)) != 0)
800 * Fetch the attribute request.
802 if ((error
= copyin(uap
->alist
, &al
, sizeof(al
))) != 0)
804 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
809 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
810 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
811 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
814 * It is legal to request volume or file attributes,
818 if (al
.fileattr
|| al
.dirattr
|| al
.forkattr
) {
820 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
823 /* handle volume attribute request */
824 error
= getvolattrlist(vp
, uap
, &al
, &context
, proc_is64bit(p
));
829 * Set up the vnode_attr structure and authorise.
831 if ((error
= getattrlist_setupvattr(&al
, &va
, &fixedsize
, &action
, proc_is64bit(p
), vnode_isdir(vp
))) != 0) {
832 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
835 if ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0) {
836 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorisation failed/denied");
840 if (va
.va_active
!= 0) {
842 * If we're going to ask for va_name, allocate a buffer to point it at
844 if (VATTR_IS_ACTIVE(&va
, va_name
)) {
845 va
.va_name
= (char *) kalloc(MAXPATHLEN
);
846 if (va
.va_name
== NULL
) {
848 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: cannot allocate va_name buffer");
854 * Call the filesystem.
856 if ((error
= vnode_getattr(vp
, &va
, &context
)) != 0) {
857 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
861 /* did we ask for something the filesystem doesn't support? */
862 if (!VATTR_ALL_SUPPORTED(&va
)) {
865 * There are a couple of special cases. If we are after object IDs,
866 * we can make do with va_fileid.
868 if ((al
.commonattr
& (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
)) && !VATTR_IS_SUPPORTED(&va
, va_linkid
))
869 VATTR_CLEAR_ACTIVE(&va
, va_linkid
); /* forget we wanted this */
871 * Many (most?) filesystems don't know their parent object id. We can get it the
874 if ((al
.commonattr
& ATTR_CMN_PAROBJID
) && !VATTR_IS_SUPPORTED(&va
, va_parentid
))
875 VATTR_CLEAR_ACTIVE(&va
, va_parentid
);
877 * And we can report datasize/alloc from total.
879 if ((al
.fileattr
& ATTR_FILE_DATALENGTH
) && !VATTR_IS_SUPPORTED(&va
, va_data_size
))
880 VATTR_CLEAR_ACTIVE(&va
, va_data_size
);
881 if ((al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) && !VATTR_IS_SUPPORTED(&va
, va_data_alloc
))
882 VATTR_CLEAR_ACTIVE(&va
, va_data_alloc
);
885 * If we don't have an encoding, go with UTF-8
887 if ((al
.commonattr
& ATTR_CMN_SCRIPT
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
888 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
891 * If we don't have a name, we'll get one from the vnode or mount point.
893 if ((al
.commonattr
& ATTR_CMN_NAME
) && !VATTR_IS_SUPPORTED(&va
, va_name
)) {
894 VATTR_CLEAR_ACTIVE(&va
, va_name
);
898 * We used to return va_nlink-2 for ATTR_DIR_ENTRYCOUNT. The va_nchildren
899 * field is preferred, but we'll fall back to va_nlink-2 for compatibility
900 * with file systems which haven't adopted va_nchildren. Note: the "- 2"
901 * reflects the "." and ".." entries which are reported via POSIX APIs, but
902 * not via Carbon (since they don't in fact exist in HFS).
904 if ((al
.dirattr
& ATTR_DIR_ENTRYCOUNT
) && !VATTR_IS_SUPPORTED(&va
, va_nchildren
) &&
905 VATTR_IS_SUPPORTED(&va
, va_nlink
)) {
906 VATTR_RETURN(&va
, va_nchildren
, va
.va_nlink
- 2);
910 if (!VATTR_ALL_SUPPORTED(&va
)) {
912 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested file attributes");
913 VFS_DEBUG(ctx
, vp
, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
914 va
.va_supported
, va
.va_active
, va
.va_active
& ~va
.va_supported
);
921 * Compute variable-space requirements.
923 varsize
= 0; /* length count */
924 if (al
.commonattr
& ATTR_CMN_NAME
) {
925 if (VATTR_IS_SUPPORTED(&va
, va_name
)) {
926 va
.va_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
930 if (vnode_isvroot(vp
)) {
931 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
932 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
933 /* special case for boot volume. Use root name when it's
934 * available (which is the volume name) or just the mount on
935 * name of "/". we must do this for binary compatibility with
936 * pre Tiger code. returning nothing for the boot volume name
937 * breaks installers - 3961058
939 cnp
= vname
= vnode_getname(vp
);
941 /* just use "/" as name */
942 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
947 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
950 cnp
= vname
= vnode_getname(vp
);
957 varsize
+= roundup(cnl
+ 1, 4);
961 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
963 * XXX This needs to change at some point; since the blob is opaque in
964 * user-space this is OK.
966 if ((al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) &&
967 VATTR_IS_SUPPORTED(&va
, va_acl
) &&
969 varsize
+= roundup(KAUTH_FILESEC_SIZE(va
.va_acl
->acl_entrycount
), 4);
972 * Allocate a target buffer for attribute results.
974 * Note that we won't ever copy out more than the caller requested, even though
975 * we might have to allocate more than they offer do that the diagnostic checks
976 * don't result in a panic if the caller's buffer is too small..
978 ab
.allocated
= fixedsize
+ varsize
;
979 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
981 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
984 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
985 if (ab
.base
== NULL
) {
987 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
992 * Pack results into the destination buffer.
994 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
995 ab
.varcursor
= ab
.base
+ fixedsize
;
996 ab
.needed
= ab
.allocated
;
998 /* common attributes **************************************************/
999 if (al
.commonattr
& ATTR_CMN_NAME
)
1000 attrlist_pack_string(&ab
, cnp
, cnl
);
1001 if (al
.commonattr
& ATTR_CMN_DEVID
)
1002 ATTR_PACK_CAST(&ab
, dev_t
, vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
1003 if (al
.commonattr
& ATTR_CMN_FSID
)
1004 ATTR_PACK(&ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
);
1005 if (al
.commonattr
& ATTR_CMN_OBJTYPE
)
1006 ATTR_PACK_CAST(&ab
, fsobj_type_t
, vp
->v_type
);
1007 if (al
.commonattr
& ATTR_CMN_OBJTAG
)
1008 ATTR_PACK_CAST(&ab
, fsobj_tag_t
, vp
->v_tag
);
1009 if (al
.commonattr
& ATTR_CMN_OBJID
) {
1012 * Carbon can't deal with us reporting the target ID
1013 * for links. So we ask the filesystem to give us the
1014 * source ID as well, and if it gives us one, we use
1017 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1018 f
.fid_objno
= va
.va_linkid
;
1020 f
.fid_objno
= va
.va_fileid
;
1022 f
.fid_generation
= 0;
1025 if (al
.commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1028 * Carbon can't deal with us reporting the target ID
1029 * for links. So we ask the filesystem to give us the
1030 * source ID as well, and if it gives us one, we use
1033 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1034 f
.fid_objno
= va
.va_linkid
;
1036 f
.fid_objno
= va
.va_fileid
;
1038 f
.fid_generation
= 0;
1041 if (al
.commonattr
& ATTR_CMN_PAROBJID
) {
1044 * If the filesystem doesn't know the parent ID, we can
1045 * try to get it via v->v_parent. Don't need to worry
1046 * about links here, as we dont allow hardlinks to
1049 if (VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
1050 f
.fid_objno
= va
.va_parentid
;
1052 struct vnode_attr lva
;
1055 pvp
= vnode_getparent(vp
);
1057 if (pvp
== NULLVP
) {
1062 VATTR_WANTED(&lva
, va_fileid
);
1063 error
= vnode_getattr(pvp
, &lva
, &context
);
1068 f
.fid_objno
= lva
.va_fileid
;
1070 f
.fid_generation
= 0;
1073 if (al
.commonattr
& ATTR_CMN_SCRIPT
)
1074 ATTR_PACK_CAST(&ab
, text_encoding_t
, va
.va_encoding
);
1075 if (al
.commonattr
& ATTR_CMN_CRTIME
)
1076 ATTR_PACK_TIME(&ab
, va
.va_create_time
, proc_is64bit(p
));
1077 if (al
.commonattr
& ATTR_CMN_MODTIME
)
1078 ATTR_PACK_TIME(&ab
, va
.va_modify_time
, proc_is64bit(p
));
1079 if (al
.commonattr
& ATTR_CMN_CHGTIME
)
1080 ATTR_PACK_TIME(&ab
, va
.va_change_time
, proc_is64bit(p
));
1081 if (al
.commonattr
& ATTR_CMN_ACCTIME
)
1082 ATTR_PACK_TIME(&ab
, va
.va_access_time
, proc_is64bit(p
));
1083 if (al
.commonattr
& ATTR_CMN_BKUPTIME
)
1084 ATTR_PACK_TIME(&ab
, va
.va_backup_time
, proc_is64bit(p
));
1085 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1088 char uio_buf
[UIO_SIZEOF(1)];
1090 fisize
= imin(32, ab
.allocated
- (ab
.fixedcursor
- ab
.base
));
1092 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1096 uio_addiov(auio
, CAST_USER_ADDR_T(ab
.fixedcursor
), fisize
);
1097 error
= vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, &fisize
, XATTR_NOSECURITY
, &context
);
1101 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1102 VFS_DEBUG(ctx
, vp
, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1103 bzero(ab
.fixedcursor
, 32);
1106 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1111 VFS_DEBUG(ctx
, vp
, "ATTRLIST - no room in caller buffer for FINDERINFO");
1113 ab
.fixedcursor
+= 32;
1115 if (al
.commonattr
& ATTR_CMN_OWNERID
)
1116 ATTR_PACK(&ab
, va
.va_uid
);
1117 if (al
.commonattr
& ATTR_CMN_GRPID
)
1118 ATTR_PACK(&ab
, va
.va_gid
);
1119 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
)
1120 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
1121 if (al
.commonattr
& ATTR_CMN_FLAGS
)
1122 ATTR_PACK(&ab
, va
.va_flags
);
1123 if (al
.commonattr
& ATTR_CMN_USERACCESS
) { /* this is expensive */
1125 if (vnode_isdir(vp
)) {
1126 if (vnode_authorize(vp
, NULL
,
1127 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, &context
) == 0)
1129 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, &context
) == 0)
1131 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, &context
) == 0)
1134 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, &context
) == 0)
1136 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, &context
) == 0)
1138 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, &context
) == 0)
1141 VFS_DEBUG(ctx
, vp
, "ATTRLIST - granting perms %d", perms
);
1142 ATTR_PACK(&ab
, perms
);
1144 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1145 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
)) {
1146 struct kauth_filesec fsec
;
1148 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1150 fsec
.fsec_magic
= KAUTH_FILESEC_MAGIC
;
1151 fsec
.fsec_owner
= kauth_null_guid
;
1152 fsec
.fsec_group
= kauth_null_guid
;
1153 attrlist_pack_variable2(&ab
, &fsec
, ((char *)&fsec
.fsec_acl
- (char *)&fsec
), va
.va_acl
, KAUTH_ACL_COPYSIZE(va
.va_acl
));
1155 attrlist_pack_variable(&ab
, NULL
, 0);
1158 if (al
.commonattr
& ATTR_CMN_UUID
) {
1159 if (!VATTR_IS_SUPPORTED(&va
, va_uuuid
)) {
1160 ATTR_PACK(&ab
, kauth_null_guid
);
1162 ATTR_PACK(&ab
, va
.va_uuuid
);
1165 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1166 if (!VATTR_IS_SUPPORTED(&va
, va_guuid
)) {
1167 ATTR_PACK(&ab
, kauth_null_guid
);
1169 ATTR_PACK(&ab
, va
.va_guuid
);
1173 /* directory attributes **************************************************/
1174 if (vnode_isdir(vp
)) {
1175 if (al
.dirattr
& ATTR_DIR_LINKCOUNT
) /* full count of entries */
1176 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nlink
);
1177 if (al
.dirattr
& ATTR_DIR_ENTRYCOUNT
)
1178 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nchildren
);
1179 if (al
.dirattr
& ATTR_DIR_MOUNTSTATUS
)
1180 ATTR_PACK_CAST(&ab
, uint32_t, (vp
->v_flag
& VROOT
) ? DIR_MNTSTATUS_MNTPOINT
: 0);
1183 /* file attributes **************************************************/
1184 if (!vnode_isdir(vp
)) {
1185 if (al
.fileattr
& ATTR_FILE_LINKCOUNT
)
1186 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nlink
);
1187 if (al
.fileattr
& ATTR_FILE_TOTALSIZE
)
1188 ATTR_PACK(&ab
, va
.va_total_size
);
1189 if (al
.fileattr
& ATTR_FILE_ALLOCSIZE
)
1190 ATTR_PACK(&ab
, va
.va_total_alloc
);
1191 if (al
.fileattr
& ATTR_FILE_IOBLOCKSIZE
)
1192 ATTR_PACK(&ab
, va
.va_iosize
);
1193 if (al
.fileattr
& ATTR_FILE_CLUMPSIZE
)
1194 ATTR_PACK_CAST(&ab
, uint32_t, 0); /* XXX value is deprecated */
1195 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1196 if ((vp
->v_type
== VCHR
) || (vp
->v_type
== VBLK
)) {
1197 ATTR_PACK(&ab
, vp
->v_specinfo
->si_rdev
);
1199 ATTR_PACK_CAST(&ab
, uint32_t, 0);
1202 if (al
.fileattr
& ATTR_FILE_DATALENGTH
) {
1203 if (VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
1204 ATTR_PACK(&ab
, va
.va_data_size
);
1206 ATTR_PACK(&ab
, va
.va_total_size
);
1209 if (al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) {
1210 if (VATTR_IS_SUPPORTED(&va
, va_data_alloc
)) {
1211 ATTR_PACK(&ab
, va
.va_data_alloc
);
1213 ATTR_PACK(&ab
, va
.va_total_alloc
);
1216 /* fetch resource fork size/allocation via xattr interface */
1217 if (al
.fileattr
& (ATTR_FILE_RSRCLENGTH
| ATTR_FILE_RSRCALLOCSIZE
)) {
1219 if ((error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &rsize
, XATTR_NOSECURITY
, &context
)) != 0) {
1220 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1227 if (al
.fileattr
& ATTR_FILE_RSRCLENGTH
)
1228 ATTR_PACK_CAST(&ab
, off_t
, rsize
);
1229 if (al
.fileattr
& ATTR_FILE_RSRCALLOCSIZE
) {
1230 uint32_t blksize
= vp
->v_mount
->mnt_vfsstat
.f_bsize
;
1233 ATTR_PACK_CAST(&ab
, off_t
, (roundup(rsize
, blksize
)));
1239 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
1240 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
1241 fixedsize
, ab
.fixedcursor
- ab
.base
, al
.commonattr
, al
.volattr
);
1242 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
1243 panic("packed variable field size mismatch; used %d but expected %d", ab
.varcursor
- ab
.base
, ab
.needed
);
1246 * In the compatible case, we report the smaller of the required and returned sizes.
1247 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1248 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1249 * they gave us, so they can always check for truncation themselves.
1251 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
1253 /* Only actually copyout as much out as the user buffer can hold */
1254 error
= copyout(ab
.base
, uap
->attributeBuffer
, imin(uap
->bufferSize
, ab
.allocated
));
1258 kfree(va
.va_name
, MAXPATHLEN
);
1260 vnode_putname(vname
);
1263 if (ab
.base
!= NULL
)
1264 FREE(ab
.base
, M_TEMP
);
1265 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
))
1266 kauth_acl_free(va
.va_acl
);
1268 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
1273 attrlist_unpack_fixed(char **cursor
, char *end
, void *buf
, ssize_t size
)
1275 /* make sure we have enough source data */
1276 if ((*cursor
) + size
> end
)
1279 bcopy(*cursor
, buf
, size
);
1284 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1285 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1286 #define ATTR_UNPACK_TIME(v, is64) \
1289 struct user_timespec us; \
1291 v.tv_sec = us.tv_sec; \
1292 v.tv_nsec = us.tv_nsec; \
1303 setattrlist(struct proc
*p
, register struct setattrlist_args
*uap
, __unused register_t
*retval
)
1306 struct vfs_context context
, *ctx
;
1307 struct vnode_attr va
;
1308 struct attrreference ar
;
1309 struct nameidata nd
;
1312 kauth_action_t action
;
1313 char *user_buf
, *cursor
, *bufend
, *fndrinfo
, *cp
, *volname
;
1314 int proc_is64
, error
;
1316 kauth_filesec_t rfsec
;
1318 context
.vc_proc
= p
;
1319 context
.vc_ucred
= kauth_cred_get();
1326 proc_is64
= proc_is64bit(p
);
1334 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0)
1335 nameiflags
|= FOLLOW
;
1336 NDINIT(&nd
, LOOKUP
, nameiflags
| AUDITVNPATH1
, UIO_USERSPACE
, uap
->path
, &context
);
1337 if ((error
= namei(&nd
)) != 0)
1343 * Fetch the attribute set and validate.
1345 if ((error
= copyin(uap
->alist
, (caddr_t
) &al
, sizeof (al
))))
1347 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
1352 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1353 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
1354 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
1357 if ((al
.volattr
& ~ATTR_VOL_SETMASK
) ||
1358 (al
.commonattr
& ~ATTR_CMN_VOLSETMASK
) ||
1362 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1366 if ((al
.commonattr
& ~ATTR_CMN_SETMASK
) ||
1367 (al
.fileattr
& ~ATTR_FILE_SETMASK
) ||
1368 (al
.dirattr
& ~ATTR_DIR_SETMASK
) ||
1369 (al
.forkattr
& ~ATTR_FORK_SETMASK
)) {
1371 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1377 * Make the naive assumption that the caller has supplied a reasonable buffer
1378 * size. We could be more careful by pulling in the fixed-size region, checking
1379 * the attrref structures, then pulling in the variable section.
1380 * We need to reconsider this for handling large ACLs, as they should probably be
1381 * brought directly into a buffer. Multiple copyins will make this slower though.
1383 * We could also map the user buffer if it is larger than some sensible mimimum.
1385 if (uap
->bufferSize
> ATTR_MAX_BUFFER
) {
1386 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size %d too large", uap
->bufferSize
);
1390 MALLOC(user_buf
, char *, uap
->bufferSize
, M_TEMP
, M_WAITOK
);
1391 if (user_buf
== NULL
) {
1392 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap
->bufferSize
);
1396 if ((error
= copyin(uap
->attributeBuffer
, user_buf
, uap
->bufferSize
)) != 0) {
1397 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer copyin failed");
1400 VFS_DEBUG(ctx
, vp
, "ATTRLIST - copied in %d bytes of user attributes to %p", uap
->bufferSize
, user_buf
);
1403 * Unpack the argument buffer.
1406 bufend
= cursor
+ uap
->bufferSize
;
1409 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
1410 ATTR_UNPACK(va
.va_encoding
);
1411 VATTR_SET_ACTIVE(&va
, va_encoding
);
1413 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
1414 ATTR_UNPACK_TIME(va
.va_create_time
, proc_is64
);
1415 VATTR_SET_ACTIVE(&va
, va_create_time
);
1417 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
1418 ATTR_UNPACK_TIME(va
.va_modify_time
, proc_is64
);
1419 VATTR_SET_ACTIVE(&va
, va_modify_time
);
1421 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
1422 ATTR_UNPACK_TIME(va
.va_change_time
, proc_is64
);
1423 VATTR_SET_ACTIVE(&va
, va_change_time
);
1425 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
1426 ATTR_UNPACK_TIME(va
.va_access_time
, proc_is64
);
1427 VATTR_SET_ACTIVE(&va
, va_access_time
);
1429 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
1430 ATTR_UNPACK_TIME(va
.va_backup_time
, proc_is64
);
1431 VATTR_SET_ACTIVE(&va
, va_backup_time
);
1433 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1434 if ((cursor
+ 32) > bufend
) {
1436 VFS_DEBUG(ctx
, vp
, "ATTRLIST - not enough data supplied for FINDERINFO");
1442 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
1443 ATTR_UNPACK(va
.va_uid
);
1444 VATTR_SET_ACTIVE(&va
, va_uid
);
1446 if (al
.commonattr
& ATTR_CMN_GRPID
) {
1447 ATTR_UNPACK(va
.va_gid
);
1448 VATTR_SET_ACTIVE(&va
, va_gid
);
1450 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1451 ATTR_UNPACK_CAST(uint32_t, va
.va_mode
);
1452 VATTR_SET_ACTIVE(&va
, va_mode
);
1454 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
1455 ATTR_UNPACK(va
.va_flags
);
1456 VATTR_SET_ACTIVE(&va
, va_flags
);
1458 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1461 * We are (for now) passed a kauth_filesec_t, but all we want from
1466 cp
+= ar
.attr_dataoffset
;
1467 rfsec
= (kauth_filesec_t
)cp
;
1468 if (((char *)(rfsec
+ 1) > bufend
) || /* no space for acl */
1469 (rfsec
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) || /* bad magic */
1470 (KAUTH_FILESEC_COPYSIZE(rfsec
) != ar
.attr_length
) || /* size does not match */
1471 ((cp
+ KAUTH_FILESEC_COPYSIZE(rfsec
)) > bufend
)) { /* ACEs overrun buffer */
1473 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied", ar
.attr_length
);
1476 nace
= rfsec
->fsec_entrycount
;
1477 if (nace
== KAUTH_FILESEC_NOACL
)
1479 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1481 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied");
1484 nace
= rfsec
->fsec_acl
.acl_entrycount
;
1485 if (nace
== KAUTH_FILESEC_NOACL
) {
1487 VATTR_SET(&va
, va_acl
, NULL
);
1490 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1492 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: supplied ACL is too large");
1495 VATTR_SET(&va
, va_acl
, &rfsec
->fsec_acl
);
1498 if (al
.commonattr
& ATTR_CMN_UUID
) {
1499 ATTR_UNPACK(va
.va_uuuid
);
1500 VATTR_SET_ACTIVE(&va
, va_uuuid
);
1502 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1503 ATTR_UNPACK(va
.va_guuid
);
1504 VATTR_SET_ACTIVE(&va
, va_guuid
);
1508 if (al
.volattr
& ATTR_VOL_INFO
) {
1509 if (al
.volattr
& ATTR_VOL_NAME
) {
1512 volname
+= ar
.attr_dataoffset
;
1513 if ((volname
+ ar
.attr_length
) > bufend
) {
1515 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume name too big for caller buffer");
1518 /* guarantee NUL termination */
1519 volname
[ar
.attr_length
- 1] = 0;
1524 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1525 /* XXX does it actually make any sense to change this? */
1527 VFS_DEBUG(ctx
, vp
, "ATTRLIST - XXX device type change not implemented");
1532 * Validate and authorize.
1535 if ((va
.va_active
!= 0LL) && ((error
= vnode_authattr(vp
, &va
, &action
, &context
)) != 0)) {
1536 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attribute changes refused: %d", error
);
1540 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1541 * and will be auth'ed by the FS.
1543 if (fndrinfo
!= NULL
) {
1544 if (al
.volattr
& ATTR_VOL_INFO
) {
1545 if (vp
->v_tag
!= VT_HFS
) {
1550 action
|= KAUTH_VNODE_WRITE_ATTRIBUTES
;
1554 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0)) {
1555 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorization failed");
1560 * Write the attributes if we have any.
1562 if ((va
.va_active
!= 0LL) && ((error
= vnode_setattr(vp
, &va
, &context
)) != 0)) {
1563 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1568 * Write the Finder Info if we have any.
1570 if (fndrinfo
!= NULL
) {
1571 if (al
.volattr
& ATTR_VOL_INFO
) {
1572 if (vp
->v_tag
== VT_HFS
) {
1573 error
= VNOP_IOCTL(vp
, HFS_SET_BOOT_INFO
, (caddr_t
)fndrinfo
, 0, &context
);
1577 /* XXX should never get here */
1580 /* write Finder Info EA */
1582 char uio_buf
[UIO_SIZEOF(1)];
1584 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1587 uio_addiov(auio
, CAST_USER_ADDR_T(fndrinfo
), 32);
1588 error
= vn_setxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, XATTR_NOSECURITY
, &context
);
1592 if (error
== 0 && need_fsevent(FSE_FINDER_INFO_CHANGED
, vp
)) {
1593 add_fsevent(FSE_FINDER_INFO_CHANGED
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
1603 * Set the volume name, if we have one
1605 if (volname
!= NULL
)
1611 vs
.f_vol_name
= volname
; /* References the setattrlist buffer directly */
1612 VFSATTR_WANTED(&vs
, f_vol_name
);
1614 if ((error
= vfs_setattr(vp
->v_mount
, &vs
, ctx
)) != 0) {
1615 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setting volume name failed");
1619 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
1621 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not set volume name");
1626 /* all done and successful */
1631 if (user_buf
!= NULL
)
1632 FREE(user_buf
, M_TEMP
);
1633 VFS_DEBUG(ctx
, vp
, "ATTRLIST - set returning %d", error
);