]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_attrlist.c
7716e41e25fc47bce84366fa2816db6d6f661e0d
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
1 /*
2 * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/namei.h>
26 #include <sys/kernel.h>
27 #include <sys/stat.h>
28 #include <sys/vnode_internal.h>
29 #include <sys/mount_internal.h>
30 #include <sys/proc_internal.h>
31 #include <sys/kauth.h>
32 #include <sys/uio_internal.h>
33 #include <sys/malloc.h>
34 #include <sys/attr.h>
35 #include <sys/sysproto.h>
36 #include <sys/xattr.h>
37 #include <sys/fsevents.h>
38 #include <kern/kalloc.h>
39 #include <miscfs/specfs/specdev.h>
40 #include <hfs/hfs.h>
41
42 #define ATTR_TIME_SIZE -1
43
44 /*
45 * Structure describing the state of an in-progress attrlist operation.
46 */
47 struct _attrlist_buf {
48 char *base;
49 char *fixedcursor;
50 char *varcursor;
51 ssize_t allocated;
52 ssize_t needed;
53 };
54
55
56 /*
57 * Pack (count) bytes from (source) into (buf).
58 */
59 static void
60 attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
61 {
62 ssize_t fit;
63
64 /* how much room left in the buffer? */
65 fit = imin(count, ab->allocated - (ab->fixedcursor - ab->base));
66 if (fit > 0)
67 bcopy(source, ab->fixedcursor, fit);
68
69 /* always move in increments of 4 */
70 ab->fixedcursor += roundup(count, 4);
71 }
72 static void
73 attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, const void *ext, ssize_t extcount)
74 {
75 struct attrreference ar;
76 ssize_t fit;
77
78 /* pack the reference to the variable object */
79 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
80 ar.attr_length = count + extcount;
81 attrlist_pack_fixed(ab, &ar, sizeof(ar));
82
83 /* calculate space and pack the variable object */
84 fit = imin(count, ab->allocated - (ab->varcursor - ab->base));
85 if (fit > 0) {
86 if (source != NULL)
87 bcopy(source, ab->varcursor, fit);
88 ab->varcursor += fit;
89 }
90 fit = imin(extcount, ab->allocated - (ab->varcursor - ab->base));
91 if (fit > 0) {
92 if (ext != NULL)
93 bcopy(ext, ab->varcursor, fit);
94 ab->varcursor += fit;
95 }
96 /* always move in increments of 4 */
97 ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
98 }
99 static void
100 attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
101 {
102 attrlist_pack_variable2(ab, source, count, NULL, 0);
103 }
104 static void
105 attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
106 {
107 struct attrreference ar;
108 ssize_t fit, space;
109
110
111 /*
112 * Supplied count is character count of string text, excluding trailing nul
113 * which we always supply here.
114 */
115 if (source == NULL) {
116 count = 0;
117 } else if (count == 0) {
118 count = strlen(source);
119 }
120
121 /*
122 * Make the reference and pack it.
123 * Note that this is entirely independent of how much we get into
124 * the buffer.
125 */
126 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
127 ar.attr_length = count + 1;
128 attrlist_pack_fixed(ab, &ar, sizeof(ar));
129
130 /* calculate how much of the string text we can copy, and do that */
131 space = ab->allocated - (ab->varcursor - ab->base);
132 fit = imin(count, space);
133 if (fit > 0)
134 bcopy(source, ab->varcursor, fit);
135 /* is there room for our trailing nul? */
136 if (space > fit)
137 ab->varcursor[fit] = '\0';
138
139 /* always move in increments of 4 */
140 ab->varcursor += roundup(count + 1, 4);
141 }
142
143 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
144 #define ATTR_PACK_CAST(b, t, v) \
145 do { \
146 t _f = (t)v; \
147 ATTR_PACK(b, _f); \
148 } while (0)
149
150 #define ATTR_PACK_TIME(b, v, is64) \
151 do { \
152 if (is64) { \
153 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
154 ATTR_PACK(b, us); \
155 } else { \
156 ATTR_PACK(b, v); \
157 } \
158 } while(0)
159
160
161 /*
162 * Table-driven setup for all valid common/volume attributes.
163 */
164 struct getvolattrlist_attrtab {
165 attrgroup_t attr;
166 uint64_t bits;
167 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
168 ssize_t size;
169 };
170 static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
171 {ATTR_CMN_NAME, 0, sizeof(struct attrreference)},
172 {ATTR_CMN_DEVID, 0, sizeof(dev_t)},
173 {ATTR_CMN_FSID, 0, sizeof(fsid_t)},
174 {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t)},
175 {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t)},
176 {ATTR_CMN_OBJID, 0, sizeof(fsobj_id_t)},
177 {ATTR_CMN_OBJPERMANENTID, 0, sizeof(fsobj_id_t)},
178 {ATTR_CMN_PAROBJID, 0, sizeof(fsobj_id_t)},
179 {ATTR_CMN_SCRIPT, 0, sizeof(text_encoding_t)},
180 {ATTR_CMN_CRTIME, VFSATTR_BIT(f_create_time), ATTR_TIME_SIZE},
181 {ATTR_CMN_MODTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
182 {ATTR_CMN_CHGTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
183 {ATTR_CMN_ACCTIME, VFSATTR_BIT(f_access_time), ATTR_TIME_SIZE},
184 {ATTR_CMN_BKUPTIME, VFSATTR_BIT(f_backup_time), ATTR_TIME_SIZE},
185 {ATTR_CMN_FNDRINFO, 0, 32},
186 {ATTR_CMN_OWNERID, 0, sizeof(uid_t)},
187 {ATTR_CMN_GRPID, 0, sizeof(gid_t)},
188 {ATTR_CMN_ACCESSMASK, 0, sizeof(uint32_t)},
189 {ATTR_CMN_FLAGS, 0, sizeof(uint32_t)},
190 {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t)},
191 {0, 0, 0}
192 };
193
194 static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
195 {ATTR_VOL_FSTYPE, 0, sizeof(uint32_t)},
196 {ATTR_VOL_SIGNATURE, VFSATTR_BIT(f_signature), sizeof(uint32_t)},
197 {ATTR_VOL_SIZE, VFSATTR_BIT(f_blocks), sizeof(off_t)},
198 {ATTR_VOL_SPACEFREE, VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
199 {ATTR_VOL_SPACEAVAIL, VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
200 {ATTR_VOL_MINALLOCATION, VFSATTR_BIT(f_bsize), sizeof(off_t)},
201 {ATTR_VOL_ALLOCATIONCLUMP, VFSATTR_BIT(f_bsize), sizeof(off_t)},
202 {ATTR_VOL_IOBLOCKSIZE, VFSATTR_BIT(f_iosize), sizeof(uint32_t)},
203 {ATTR_VOL_OBJCOUNT, VFSATTR_BIT(f_objcount), sizeof(uint32_t)},
204 {ATTR_VOL_FILECOUNT, VFSATTR_BIT(f_filecount), sizeof(uint32_t)},
205 {ATTR_VOL_DIRCOUNT, VFSATTR_BIT(f_dircount), sizeof(uint32_t)},
206 {ATTR_VOL_MAXOBJCOUNT, VFSATTR_BIT(f_maxobjcount), sizeof(uint32_t)},
207 {ATTR_VOL_MOUNTPOINT, 0, sizeof(struct attrreference)},
208 {ATTR_VOL_NAME, VFSATTR_BIT(f_vol_name), sizeof(struct attrreference)},
209 {ATTR_VOL_MOUNTFLAGS, 0, sizeof(uint32_t)},
210 {ATTR_VOL_MOUNTEDDEVICE, 0, sizeof(struct attrreference)},
211 {ATTR_VOL_ENCODINGSUSED, 0, sizeof(uint64_t)},
212 {ATTR_VOL_CAPABILITIES, VFSATTR_BIT(f_capabilities), sizeof(vol_capabilities_attr_t)},
213 {ATTR_VOL_ATTRIBUTES, VFSATTR_BIT(f_attributes), sizeof(vol_attributes_attr_t)},
214 {ATTR_VOL_INFO, 0, 0},
215 {0, 0, 0}
216 };
217
218 static int
219 getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
220 ssize_t *sizep, int is_64bit)
221 {
222 attrgroup_t recognised;
223
224 recognised = 0;
225 do {
226 /* is this attribute set? */
227 if (tab->attr & attrs) {
228 recognised |= tab->attr;
229 vsp->f_active |= tab->bits;
230 if (tab->size == ATTR_TIME_SIZE) {
231 if (is_64bit) {
232 *sizep += sizeof(struct user_timespec);
233 } else {
234 *sizep += sizeof(struct timespec);
235 }
236 } else {
237 *sizep += tab->size;
238 }
239 }
240 } while ((++tab)->attr != 0);
241
242 /* check to make sure that we recognised all of the passed-in attributes */
243 if (attrs & ~recognised)
244 return(EINVAL);
245 return(0);
246 }
247
248 /*
249 * Given the attributes listed in alp, configure vap to request
250 * the data from a filesystem.
251 */
252 static int
253 getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
254 {
255 int error;
256
257 /*
258 * Parse the above tables.
259 */
260 *sizep = sizeof(uint32_t); /* length count */
261 if (alp->commonattr &&
262 (error = getvolattrlist_parsetab(getvolattrlist_common_tab, alp->commonattr, vsp, sizep, is_64bit)) != 0)
263 return(error);
264 if (alp->volattr &&
265 (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
266 return(error);
267
268 return(0);
269 }
270
271 /*
272 * Table-driven setup for all valid common/dir/file/fork attributes against files.
273 */
274 struct getattrlist_attrtab {
275 attrgroup_t attr;
276 uint64_t bits;
277 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
278 ssize_t size;
279 kauth_action_t action;
280 };
281 static struct getattrlist_attrtab getattrlist_common_tab[] = {
282 {ATTR_CMN_NAME, VATTR_BIT(va_name), sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES},
283 {ATTR_CMN_DEVID, 0, sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
284 {ATTR_CMN_FSID, VATTR_BIT(va_fsid), sizeof(fsid_t), KAUTH_VNODE_READ_ATTRIBUTES},
285 {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t), KAUTH_VNODE_READ_ATTRIBUTES},
286 {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t), KAUTH_VNODE_READ_ATTRIBUTES},
287 {ATTR_CMN_OBJID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
288 {ATTR_CMN_OBJPERMANENTID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
289 {ATTR_CMN_PAROBJID, VATTR_BIT(va_parentid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
290 {ATTR_CMN_SCRIPT, VATTR_BIT(va_encoding), sizeof(text_encoding_t), KAUTH_VNODE_READ_ATTRIBUTES},
291 {ATTR_CMN_CRTIME, VATTR_BIT(va_create_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
292 {ATTR_CMN_MODTIME, VATTR_BIT(va_modify_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
293 {ATTR_CMN_CHGTIME, VATTR_BIT(va_change_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
294 {ATTR_CMN_ACCTIME, VATTR_BIT(va_access_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
295 {ATTR_CMN_BKUPTIME, VATTR_BIT(va_backup_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
296 {ATTR_CMN_FNDRINFO, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES},
297 {ATTR_CMN_OWNERID, VATTR_BIT(va_uid), sizeof(uid_t), KAUTH_VNODE_READ_ATTRIBUTES},
298 {ATTR_CMN_GRPID, VATTR_BIT(va_gid), sizeof(gid_t), KAUTH_VNODE_READ_ATTRIBUTES},
299 {ATTR_CMN_ACCESSMASK, VATTR_BIT(va_mode), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
300 {ATTR_CMN_FLAGS, VATTR_BIT(va_flags), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
301 {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
302 {ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl), sizeof(struct attrreference), KAUTH_VNODE_READ_SECURITY},
303 {ATTR_CMN_UUID, VATTR_BIT(va_uuuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
304 {ATTR_CMN_GRPUUID, VATTR_BIT(va_guuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
305 {0, 0, 0, 0}
306 };
307 static struct getattrlist_attrtab getattrlist_dir_tab[] = {
308 {ATTR_DIR_LINKCOUNT, VATTR_BIT(va_nlink), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
309 {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
310 /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
311 {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nlink), 0, KAUTH_VNODE_READ_ATTRIBUTES},
312 {ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
313 {0, 0, 0, 0}
314 };
315 static struct getattrlist_attrtab getattrlist_file_tab[] = {
316 {ATTR_FILE_LINKCOUNT, VATTR_BIT(va_nlink), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
317 {ATTR_FILE_TOTALSIZE, VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
318 {ATTR_FILE_ALLOCSIZE, VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
319 {ATTR_FILE_IOBLOCKSIZE, VATTR_BIT(va_iosize), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
320 {ATTR_FILE_DEVTYPE, VATTR_BIT(va_rdev), sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
321 {ATTR_FILE_DATALENGTH, VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
322 {ATTR_FILE_DATAALLOCSIZE, VATTR_BIT(va_total_alloc)| VATTR_BIT(va_data_alloc), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
323 {ATTR_FILE_RSRCLENGTH, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
324 {ATTR_FILE_RSRCALLOCSIZE, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
325 {0, 0, 0, 0}
326 };
327
328 static int
329 getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, struct vnode_attr *vap,
330 ssize_t *sizep, kauth_action_t *actionp, int is_64bit)
331 {
332 attrgroup_t recognised;
333
334 recognised = 0;
335 do {
336 /* is this attribute set? */
337 if (tab->attr & attrs) {
338 recognised |= tab->attr;
339 vap->va_active |= tab->bits;
340 if (tab->size == ATTR_TIME_SIZE) {
341 if (is_64bit) {
342 *sizep += sizeof(struct user_timespec);
343 } else {
344 *sizep += sizeof(struct timespec);
345 }
346 } else {
347 *sizep += tab->size;
348 }
349 *actionp |= tab->action;
350 }
351 } while ((++tab)->attr != 0);
352
353 /* check to make sure that we recognised all of the passed-in attributes */
354 if (attrs & ~recognised)
355 return(EINVAL);
356 return(0);
357 }
358
359 /*
360 * Given the attributes listed in alp, configure vap to request
361 * the data from a filesystem.
362 */
363 static int
364 getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
365 {
366 int error;
367
368 /*
369 * Parse the above tables.
370 */
371 *sizep = sizeof(uint32_t); /* length count */
372 *actionp = 0;
373 if (alp->commonattr &&
374 (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
375 return(error);
376 if (isdir && alp->dirattr &&
377 (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
378 return(error);
379 if (!isdir && alp->fileattr &&
380 (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
381 return(error);
382
383 return(0);
384 }
385
386
387 /*
388 * Find something resembling a terminal component name in the mountedonname for vp
389 *
390 */
391 static void
392 getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
393 {
394 int counting;
395 const char *cp;
396
397 /*
398 * We're looking for the last sequence of non / characters, but
399 * not including any trailing / characters.
400 */
401 *np = NULL;
402 *nl = 0;
403 counting = 0;
404 for (cp = mn; *cp != 0; cp++) {
405 if (!counting) {
406 /* start of run of chars */
407 if (*cp != '/') {
408 *np = cp;
409 counting = 1;
410 }
411 } else {
412 /* end of run of chars */
413 if (*cp == '/') {
414 *nl = cp - *np;
415 counting = 0;
416 }
417 }
418 }
419 /* need to close run? */
420 if (counting)
421 *nl = cp - *np;
422 }
423
424
425 static int
426 getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp, vfs_context_t ctx, int is_64bit)
427 {
428 struct vfs_attr vs;
429 struct vnode_attr va;
430 struct _attrlist_buf ab;
431 int error;
432 ssize_t fixedsize, varsize;
433 const char *cnp;
434 ssize_t cnl;
435 mount_t mnt;
436
437 ab.base = NULL;
438 VATTR_INIT(&va);
439 VFSATTR_INIT(&vs);
440 vs.f_vol_name = NULL;
441 mnt = vp->v_mount;
442
443
444 /*
445 * For now, the vnode must be the root of its filesystem.
446 * To relax this, we need to be able to find the root vnode of a filesystem
447 * from any vnode in the filesystem.
448 */
449 if (!vnode_isvroot(vp)) {
450 error = EINVAL;
451 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
452 goto out;
453 }
454
455 /*
456 * Set up the vfs_attr structure and call the filesystem.
457 */
458 if ((error = getvolattrlist_setupvfsattr(alp, &vs, &fixedsize, is_64bit)) != 0) {
459 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
460 goto out;
461 }
462 if (vs.f_active != 0) {
463 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
464 if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
465 vs.f_vol_name = (char *) kalloc(MAXPATHLEN);
466 if (vs.f_vol_name == NULL) {
467 error = ENOMEM;
468 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
469 goto out;
470 }
471 }
472
473 VFS_DEBUG(ctx, vp, "ATTRLIST - calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
474 if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
475 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
476 goto out;
477 }
478
479 /*
480 * Did we ask for something the filesystem doesn't support?
481 */
482 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
483 /* default value for volume subtype */
484 if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
485 && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype))
486 VFSATTR_RETURN(&vs, f_fssubtype, 0);
487
488 /*
489 * If the file system didn't supply f_signature, then
490 * default it to 'BD', which is the generic signature
491 * that most Carbon file systems should return.
492 */
493 if (VFSATTR_IS_ACTIVE(&vs, f_signature)
494 && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
495 VFSATTR_RETURN(&vs, f_signature, 0x4244);
496
497 /* default for block size */
498 if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
499 && !VFSATTR_IS_SUPPORTED(&vs, f_bsize))
500 VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
501
502 /* check to see if our fixups were enough */
503 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
504 error = EINVAL;
505 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested volume attributes");
506 VFS_DEBUG(ctx, vp, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
507 vs.f_active, vs.f_supported, vs.f_active & ~vs.f_supported);
508 goto out;
509 }
510 }
511 }
512
513 /*
514 * Some fields require data from the root vp
515 */
516 if (alp->commonattr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | ATTR_CMN_SCRIPT)) {
517 VATTR_WANTED(&va, va_uid);
518 VATTR_WANTED(&va, va_gid);
519 VATTR_WANTED(&va, va_mode);
520 VATTR_WANTED(&va, va_flags);
521 VATTR_WANTED(&va, va_encoding);
522
523 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
524 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
525 goto out;
526 }
527
528 if (VATTR_IS_ACTIVE(&va, va_encoding) && !VATTR_IS_SUPPORTED(&va, va_encoding))
529 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
530 }
531
532 /*
533 * Compute variable-size buffer requirements.
534 */
535 varsize = 0;
536 if (alp->commonattr & ATTR_CMN_NAME) {
537 if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
538 vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
539 /* special case for boot volume. Use root name when it's
540 * available (which is the volume name) or just the mount on
541 * name of "/". we must do this for binary compatibility with
542 * pre Tiger code. returning nothing for the boot volume name
543 * breaks installers - 3961058
544 */
545 cnp = vnode_getname(vp);
546 if (cnp == NULL) {
547 /* just use "/" as name */
548 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
549 }
550 cnl = strlen(cnp);
551 }
552 else {
553 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
554 }
555 if (alp->commonattr & ATTR_CMN_NAME)
556 varsize += roundup(cnl + 1, 4);
557 }
558 if (alp->volattr & ATTR_VOL_MOUNTPOINT)
559 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
560 if (alp->volattr & ATTR_VOL_NAME) {
561 vs.f_vol_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
562 varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
563 }
564 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
565 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
566
567 /*
568 * Allocate a target buffer for attribute results.
569 * Note that since we won't ever copy out more than the caller requested,
570 * we never need to allocate more than they offer.
571 */
572 ab.allocated = imin(uap->bufferSize, fixedsize + varsize);
573 if (ab.allocated > ATTR_MAX_BUFFER) {
574 error = ENOMEM;
575 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
576 goto out;
577 }
578 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
579 if (ab.base == NULL) {
580 error = ENOMEM;
581 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
582 goto out;
583 }
584
585 /*
586 * Pack results into the destination buffer.
587 */
588 ab.fixedcursor = ab.base + sizeof(uint32_t);
589 ab.varcursor = ab.base + fixedsize;
590 ab.needed = fixedsize + varsize;
591
592 /* common attributes **************************************************/
593 if (alp->commonattr & ATTR_CMN_NAME)
594 attrlist_pack_string(&ab, cnp, cnl);
595 if (alp->commonattr & ATTR_CMN_DEVID)
596 ATTR_PACK_CAST(&ab, dev_t, mnt->mnt_vfsstat.f_fsid.val[0]);
597 if (alp->commonattr & ATTR_CMN_FSID)
598 ATTR_PACK(&ab, mnt->mnt_vfsstat.f_fsid);
599 if (alp->commonattr & ATTR_CMN_OBJTYPE)
600 ATTR_PACK_CAST(&ab, fsobj_type_t, 0);
601 if (alp->commonattr & ATTR_CMN_OBJTAG)
602 ATTR_PACK_CAST(&ab, fsobj_tag_t, vp->v_tag);
603 if (alp->commonattr & ATTR_CMN_OBJID) {
604 fsobj_id_t f = {0, 0};
605 ATTR_PACK(&ab, f);
606 }
607 if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
608 fsobj_id_t f = {0, 0};
609 ATTR_PACK(&ab, f);
610 }
611 if (alp->commonattr & ATTR_CMN_PAROBJID) {
612 fsobj_id_t f = {0, 0};
613 ATTR_PACK(&ab, f);
614 }
615 /* note that this returns the encoding for the volume name, not the node name */
616 if (alp->commonattr & ATTR_CMN_SCRIPT)
617 ATTR_PACK_CAST(&ab, text_encoding_t, va.va_encoding);
618 if (alp->commonattr & ATTR_CMN_CRTIME)
619 ATTR_PACK_TIME(&ab, vs.f_create_time, is_64bit);
620 if (alp->commonattr & ATTR_CMN_MODTIME)
621 ATTR_PACK_TIME(&ab, vs.f_modify_time, is_64bit);
622 if (alp->commonattr & ATTR_CMN_CHGTIME)
623 ATTR_PACK_TIME(&ab, vs.f_modify_time, is_64bit);
624 if (alp->commonattr & ATTR_CMN_ACCTIME)
625 ATTR_PACK_TIME(&ab, vs.f_access_time, is_64bit);
626 if (alp->commonattr & ATTR_CMN_BKUPTIME)
627 ATTR_PACK_TIME(&ab, vs.f_backup_time, is_64bit);
628 if (alp->commonattr & ATTR_CMN_FNDRINFO) {
629 char f[32];
630 /*
631 * This attribute isn't really Finder Info, at least for HFS.
632 */
633 if (vp->v_tag == VT_HFS) {
634 if ((error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx)) != 0)
635 goto out;
636 } else {
637 /* XXX we could at least pass out the volume UUID here */
638 bzero(&f, sizeof(f));
639 }
640 attrlist_pack_fixed(&ab, f, sizeof(f));
641 }
642 if (alp->commonattr & ATTR_CMN_OWNERID)
643 ATTR_PACK(&ab, va.va_uid);
644 if (alp->commonattr & ATTR_CMN_GRPID)
645 ATTR_PACK(&ab, va.va_gid);
646 if (alp->commonattr & ATTR_CMN_ACCESSMASK)
647 ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
648 if (alp->commonattr & ATTR_CMN_FLAGS)
649 ATTR_PACK(&ab, va.va_flags);
650 if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */
651 uint32_t perms = 0;
652 if (vnode_isdir(vp)) {
653 if (vnode_authorize(vp, NULL,
654 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
655 perms |= W_OK;
656 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
657 perms |= R_OK;
658 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
659 perms |= X_OK;
660 } else {
661 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
662 perms |= W_OK;
663 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
664 perms |= R_OK;
665 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
666 perms |= X_OK;
667 }
668 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
669 ATTR_PACK(&ab, perms);
670 }
671
672 /* volume attributes **************************************************/
673
674 if (alp->volattr & ATTR_VOL_FSTYPE)
675 ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
676 if (alp->volattr & ATTR_VOL_SIGNATURE)
677 ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
678 if (alp->volattr & ATTR_VOL_SIZE)
679 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
680 if (alp->volattr & ATTR_VOL_SPACEFREE)
681 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
682 if (alp->volattr & ATTR_VOL_SPACEAVAIL)
683 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
684 if (alp->volattr & ATTR_VOL_MINALLOCATION)
685 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
686 if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP)
687 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
688 if (alp->volattr & ATTR_VOL_IOBLOCKSIZE)
689 ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
690 if (alp->volattr & ATTR_VOL_OBJCOUNT)
691 ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
692 if (alp->volattr & ATTR_VOL_FILECOUNT)
693 ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
694 if (alp->volattr & ATTR_VOL_DIRCOUNT)
695 ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
696 if (alp->volattr & ATTR_VOL_MAXOBJCOUNT)
697 ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
698 if (alp->volattr & ATTR_VOL_MOUNTPOINT)
699 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
700 if (alp->volattr & ATTR_VOL_NAME)
701 attrlist_pack_string(&ab, vs.f_vol_name, 0);
702 if (alp->volattr & ATTR_VOL_MOUNTFLAGS)
703 ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
704 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
705 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
706 if (alp->volattr & ATTR_VOL_ENCODINGSUSED)
707 ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */
708 if (alp->volattr & ATTR_VOL_CAPABILITIES) {
709 /* fix up volume capabilities */
710 if (vfs_extendedsecurity(mnt)) {
711 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
712 } else {
713 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
714 }
715 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
716 ATTR_PACK(&ab, vs.f_capabilities);
717 }
718 if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
719 /* fix up volume attribute information */
720 if (vfs_extendedsecurity(mnt)) {
721 vs.f_attributes.validattr.commonattr |= (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
722 } else {
723 vs.f_attributes.validattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
724 vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
725 }
726 ATTR_PACK(&ab, vs.f_attributes);
727 }
728
729 /* diagnostic */
730 if ((ab.fixedcursor - ab.base) != fixedsize)
731 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
732 fixedsize, ab.fixedcursor - ab.base, alp->commonattr, alp->volattr);
733 if (ab.varcursor != (ab.base + ab.needed))
734 panic("packed variable field size mismatch; used %d but expected %d", ab.varcursor - ab.base, ab.needed);
735
736 /*
737 * In the compatible case, we report the smaller of the required and returned sizes.
738 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
739 * of the result buffer, even if we copied less out. The caller knows how big a buffer
740 * they gave us, so they can always check for truncation themselves.
741 */
742 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
743
744 error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
745
746 out:
747 if (vs.f_vol_name != NULL)
748 kfree(vs.f_vol_name, MAXPATHLEN);
749 if (ab.base != NULL)
750 FREE(ab.base, M_TEMP);
751 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
752 return(error);
753 }
754
755 /*
756 * Obtain attribute information about a filesystem object.
757 */
758 int
759 getattrlist(struct proc *p, struct getattrlist_args *uap, __unused register_t *retval)
760 {
761 struct attrlist al;
762 struct vnode_attr va;
763 struct vfs_context context, *ctx;
764 struct nameidata nd;
765 struct _attrlist_buf ab;
766 vnode_t vp;
767 u_long nameiflags;
768 kauth_action_t action;
769 ssize_t fixedsize, varsize;
770 const char *cnp;
771 char *vname = NULL;
772 ssize_t cnl;
773 int error;
774
775 context.vc_proc = p;
776 context.vc_ucred = kauth_cred_get();
777 ctx = &context;
778 vp = NULL;
779 error = 0;
780 VATTR_INIT(&va);
781 va.va_name = NULL;
782 ab.base = NULL;
783 cnp = "unknown";
784 cnl = 0;
785
786 /*
787 * Look up the file.
788 */
789 nameiflags = AUDITVNPATH1;
790 if (!(uap->options & FSOPT_NOFOLLOW))
791 nameiflags |= FOLLOW;
792 NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, &context);
793
794 if ((error = namei(&nd)) != 0)
795 goto out;
796 vp = nd.ni_vp;
797 nameidone(&nd);
798
799 /*
800 * Fetch the attribute request.
801 */
802 if ((error = copyin(uap->alist, &al, sizeof(al))) != 0)
803 goto out;
804 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
805 error = EINVAL;
806 goto out;
807 }
808
809 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
810 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
811 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
812
813 /*
814 * It is legal to request volume or file attributes,
815 * but not both.
816 */
817 if (al.volattr) {
818 if (al.fileattr || al.dirattr || al.forkattr) {
819 error = EINVAL;
820 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
821 goto out;
822 }
823 /* handle volume attribute request */
824 error = getvolattrlist(vp, uap, &al, &context, proc_is64bit(p));
825 goto out;
826 }
827
828 /*
829 * Set up the vnode_attr structure and authorise.
830 */
831 if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64bit(p), vnode_isdir(vp))) != 0) {
832 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
833 goto out;
834 }
835 if ((error = vnode_authorize(vp, NULL, action, &context)) != 0) {
836 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
837 goto out;
838 }
839
840 if (va.va_active != 0) {
841 /*
842 * If we're going to ask for va_name, allocate a buffer to point it at
843 */
844 if (VATTR_IS_ACTIVE(&va, va_name)) {
845 va.va_name = (char *) kalloc(MAXPATHLEN);
846 if (va.va_name == NULL) {
847 error = ENOMEM;
848 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
849 goto out;
850 }
851 }
852
853 /*
854 * Call the filesystem.
855 */
856 if ((error = vnode_getattr(vp, &va, &context)) != 0) {
857 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
858 goto out;
859 }
860
861 /* did we ask for something the filesystem doesn't support? */
862 if (!VATTR_ALL_SUPPORTED(&va)) {
863
864 /*
865 * There are a couple of special cases. If we are after object IDs,
866 * we can make do with va_fileid.
867 */
868 if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
869 VATTR_CLEAR_ACTIVE(&va, va_linkid); /* forget we wanted this */
870 /*
871 * Many (most?) filesystems don't know their parent object id. We can get it the
872 * hard way.
873 */
874 if ((al.commonattr & ATTR_CMN_PAROBJID) && !VATTR_IS_SUPPORTED(&va, va_parentid))
875 VATTR_CLEAR_ACTIVE(&va, va_parentid);
876 /*
877 * And we can report datasize/alloc from total.
878 */
879 if ((al.fileattr & ATTR_FILE_DATALENGTH) && !VATTR_IS_SUPPORTED(&va, va_data_size))
880 VATTR_CLEAR_ACTIVE(&va, va_data_size);
881 if ((al.fileattr & ATTR_FILE_DATAALLOCSIZE) && !VATTR_IS_SUPPORTED(&va, va_data_alloc))
882 VATTR_CLEAR_ACTIVE(&va, va_data_alloc);
883
884 /*
885 * If we don't have an encoding, go with UTF-8
886 */
887 if ((al.commonattr & ATTR_CMN_SCRIPT) && !VATTR_IS_SUPPORTED(&va, va_encoding))
888 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
889
890 /*
891 * If we don't have a name, we'll get one from the vnode or mount point.
892 */
893 if ((al.commonattr & ATTR_CMN_NAME) && !VATTR_IS_SUPPORTED(&va, va_name)) {
894 VATTR_CLEAR_ACTIVE(&va, va_name);
895 }
896
897 /*
898 * We used to return va_nlink-2 for ATTR_DIR_ENTRYCOUNT. The va_nchildren
899 * field is preferred, but we'll fall back to va_nlink-2 for compatibility
900 * with file systems which haven't adopted va_nchildren. Note: the "- 2"
901 * reflects the "." and ".." entries which are reported via POSIX APIs, but
902 * not via Carbon (since they don't in fact exist in HFS).
903 */
904 if ((al.dirattr & ATTR_DIR_ENTRYCOUNT) && !VATTR_IS_SUPPORTED(&va, va_nchildren) &&
905 VATTR_IS_SUPPORTED(&va, va_nlink)) {
906 VATTR_RETURN(&va, va_nchildren, va.va_nlink - 2);
907 }
908
909 /* check again */
910 if (!VATTR_ALL_SUPPORTED(&va)) {
911 error = ENOTSUP;
912 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested file attributes");
913 VFS_DEBUG(ctx, vp, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
914 va.va_supported, va.va_active, va.va_active & ~va.va_supported);
915 goto out;
916 }
917 }
918 }
919
920 /*
921 * Compute variable-space requirements.
922 */
923 varsize = 0; /* length count */
924 if (al.commonattr & ATTR_CMN_NAME) {
925 if (VATTR_IS_SUPPORTED(&va, va_name)) {
926 va.va_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
927 cnp = va.va_name;
928 cnl = strlen(cnp);
929 } else {
930 if (vnode_isvroot(vp)) {
931 if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
932 vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
933 /* special case for boot volume. Use root name when it's
934 * available (which is the volume name) or just the mount on
935 * name of "/". we must do this for binary compatibility with
936 * pre Tiger code. returning nothing for the boot volume name
937 * breaks installers - 3961058
938 */
939 cnp = vname = vnode_getname(vp);
940 if (cnp == NULL) {
941 /* just use "/" as name */
942 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
943 }
944 cnl = strlen(cnp);
945 }
946 else {
947 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
948 }
949 } else {
950 cnp = vname = vnode_getname(vp);
951 cnl = 0;
952 if (cnp != NULL) {
953 cnl = strlen(cnp);
954 }
955 }
956 }
957 varsize += roundup(cnl + 1, 4);
958 }
959
960 /*
961 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
962 *
963 * XXX This needs to change at some point; since the blob is opaque in
964 * user-space this is OK.
965 */
966 if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
967 VATTR_IS_SUPPORTED(&va, va_acl) &&
968 (va.va_acl != NULL))
969 varsize += roundup(KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount), 4);
970
971 /*
972 * Allocate a target buffer for attribute results.
973 * Note that since we won't ever copy out more than the caller requested,
974 * we never need to allocate more than they offer.
975 */
976 ab.allocated = imin(uap->bufferSize, fixedsize + varsize);
977 if (ab.allocated > ATTR_MAX_BUFFER) {
978 error = ENOMEM;
979 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
980 goto out;
981 }
982 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
983 if (ab.base == NULL) {
984 error = ENOMEM;
985 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
986 goto out;
987 }
988
989 /*
990 * Pack results into the destination buffer.
991 */
992 ab.fixedcursor = ab.base + sizeof(uint32_t);
993 ab.varcursor = ab.base + fixedsize;
994 ab.needed = fixedsize + varsize;
995
996 /* common attributes **************************************************/
997 if (al.commonattr & ATTR_CMN_NAME)
998 attrlist_pack_string(&ab, cnp, cnl);
999 if (al.commonattr & ATTR_CMN_DEVID)
1000 ATTR_PACK_CAST(&ab, dev_t, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
1001 if (al.commonattr & ATTR_CMN_FSID)
1002 ATTR_PACK(&ab, vp->v_mount->mnt_vfsstat.f_fsid);
1003 if (al.commonattr & ATTR_CMN_OBJTYPE)
1004 ATTR_PACK_CAST(&ab, fsobj_type_t, vp->v_type);
1005 if (al.commonattr & ATTR_CMN_OBJTAG)
1006 ATTR_PACK_CAST(&ab, fsobj_tag_t, vp->v_tag);
1007 if (al.commonattr & ATTR_CMN_OBJID) {
1008 fsobj_id_t f;
1009 /*
1010 * Carbon can't deal with us reporting the target ID
1011 * for links. So we ask the filesystem to give us the
1012 * source ID as well, and if it gives us one, we use
1013 * it instead.
1014 */
1015 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1016 f.fid_objno = va.va_linkid;
1017 } else {
1018 f.fid_objno = va.va_fileid;
1019 }
1020 f.fid_generation = 0;
1021 ATTR_PACK(&ab, f);
1022 }
1023 if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
1024 fsobj_id_t f;
1025 /*
1026 * Carbon can't deal with us reporting the target ID
1027 * for links. So we ask the filesystem to give us the
1028 * source ID as well, and if it gives us one, we use
1029 * it instead.
1030 */
1031 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1032 f.fid_objno = va.va_linkid;
1033 } else {
1034 f.fid_objno = va.va_fileid;
1035 }
1036 f.fid_generation = 0;
1037 ATTR_PACK(&ab, f);
1038 }
1039 if (al.commonattr & ATTR_CMN_PAROBJID) {
1040 fsobj_id_t f;
1041 /*
1042 * If the filesystem doesn't know the parent ID, we can
1043 * try to get it via v->v_parent. Don't need to worry
1044 * about links here, as we dont allow hardlinks to
1045 * directories.
1046 */
1047 if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
1048 f.fid_objno = va.va_parentid;
1049 } else {
1050 struct vnode_attr lva;
1051 vnode_t pvp;
1052
1053 pvp = vnode_getparent(vp);
1054
1055 if (pvp == NULLVP) {
1056 error = ENOTSUP;
1057 goto out;
1058 }
1059 VATTR_INIT(&lva);
1060 VATTR_WANTED(&lva, va_fileid);
1061 error = vnode_getattr(pvp, &lva, &context);
1062 vnode_put(pvp);
1063
1064 if (error != 0)
1065 goto out;
1066 f.fid_objno = lva.va_fileid;
1067 }
1068 f.fid_generation = 0;
1069 ATTR_PACK(&ab, f);
1070 }
1071 if (al.commonattr & ATTR_CMN_SCRIPT)
1072 ATTR_PACK_CAST(&ab, text_encoding_t, va.va_encoding);
1073 if (al.commonattr & ATTR_CMN_CRTIME)
1074 ATTR_PACK_TIME(&ab, va.va_create_time, proc_is64bit(p));
1075 if (al.commonattr & ATTR_CMN_MODTIME)
1076 ATTR_PACK_TIME(&ab, va.va_modify_time, proc_is64bit(p));
1077 if (al.commonattr & ATTR_CMN_CHGTIME)
1078 ATTR_PACK_TIME(&ab, va.va_change_time, proc_is64bit(p));
1079 if (al.commonattr & ATTR_CMN_ACCTIME)
1080 ATTR_PACK_TIME(&ab, va.va_access_time, proc_is64bit(p));
1081 if (al.commonattr & ATTR_CMN_BKUPTIME)
1082 ATTR_PACK_TIME(&ab, va.va_backup_time, proc_is64bit(p));
1083 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1084 uio_t auio;
1085 size_t fisize;
1086 char uio_buf[UIO_SIZEOF(1)];
1087
1088 fisize = imin(32, ab.allocated - (ab.fixedcursor - ab.base));
1089 if (fisize > 0) {
1090 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
1091 error = ENOMEM;
1092 goto out;
1093 } else {
1094 uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
1095 error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, &fisize, XATTR_NOSECURITY, &context);
1096 uio_free(auio);
1097 }
1098 if (error != 0) {
1099 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
1100 VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1101 bzero(ab.fixedcursor, 32);
1102 error = 0;
1103 } else {
1104 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1105 goto out;
1106 }
1107 }
1108 } else {
1109 VFS_DEBUG(ctx, vp, "ATTRLIST - no room in caller buffer for FINDERINFO");
1110 }
1111 ab.fixedcursor += 32;
1112 }
1113 if (al.commonattr & ATTR_CMN_OWNERID)
1114 ATTR_PACK(&ab, va.va_uid);
1115 if (al.commonattr & ATTR_CMN_GRPID)
1116 ATTR_PACK(&ab, va.va_gid);
1117 if (al.commonattr & ATTR_CMN_ACCESSMASK)
1118 ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
1119 if (al.commonattr & ATTR_CMN_FLAGS)
1120 ATTR_PACK(&ab, va.va_flags);
1121 if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
1122 uint32_t perms = 0;
1123 if (vnode_isdir(vp)) {
1124 if (vnode_authorize(vp, NULL,
1125 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, &context) == 0)
1126 perms |= W_OK;
1127 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, &context) == 0)
1128 perms |= R_OK;
1129 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, &context) == 0)
1130 perms |= X_OK;
1131 } else {
1132 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, &context) == 0)
1133 perms |= W_OK;
1134 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, &context) == 0)
1135 perms |= R_OK;
1136 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, &context) == 0)
1137 perms |= X_OK;
1138 }
1139 VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
1140 ATTR_PACK(&ab, perms);
1141 }
1142 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1143 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
1144 struct kauth_filesec fsec;
1145 /*
1146 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1147 */
1148 fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
1149 fsec.fsec_owner = kauth_null_guid;
1150 fsec.fsec_group = kauth_null_guid;
1151 attrlist_pack_variable2(&ab, &fsec, ((char *)&fsec.fsec_acl - (char *)&fsec), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
1152 } else {
1153 attrlist_pack_variable(&ab, NULL, 0);
1154 }
1155 }
1156 if (al.commonattr & ATTR_CMN_UUID) {
1157 if (!VATTR_IS_SUPPORTED(&va, va_uuuid)) {
1158 ATTR_PACK(&ab, kauth_null_guid);
1159 } else {
1160 ATTR_PACK(&ab, va.va_uuuid);
1161 }
1162 }
1163 if (al.commonattr & ATTR_CMN_GRPUUID) {
1164 if (!VATTR_IS_SUPPORTED(&va, va_guuid)) {
1165 ATTR_PACK(&ab, kauth_null_guid);
1166 } else {
1167 ATTR_PACK(&ab, va.va_guuid);
1168 }
1169 }
1170
1171 /* directory attributes **************************************************/
1172 if (vnode_isdir(vp)) {
1173 if (al.dirattr & ATTR_DIR_LINKCOUNT) /* full count of entries */
1174 ATTR_PACK_CAST(&ab, uint32_t, va.va_nlink);
1175 if (al.dirattr & ATTR_DIR_ENTRYCOUNT)
1176 ATTR_PACK_CAST(&ab, uint32_t, va.va_nchildren);
1177 if (al.dirattr & ATTR_DIR_MOUNTSTATUS)
1178 ATTR_PACK_CAST(&ab, uint32_t, (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0);
1179 }
1180
1181 /* file attributes **************************************************/
1182 if (!vnode_isdir(vp)) {
1183 if (al.fileattr & ATTR_FILE_LINKCOUNT)
1184 ATTR_PACK_CAST(&ab, uint32_t, va.va_nlink);
1185 if (al.fileattr & ATTR_FILE_TOTALSIZE)
1186 ATTR_PACK(&ab, va.va_total_size);
1187 if (al.fileattr & ATTR_FILE_ALLOCSIZE)
1188 ATTR_PACK(&ab, va.va_total_alloc);
1189 if (al.fileattr & ATTR_FILE_IOBLOCKSIZE)
1190 ATTR_PACK(&ab, va.va_iosize);
1191 if (al.fileattr & ATTR_FILE_CLUMPSIZE)
1192 ATTR_PACK_CAST(&ab, uint32_t, 0); /* XXX value is deprecated */
1193 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1194 if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
1195 ATTR_PACK(&ab, vp->v_specinfo->si_rdev);
1196 } else {
1197 ATTR_PACK_CAST(&ab, uint32_t, 0);
1198 }
1199 }
1200 if (al.fileattr & ATTR_FILE_DATALENGTH) {
1201 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
1202 ATTR_PACK(&ab, va.va_data_size);
1203 } else {
1204 ATTR_PACK(&ab, va.va_total_size);
1205 }
1206 }
1207 if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
1208 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
1209 ATTR_PACK(&ab, va.va_data_alloc);
1210 } else {
1211 ATTR_PACK(&ab, va.va_total_alloc);
1212 }
1213 }
1214 /* fetch resource fork size/allocation via xattr interface */
1215 if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
1216 size_t rsize;
1217 if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, &context)) != 0) {
1218 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
1219 rsize = 0;
1220 error = 0;
1221 } else {
1222 goto out;
1223 }
1224 }
1225 if (al.fileattr & ATTR_FILE_RSRCLENGTH)
1226 ATTR_PACK_CAST(&ab, off_t, rsize);
1227 if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
1228 uint32_t blksize = vp->v_mount->mnt_vfsstat.f_bsize;
1229 if (blksize == 0)
1230 blksize = 512;
1231 ATTR_PACK_CAST(&ab, off_t, (roundup(rsize, blksize)));
1232 }
1233 }
1234 }
1235
1236 /* diagnostic */
1237 if ((ab.fixedcursor - ab.base) != fixedsize)
1238 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
1239 fixedsize, ab.fixedcursor - ab.base, al.commonattr, al.volattr);
1240 if (ab.varcursor != (ab.base + ab.needed))
1241 panic("packed variable field size mismatch; used %d but expected %d", ab.varcursor - ab.base, ab.needed);
1242
1243 /*
1244 * In the compatible case, we report the smaller of the required and returned sizes.
1245 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1246 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1247 * they gave us, so they can always check for truncation themselves.
1248 */
1249 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
1250
1251 error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
1252
1253 out:
1254 if (va.va_name)
1255 kfree(va.va_name, MAXPATHLEN);
1256 if (vname)
1257 vnode_putname(vname);
1258 if (vp)
1259 vnode_put(vp);
1260 if (ab.base != NULL)
1261 FREE(ab.base, M_TEMP);
1262 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
1263 kauth_acl_free(va.va_acl);
1264
1265 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1266 return(error);
1267 }
1268
1269 static int
1270 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
1271 {
1272 /* make sure we have enough source data */
1273 if ((*cursor) + size > end)
1274 return(EINVAL);
1275
1276 bcopy(*cursor, buf, size);
1277 *cursor += size;
1278 return(0);
1279 }
1280
1281 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1282 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1283 #define ATTR_UNPACK_TIME(v, is64) \
1284 do { \
1285 if (is64) { \
1286 struct user_timespec us; \
1287 ATTR_UNPACK(us); \
1288 v.tv_sec = us.tv_sec; \
1289 v.tv_nsec = us.tv_nsec; \
1290 } else { \
1291 ATTR_UNPACK(v); \
1292 } \
1293 } while(0)
1294
1295
1296 /*
1297 * Write attributes.
1298 */
1299 int
1300 setattrlist(struct proc *p, register struct setattrlist_args *uap, __unused register_t *retval)
1301 {
1302 struct attrlist al;
1303 struct vfs_context context, *ctx;
1304 struct vnode_attr va;
1305 struct attrreference ar;
1306 struct nameidata nd;
1307 vnode_t vp;
1308 u_long nameiflags;
1309 kauth_action_t action;
1310 char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
1311 int proc_is64, error;
1312 uint32_t nace;
1313 kauth_filesec_t rfsec;
1314
1315 context.vc_proc = p;
1316 context.vc_ucred = kauth_cred_get();
1317 ctx = &context;
1318 vp = NULL;
1319 user_buf = NULL;
1320 fndrinfo = NULL;
1321 volname = NULL;
1322 error = 0;
1323 proc_is64 = proc_is64bit(p);
1324 VATTR_INIT(&va);
1325
1326
1327 /*
1328 * Look up the file.
1329 */
1330 nameiflags = 0;
1331 if ((uap->options & FSOPT_NOFOLLOW) == 0)
1332 nameiflags |= FOLLOW;
1333 NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, uap->path, &context);
1334 if ((error = namei(&nd)) != 0)
1335 goto out;
1336 vp = nd.ni_vp;
1337 nameidone(&nd);
1338
1339 /*
1340 * Fetch the attribute set and validate.
1341 */
1342 if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
1343 goto out;
1344 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
1345 error = EINVAL;
1346 goto out;
1347 }
1348
1349 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1350 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
1351 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
1352
1353 if (al.volattr) {
1354 if ((al.volattr & ~ATTR_VOL_SETMASK) ||
1355 (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
1356 al.fileattr ||
1357 al.forkattr) {
1358 error = EINVAL;
1359 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1360 goto out;
1361 }
1362 } else {
1363 if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
1364 (al.fileattr & ~ATTR_FILE_SETMASK) ||
1365 (al.dirattr & ~ATTR_DIR_SETMASK) ||
1366 (al.forkattr & ~ATTR_FORK_SETMASK)) {
1367 error = EINVAL;
1368 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1369 goto out;
1370 }
1371 }
1372
1373 /*
1374 * Make the naive assumption that the caller has supplied a reasonable buffer
1375 * size. We could be more careful by pulling in the fixed-size region, checking
1376 * the attrref structures, then pulling in the variable section.
1377 * We need to reconsider this for handling large ACLs, as they should probably be
1378 * brought directly into a buffer. Multiple copyins will make this slower though.
1379 *
1380 * We could also map the user buffer if it is larger than some sensible mimimum.
1381 */
1382 if (uap->bufferSize > ATTR_MAX_BUFFER) {
1383 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
1384 error = ENOMEM;
1385 goto out;
1386 }
1387 MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
1388 if (user_buf == NULL) {
1389 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
1390 error = ENOMEM;
1391 goto out;
1392 }
1393 if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
1394 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
1395 goto out;
1396 }
1397 VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
1398
1399 /*
1400 * Unpack the argument buffer.
1401 */
1402 cursor = user_buf;
1403 bufend = cursor + uap->bufferSize;
1404
1405 /* common */
1406 if (al.commonattr & ATTR_CMN_SCRIPT) {
1407 ATTR_UNPACK(va.va_encoding);
1408 VATTR_SET_ACTIVE(&va, va_encoding);
1409 }
1410 if (al.commonattr & ATTR_CMN_CRTIME) {
1411 ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
1412 VATTR_SET_ACTIVE(&va, va_create_time);
1413 }
1414 if (al.commonattr & ATTR_CMN_MODTIME) {
1415 ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
1416 VATTR_SET_ACTIVE(&va, va_modify_time);
1417 }
1418 if (al.commonattr & ATTR_CMN_CHGTIME) {
1419 ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
1420 VATTR_SET_ACTIVE(&va, va_change_time);
1421 }
1422 if (al.commonattr & ATTR_CMN_ACCTIME) {
1423 ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
1424 VATTR_SET_ACTIVE(&va, va_access_time);
1425 }
1426 if (al.commonattr & ATTR_CMN_BKUPTIME) {
1427 ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
1428 VATTR_SET_ACTIVE(&va, va_backup_time);
1429 }
1430 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1431 if ((cursor + 32) > bufend) {
1432 error = EINVAL;
1433 VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
1434 goto out;
1435 }
1436 fndrinfo = cursor;
1437 cursor += 32;
1438 }
1439 if (al.commonattr & ATTR_CMN_OWNERID) {
1440 ATTR_UNPACK(va.va_uid);
1441 VATTR_SET_ACTIVE(&va, va_uid);
1442 }
1443 if (al.commonattr & ATTR_CMN_GRPID) {
1444 ATTR_UNPACK(va.va_gid);
1445 VATTR_SET_ACTIVE(&va, va_gid);
1446 }
1447 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1448 ATTR_UNPACK_CAST(uint32_t, va.va_mode);
1449 VATTR_SET_ACTIVE(&va, va_mode);
1450 }
1451 if (al.commonattr & ATTR_CMN_FLAGS) {
1452 ATTR_UNPACK(va.va_flags);
1453 VATTR_SET_ACTIVE(&va, va_flags);
1454 }
1455 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1456
1457 /*
1458 * We are (for now) passed a kauth_filesec_t, but all we want from
1459 * it is the ACL.
1460 */
1461 cp = cursor;
1462 ATTR_UNPACK(ar);
1463 cp += ar.attr_dataoffset;
1464 rfsec = (kauth_filesec_t)cp;
1465 if (((char *)(rfsec + 1) > bufend) || /* no space for acl */
1466 (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
1467 (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
1468 ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
1469 error = EINVAL;
1470 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
1471 goto out;
1472 }
1473 nace = rfsec->fsec_entrycount;
1474 if (nace == KAUTH_FILESEC_NOACL)
1475 nace = 0;
1476 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1477 error = EINVAL;
1478 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
1479 goto out;
1480 }
1481 nace = rfsec->fsec_acl.acl_entrycount;
1482 if (nace == KAUTH_FILESEC_NOACL) {
1483 /* deleting ACL */
1484 VATTR_SET(&va, va_acl, NULL);
1485 } else {
1486
1487 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1488 error = EINVAL;
1489 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
1490 goto out;
1491 }
1492 VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
1493 }
1494 }
1495 if (al.commonattr & ATTR_CMN_UUID) {
1496 ATTR_UNPACK(va.va_uuuid);
1497 VATTR_SET_ACTIVE(&va, va_uuuid);
1498 }
1499 if (al.commonattr & ATTR_CMN_GRPUUID) {
1500 ATTR_UNPACK(va.va_guuid);
1501 VATTR_SET_ACTIVE(&va, va_guuid);
1502 }
1503
1504 /* volume */
1505 if (al.volattr & ATTR_VOL_INFO) {
1506 if (al.volattr & ATTR_VOL_NAME) {
1507 volname = cursor;
1508 ATTR_UNPACK(ar);
1509 volname += ar.attr_dataoffset;
1510 if ((volname + ar.attr_length) > bufend) {
1511 error = EINVAL;
1512 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
1513 goto out;
1514 }
1515 /* guarantee NUL termination */
1516 volname[ar.attr_length - 1] = 0;
1517 }
1518 }
1519
1520 /* file */
1521 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1522 /* XXX does it actually make any sense to change this? */
1523 error = EINVAL;
1524 VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
1525 goto out;
1526 }
1527
1528 /*
1529 * Validate and authorize.
1530 */
1531 action = 0;
1532 if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, &context)) != 0)) {
1533 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
1534 goto out;
1535 }
1536 /*
1537 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1538 * and will be auth'ed by the FS.
1539 */
1540 if (fndrinfo != NULL) {
1541 if (al.volattr & ATTR_VOL_INFO) {
1542 if (vp->v_tag != VT_HFS) {
1543 error = EINVAL;
1544 goto out;
1545 }
1546 } else {
1547 action |= KAUTH_VNODE_WRITE_ATTRIBUTES;
1548 }
1549 }
1550
1551 if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
1552 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
1553 goto out;
1554 }
1555
1556 /*
1557 * Write the attributes if we have any.
1558 */
1559 if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, &context)) != 0)) {
1560 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1561 goto out;
1562 }
1563
1564 /*
1565 * Write the Finder Info if we have any.
1566 */
1567 if (fndrinfo != NULL) {
1568 if (al.volattr & ATTR_VOL_INFO) {
1569 if (vp->v_tag == VT_HFS) {
1570 error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, &context);
1571 if (error != 0)
1572 goto out;
1573 } else {
1574 /* XXX should never get here */
1575 }
1576 } else {
1577 /* write Finder Info EA */
1578 uio_t auio;
1579 char uio_buf[UIO_SIZEOF(1)];
1580
1581 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
1582 error = ENOMEM;
1583 } else {
1584 uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
1585 error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, &context);
1586 uio_free(auio);
1587 }
1588
1589 if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
1590 add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
1591 }
1592
1593 if (error != 0) {
1594 goto out;
1595 }
1596 }
1597 }
1598
1599 /*
1600 * Set the volume name, if we have one
1601 */
1602 if (volname != NULL)
1603 {
1604 struct vfs_attr vs;
1605
1606 VFSATTR_INIT(&vs);
1607
1608 vs.f_vol_name = volname; /* References the setattrlist buffer directly */
1609 VFSATTR_WANTED(&vs, f_vol_name);
1610
1611 if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
1612 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
1613 goto out;
1614 }
1615
1616 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
1617 error = EINVAL;
1618 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
1619 goto out;
1620 }
1621 }
1622
1623 /* all done and successful */
1624
1625 out:
1626 if (vp != NULL)
1627 vnode_put(vp);
1628 if (user_buf != NULL)
1629 FREE(user_buf, M_TEMP);
1630 VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
1631 return(error);
1632 }