]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_attrlist.c
eab6a261a896b847f7fc50b7b94ffbda5ded5a00
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
1 /*
2 * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
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
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
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.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/namei.h>
34 #include <sys/kernel.h>
35 #include <sys/stat.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>
42 #include <sys/attr.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>
48 #include <hfs/hfs.h>
49
50 #define ATTR_TIME_SIZE -1
51
52 /*
53 * Structure describing the state of an in-progress attrlist operation.
54 */
55 struct _attrlist_buf {
56 char *base;
57 char *fixedcursor;
58 char *varcursor;
59 ssize_t allocated;
60 ssize_t needed;
61 };
62
63
64 /*
65 * Pack (count) bytes from (source) into (buf).
66 */
67 static void
68 attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
69 {
70 ssize_t fit;
71
72 /* how much room left in the buffer? */
73 fit = imin(count, ab->allocated - (ab->fixedcursor - ab->base));
74 if (fit > 0)
75 bcopy(source, ab->fixedcursor, fit);
76
77 /* always move in increments of 4 */
78 ab->fixedcursor += roundup(count, 4);
79 }
80 static void
81 attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, const void *ext, ssize_t extcount)
82 {
83 struct attrreference ar;
84 ssize_t fit;
85
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));
90
91 /* calculate space and pack the variable object */
92 fit = imin(count, ab->allocated - (ab->varcursor - ab->base));
93 if (fit > 0) {
94 if (source != NULL)
95 bcopy(source, ab->varcursor, fit);
96 ab->varcursor += fit;
97 }
98 fit = imin(extcount, ab->allocated - (ab->varcursor - ab->base));
99 if (fit > 0) {
100 if (ext != NULL)
101 bcopy(ext, ab->varcursor, fit);
102 ab->varcursor += fit;
103 }
104 /* always move in increments of 4 */
105 ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
106 }
107 static void
108 attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
109 {
110 attrlist_pack_variable2(ab, source, count, NULL, 0);
111 }
112 static void
113 attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
114 {
115 struct attrreference ar;
116 ssize_t fit, space;
117
118
119 /*
120 * Supplied count is character count of string text, excluding trailing nul
121 * which we always supply here.
122 */
123 if (source == NULL) {
124 count = 0;
125 } else if (count == 0) {
126 count = strlen(source);
127 }
128
129 /*
130 * Make the reference and pack it.
131 * Note that this is entirely independent of how much we get into
132 * the buffer.
133 */
134 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
135 ar.attr_length = count + 1;
136 attrlist_pack_fixed(ab, &ar, sizeof(ar));
137
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);
141 if (fit > 0)
142 bcopy(source, ab->varcursor, fit);
143 /* is there room for our trailing nul? */
144 if (space > fit)
145 ab->varcursor[fit] = '\0';
146
147 /* always move in increments of 4 */
148 ab->varcursor += roundup(count + 1, 4);
149 }
150
151 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
152 #define ATTR_PACK_CAST(b, t, v) \
153 do { \
154 t _f = (t)v; \
155 ATTR_PACK(b, _f); \
156 } while (0)
157
158 #define ATTR_PACK_TIME(b, v, is64) \
159 do { \
160 if (is64) { \
161 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
162 ATTR_PACK(b, us); \
163 } else { \
164 ATTR_PACK(b, v); \
165 } \
166 } while(0)
167
168
169 /*
170 * Table-driven setup for all valid common/volume attributes.
171 */
172 struct getvolattrlist_attrtab {
173 attrgroup_t attr;
174 uint64_t bits;
175 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
176 ssize_t size;
177 };
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)},
199 {0, 0, 0}
200 };
201
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},
223 {0, 0, 0}
224 };
225
226 static int
227 getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
228 ssize_t *sizep, int is_64bit)
229 {
230 attrgroup_t recognised;
231
232 recognised = 0;
233 do {
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) {
239 if (is_64bit) {
240 *sizep += sizeof(struct user_timespec);
241 } else {
242 *sizep += sizeof(struct timespec);
243 }
244 } else {
245 *sizep += tab->size;
246 }
247 }
248 } while ((++tab)->attr != 0);
249
250 /* check to make sure that we recognised all of the passed-in attributes */
251 if (attrs & ~recognised)
252 return(EINVAL);
253 return(0);
254 }
255
256 /*
257 * Given the attributes listed in alp, configure vap to request
258 * the data from a filesystem.
259 */
260 static int
261 getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
262 {
263 int error;
264
265 /*
266 * Parse the above tables.
267 */
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)
271 return(error);
272 if (alp->volattr &&
273 (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
274 return(error);
275
276 return(0);
277 }
278
279 /*
280 * Table-driven setup for all valid common/dir/file/fork attributes against files.
281 */
282 struct getattrlist_attrtab {
283 attrgroup_t attr;
284 uint64_t bits;
285 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
286 ssize_t size;
287 kauth_action_t action;
288 };
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},
313 {0, 0, 0, 0}
314 };
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},
321 {0, 0, 0, 0}
322 };
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},
333 {0, 0, 0, 0}
334 };
335
336 static int
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)
339 {
340 attrgroup_t recognised;
341
342 recognised = 0;
343 do {
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) {
349 if (is_64bit) {
350 *sizep += sizeof(struct user_timespec);
351 } else {
352 *sizep += sizeof(struct timespec);
353 }
354 } else {
355 *sizep += tab->size;
356 }
357 *actionp |= tab->action;
358 }
359 } while ((++tab)->attr != 0);
360
361 /* check to make sure that we recognised all of the passed-in attributes */
362 if (attrs & ~recognised)
363 return(EINVAL);
364 return(0);
365 }
366
367 /*
368 * Given the attributes listed in alp, configure vap to request
369 * the data from a filesystem.
370 */
371 static int
372 getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
373 {
374 int error;
375
376 /*
377 * Parse the above tables.
378 */
379 *sizep = sizeof(uint32_t); /* length count */
380 *actionp = 0;
381 if (alp->commonattr &&
382 (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
383 return(error);
384 if (isdir && alp->dirattr &&
385 (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
386 return(error);
387 if (!isdir && alp->fileattr &&
388 (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
389 return(error);
390
391 return(0);
392 }
393
394
395 /*
396 * Find something resembling a terminal component name in the mountedonname for vp
397 *
398 */
399 static void
400 getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
401 {
402 int counting;
403 const char *cp;
404
405 /*
406 * We're looking for the last sequence of non / characters, but
407 * not including any trailing / characters.
408 */
409 *np = NULL;
410 *nl = 0;
411 counting = 0;
412 for (cp = mn; *cp != 0; cp++) {
413 if (!counting) {
414 /* start of run of chars */
415 if (*cp != '/') {
416 *np = cp;
417 counting = 1;
418 }
419 } else {
420 /* end of run of chars */
421 if (*cp == '/') {
422 *nl = cp - *np;
423 counting = 0;
424 }
425 }
426 }
427 /* need to close run? */
428 if (counting)
429 *nl = cp - *np;
430 }
431
432
433 static int
434 getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp, vfs_context_t ctx, int is_64bit)
435 {
436 struct vfs_attr vs;
437 struct vnode_attr va;
438 struct _attrlist_buf ab;
439 int error;
440 ssize_t fixedsize, varsize;
441 const char *cnp;
442 ssize_t cnl;
443 mount_t mnt;
444
445 ab.base = NULL;
446 VATTR_INIT(&va);
447 VFSATTR_INIT(&vs);
448 vs.f_vol_name = NULL;
449 mnt = vp->v_mount;
450
451
452 /*
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.
456 */
457 if (!vnode_isvroot(vp)) {
458 error = EINVAL;
459 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
460 goto out;
461 }
462
463 /*
464 * Set up the vfs_attr structure and call the filesystem.
465 */
466 if ((error = getvolattrlist_setupvfsattr(alp, &vs, &fixedsize, is_64bit)) != 0) {
467 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
468 goto out;
469 }
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) {
475 error = ENOMEM;
476 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
477 goto out;
478 }
479 }
480
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);
484 goto out;
485 }
486
487 /*
488 * Did we ask for something the filesystem doesn't support?
489 */
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);
495
496 /*
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.
500 */
501 if (VFSATTR_IS_ACTIVE(&vs, f_signature)
502 && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
503 VFSATTR_RETURN(&vs, f_signature, 0x4244);
504
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);
509
510 /* check to see if our fixups were enough */
511 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
512 error = EINVAL;
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);
516 goto out;
517 }
518 }
519 }
520
521 /*
522 * Some fields require data from the root vp
523 */
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);
530
531 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
532 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
533 goto out;
534 }
535
536 if (VATTR_IS_ACTIVE(&va, va_encoding) && !VATTR_IS_SUPPORTED(&va, va_encoding))
537 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
538 }
539
540 /*
541 * Compute variable-size buffer requirements.
542 */
543 varsize = 0;
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
552 */
553 cnp = vnode_getname(vp);
554 if (cnp == NULL) {
555 /* just use "/" as name */
556 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
557 }
558 cnl = strlen(cnp);
559 }
560 else {
561 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
562 }
563 if (alp->commonattr & ATTR_CMN_NAME)
564 varsize += roundup(cnl + 1, 4);
565 }
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);
571 }
572 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
573 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
574
575 /*
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.
579 */
580 ab.allocated = imin(uap->bufferSize, fixedsize + varsize);
581 if (ab.allocated > ATTR_MAX_BUFFER) {
582 error = ENOMEM;
583 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
584 goto out;
585 }
586 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
587 if (ab.base == NULL) {
588 error = ENOMEM;
589 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
590 goto out;
591 }
592
593 /*
594 * Pack results into the destination buffer.
595 */
596 ab.fixedcursor = ab.base + sizeof(uint32_t);
597 ab.varcursor = ab.base + fixedsize;
598 ab.needed = fixedsize + varsize;
599
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};
613 ATTR_PACK(&ab, f);
614 }
615 if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
616 fsobj_id_t f = {0, 0};
617 ATTR_PACK(&ab, f);
618 }
619 if (alp->commonattr & ATTR_CMN_PAROBJID) {
620 fsobj_id_t f = {0, 0};
621 ATTR_PACK(&ab, f);
622 }
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) {
637 char f[32];
638 /*
639 * This attribute isn't really Finder Info, at least for HFS.
640 */
641 if (vp->v_tag == VT_HFS) {
642 if ((error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx)) != 0)
643 goto out;
644 } else {
645 /* XXX we could at least pass out the volume UUID here */
646 bzero(&f, sizeof(f));
647 }
648 attrlist_pack_fixed(&ab, f, sizeof(f));
649 }
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 */
659 uint32_t perms = 0;
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)
663 perms |= W_OK;
664 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
665 perms |= R_OK;
666 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
667 perms |= X_OK;
668 } else {
669 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
670 perms |= W_OK;
671 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
672 perms |= R_OK;
673 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
674 perms |= X_OK;
675 }
676 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
677 ATTR_PACK(&ab, perms);
678 }
679
680 /* volume attributes **************************************************/
681
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;
720 } else {
721 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
722 }
723 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
724 ATTR_PACK(&ab, vs.f_capabilities);
725 }
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);
730 } else {
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);
733 }
734 ATTR_PACK(&ab, vs.f_attributes);
735 }
736
737 /* diagnostic */
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);
743
744 /*
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.
749 */
750 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
751
752 error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
753
754 out:
755 if (vs.f_vol_name != NULL)
756 kfree(vs.f_vol_name, MAXPATHLEN);
757 if (ab.base != NULL)
758 FREE(ab.base, M_TEMP);
759 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
760 return(error);
761 }
762
763 /*
764 * Obtain attribute information about a filesystem object.
765 */
766 int
767 getattrlist(struct proc *p, struct getattrlist_args *uap, __unused register_t *retval)
768 {
769 struct attrlist al;
770 struct vnode_attr va;
771 struct vfs_context context, *ctx;
772 struct nameidata nd;
773 struct _attrlist_buf ab;
774 vnode_t vp;
775 u_long nameiflags;
776 kauth_action_t action;
777 ssize_t fixedsize, varsize;
778 const char *cnp;
779 char *vname = NULL;
780 ssize_t cnl;
781 int error;
782
783 context.vc_proc = p;
784 context.vc_ucred = kauth_cred_get();
785 ctx = &context;
786 vp = NULL;
787 error = 0;
788 VATTR_INIT(&va);
789 va.va_name = NULL;
790 ab.base = NULL;
791 cnp = "unknown";
792 cnl = 0;
793
794 /*
795 * Look up the file.
796 */
797 nameiflags = AUDITVNPATH1;
798 if (!(uap->options & FSOPT_NOFOLLOW))
799 nameiflags |= FOLLOW;
800 NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, &context);
801
802 if ((error = namei(&nd)) != 0)
803 goto out;
804 vp = nd.ni_vp;
805 nameidone(&nd);
806
807 /*
808 * Fetch the attribute request.
809 */
810 if ((error = copyin(uap->alist, &al, sizeof(al))) != 0)
811 goto out;
812 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
813 error = EINVAL;
814 goto out;
815 }
816
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);
820
821 /*
822 * It is legal to request volume or file attributes,
823 * but not both.
824 */
825 if (al.volattr) {
826 if (al.fileattr || al.dirattr || al.forkattr) {
827 error = EINVAL;
828 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
829 goto out;
830 }
831 /* handle volume attribute request */
832 error = getvolattrlist(vp, uap, &al, &context, proc_is64bit(p));
833 goto out;
834 }
835
836 /*
837 * Set up the vnode_attr structure and authorise.
838 */
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");
841 goto out;
842 }
843 if ((error = vnode_authorize(vp, NULL, action, &context)) != 0) {
844 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
845 goto out;
846 }
847
848 if (va.va_active != 0) {
849 /*
850 * If we're going to ask for va_name, allocate a buffer to point it at
851 */
852 if (VATTR_IS_ACTIVE(&va, va_name)) {
853 va.va_name = (char *) kalloc(MAXPATHLEN);
854 if (va.va_name == NULL) {
855 error = ENOMEM;
856 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
857 goto out;
858 }
859 }
860
861 /*
862 * Call the filesystem.
863 */
864 if ((error = vnode_getattr(vp, &va, &context)) != 0) {
865 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
866 goto out;
867 }
868
869 /* did we ask for something the filesystem doesn't support? */
870 if (!VATTR_ALL_SUPPORTED(&va)) {
871
872 /*
873 * There are a couple of special cases. If we are after object IDs,
874 * we can make do with va_fileid.
875 */
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 */
878 /*
879 * Many (most?) filesystems don't know their parent object id. We can get it the
880 * hard way.
881 */
882 if ((al.commonattr & ATTR_CMN_PAROBJID) && !VATTR_IS_SUPPORTED(&va, va_parentid))
883 VATTR_CLEAR_ACTIVE(&va, va_parentid);
884 /*
885 * And we can report datasize/alloc from total.
886 */
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);
891
892 /*
893 * If we don't have an encoding, go with UTF-8
894 */
895 if ((al.commonattr & ATTR_CMN_SCRIPT) && !VATTR_IS_SUPPORTED(&va, va_encoding))
896 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
897
898 /*
899 * If we don't have a name, we'll get one from the vnode or mount point.
900 */
901 if ((al.commonattr & ATTR_CMN_NAME) && !VATTR_IS_SUPPORTED(&va, va_name)) {
902 VATTR_CLEAR_ACTIVE(&va, va_name);
903 }
904
905 /*
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).
911 */
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);
915 }
916
917 /* check again */
918 if (!VATTR_ALL_SUPPORTED(&va)) {
919 error = ENOTSUP;
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);
923 goto out;
924 }
925 }
926 }
927
928 /*
929 * Compute variable-space requirements.
930 */
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 */
935 cnp = va.va_name;
936 cnl = strlen(cnp);
937 } else {
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
946 */
947 cnp = vname = vnode_getname(vp);
948 if (cnp == NULL) {
949 /* just use "/" as name */
950 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
951 }
952 cnl = strlen(cnp);
953 }
954 else {
955 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
956 }
957 } else {
958 cnp = vname = vnode_getname(vp);
959 cnl = 0;
960 if (cnp != NULL) {
961 cnl = strlen(cnp);
962 }
963 }
964 }
965 varsize += roundup(cnl + 1, 4);
966 }
967
968 /*
969 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
970 *
971 * XXX This needs to change at some point; since the blob is opaque in
972 * user-space this is OK.
973 */
974 if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
975 VATTR_IS_SUPPORTED(&va, va_acl) &&
976 (va.va_acl != NULL))
977 varsize += roundup(KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount), 4);
978
979 /*
980 * Allocate a target buffer for attribute results.
981 *
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..
985 */
986 ab.allocated = fixedsize + varsize;
987 if (ab.allocated > ATTR_MAX_BUFFER) {
988 error = ENOMEM;
989 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
990 goto out;
991 }
992 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
993 if (ab.base == NULL) {
994 error = ENOMEM;
995 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
996 goto out;
997 }
998
999 /*
1000 * Pack results into the destination buffer.
1001 */
1002 ab.fixedcursor = ab.base + sizeof(uint32_t);
1003 ab.varcursor = ab.base + fixedsize;
1004 ab.needed = ab.allocated;
1005
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) {
1018 fsobj_id_t f;
1019 /*
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
1023 * it instead.
1024 */
1025 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1026 f.fid_objno = va.va_linkid;
1027 } else {
1028 f.fid_objno = va.va_fileid;
1029 }
1030 f.fid_generation = 0;
1031 ATTR_PACK(&ab, f);
1032 }
1033 if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
1034 fsobj_id_t f;
1035 /*
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
1039 * it instead.
1040 */
1041 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1042 f.fid_objno = va.va_linkid;
1043 } else {
1044 f.fid_objno = va.va_fileid;
1045 }
1046 f.fid_generation = 0;
1047 ATTR_PACK(&ab, f);
1048 }
1049 if (al.commonattr & ATTR_CMN_PAROBJID) {
1050 fsobj_id_t f;
1051 /*
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
1055 * directories.
1056 */
1057 if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
1058 f.fid_objno = va.va_parentid;
1059 } else {
1060 struct vnode_attr lva;
1061 vnode_t pvp;
1062
1063 pvp = vnode_getparent(vp);
1064
1065 if (pvp == NULLVP) {
1066 error = ENOTSUP;
1067 goto out;
1068 }
1069 VATTR_INIT(&lva);
1070 VATTR_WANTED(&lva, va_fileid);
1071 error = vnode_getattr(pvp, &lva, &context);
1072 vnode_put(pvp);
1073
1074 if (error != 0)
1075 goto out;
1076 f.fid_objno = lva.va_fileid;
1077 }
1078 f.fid_generation = 0;
1079 ATTR_PACK(&ab, f);
1080 }
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) {
1094 uio_t auio;
1095 size_t fisize;
1096 char uio_buf[UIO_SIZEOF(1)];
1097
1098 fisize = imin(32, ab.allocated - (ab.fixedcursor - ab.base));
1099 if (fisize > 0) {
1100 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
1101 error = ENOMEM;
1102 goto out;
1103 } else {
1104 uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
1105 error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, &fisize, XATTR_NOSECURITY, &context);
1106 uio_free(auio);
1107 }
1108 if (error != 0) {
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);
1112 error = 0;
1113 } else {
1114 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1115 goto out;
1116 }
1117 }
1118 } else {
1119 VFS_DEBUG(ctx, vp, "ATTRLIST - no room in caller buffer for FINDERINFO");
1120 }
1121 ab.fixedcursor += 32;
1122 }
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 */
1132 uint32_t perms = 0;
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)
1136 perms |= W_OK;
1137 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, &context) == 0)
1138 perms |= R_OK;
1139 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, &context) == 0)
1140 perms |= X_OK;
1141 } else {
1142 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, &context) == 0)
1143 perms |= W_OK;
1144 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, &context) == 0)
1145 perms |= R_OK;
1146 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, &context) == 0)
1147 perms |= X_OK;
1148 }
1149 VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
1150 ATTR_PACK(&ab, perms);
1151 }
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;
1155 /*
1156 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1157 */
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));
1162 } else {
1163 attrlist_pack_variable(&ab, NULL, 0);
1164 }
1165 }
1166 if (al.commonattr & ATTR_CMN_UUID) {
1167 if (!VATTR_IS_SUPPORTED(&va, va_uuuid)) {
1168 ATTR_PACK(&ab, kauth_null_guid);
1169 } else {
1170 ATTR_PACK(&ab, va.va_uuuid);
1171 }
1172 }
1173 if (al.commonattr & ATTR_CMN_GRPUUID) {
1174 if (!VATTR_IS_SUPPORTED(&va, va_guuid)) {
1175 ATTR_PACK(&ab, kauth_null_guid);
1176 } else {
1177 ATTR_PACK(&ab, va.va_guuid);
1178 }
1179 }
1180
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);
1189 }
1190
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);
1206 } else {
1207 ATTR_PACK_CAST(&ab, uint32_t, 0);
1208 }
1209 }
1210 if (al.fileattr & ATTR_FILE_DATALENGTH) {
1211 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
1212 ATTR_PACK(&ab, va.va_data_size);
1213 } else {
1214 ATTR_PACK(&ab, va.va_total_size);
1215 }
1216 }
1217 if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
1218 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
1219 ATTR_PACK(&ab, va.va_data_alloc);
1220 } else {
1221 ATTR_PACK(&ab, va.va_total_alloc);
1222 }
1223 }
1224 /* fetch resource fork size/allocation via xattr interface */
1225 if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
1226 size_t rsize;
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)) {
1229 rsize = 0;
1230 error = 0;
1231 } else {
1232 goto out;
1233 }
1234 }
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;
1239 if (blksize == 0)
1240 blksize = 512;
1241 ATTR_PACK_CAST(&ab, off_t, (roundup(rsize, blksize)));
1242 }
1243 }
1244 }
1245
1246 /* diagnostic */
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);
1252
1253 /*
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.
1258 */
1259 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
1260
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));
1263
1264 out:
1265 if (va.va_name)
1266 kfree(va.va_name, MAXPATHLEN);
1267 if (vname)
1268 vnode_putname(vname);
1269 if (vp)
1270 vnode_put(vp);
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);
1275
1276 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1277 return(error);
1278 }
1279
1280 static int
1281 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
1282 {
1283 /* make sure we have enough source data */
1284 if ((*cursor) + size > end)
1285 return(EINVAL);
1286
1287 bcopy(*cursor, buf, size);
1288 *cursor += size;
1289 return(0);
1290 }
1291
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) \
1295 do { \
1296 if (is64) { \
1297 struct user_timespec us; \
1298 ATTR_UNPACK(us); \
1299 v.tv_sec = us.tv_sec; \
1300 v.tv_nsec = us.tv_nsec; \
1301 } else { \
1302 ATTR_UNPACK(v); \
1303 } \
1304 } while(0)
1305
1306
1307 /*
1308 * Write attributes.
1309 */
1310 int
1311 setattrlist(struct proc *p, register struct setattrlist_args *uap, __unused register_t *retval)
1312 {
1313 struct attrlist al;
1314 struct vfs_context context, *ctx;
1315 struct vnode_attr va;
1316 struct attrreference ar;
1317 struct nameidata nd;
1318 vnode_t vp;
1319 u_long nameiflags;
1320 kauth_action_t action;
1321 char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
1322 int proc_is64, error;
1323 uint32_t nace;
1324 kauth_filesec_t rfsec;
1325
1326 context.vc_proc = p;
1327 context.vc_ucred = kauth_cred_get();
1328 ctx = &context;
1329 vp = NULL;
1330 user_buf = NULL;
1331 fndrinfo = NULL;
1332 volname = NULL;
1333 error = 0;
1334 proc_is64 = proc_is64bit(p);
1335 VATTR_INIT(&va);
1336
1337
1338 /*
1339 * Look up the file.
1340 */
1341 nameiflags = 0;
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)
1346 goto out;
1347 vp = nd.ni_vp;
1348 nameidone(&nd);
1349
1350 /*
1351 * Fetch the attribute set and validate.
1352 */
1353 if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
1354 goto out;
1355 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
1356 error = EINVAL;
1357 goto out;
1358 }
1359
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);
1363
1364 if (al.volattr) {
1365 if ((al.volattr & ~ATTR_VOL_SETMASK) ||
1366 (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
1367 al.fileattr ||
1368 al.forkattr) {
1369 error = EINVAL;
1370 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1371 goto out;
1372 }
1373 } else {
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)) {
1378 error = EINVAL;
1379 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1380 goto out;
1381 }
1382 }
1383
1384 /*
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.
1390 *
1391 * We could also map the user buffer if it is larger than some sensible mimimum.
1392 */
1393 if (uap->bufferSize > ATTR_MAX_BUFFER) {
1394 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
1395 error = ENOMEM;
1396 goto out;
1397 }
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);
1401 error = ENOMEM;
1402 goto out;
1403 }
1404 if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
1405 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
1406 goto out;
1407 }
1408 VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
1409
1410 /*
1411 * Unpack the argument buffer.
1412 */
1413 cursor = user_buf;
1414 bufend = cursor + uap->bufferSize;
1415
1416 /* common */
1417 if (al.commonattr & ATTR_CMN_SCRIPT) {
1418 ATTR_UNPACK(va.va_encoding);
1419 VATTR_SET_ACTIVE(&va, va_encoding);
1420 }
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);
1424 }
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);
1428 }
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);
1432 }
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);
1436 }
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);
1440 }
1441 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1442 if ((cursor + 32) > bufend) {
1443 error = EINVAL;
1444 VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
1445 goto out;
1446 }
1447 fndrinfo = cursor;
1448 cursor += 32;
1449 }
1450 if (al.commonattr & ATTR_CMN_OWNERID) {
1451 ATTR_UNPACK(va.va_uid);
1452 VATTR_SET_ACTIVE(&va, va_uid);
1453 }
1454 if (al.commonattr & ATTR_CMN_GRPID) {
1455 ATTR_UNPACK(va.va_gid);
1456 VATTR_SET_ACTIVE(&va, va_gid);
1457 }
1458 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1459 ATTR_UNPACK_CAST(uint32_t, va.va_mode);
1460 VATTR_SET_ACTIVE(&va, va_mode);
1461 }
1462 if (al.commonattr & ATTR_CMN_FLAGS) {
1463 ATTR_UNPACK(va.va_flags);
1464 VATTR_SET_ACTIVE(&va, va_flags);
1465 }
1466 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1467
1468 /*
1469 * We are (for now) passed a kauth_filesec_t, but all we want from
1470 * it is the ACL.
1471 */
1472 cp = cursor;
1473 ATTR_UNPACK(ar);
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 */
1480 error = EINVAL;
1481 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
1482 goto out;
1483 }
1484 nace = rfsec->fsec_entrycount;
1485 if (nace == KAUTH_FILESEC_NOACL)
1486 nace = 0;
1487 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1488 error = EINVAL;
1489 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
1490 goto out;
1491 }
1492 nace = rfsec->fsec_acl.acl_entrycount;
1493 if (nace == KAUTH_FILESEC_NOACL) {
1494 /* deleting ACL */
1495 VATTR_SET(&va, va_acl, NULL);
1496 } else {
1497
1498 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1499 error = EINVAL;
1500 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
1501 goto out;
1502 }
1503 VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
1504 }
1505 }
1506 if (al.commonattr & ATTR_CMN_UUID) {
1507 ATTR_UNPACK(va.va_uuuid);
1508 VATTR_SET_ACTIVE(&va, va_uuuid);
1509 }
1510 if (al.commonattr & ATTR_CMN_GRPUUID) {
1511 ATTR_UNPACK(va.va_guuid);
1512 VATTR_SET_ACTIVE(&va, va_guuid);
1513 }
1514
1515 /* volume */
1516 if (al.volattr & ATTR_VOL_INFO) {
1517 if (al.volattr & ATTR_VOL_NAME) {
1518 volname = cursor;
1519 ATTR_UNPACK(ar);
1520 volname += ar.attr_dataoffset;
1521 if ((volname + ar.attr_length) > bufend) {
1522 error = EINVAL;
1523 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
1524 goto out;
1525 }
1526 /* guarantee NUL termination */
1527 volname[ar.attr_length - 1] = 0;
1528 }
1529 }
1530
1531 /* file */
1532 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1533 /* XXX does it actually make any sense to change this? */
1534 error = EINVAL;
1535 VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
1536 goto out;
1537 }
1538
1539 /*
1540 * Validate and authorize.
1541 */
1542 action = 0;
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);
1545 goto out;
1546 }
1547 /*
1548 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1549 * and will be auth'ed by the FS.
1550 */
1551 if (fndrinfo != NULL) {
1552 if (al.volattr & ATTR_VOL_INFO) {
1553 if (vp->v_tag != VT_HFS) {
1554 error = EINVAL;
1555 goto out;
1556 }
1557 } else {
1558 action |= KAUTH_VNODE_WRITE_ATTRIBUTES;
1559 }
1560 }
1561
1562 if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
1563 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
1564 goto out;
1565 }
1566
1567 /*
1568 * Write the attributes if we have any.
1569 */
1570 if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, &context)) != 0)) {
1571 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1572 goto out;
1573 }
1574
1575 /*
1576 * Write the Finder Info if we have any.
1577 */
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);
1582 if (error != 0)
1583 goto out;
1584 } else {
1585 /* XXX should never get here */
1586 }
1587 } else {
1588 /* write Finder Info EA */
1589 uio_t auio;
1590 char uio_buf[UIO_SIZEOF(1)];
1591
1592 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
1593 error = ENOMEM;
1594 } else {
1595 uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
1596 error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, &context);
1597 uio_free(auio);
1598 }
1599
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);
1602 }
1603
1604 if (error != 0) {
1605 goto out;
1606 }
1607 }
1608 }
1609
1610 /*
1611 * Set the volume name, if we have one
1612 */
1613 if (volname != NULL)
1614 {
1615 struct vfs_attr vs;
1616
1617 VFSATTR_INIT(&vs);
1618
1619 vs.f_vol_name = volname; /* References the setattrlist buffer directly */
1620 VFSATTR_WANTED(&vs, f_vol_name);
1621
1622 if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
1623 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
1624 goto out;
1625 }
1626
1627 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
1628 error = EINVAL;
1629 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
1630 goto out;
1631 }
1632 }
1633
1634 /* all done and successful */
1635
1636 out:
1637 if (vp != NULL)
1638 vnode_put(vp);
1639 if (user_buf != NULL)
1640 FREE(user_buf, M_TEMP);
1641 VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
1642 return(error);
1643 }