2 * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/namei.h>
34 #include <sys/kernel.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/mount_internal.h>
38 #include <sys/proc_internal.h>
39 #include <sys/kauth.h>
40 #include <sys/uio_internal.h>
41 #include <sys/malloc.h>
43 #include <sys/sysproto.h>
44 #include <sys/xattr.h>
45 #include <sys/fsevents.h>
46 #include <kern/kalloc.h>
47 #include <miscfs/specfs/specdev.h>
50 #define ATTR_TIME_SIZE -1
53 * Structure describing the state of an in-progress attrlist operation.
55 struct _attrlist_buf
{
65 * Pack (count) bytes from (source) into (buf).
68 attrlist_pack_fixed(struct _attrlist_buf
*ab
, void *source
, ssize_t count
)
72 /* how much room left in the buffer? */
73 fit
= imin(count
, ab
->allocated
- (ab
->fixedcursor
- ab
->base
));
75 bcopy(source
, ab
->fixedcursor
, fit
);
77 /* always move in increments of 4 */
78 ab
->fixedcursor
+= roundup(count
, 4);
81 attrlist_pack_variable2(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
, const void *ext
, ssize_t extcount
)
83 struct attrreference ar
;
86 /* pack the reference to the variable object */
87 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
88 ar
.attr_length
= count
+ extcount
;
89 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
91 /* calculate space and pack the variable object */
92 fit
= imin(count
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
95 bcopy(source
, ab
->varcursor
, fit
);
98 fit
= imin(extcount
, ab
->allocated
- (ab
->varcursor
- ab
->base
));
101 bcopy(ext
, ab
->varcursor
, fit
);
102 ab
->varcursor
+= fit
;
104 /* always move in increments of 4 */
105 ab
->varcursor
= (char *)roundup((uintptr_t)ab
->varcursor
, 4);
108 attrlist_pack_variable(struct _attrlist_buf
*ab
, const void *source
, ssize_t count
)
110 attrlist_pack_variable2(ab
, source
, count
, NULL
, 0);
113 attrlist_pack_string(struct _attrlist_buf
*ab
, const char *source
, ssize_t count
)
115 struct attrreference ar
;
120 * Supplied count is character count of string text, excluding trailing nul
121 * which we always supply here.
123 if (source
== NULL
) {
125 } else if (count
== 0) {
126 count
= strlen(source
);
130 * Make the reference and pack it.
131 * Note that this is entirely independent of how much we get into
134 ar
.attr_dataoffset
= ab
->varcursor
- ab
->fixedcursor
;
135 ar
.attr_length
= count
+ 1;
136 attrlist_pack_fixed(ab
, &ar
, sizeof(ar
));
138 /* calculate how much of the string text we can copy, and do that */
139 space
= ab
->allocated
- (ab
->varcursor
- ab
->base
);
140 fit
= imin(count
, space
);
142 bcopy(source
, ab
->varcursor
, fit
);
143 /* is there room for our trailing nul? */
145 ab
->varcursor
[fit
] = '\0';
147 /* always move in increments of 4 */
148 ab
->varcursor
+= roundup(count
+ 1, 4);
151 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
152 #define ATTR_PACK_CAST(b, t, v) \
158 #define ATTR_PACK_TIME(b, v, is64) \
161 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
170 * Table-driven setup for all valid common/volume attributes.
172 struct getvolattrlist_attrtab
{
175 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
178 static struct getvolattrlist_attrtab getvolattrlist_common_tab
[] = {
179 {ATTR_CMN_NAME
, 0, sizeof(struct attrreference
)},
180 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
)},
181 {ATTR_CMN_FSID
, 0, sizeof(fsid_t
)},
182 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
)},
183 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
)},
184 {ATTR_CMN_OBJID
, 0, sizeof(fsobj_id_t
)},
185 {ATTR_CMN_OBJPERMANENTID
, 0, sizeof(fsobj_id_t
)},
186 {ATTR_CMN_PAROBJID
, 0, sizeof(fsobj_id_t
)},
187 {ATTR_CMN_SCRIPT
, 0, sizeof(text_encoding_t
)},
188 {ATTR_CMN_CRTIME
, VFSATTR_BIT(f_create_time
), ATTR_TIME_SIZE
},
189 {ATTR_CMN_MODTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
190 {ATTR_CMN_CHGTIME
, VFSATTR_BIT(f_modify_time
), ATTR_TIME_SIZE
},
191 {ATTR_CMN_ACCTIME
, VFSATTR_BIT(f_access_time
), ATTR_TIME_SIZE
},
192 {ATTR_CMN_BKUPTIME
, VFSATTR_BIT(f_backup_time
), ATTR_TIME_SIZE
},
193 {ATTR_CMN_FNDRINFO
, 0, 32},
194 {ATTR_CMN_OWNERID
, 0, sizeof(uid_t
)},
195 {ATTR_CMN_GRPID
, 0, sizeof(gid_t
)},
196 {ATTR_CMN_ACCESSMASK
, 0, sizeof(uint32_t)},
197 {ATTR_CMN_FLAGS
, 0, sizeof(uint32_t)},
198 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t)},
202 static struct getvolattrlist_attrtab getvolattrlist_vol_tab
[] = {
203 {ATTR_VOL_FSTYPE
, 0, sizeof(uint32_t)},
204 {ATTR_VOL_SIGNATURE
, VFSATTR_BIT(f_signature
), sizeof(uint32_t)},
205 {ATTR_VOL_SIZE
, VFSATTR_BIT(f_blocks
), sizeof(off_t
)},
206 {ATTR_VOL_SPACEFREE
, VFSATTR_BIT(f_bfree
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
207 {ATTR_VOL_SPACEAVAIL
, VFSATTR_BIT(f_bavail
) | VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
208 {ATTR_VOL_MINALLOCATION
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
209 {ATTR_VOL_ALLOCATIONCLUMP
, VFSATTR_BIT(f_bsize
), sizeof(off_t
)},
210 {ATTR_VOL_IOBLOCKSIZE
, VFSATTR_BIT(f_iosize
), sizeof(uint32_t)},
211 {ATTR_VOL_OBJCOUNT
, VFSATTR_BIT(f_objcount
), sizeof(uint32_t)},
212 {ATTR_VOL_FILECOUNT
, VFSATTR_BIT(f_filecount
), sizeof(uint32_t)},
213 {ATTR_VOL_DIRCOUNT
, VFSATTR_BIT(f_dircount
), sizeof(uint32_t)},
214 {ATTR_VOL_MAXOBJCOUNT
, VFSATTR_BIT(f_maxobjcount
), sizeof(uint32_t)},
215 {ATTR_VOL_MOUNTPOINT
, 0, sizeof(struct attrreference
)},
216 {ATTR_VOL_NAME
, VFSATTR_BIT(f_vol_name
), sizeof(struct attrreference
)},
217 {ATTR_VOL_MOUNTFLAGS
, 0, sizeof(uint32_t)},
218 {ATTR_VOL_MOUNTEDDEVICE
, 0, sizeof(struct attrreference
)},
219 {ATTR_VOL_ENCODINGSUSED
, 0, sizeof(uint64_t)},
220 {ATTR_VOL_CAPABILITIES
, VFSATTR_BIT(f_capabilities
), sizeof(vol_capabilities_attr_t
)},
221 {ATTR_VOL_ATTRIBUTES
, VFSATTR_BIT(f_attributes
), sizeof(vol_attributes_attr_t
)},
222 {ATTR_VOL_INFO
, 0, 0},
227 getvolattrlist_parsetab(struct getvolattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vfs_attr
*vsp
,
228 ssize_t
*sizep
, int is_64bit
)
230 attrgroup_t recognised
;
234 /* is this attribute set? */
235 if (tab
->attr
& attrs
) {
236 recognised
|= tab
->attr
;
237 vsp
->f_active
|= tab
->bits
;
238 if (tab
->size
== ATTR_TIME_SIZE
) {
240 *sizep
+= sizeof(struct user_timespec
);
242 *sizep
+= sizeof(struct timespec
);
248 } while ((++tab
)->attr
!= 0);
250 /* check to make sure that we recognised all of the passed-in attributes */
251 if (attrs
& ~recognised
)
257 * Given the attributes listed in alp, configure vap to request
258 * the data from a filesystem.
261 getvolattrlist_setupvfsattr(struct attrlist
*alp
, struct vfs_attr
*vsp
, ssize_t
*sizep
, int is_64bit
)
266 * Parse the above tables.
268 *sizep
= sizeof(uint32_t); /* length count */
269 if (alp
->commonattr
&&
270 (error
= getvolattrlist_parsetab(getvolattrlist_common_tab
, alp
->commonattr
, vsp
, sizep
, is_64bit
)) != 0)
273 (error
= getvolattrlist_parsetab(getvolattrlist_vol_tab
, alp
->volattr
, vsp
, sizep
, is_64bit
)) != 0)
280 * Table-driven setup for all valid common/dir/file/fork attributes against files.
282 struct getattrlist_attrtab
{
285 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
287 kauth_action_t action
;
289 static struct getattrlist_attrtab getattrlist_common_tab
[] = {
290 {ATTR_CMN_NAME
, VATTR_BIT(va_name
), sizeof(struct attrreference
), KAUTH_VNODE_READ_ATTRIBUTES
},
291 {ATTR_CMN_DEVID
, 0, sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
292 {ATTR_CMN_FSID
, VATTR_BIT(va_fsid
), sizeof(fsid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
293 {ATTR_CMN_OBJTYPE
, 0, sizeof(fsobj_type_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
294 {ATTR_CMN_OBJTAG
, 0, sizeof(fsobj_tag_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
295 {ATTR_CMN_OBJID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
296 {ATTR_CMN_OBJPERMANENTID
, VATTR_BIT(va_fileid
) | VATTR_BIT(va_linkid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
297 {ATTR_CMN_PAROBJID
, VATTR_BIT(va_parentid
), sizeof(fsobj_id_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
298 {ATTR_CMN_SCRIPT
, VATTR_BIT(va_encoding
), sizeof(text_encoding_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
299 {ATTR_CMN_CRTIME
, VATTR_BIT(va_create_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
300 {ATTR_CMN_MODTIME
, VATTR_BIT(va_modify_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
301 {ATTR_CMN_CHGTIME
, VATTR_BIT(va_change_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
302 {ATTR_CMN_ACCTIME
, VATTR_BIT(va_access_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
303 {ATTR_CMN_BKUPTIME
, VATTR_BIT(va_backup_time
), ATTR_TIME_SIZE
, KAUTH_VNODE_READ_ATTRIBUTES
},
304 {ATTR_CMN_FNDRINFO
, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES
},
305 {ATTR_CMN_OWNERID
, VATTR_BIT(va_uid
), sizeof(uid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
306 {ATTR_CMN_GRPID
, VATTR_BIT(va_gid
), sizeof(gid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
307 {ATTR_CMN_ACCESSMASK
, VATTR_BIT(va_mode
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
308 {ATTR_CMN_FLAGS
, VATTR_BIT(va_flags
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
309 {ATTR_CMN_USERACCESS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
310 {ATTR_CMN_EXTENDED_SECURITY
, VATTR_BIT(va_acl
), sizeof(struct attrreference
), KAUTH_VNODE_READ_SECURITY
},
311 {ATTR_CMN_UUID
, VATTR_BIT(va_uuuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
312 {ATTR_CMN_GRPUUID
, VATTR_BIT(va_guuid
), sizeof(guid_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
315 static struct getattrlist_attrtab getattrlist_dir_tab
[] = {
316 {ATTR_DIR_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
317 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nchildren
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
318 /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
319 {ATTR_DIR_ENTRYCOUNT
, VATTR_BIT(va_nlink
), 0, KAUTH_VNODE_READ_ATTRIBUTES
},
320 {ATTR_DIR_MOUNTSTATUS
, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
323 static struct getattrlist_attrtab getattrlist_file_tab
[] = {
324 {ATTR_FILE_LINKCOUNT
, VATTR_BIT(va_nlink
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
325 {ATTR_FILE_TOTALSIZE
, VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
326 {ATTR_FILE_ALLOCSIZE
, VATTR_BIT(va_total_alloc
) | VATTR_BIT(va_total_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
327 {ATTR_FILE_IOBLOCKSIZE
, VATTR_BIT(va_iosize
), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES
},
328 {ATTR_FILE_DEVTYPE
, VATTR_BIT(va_rdev
), sizeof(dev_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
329 {ATTR_FILE_DATALENGTH
, VATTR_BIT(va_total_size
) | VATTR_BIT(va_data_size
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
330 {ATTR_FILE_DATAALLOCSIZE
, VATTR_BIT(va_total_alloc
)| VATTR_BIT(va_data_alloc
), sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
331 {ATTR_FILE_RSRCLENGTH
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
332 {ATTR_FILE_RSRCALLOCSIZE
, 0, sizeof(off_t
), KAUTH_VNODE_READ_ATTRIBUTES
},
337 getattrlist_parsetab(struct getattrlist_attrtab
*tab
, attrgroup_t attrs
, struct vnode_attr
*vap
,
338 ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
)
340 attrgroup_t recognised
;
344 /* is this attribute set? */
345 if (tab
->attr
& attrs
) {
346 recognised
|= tab
->attr
;
347 vap
->va_active
|= tab
->bits
;
348 if (tab
->size
== ATTR_TIME_SIZE
) {
350 *sizep
+= sizeof(struct user_timespec
);
352 *sizep
+= sizeof(struct timespec
);
357 *actionp
|= tab
->action
;
359 } while ((++tab
)->attr
!= 0);
361 /* check to make sure that we recognised all of the passed-in attributes */
362 if (attrs
& ~recognised
)
368 * Given the attributes listed in alp, configure vap to request
369 * the data from a filesystem.
372 getattrlist_setupvattr(struct attrlist
*alp
, struct vnode_attr
*vap
, ssize_t
*sizep
, kauth_action_t
*actionp
, int is_64bit
, int isdir
)
377 * Parse the above tables.
379 *sizep
= sizeof(uint32_t); /* length count */
381 if (alp
->commonattr
&&
382 (error
= getattrlist_parsetab(getattrlist_common_tab
, alp
->commonattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
384 if (isdir
&& alp
->dirattr
&&
385 (error
= getattrlist_parsetab(getattrlist_dir_tab
, alp
->dirattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
387 if (!isdir
&& alp
->fileattr
&&
388 (error
= getattrlist_parsetab(getattrlist_file_tab
, alp
->fileattr
, vap
, sizep
, actionp
, is_64bit
)) != 0)
396 * Find something resembling a terminal component name in the mountedonname for vp
400 getattrlist_findnamecomp(const char *mn
, const char **np
, ssize_t
*nl
)
406 * We're looking for the last sequence of non / characters, but
407 * not including any trailing / characters.
412 for (cp
= mn
; *cp
!= 0; cp
++) {
414 /* start of run of chars */
420 /* end of run of chars */
427 /* need to close run? */
434 getvolattrlist(vnode_t vp
, struct getattrlist_args
*uap
, struct attrlist
*alp
, vfs_context_t ctx
, int is_64bit
)
437 struct vnode_attr va
;
438 struct _attrlist_buf ab
;
440 ssize_t fixedsize
, varsize
;
448 vs
.f_vol_name
= NULL
;
453 * For now, the vnode must be the root of its filesystem.
454 * To relax this, we need to be able to find the root vnode of a filesystem
455 * from any vnode in the filesystem.
457 if (!vnode_isvroot(vp
)) {
459 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
464 * Set up the vfs_attr structure and call the filesystem.
466 if ((error
= getvolattrlist_setupvfsattr(alp
, &vs
, &fixedsize
, is_64bit
)) != 0) {
467 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
470 if (vs
.f_active
!= 0) {
471 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
472 if (VFSATTR_IS_ACTIVE(&vs
, f_vol_name
)) {
473 vs
.f_vol_name
= (char *) kalloc(MAXPATHLEN
);
474 if (vs
.f_vol_name
== NULL
) {
476 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
481 VFS_DEBUG(ctx
, vp
, "ATTRLIST - calling to get %016llx with supported %016llx", vs
.f_active
, vs
.f_supported
);
482 if ((error
= vfs_getattr(mnt
, &vs
, ctx
)) != 0) {
483 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
488 * Did we ask for something the filesystem doesn't support?
490 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
491 /* default value for volume subtype */
492 if (VFSATTR_IS_ACTIVE(&vs
, f_fssubtype
)
493 && !VFSATTR_IS_SUPPORTED(&vs
, f_fssubtype
))
494 VFSATTR_RETURN(&vs
, f_fssubtype
, 0);
497 * If the file system didn't supply f_signature, then
498 * default it to 'BD', which is the generic signature
499 * that most Carbon file systems should return.
501 if (VFSATTR_IS_ACTIVE(&vs
, f_signature
)
502 && !VFSATTR_IS_SUPPORTED(&vs
, f_signature
))
503 VFSATTR_RETURN(&vs
, f_signature
, 0x4244);
505 /* default for block size */
506 if (VFSATTR_IS_ACTIVE(&vs
, f_bsize
)
507 && !VFSATTR_IS_SUPPORTED(&vs
, f_bsize
))
508 VFSATTR_RETURN(&vs
, f_bsize
, mnt
->mnt_devblocksize
);
510 /* check to see if our fixups were enough */
511 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
513 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested volume attributes");
514 VFS_DEBUG(ctx
, vp
, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
515 vs
.f_active
, vs
.f_supported
, vs
.f_active
& ~vs
.f_supported
);
522 * Some fields require data from the root vp
524 if (alp
->commonattr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
| ATTR_CMN_ACCESSMASK
| ATTR_CMN_FLAGS
| ATTR_CMN_SCRIPT
)) {
525 VATTR_WANTED(&va
, va_uid
);
526 VATTR_WANTED(&va
, va_gid
);
527 VATTR_WANTED(&va
, va_mode
);
528 VATTR_WANTED(&va
, va_flags
);
529 VATTR_WANTED(&va
, va_encoding
);
531 if ((error
= vnode_getattr(vp
, &va
, ctx
)) != 0) {
532 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp
);
536 if (VATTR_IS_ACTIVE(&va
, va_encoding
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
537 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
541 * Compute variable-size buffer requirements.
544 if (alp
->commonattr
& ATTR_CMN_NAME
) {
545 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
546 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
547 /* special case for boot volume. Use root name when it's
548 * available (which is the volume name) or just the mount on
549 * name of "/". we must do this for binary compatibility with
550 * pre Tiger code. returning nothing for the boot volume name
551 * breaks installers - 3961058
553 cnp
= vnode_getname(vp
);
555 /* just use "/" as name */
556 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
561 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
563 if (alp
->commonattr
& ATTR_CMN_NAME
)
564 varsize
+= roundup(cnl
+ 1, 4);
566 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
567 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntonname
) + 1, 4);
568 if (alp
->volattr
& ATTR_VOL_NAME
) {
569 vs
.f_vol_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
570 varsize
+= roundup(strlen(vs
.f_vol_name
) + 1, 4);
572 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
573 varsize
+= roundup(strlen(mnt
->mnt_vfsstat
.f_mntfromname
) + 1, 4);
576 * Allocate a target buffer for attribute results.
577 * Note that since we won't ever copy out more than the caller requested,
578 * we never need to allocate more than they offer.
580 ab
.allocated
= imin(uap
->bufferSize
, fixedsize
+ varsize
);
581 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
583 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
586 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
587 if (ab
.base
== NULL
) {
589 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
594 * Pack results into the destination buffer.
596 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
597 ab
.varcursor
= ab
.base
+ fixedsize
;
598 ab
.needed
= fixedsize
+ varsize
;
600 /* common attributes **************************************************/
601 if (alp
->commonattr
& ATTR_CMN_NAME
)
602 attrlist_pack_string(&ab
, cnp
, cnl
);
603 if (alp
->commonattr
& ATTR_CMN_DEVID
)
604 ATTR_PACK_CAST(&ab
, dev_t
, mnt
->mnt_vfsstat
.f_fsid
.val
[0]);
605 if (alp
->commonattr
& ATTR_CMN_FSID
)
606 ATTR_PACK(&ab
, mnt
->mnt_vfsstat
.f_fsid
);
607 if (alp
->commonattr
& ATTR_CMN_OBJTYPE
)
608 ATTR_PACK_CAST(&ab
, fsobj_type_t
, 0);
609 if (alp
->commonattr
& ATTR_CMN_OBJTAG
)
610 ATTR_PACK_CAST(&ab
, fsobj_tag_t
, vp
->v_tag
);
611 if (alp
->commonattr
& ATTR_CMN_OBJID
) {
612 fsobj_id_t f
= {0, 0};
615 if (alp
->commonattr
& ATTR_CMN_OBJPERMANENTID
) {
616 fsobj_id_t f
= {0, 0};
619 if (alp
->commonattr
& ATTR_CMN_PAROBJID
) {
620 fsobj_id_t f
= {0, 0};
623 /* note that this returns the encoding for the volume name, not the node name */
624 if (alp
->commonattr
& ATTR_CMN_SCRIPT
)
625 ATTR_PACK_CAST(&ab
, text_encoding_t
, va
.va_encoding
);
626 if (alp
->commonattr
& ATTR_CMN_CRTIME
)
627 ATTR_PACK_TIME(&ab
, vs
.f_create_time
, is_64bit
);
628 if (alp
->commonattr
& ATTR_CMN_MODTIME
)
629 ATTR_PACK_TIME(&ab
, vs
.f_modify_time
, is_64bit
);
630 if (alp
->commonattr
& ATTR_CMN_CHGTIME
)
631 ATTR_PACK_TIME(&ab
, vs
.f_modify_time
, is_64bit
);
632 if (alp
->commonattr
& ATTR_CMN_ACCTIME
)
633 ATTR_PACK_TIME(&ab
, vs
.f_access_time
, is_64bit
);
634 if (alp
->commonattr
& ATTR_CMN_BKUPTIME
)
635 ATTR_PACK_TIME(&ab
, vs
.f_backup_time
, is_64bit
);
636 if (alp
->commonattr
& ATTR_CMN_FNDRINFO
) {
639 * This attribute isn't really Finder Info, at least for HFS.
641 if (vp
->v_tag
== VT_HFS
) {
642 if ((error
= VNOP_IOCTL(vp
, HFS_GET_BOOT_INFO
, (caddr_t
)&f
, 0, ctx
)) != 0)
645 /* XXX we could at least pass out the volume UUID here */
646 bzero(&f
, sizeof(f
));
648 attrlist_pack_fixed(&ab
, f
, sizeof(f
));
650 if (alp
->commonattr
& ATTR_CMN_OWNERID
)
651 ATTR_PACK(&ab
, va
.va_uid
);
652 if (alp
->commonattr
& ATTR_CMN_GRPID
)
653 ATTR_PACK(&ab
, va
.va_gid
);
654 if (alp
->commonattr
& ATTR_CMN_ACCESSMASK
)
655 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
656 if (alp
->commonattr
& ATTR_CMN_FLAGS
)
657 ATTR_PACK(&ab
, va
.va_flags
);
658 if (alp
->commonattr
& ATTR_CMN_USERACCESS
) { /* XXX this is expensive and also duplicate work */
660 if (vnode_isdir(vp
)) {
661 if (vnode_authorize(vp
, NULL
,
662 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, ctx
) == 0)
664 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, ctx
) == 0)
666 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, ctx
) == 0)
669 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, ctx
) == 0)
671 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, ctx
) == 0)
673 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, ctx
) == 0)
676 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms
);
677 ATTR_PACK(&ab
, perms
);
680 /* volume attributes **************************************************/
682 if (alp
->volattr
& ATTR_VOL_FSTYPE
)
683 ATTR_PACK_CAST(&ab
, uint32_t, vfs_typenum(mnt
));
684 if (alp
->volattr
& ATTR_VOL_SIGNATURE
)
685 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_signature
);
686 if (alp
->volattr
& ATTR_VOL_SIZE
)
687 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_blocks
);
688 if (alp
->volattr
& ATTR_VOL_SPACEFREE
)
689 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bfree
);
690 if (alp
->volattr
& ATTR_VOL_SPACEAVAIL
)
691 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
* vs
.f_bavail
);
692 if (alp
->volattr
& ATTR_VOL_MINALLOCATION
)
693 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
);
694 if (alp
->volattr
& ATTR_VOL_ALLOCATIONCLUMP
)
695 ATTR_PACK_CAST(&ab
, off_t
, vs
.f_bsize
); /* not strictly true */
696 if (alp
->volattr
& ATTR_VOL_IOBLOCKSIZE
)
697 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_iosize
);
698 if (alp
->volattr
& ATTR_VOL_OBJCOUNT
)
699 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_objcount
);
700 if (alp
->volattr
& ATTR_VOL_FILECOUNT
)
701 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_filecount
);
702 if (alp
->volattr
& ATTR_VOL_DIRCOUNT
)
703 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_dircount
);
704 if (alp
->volattr
& ATTR_VOL_MAXOBJCOUNT
)
705 ATTR_PACK_CAST(&ab
, uint32_t, vs
.f_maxobjcount
);
706 if (alp
->volattr
& ATTR_VOL_MOUNTPOINT
)
707 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntonname
, 0);
708 if (alp
->volattr
& ATTR_VOL_NAME
)
709 attrlist_pack_string(&ab
, vs
.f_vol_name
, 0);
710 if (alp
->volattr
& ATTR_VOL_MOUNTFLAGS
)
711 ATTR_PACK_CAST(&ab
, uint32_t, mnt
->mnt_flag
);
712 if (alp
->volattr
& ATTR_VOL_MOUNTEDDEVICE
)
713 attrlist_pack_string(&ab
, mnt
->mnt_vfsstat
.f_mntfromname
, 0);
714 if (alp
->volattr
& ATTR_VOL_ENCODINGSUSED
)
715 ATTR_PACK_CAST(&ab
, uint64_t, ~0LL); /* return all encodings */
716 if (alp
->volattr
& ATTR_VOL_CAPABILITIES
) {
717 /* fix up volume capabilities */
718 if (vfs_extendedsecurity(mnt
)) {
719 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
721 vs
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] &= ~VOL_CAP_INT_EXTENDED_SECURITY
;
723 vs
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] |= VOL_CAP_INT_EXTENDED_SECURITY
;
724 ATTR_PACK(&ab
, vs
.f_capabilities
);
726 if (alp
->volattr
& ATTR_VOL_ATTRIBUTES
) {
727 /* fix up volume attribute information */
728 if (vfs_extendedsecurity(mnt
)) {
729 vs
.f_attributes
.validattr
.commonattr
|= (ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
731 vs
.f_attributes
.validattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
732 vs
.f_attributes
.nativeattr
.commonattr
&= ~(ATTR_CMN_EXTENDED_SECURITY
| ATTR_CMN_UUID
| ATTR_CMN_GRPUUID
);
734 ATTR_PACK(&ab
, vs
.f_attributes
);
738 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
739 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
740 fixedsize
, ab
.fixedcursor
- ab
.base
, alp
->commonattr
, alp
->volattr
);
741 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
742 panic("packed variable field size mismatch; used %d but expected %d", ab
.varcursor
- ab
.base
, ab
.needed
);
745 * In the compatible case, we report the smaller of the required and returned sizes.
746 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
747 * of the result buffer, even if we copied less out. The caller knows how big a buffer
748 * they gave us, so they can always check for truncation themselves.
750 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
752 error
= copyout(ab
.base
, uap
->attributeBuffer
, ab
.allocated
);
755 if (vs
.f_vol_name
!= NULL
)
756 kfree(vs
.f_vol_name
, MAXPATHLEN
);
758 FREE(ab
.base
, M_TEMP
);
759 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
764 * Obtain attribute information about a filesystem object.
767 getattrlist(struct proc
*p
, struct getattrlist_args
*uap
, __unused register_t
*retval
)
770 struct vnode_attr va
;
771 struct vfs_context context
, *ctx
;
773 struct _attrlist_buf ab
;
776 kauth_action_t action
;
777 ssize_t fixedsize
, varsize
;
784 context
.vc_ucred
= kauth_cred_get();
797 nameiflags
= AUDITVNPATH1
;
798 if (!(uap
->options
& FSOPT_NOFOLLOW
))
799 nameiflags
|= FOLLOW
;
800 NDINIT(&nd
, LOOKUP
, nameiflags
, UIO_USERSPACE
, uap
->path
, &context
);
802 if ((error
= namei(&nd
)) != 0)
808 * Fetch the attribute request.
810 if ((error
= copyin(uap
->alist
, &al
, sizeof(al
))) != 0)
812 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
817 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
818 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
819 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
822 * It is legal to request volume or file attributes,
826 if (al
.fileattr
|| al
.dirattr
|| al
.forkattr
) {
828 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
831 /* handle volume attribute request */
832 error
= getvolattrlist(vp
, uap
, &al
, &context
, proc_is64bit(p
));
837 * Set up the vnode_attr structure and authorise.
839 if ((error
= getattrlist_setupvattr(&al
, &va
, &fixedsize
, &action
, proc_is64bit(p
), vnode_isdir(vp
))) != 0) {
840 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setup for request failed");
843 if ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0) {
844 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorisation failed/denied");
848 if (va
.va_active
!= 0) {
850 * If we're going to ask for va_name, allocate a buffer to point it at
852 if (VATTR_IS_ACTIVE(&va
, va_name
)) {
853 va
.va_name
= (char *) kalloc(MAXPATHLEN
);
854 if (va
.va_name
== NULL
) {
856 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: cannot allocate va_name buffer");
862 * Call the filesystem.
864 if ((error
= vnode_getattr(vp
, &va
, &context
)) != 0) {
865 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
869 /* did we ask for something the filesystem doesn't support? */
870 if (!VATTR_ALL_SUPPORTED(&va
)) {
873 * There are a couple of special cases. If we are after object IDs,
874 * we can make do with va_fileid.
876 if ((al
.commonattr
& (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
)) && !VATTR_IS_SUPPORTED(&va
, va_linkid
))
877 VATTR_CLEAR_ACTIVE(&va
, va_linkid
); /* forget we wanted this */
879 * Many (most?) filesystems don't know their parent object id. We can get it the
882 if ((al
.commonattr
& ATTR_CMN_PAROBJID
) && !VATTR_IS_SUPPORTED(&va
, va_parentid
))
883 VATTR_CLEAR_ACTIVE(&va
, va_parentid
);
885 * And we can report datasize/alloc from total.
887 if ((al
.fileattr
& ATTR_FILE_DATALENGTH
) && !VATTR_IS_SUPPORTED(&va
, va_data_size
))
888 VATTR_CLEAR_ACTIVE(&va
, va_data_size
);
889 if ((al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) && !VATTR_IS_SUPPORTED(&va
, va_data_alloc
))
890 VATTR_CLEAR_ACTIVE(&va
, va_data_alloc
);
893 * If we don't have an encoding, go with UTF-8
895 if ((al
.commonattr
& ATTR_CMN_SCRIPT
) && !VATTR_IS_SUPPORTED(&va
, va_encoding
))
896 VATTR_RETURN(&va
, va_encoding
, 0x7e /* kTextEncodingMacUnicode */);
899 * If we don't have a name, we'll get one from the vnode or mount point.
901 if ((al
.commonattr
& ATTR_CMN_NAME
) && !VATTR_IS_SUPPORTED(&va
, va_name
)) {
902 VATTR_CLEAR_ACTIVE(&va
, va_name
);
906 * We used to return va_nlink-2 for ATTR_DIR_ENTRYCOUNT. The va_nchildren
907 * field is preferred, but we'll fall back to va_nlink-2 for compatibility
908 * with file systems which haven't adopted va_nchildren. Note: the "- 2"
909 * reflects the "." and ".." entries which are reported via POSIX APIs, but
910 * not via Carbon (since they don't in fact exist in HFS).
912 if ((al
.dirattr
& ATTR_DIR_ENTRYCOUNT
) && !VATTR_IS_SUPPORTED(&va
, va_nchildren
) &&
913 VATTR_IS_SUPPORTED(&va
, va_nlink
)) {
914 VATTR_RETURN(&va
, va_nchildren
, va
.va_nlink
- 2);
918 if (!VATTR_ALL_SUPPORTED(&va
)) {
920 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not get all requested file attributes");
921 VFS_DEBUG(ctx
, vp
, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
922 va
.va_supported
, va
.va_active
, va
.va_active
& ~va
.va_supported
);
929 * Compute variable-space requirements.
931 varsize
= 0; /* length count */
932 if (al
.commonattr
& ATTR_CMN_NAME
) {
933 if (VATTR_IS_SUPPORTED(&va
, va_name
)) {
934 va
.va_name
[MAXPATHLEN
-1] = '\0'; /* Ensure nul-termination */
938 if (vnode_isvroot(vp
)) {
939 if (vp
->v_mount
->mnt_vfsstat
.f_mntonname
[1] == 0x00 &&
940 vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0] == '/') {
941 /* special case for boot volume. Use root name when it's
942 * available (which is the volume name) or just the mount on
943 * name of "/". we must do this for binary compatibility with
944 * pre Tiger code. returning nothing for the boot volume name
945 * breaks installers - 3961058
947 cnp
= vname
= vnode_getname(vp
);
949 /* just use "/" as name */
950 cnp
= &vp
->v_mount
->mnt_vfsstat
.f_mntonname
[0];
955 getattrlist_findnamecomp(vp
->v_mount
->mnt_vfsstat
.f_mntonname
, &cnp
, &cnl
);
958 cnp
= vname
= vnode_getname(vp
);
965 varsize
+= roundup(cnl
+ 1, 4);
969 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
971 * XXX This needs to change at some point; since the blob is opaque in
972 * user-space this is OK.
974 if ((al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) &&
975 VATTR_IS_SUPPORTED(&va
, va_acl
) &&
977 varsize
+= roundup(KAUTH_FILESEC_SIZE(va
.va_acl
->acl_entrycount
), 4);
980 * Allocate a target buffer for attribute results.
982 * Note that we won't ever copy out more than the caller requested, even though
983 * we might have to allocate more than they offer do that the diagnostic checks
984 * don't result in a panic if the caller's buffer is too small..
986 ab
.allocated
= fixedsize
+ varsize
;
987 if (ab
.allocated
> ATTR_MAX_BUFFER
) {
989 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab
.allocated
, ATTR_MAX_BUFFER
);
992 MALLOC(ab
.base
, char *, ab
.allocated
, M_TEMP
, M_WAITOK
);
993 if (ab
.base
== NULL
) {
995 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab
.allocated
);
1000 * Pack results into the destination buffer.
1002 ab
.fixedcursor
= ab
.base
+ sizeof(uint32_t);
1003 ab
.varcursor
= ab
.base
+ fixedsize
;
1004 ab
.needed
= ab
.allocated
;
1006 /* common attributes **************************************************/
1007 if (al
.commonattr
& ATTR_CMN_NAME
)
1008 attrlist_pack_string(&ab
, cnp
, cnl
);
1009 if (al
.commonattr
& ATTR_CMN_DEVID
)
1010 ATTR_PACK_CAST(&ab
, dev_t
, vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
1011 if (al
.commonattr
& ATTR_CMN_FSID
)
1012 ATTR_PACK(&ab
, vp
->v_mount
->mnt_vfsstat
.f_fsid
);
1013 if (al
.commonattr
& ATTR_CMN_OBJTYPE
)
1014 ATTR_PACK_CAST(&ab
, fsobj_type_t
, vp
->v_type
);
1015 if (al
.commonattr
& ATTR_CMN_OBJTAG
)
1016 ATTR_PACK_CAST(&ab
, fsobj_tag_t
, vp
->v_tag
);
1017 if (al
.commonattr
& ATTR_CMN_OBJID
) {
1020 * Carbon can't deal with us reporting the target ID
1021 * for links. So we ask the filesystem to give us the
1022 * source ID as well, and if it gives us one, we use
1025 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1026 f
.fid_objno
= va
.va_linkid
;
1028 f
.fid_objno
= va
.va_fileid
;
1030 f
.fid_generation
= 0;
1033 if (al
.commonattr
& ATTR_CMN_OBJPERMANENTID
) {
1036 * Carbon can't deal with us reporting the target ID
1037 * for links. So we ask the filesystem to give us the
1038 * source ID as well, and if it gives us one, we use
1041 if (VATTR_IS_SUPPORTED(&va
, va_linkid
)) {
1042 f
.fid_objno
= va
.va_linkid
;
1044 f
.fid_objno
= va
.va_fileid
;
1046 f
.fid_generation
= 0;
1049 if (al
.commonattr
& ATTR_CMN_PAROBJID
) {
1052 * If the filesystem doesn't know the parent ID, we can
1053 * try to get it via v->v_parent. Don't need to worry
1054 * about links here, as we dont allow hardlinks to
1057 if (VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
1058 f
.fid_objno
= va
.va_parentid
;
1060 struct vnode_attr lva
;
1063 pvp
= vnode_getparent(vp
);
1065 if (pvp
== NULLVP
) {
1070 VATTR_WANTED(&lva
, va_fileid
);
1071 error
= vnode_getattr(pvp
, &lva
, &context
);
1076 f
.fid_objno
= lva
.va_fileid
;
1078 f
.fid_generation
= 0;
1081 if (al
.commonattr
& ATTR_CMN_SCRIPT
)
1082 ATTR_PACK_CAST(&ab
, text_encoding_t
, va
.va_encoding
);
1083 if (al
.commonattr
& ATTR_CMN_CRTIME
)
1084 ATTR_PACK_TIME(&ab
, va
.va_create_time
, proc_is64bit(p
));
1085 if (al
.commonattr
& ATTR_CMN_MODTIME
)
1086 ATTR_PACK_TIME(&ab
, va
.va_modify_time
, proc_is64bit(p
));
1087 if (al
.commonattr
& ATTR_CMN_CHGTIME
)
1088 ATTR_PACK_TIME(&ab
, va
.va_change_time
, proc_is64bit(p
));
1089 if (al
.commonattr
& ATTR_CMN_ACCTIME
)
1090 ATTR_PACK_TIME(&ab
, va
.va_access_time
, proc_is64bit(p
));
1091 if (al
.commonattr
& ATTR_CMN_BKUPTIME
)
1092 ATTR_PACK_TIME(&ab
, va
.va_backup_time
, proc_is64bit(p
));
1093 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1096 char uio_buf
[UIO_SIZEOF(1)];
1098 fisize
= imin(32, ab
.allocated
- (ab
.fixedcursor
- ab
.base
));
1100 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1104 uio_addiov(auio
, CAST_USER_ADDR_T(ab
.fixedcursor
), fisize
);
1105 error
= vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, &fisize
, XATTR_NOSECURITY
, &context
);
1109 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1110 VFS_DEBUG(ctx
, vp
, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1111 bzero(ab
.fixedcursor
, 32);
1114 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1119 VFS_DEBUG(ctx
, vp
, "ATTRLIST - no room in caller buffer for FINDERINFO");
1121 ab
.fixedcursor
+= 32;
1123 if (al
.commonattr
& ATTR_CMN_OWNERID
)
1124 ATTR_PACK(&ab
, va
.va_uid
);
1125 if (al
.commonattr
& ATTR_CMN_GRPID
)
1126 ATTR_PACK(&ab
, va
.va_gid
);
1127 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
)
1128 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_mode
);
1129 if (al
.commonattr
& ATTR_CMN_FLAGS
)
1130 ATTR_PACK(&ab
, va
.va_flags
);
1131 if (al
.commonattr
& ATTR_CMN_USERACCESS
) { /* this is expensive */
1133 if (vnode_isdir(vp
)) {
1134 if (vnode_authorize(vp
, NULL
,
1135 KAUTH_VNODE_ACCESS
| KAUTH_VNODE_ADD_FILE
| KAUTH_VNODE_ADD_SUBDIRECTORY
| KAUTH_VNODE_DELETE_CHILD
, &context
) == 0)
1137 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_LIST_DIRECTORY
, &context
) == 0)
1139 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_SEARCH
, &context
) == 0)
1142 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_WRITE_DATA
, &context
) == 0)
1144 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_READ_DATA
, &context
) == 0)
1146 if (vnode_authorize(vp
, NULL
, KAUTH_VNODE_ACCESS
| KAUTH_VNODE_EXECUTE
, &context
) == 0)
1149 VFS_DEBUG(ctx
, vp
, "ATTRLIST - granting perms %d", perms
);
1150 ATTR_PACK(&ab
, perms
);
1152 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1153 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
)) {
1154 struct kauth_filesec fsec
;
1156 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1158 fsec
.fsec_magic
= KAUTH_FILESEC_MAGIC
;
1159 fsec
.fsec_owner
= kauth_null_guid
;
1160 fsec
.fsec_group
= kauth_null_guid
;
1161 attrlist_pack_variable2(&ab
, &fsec
, ((char *)&fsec
.fsec_acl
- (char *)&fsec
), va
.va_acl
, KAUTH_ACL_COPYSIZE(va
.va_acl
));
1163 attrlist_pack_variable(&ab
, NULL
, 0);
1166 if (al
.commonattr
& ATTR_CMN_UUID
) {
1167 if (!VATTR_IS_SUPPORTED(&va
, va_uuuid
)) {
1168 ATTR_PACK(&ab
, kauth_null_guid
);
1170 ATTR_PACK(&ab
, va
.va_uuuid
);
1173 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1174 if (!VATTR_IS_SUPPORTED(&va
, va_guuid
)) {
1175 ATTR_PACK(&ab
, kauth_null_guid
);
1177 ATTR_PACK(&ab
, va
.va_guuid
);
1181 /* directory attributes **************************************************/
1182 if (vnode_isdir(vp
)) {
1183 if (al
.dirattr
& ATTR_DIR_LINKCOUNT
) /* full count of entries */
1184 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nlink
);
1185 if (al
.dirattr
& ATTR_DIR_ENTRYCOUNT
)
1186 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nchildren
);
1187 if (al
.dirattr
& ATTR_DIR_MOUNTSTATUS
)
1188 ATTR_PACK_CAST(&ab
, uint32_t, (vp
->v_flag
& VROOT
) ? DIR_MNTSTATUS_MNTPOINT
: 0);
1191 /* file attributes **************************************************/
1192 if (!vnode_isdir(vp
)) {
1193 if (al
.fileattr
& ATTR_FILE_LINKCOUNT
)
1194 ATTR_PACK_CAST(&ab
, uint32_t, va
.va_nlink
);
1195 if (al
.fileattr
& ATTR_FILE_TOTALSIZE
)
1196 ATTR_PACK(&ab
, va
.va_total_size
);
1197 if (al
.fileattr
& ATTR_FILE_ALLOCSIZE
)
1198 ATTR_PACK(&ab
, va
.va_total_alloc
);
1199 if (al
.fileattr
& ATTR_FILE_IOBLOCKSIZE
)
1200 ATTR_PACK(&ab
, va
.va_iosize
);
1201 if (al
.fileattr
& ATTR_FILE_CLUMPSIZE
)
1202 ATTR_PACK_CAST(&ab
, uint32_t, 0); /* XXX value is deprecated */
1203 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1204 if ((vp
->v_type
== VCHR
) || (vp
->v_type
== VBLK
)) {
1205 ATTR_PACK(&ab
, vp
->v_specinfo
->si_rdev
);
1207 ATTR_PACK_CAST(&ab
, uint32_t, 0);
1210 if (al
.fileattr
& ATTR_FILE_DATALENGTH
) {
1211 if (VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
1212 ATTR_PACK(&ab
, va
.va_data_size
);
1214 ATTR_PACK(&ab
, va
.va_total_size
);
1217 if (al
.fileattr
& ATTR_FILE_DATAALLOCSIZE
) {
1218 if (VATTR_IS_SUPPORTED(&va
, va_data_alloc
)) {
1219 ATTR_PACK(&ab
, va
.va_data_alloc
);
1221 ATTR_PACK(&ab
, va
.va_total_alloc
);
1224 /* fetch resource fork size/allocation via xattr interface */
1225 if (al
.fileattr
& (ATTR_FILE_RSRCLENGTH
| ATTR_FILE_RSRCALLOCSIZE
)) {
1227 if ((error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &rsize
, XATTR_NOSECURITY
, &context
)) != 0) {
1228 if ((error
== ENOENT
) || (error
== ENOATTR
) || (error
== ENOTSUP
) || (error
== EPERM
)) {
1235 if (al
.fileattr
& ATTR_FILE_RSRCLENGTH
)
1236 ATTR_PACK_CAST(&ab
, off_t
, rsize
);
1237 if (al
.fileattr
& ATTR_FILE_RSRCALLOCSIZE
) {
1238 uint32_t blksize
= vp
->v_mount
->mnt_vfsstat
.f_bsize
;
1241 ATTR_PACK_CAST(&ab
, off_t
, (roundup(rsize
, blksize
)));
1247 if ((ab
.fixedcursor
- ab
.base
) != fixedsize
)
1248 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
1249 fixedsize
, ab
.fixedcursor
- ab
.base
, al
.commonattr
, al
.volattr
);
1250 if (ab
.varcursor
!= (ab
.base
+ ab
.needed
))
1251 panic("packed variable field size mismatch; used %d but expected %d", ab
.varcursor
- ab
.base
, ab
.needed
);
1254 * In the compatible case, we report the smaller of the required and returned sizes.
1255 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1256 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1257 * they gave us, so they can always check for truncation themselves.
1259 *(uint32_t *)ab
.base
= (uap
->options
& FSOPT_REPORT_FULLSIZE
) ? ab
.needed
: imin(ab
.allocated
, ab
.needed
);
1261 /* Only actually copyout as much out as the user buffer can hold */
1262 error
= copyout(ab
.base
, uap
->attributeBuffer
, imin(uap
->bufferSize
, ab
.allocated
));
1266 kfree(va
.va_name
, MAXPATHLEN
);
1268 vnode_putname(vname
);
1271 if (ab
.base
!= NULL
)
1272 FREE(ab
.base
, M_TEMP
);
1273 if (VATTR_IS_SUPPORTED(&va
, va_acl
) && (va
.va_acl
!= NULL
))
1274 kauth_acl_free(va
.va_acl
);
1276 VFS_DEBUG(ctx
, vp
, "ATTRLIST - returning %d", error
);
1281 attrlist_unpack_fixed(char **cursor
, char *end
, void *buf
, ssize_t size
)
1283 /* make sure we have enough source data */
1284 if ((*cursor
) + size
> end
)
1287 bcopy(*cursor
, buf
, size
);
1292 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1293 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1294 #define ATTR_UNPACK_TIME(v, is64) \
1297 struct user_timespec us; \
1299 v.tv_sec = us.tv_sec; \
1300 v.tv_nsec = us.tv_nsec; \
1311 setattrlist(struct proc
*p
, register struct setattrlist_args
*uap
, __unused register_t
*retval
)
1314 struct vfs_context context
, *ctx
;
1315 struct vnode_attr va
;
1316 struct attrreference ar
;
1317 struct nameidata nd
;
1320 kauth_action_t action
;
1321 char *user_buf
, *cursor
, *bufend
, *fndrinfo
, *cp
, *volname
;
1322 int proc_is64
, error
;
1324 kauth_filesec_t rfsec
;
1326 context
.vc_proc
= p
;
1327 context
.vc_ucred
= kauth_cred_get();
1334 proc_is64
= proc_is64bit(p
);
1342 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0)
1343 nameiflags
|= FOLLOW
;
1344 NDINIT(&nd
, LOOKUP
, nameiflags
| AUDITVNPATH1
, UIO_USERSPACE
, uap
->path
, &context
);
1345 if ((error
= namei(&nd
)) != 0)
1351 * Fetch the attribute set and validate.
1353 if ((error
= copyin(uap
->alist
, (caddr_t
) &al
, sizeof (al
))))
1355 if (al
.bitmapcount
!= ATTR_BIT_MAP_COUNT
) {
1360 VFS_DEBUG(ctx
, vp
, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1361 vp
, p
->p_comm
, al
.commonattr
, al
.volattr
, al
.fileattr
, al
.dirattr
, al
.forkattr
,
1362 (uap
->options
& FSOPT_NOFOLLOW
) ? "no":"", vp
->v_name
);
1365 if ((al
.volattr
& ~ATTR_VOL_SETMASK
) ||
1366 (al
.commonattr
& ~ATTR_CMN_VOLSETMASK
) ||
1370 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1374 if ((al
.commonattr
& ~ATTR_CMN_SETMASK
) ||
1375 (al
.fileattr
& ~ATTR_FILE_SETMASK
) ||
1376 (al
.dirattr
& ~ATTR_DIR_SETMASK
) ||
1377 (al
.forkattr
& ~ATTR_FORK_SETMASK
)) {
1379 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1385 * Make the naive assumption that the caller has supplied a reasonable buffer
1386 * size. We could be more careful by pulling in the fixed-size region, checking
1387 * the attrref structures, then pulling in the variable section.
1388 * We need to reconsider this for handling large ACLs, as they should probably be
1389 * brought directly into a buffer. Multiple copyins will make this slower though.
1391 * We could also map the user buffer if it is larger than some sensible mimimum.
1393 if (uap
->bufferSize
> ATTR_MAX_BUFFER
) {
1394 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer size %d too large", uap
->bufferSize
);
1398 MALLOC(user_buf
, char *, uap
->bufferSize
, M_TEMP
, M_WAITOK
);
1399 if (user_buf
== NULL
) {
1400 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap
->bufferSize
);
1404 if ((error
= copyin(uap
->attributeBuffer
, user_buf
, uap
->bufferSize
)) != 0) {
1405 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: buffer copyin failed");
1408 VFS_DEBUG(ctx
, vp
, "ATTRLIST - copied in %d bytes of user attributes to %p", uap
->bufferSize
, user_buf
);
1411 * Unpack the argument buffer.
1414 bufend
= cursor
+ uap
->bufferSize
;
1417 if (al
.commonattr
& ATTR_CMN_SCRIPT
) {
1418 ATTR_UNPACK(va
.va_encoding
);
1419 VATTR_SET_ACTIVE(&va
, va_encoding
);
1421 if (al
.commonattr
& ATTR_CMN_CRTIME
) {
1422 ATTR_UNPACK_TIME(va
.va_create_time
, proc_is64
);
1423 VATTR_SET_ACTIVE(&va
, va_create_time
);
1425 if (al
.commonattr
& ATTR_CMN_MODTIME
) {
1426 ATTR_UNPACK_TIME(va
.va_modify_time
, proc_is64
);
1427 VATTR_SET_ACTIVE(&va
, va_modify_time
);
1429 if (al
.commonattr
& ATTR_CMN_CHGTIME
) {
1430 ATTR_UNPACK_TIME(va
.va_change_time
, proc_is64
);
1431 VATTR_SET_ACTIVE(&va
, va_change_time
);
1433 if (al
.commonattr
& ATTR_CMN_ACCTIME
) {
1434 ATTR_UNPACK_TIME(va
.va_access_time
, proc_is64
);
1435 VATTR_SET_ACTIVE(&va
, va_access_time
);
1437 if (al
.commonattr
& ATTR_CMN_BKUPTIME
) {
1438 ATTR_UNPACK_TIME(va
.va_backup_time
, proc_is64
);
1439 VATTR_SET_ACTIVE(&va
, va_backup_time
);
1441 if (al
.commonattr
& ATTR_CMN_FNDRINFO
) {
1442 if ((cursor
+ 32) > bufend
) {
1444 VFS_DEBUG(ctx
, vp
, "ATTRLIST - not enough data supplied for FINDERINFO");
1450 if (al
.commonattr
& ATTR_CMN_OWNERID
) {
1451 ATTR_UNPACK(va
.va_uid
);
1452 VATTR_SET_ACTIVE(&va
, va_uid
);
1454 if (al
.commonattr
& ATTR_CMN_GRPID
) {
1455 ATTR_UNPACK(va
.va_gid
);
1456 VATTR_SET_ACTIVE(&va
, va_gid
);
1458 if (al
.commonattr
& ATTR_CMN_ACCESSMASK
) {
1459 ATTR_UNPACK_CAST(uint32_t, va
.va_mode
);
1460 VATTR_SET_ACTIVE(&va
, va_mode
);
1462 if (al
.commonattr
& ATTR_CMN_FLAGS
) {
1463 ATTR_UNPACK(va
.va_flags
);
1464 VATTR_SET_ACTIVE(&va
, va_flags
);
1466 if (al
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) {
1469 * We are (for now) passed a kauth_filesec_t, but all we want from
1474 cp
+= ar
.attr_dataoffset
;
1475 rfsec
= (kauth_filesec_t
)cp
;
1476 if (((char *)(rfsec
+ 1) > bufend
) || /* no space for acl */
1477 (rfsec
->fsec_magic
!= KAUTH_FILESEC_MAGIC
) || /* bad magic */
1478 (KAUTH_FILESEC_COPYSIZE(rfsec
) != ar
.attr_length
) || /* size does not match */
1479 ((cp
+ KAUTH_FILESEC_COPYSIZE(rfsec
)) > bufend
)) { /* ACEs overrun buffer */
1481 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied", ar
.attr_length
);
1484 nace
= rfsec
->fsec_entrycount
;
1485 if (nace
== KAUTH_FILESEC_NOACL
)
1487 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1489 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: bad ACL supplied");
1492 nace
= rfsec
->fsec_acl
.acl_entrycount
;
1493 if (nace
== KAUTH_FILESEC_NOACL
) {
1495 VATTR_SET(&va
, va_acl
, NULL
);
1498 if (nace
> KAUTH_ACL_MAX_ENTRIES
) { /* ACL size invalid */
1500 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: supplied ACL is too large");
1503 VATTR_SET(&va
, va_acl
, &rfsec
->fsec_acl
);
1506 if (al
.commonattr
& ATTR_CMN_UUID
) {
1507 ATTR_UNPACK(va
.va_uuuid
);
1508 VATTR_SET_ACTIVE(&va
, va_uuuid
);
1510 if (al
.commonattr
& ATTR_CMN_GRPUUID
) {
1511 ATTR_UNPACK(va
.va_guuid
);
1512 VATTR_SET_ACTIVE(&va
, va_guuid
);
1516 if (al
.volattr
& ATTR_VOL_INFO
) {
1517 if (al
.volattr
& ATTR_VOL_NAME
) {
1520 volname
+= ar
.attr_dataoffset
;
1521 if ((volname
+ ar
.attr_length
) > bufend
) {
1523 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: volume name too big for caller buffer");
1526 /* guarantee NUL termination */
1527 volname
[ar
.attr_length
- 1] = 0;
1532 if (al
.fileattr
& ATTR_FILE_DEVTYPE
) {
1533 /* XXX does it actually make any sense to change this? */
1535 VFS_DEBUG(ctx
, vp
, "ATTRLIST - XXX device type change not implemented");
1540 * Validate and authorize.
1543 if ((va
.va_active
!= 0LL) && ((error
= vnode_authattr(vp
, &va
, &action
, &context
)) != 0)) {
1544 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: attribute changes refused: %d", error
);
1548 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1549 * and will be auth'ed by the FS.
1551 if (fndrinfo
!= NULL
) {
1552 if (al
.volattr
& ATTR_VOL_INFO
) {
1553 if (vp
->v_tag
!= VT_HFS
) {
1558 action
|= KAUTH_VNODE_WRITE_ATTRIBUTES
;
1562 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, &context
)) != 0)) {
1563 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: authorization failed");
1568 * Write the attributes if we have any.
1570 if ((va
.va_active
!= 0LL) && ((error
= vnode_setattr(vp
, &va
, &context
)) != 0)) {
1571 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: filesystem returned %d", error
);
1576 * Write the Finder Info if we have any.
1578 if (fndrinfo
!= NULL
) {
1579 if (al
.volattr
& ATTR_VOL_INFO
) {
1580 if (vp
->v_tag
== VT_HFS
) {
1581 error
= VNOP_IOCTL(vp
, HFS_SET_BOOT_INFO
, (caddr_t
)fndrinfo
, 0, &context
);
1585 /* XXX should never get here */
1588 /* write Finder Info EA */
1590 char uio_buf
[UIO_SIZEOF(1)];
1592 if ((auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
, uio_buf
, sizeof(uio_buf
))) == NULL
) {
1595 uio_addiov(auio
, CAST_USER_ADDR_T(fndrinfo
), 32);
1596 error
= vn_setxattr(vp
, XATTR_FINDERINFO_NAME
, auio
, XATTR_NOSECURITY
, &context
);
1600 if (error
== 0 && need_fsevent(FSE_FINDER_INFO_CHANGED
, vp
)) {
1601 add_fsevent(FSE_FINDER_INFO_CHANGED
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
1611 * Set the volume name, if we have one
1613 if (volname
!= NULL
)
1619 vs
.f_vol_name
= volname
; /* References the setattrlist buffer directly */
1620 VFSATTR_WANTED(&vs
, f_vol_name
);
1622 if ((error
= vfs_setattr(vp
->v_mount
, &vs
, ctx
)) != 0) {
1623 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: setting volume name failed");
1627 if (!VFSATTR_ALL_SUPPORTED(&vs
)) {
1629 VFS_DEBUG(ctx
, vp
, "ATTRLIST - ERROR: could not set volume name");
1634 /* all done and successful */
1639 if (user_buf
!= NULL
)
1640 FREE(user_buf
, M_TEMP
);
1641 VFS_DEBUG(ctx
, vp
, "ATTRLIST - set returning %d", error
);