]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_attrlist.c
xnu-792.10.96.tar.gz
[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 *
974 * Note that we won't ever copy out more than the caller requested, even though
975 * we might have to allocate more than they offer do that the diagnostic checks
976 * don't result in a panic if the caller's buffer is too small..
977 */
978 ab.allocated = fixedsize + varsize;
979 if (ab.allocated > ATTR_MAX_BUFFER) {
980 error = ENOMEM;
981 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
982 goto out;
983 }
984 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
985 if (ab.base == NULL) {
986 error = ENOMEM;
987 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
988 goto out;
989 }
990
991 /*
992 * Pack results into the destination buffer.
993 */
994 ab.fixedcursor = ab.base + sizeof(uint32_t);
995 ab.varcursor = ab.base + fixedsize;
996 ab.needed = ab.allocated;
997
998 /* common attributes **************************************************/
999 if (al.commonattr & ATTR_CMN_NAME)
1000 attrlist_pack_string(&ab, cnp, cnl);
1001 if (al.commonattr & ATTR_CMN_DEVID)
1002 ATTR_PACK_CAST(&ab, dev_t, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
1003 if (al.commonattr & ATTR_CMN_FSID)
1004 ATTR_PACK(&ab, vp->v_mount->mnt_vfsstat.f_fsid);
1005 if (al.commonattr & ATTR_CMN_OBJTYPE)
1006 ATTR_PACK_CAST(&ab, fsobj_type_t, vp->v_type);
1007 if (al.commonattr & ATTR_CMN_OBJTAG)
1008 ATTR_PACK_CAST(&ab, fsobj_tag_t, vp->v_tag);
1009 if (al.commonattr & ATTR_CMN_OBJID) {
1010 fsobj_id_t f;
1011 /*
1012 * Carbon can't deal with us reporting the target ID
1013 * for links. So we ask the filesystem to give us the
1014 * source ID as well, and if it gives us one, we use
1015 * it instead.
1016 */
1017 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1018 f.fid_objno = va.va_linkid;
1019 } else {
1020 f.fid_objno = va.va_fileid;
1021 }
1022 f.fid_generation = 0;
1023 ATTR_PACK(&ab, f);
1024 }
1025 if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
1026 fsobj_id_t f;
1027 /*
1028 * Carbon can't deal with us reporting the target ID
1029 * for links. So we ask the filesystem to give us the
1030 * source ID as well, and if it gives us one, we use
1031 * it instead.
1032 */
1033 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1034 f.fid_objno = va.va_linkid;
1035 } else {
1036 f.fid_objno = va.va_fileid;
1037 }
1038 f.fid_generation = 0;
1039 ATTR_PACK(&ab, f);
1040 }
1041 if (al.commonattr & ATTR_CMN_PAROBJID) {
1042 fsobj_id_t f;
1043 /*
1044 * If the filesystem doesn't know the parent ID, we can
1045 * try to get it via v->v_parent. Don't need to worry
1046 * about links here, as we dont allow hardlinks to
1047 * directories.
1048 */
1049 if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
1050 f.fid_objno = va.va_parentid;
1051 } else {
1052 struct vnode_attr lva;
1053 vnode_t pvp;
1054
1055 pvp = vnode_getparent(vp);
1056
1057 if (pvp == NULLVP) {
1058 error = ENOTSUP;
1059 goto out;
1060 }
1061 VATTR_INIT(&lva);
1062 VATTR_WANTED(&lva, va_fileid);
1063 error = vnode_getattr(pvp, &lva, &context);
1064 vnode_put(pvp);
1065
1066 if (error != 0)
1067 goto out;
1068 f.fid_objno = lva.va_fileid;
1069 }
1070 f.fid_generation = 0;
1071 ATTR_PACK(&ab, f);
1072 }
1073 if (al.commonattr & ATTR_CMN_SCRIPT)
1074 ATTR_PACK_CAST(&ab, text_encoding_t, va.va_encoding);
1075 if (al.commonattr & ATTR_CMN_CRTIME)
1076 ATTR_PACK_TIME(&ab, va.va_create_time, proc_is64bit(p));
1077 if (al.commonattr & ATTR_CMN_MODTIME)
1078 ATTR_PACK_TIME(&ab, va.va_modify_time, proc_is64bit(p));
1079 if (al.commonattr & ATTR_CMN_CHGTIME)
1080 ATTR_PACK_TIME(&ab, va.va_change_time, proc_is64bit(p));
1081 if (al.commonattr & ATTR_CMN_ACCTIME)
1082 ATTR_PACK_TIME(&ab, va.va_access_time, proc_is64bit(p));
1083 if (al.commonattr & ATTR_CMN_BKUPTIME)
1084 ATTR_PACK_TIME(&ab, va.va_backup_time, proc_is64bit(p));
1085 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1086 uio_t auio;
1087 size_t fisize;
1088 char uio_buf[UIO_SIZEOF(1)];
1089
1090 fisize = imin(32, ab.allocated - (ab.fixedcursor - ab.base));
1091 if (fisize > 0) {
1092 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
1093 error = ENOMEM;
1094 goto out;
1095 } else {
1096 uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
1097 error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, &fisize, XATTR_NOSECURITY, &context);
1098 uio_free(auio);
1099 }
1100 if (error != 0) {
1101 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
1102 VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1103 bzero(ab.fixedcursor, 32);
1104 error = 0;
1105 } else {
1106 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1107 goto out;
1108 }
1109 }
1110 } else {
1111 VFS_DEBUG(ctx, vp, "ATTRLIST - no room in caller buffer for FINDERINFO");
1112 }
1113 ab.fixedcursor += 32;
1114 }
1115 if (al.commonattr & ATTR_CMN_OWNERID)
1116 ATTR_PACK(&ab, va.va_uid);
1117 if (al.commonattr & ATTR_CMN_GRPID)
1118 ATTR_PACK(&ab, va.va_gid);
1119 if (al.commonattr & ATTR_CMN_ACCESSMASK)
1120 ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
1121 if (al.commonattr & ATTR_CMN_FLAGS)
1122 ATTR_PACK(&ab, va.va_flags);
1123 if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
1124 uint32_t perms = 0;
1125 if (vnode_isdir(vp)) {
1126 if (vnode_authorize(vp, NULL,
1127 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, &context) == 0)
1128 perms |= W_OK;
1129 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, &context) == 0)
1130 perms |= R_OK;
1131 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, &context) == 0)
1132 perms |= X_OK;
1133 } else {
1134 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, &context) == 0)
1135 perms |= W_OK;
1136 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, &context) == 0)
1137 perms |= R_OK;
1138 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, &context) == 0)
1139 perms |= X_OK;
1140 }
1141 VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
1142 ATTR_PACK(&ab, perms);
1143 }
1144 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1145 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
1146 struct kauth_filesec fsec;
1147 /*
1148 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1149 */
1150 fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
1151 fsec.fsec_owner = kauth_null_guid;
1152 fsec.fsec_group = kauth_null_guid;
1153 attrlist_pack_variable2(&ab, &fsec, ((char *)&fsec.fsec_acl - (char *)&fsec), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
1154 } else {
1155 attrlist_pack_variable(&ab, NULL, 0);
1156 }
1157 }
1158 if (al.commonattr & ATTR_CMN_UUID) {
1159 if (!VATTR_IS_SUPPORTED(&va, va_uuuid)) {
1160 ATTR_PACK(&ab, kauth_null_guid);
1161 } else {
1162 ATTR_PACK(&ab, va.va_uuuid);
1163 }
1164 }
1165 if (al.commonattr & ATTR_CMN_GRPUUID) {
1166 if (!VATTR_IS_SUPPORTED(&va, va_guuid)) {
1167 ATTR_PACK(&ab, kauth_null_guid);
1168 } else {
1169 ATTR_PACK(&ab, va.va_guuid);
1170 }
1171 }
1172
1173 /* directory attributes **************************************************/
1174 if (vnode_isdir(vp)) {
1175 if (al.dirattr & ATTR_DIR_LINKCOUNT) /* full count of entries */
1176 ATTR_PACK_CAST(&ab, uint32_t, va.va_nlink);
1177 if (al.dirattr & ATTR_DIR_ENTRYCOUNT)
1178 ATTR_PACK_CAST(&ab, uint32_t, va.va_nchildren);
1179 if (al.dirattr & ATTR_DIR_MOUNTSTATUS)
1180 ATTR_PACK_CAST(&ab, uint32_t, (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0);
1181 }
1182
1183 /* file attributes **************************************************/
1184 if (!vnode_isdir(vp)) {
1185 if (al.fileattr & ATTR_FILE_LINKCOUNT)
1186 ATTR_PACK_CAST(&ab, uint32_t, va.va_nlink);
1187 if (al.fileattr & ATTR_FILE_TOTALSIZE)
1188 ATTR_PACK(&ab, va.va_total_size);
1189 if (al.fileattr & ATTR_FILE_ALLOCSIZE)
1190 ATTR_PACK(&ab, va.va_total_alloc);
1191 if (al.fileattr & ATTR_FILE_IOBLOCKSIZE)
1192 ATTR_PACK(&ab, va.va_iosize);
1193 if (al.fileattr & ATTR_FILE_CLUMPSIZE)
1194 ATTR_PACK_CAST(&ab, uint32_t, 0); /* XXX value is deprecated */
1195 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1196 if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
1197 ATTR_PACK(&ab, vp->v_specinfo->si_rdev);
1198 } else {
1199 ATTR_PACK_CAST(&ab, uint32_t, 0);
1200 }
1201 }
1202 if (al.fileattr & ATTR_FILE_DATALENGTH) {
1203 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
1204 ATTR_PACK(&ab, va.va_data_size);
1205 } else {
1206 ATTR_PACK(&ab, va.va_total_size);
1207 }
1208 }
1209 if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
1210 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
1211 ATTR_PACK(&ab, va.va_data_alloc);
1212 } else {
1213 ATTR_PACK(&ab, va.va_total_alloc);
1214 }
1215 }
1216 /* fetch resource fork size/allocation via xattr interface */
1217 if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
1218 size_t rsize;
1219 if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, &context)) != 0) {
1220 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
1221 rsize = 0;
1222 error = 0;
1223 } else {
1224 goto out;
1225 }
1226 }
1227 if (al.fileattr & ATTR_FILE_RSRCLENGTH)
1228 ATTR_PACK_CAST(&ab, off_t, rsize);
1229 if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
1230 uint32_t blksize = vp->v_mount->mnt_vfsstat.f_bsize;
1231 if (blksize == 0)
1232 blksize = 512;
1233 ATTR_PACK_CAST(&ab, off_t, (roundup(rsize, blksize)));
1234 }
1235 }
1236 }
1237
1238 /* diagnostic */
1239 if ((ab.fixedcursor - ab.base) != fixedsize)
1240 panic("packed field size mismatch; allocated %d but packed %d for common %08x vol %08x",
1241 fixedsize, ab.fixedcursor - ab.base, al.commonattr, al.volattr);
1242 if (ab.varcursor != (ab.base + ab.needed))
1243 panic("packed variable field size mismatch; used %d but expected %d", ab.varcursor - ab.base, ab.needed);
1244
1245 /*
1246 * In the compatible case, we report the smaller of the required and returned sizes.
1247 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1248 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1249 * they gave us, so they can always check for truncation themselves.
1250 */
1251 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
1252
1253 /* Only actually copyout as much out as the user buffer can hold */
1254 error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
1255
1256 out:
1257 if (va.va_name)
1258 kfree(va.va_name, MAXPATHLEN);
1259 if (vname)
1260 vnode_putname(vname);
1261 if (vp)
1262 vnode_put(vp);
1263 if (ab.base != NULL)
1264 FREE(ab.base, M_TEMP);
1265 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
1266 kauth_acl_free(va.va_acl);
1267
1268 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1269 return(error);
1270 }
1271
1272 static int
1273 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
1274 {
1275 /* make sure we have enough source data */
1276 if ((*cursor) + size > end)
1277 return(EINVAL);
1278
1279 bcopy(*cursor, buf, size);
1280 *cursor += size;
1281 return(0);
1282 }
1283
1284 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1285 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1286 #define ATTR_UNPACK_TIME(v, is64) \
1287 do { \
1288 if (is64) { \
1289 struct user_timespec us; \
1290 ATTR_UNPACK(us); \
1291 v.tv_sec = us.tv_sec; \
1292 v.tv_nsec = us.tv_nsec; \
1293 } else { \
1294 ATTR_UNPACK(v); \
1295 } \
1296 } while(0)
1297
1298
1299 /*
1300 * Write attributes.
1301 */
1302 int
1303 setattrlist(struct proc *p, register struct setattrlist_args *uap, __unused register_t *retval)
1304 {
1305 struct attrlist al;
1306 struct vfs_context context, *ctx;
1307 struct vnode_attr va;
1308 struct attrreference ar;
1309 struct nameidata nd;
1310 vnode_t vp;
1311 u_long nameiflags;
1312 kauth_action_t action;
1313 char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
1314 int proc_is64, error;
1315 uint32_t nace;
1316 kauth_filesec_t rfsec;
1317
1318 context.vc_proc = p;
1319 context.vc_ucred = kauth_cred_get();
1320 ctx = &context;
1321 vp = NULL;
1322 user_buf = NULL;
1323 fndrinfo = NULL;
1324 volname = NULL;
1325 error = 0;
1326 proc_is64 = proc_is64bit(p);
1327 VATTR_INIT(&va);
1328
1329
1330 /*
1331 * Look up the file.
1332 */
1333 nameiflags = 0;
1334 if ((uap->options & FSOPT_NOFOLLOW) == 0)
1335 nameiflags |= FOLLOW;
1336 NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, uap->path, &context);
1337 if ((error = namei(&nd)) != 0)
1338 goto out;
1339 vp = nd.ni_vp;
1340 nameidone(&nd);
1341
1342 /*
1343 * Fetch the attribute set and validate.
1344 */
1345 if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
1346 goto out;
1347 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
1348 error = EINVAL;
1349 goto out;
1350 }
1351
1352 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1353 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
1354 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
1355
1356 if (al.volattr) {
1357 if ((al.volattr & ~ATTR_VOL_SETMASK) ||
1358 (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
1359 al.fileattr ||
1360 al.forkattr) {
1361 error = EINVAL;
1362 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1363 goto out;
1364 }
1365 } else {
1366 if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
1367 (al.fileattr & ~ATTR_FILE_SETMASK) ||
1368 (al.dirattr & ~ATTR_DIR_SETMASK) ||
1369 (al.forkattr & ~ATTR_FORK_SETMASK)) {
1370 error = EINVAL;
1371 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1372 goto out;
1373 }
1374 }
1375
1376 /*
1377 * Make the naive assumption that the caller has supplied a reasonable buffer
1378 * size. We could be more careful by pulling in the fixed-size region, checking
1379 * the attrref structures, then pulling in the variable section.
1380 * We need to reconsider this for handling large ACLs, as they should probably be
1381 * brought directly into a buffer. Multiple copyins will make this slower though.
1382 *
1383 * We could also map the user buffer if it is larger than some sensible mimimum.
1384 */
1385 if (uap->bufferSize > ATTR_MAX_BUFFER) {
1386 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
1387 error = ENOMEM;
1388 goto out;
1389 }
1390 MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
1391 if (user_buf == NULL) {
1392 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
1393 error = ENOMEM;
1394 goto out;
1395 }
1396 if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
1397 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
1398 goto out;
1399 }
1400 VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
1401
1402 /*
1403 * Unpack the argument buffer.
1404 */
1405 cursor = user_buf;
1406 bufend = cursor + uap->bufferSize;
1407
1408 /* common */
1409 if (al.commonattr & ATTR_CMN_SCRIPT) {
1410 ATTR_UNPACK(va.va_encoding);
1411 VATTR_SET_ACTIVE(&va, va_encoding);
1412 }
1413 if (al.commonattr & ATTR_CMN_CRTIME) {
1414 ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
1415 VATTR_SET_ACTIVE(&va, va_create_time);
1416 }
1417 if (al.commonattr & ATTR_CMN_MODTIME) {
1418 ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
1419 VATTR_SET_ACTIVE(&va, va_modify_time);
1420 }
1421 if (al.commonattr & ATTR_CMN_CHGTIME) {
1422 ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
1423 VATTR_SET_ACTIVE(&va, va_change_time);
1424 }
1425 if (al.commonattr & ATTR_CMN_ACCTIME) {
1426 ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
1427 VATTR_SET_ACTIVE(&va, va_access_time);
1428 }
1429 if (al.commonattr & ATTR_CMN_BKUPTIME) {
1430 ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
1431 VATTR_SET_ACTIVE(&va, va_backup_time);
1432 }
1433 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1434 if ((cursor + 32) > bufend) {
1435 error = EINVAL;
1436 VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
1437 goto out;
1438 }
1439 fndrinfo = cursor;
1440 cursor += 32;
1441 }
1442 if (al.commonattr & ATTR_CMN_OWNERID) {
1443 ATTR_UNPACK(va.va_uid);
1444 VATTR_SET_ACTIVE(&va, va_uid);
1445 }
1446 if (al.commonattr & ATTR_CMN_GRPID) {
1447 ATTR_UNPACK(va.va_gid);
1448 VATTR_SET_ACTIVE(&va, va_gid);
1449 }
1450 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1451 ATTR_UNPACK_CAST(uint32_t, va.va_mode);
1452 VATTR_SET_ACTIVE(&va, va_mode);
1453 }
1454 if (al.commonattr & ATTR_CMN_FLAGS) {
1455 ATTR_UNPACK(va.va_flags);
1456 VATTR_SET_ACTIVE(&va, va_flags);
1457 }
1458 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1459
1460 /*
1461 * We are (for now) passed a kauth_filesec_t, but all we want from
1462 * it is the ACL.
1463 */
1464 cp = cursor;
1465 ATTR_UNPACK(ar);
1466 cp += ar.attr_dataoffset;
1467 rfsec = (kauth_filesec_t)cp;
1468 if (((char *)(rfsec + 1) > bufend) || /* no space for acl */
1469 (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
1470 (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
1471 ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
1472 error = EINVAL;
1473 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
1474 goto out;
1475 }
1476 nace = rfsec->fsec_entrycount;
1477 if (nace == KAUTH_FILESEC_NOACL)
1478 nace = 0;
1479 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1480 error = EINVAL;
1481 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
1482 goto out;
1483 }
1484 nace = rfsec->fsec_acl.acl_entrycount;
1485 if (nace == KAUTH_FILESEC_NOACL) {
1486 /* deleting ACL */
1487 VATTR_SET(&va, va_acl, NULL);
1488 } else {
1489
1490 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1491 error = EINVAL;
1492 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
1493 goto out;
1494 }
1495 VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
1496 }
1497 }
1498 if (al.commonattr & ATTR_CMN_UUID) {
1499 ATTR_UNPACK(va.va_uuuid);
1500 VATTR_SET_ACTIVE(&va, va_uuuid);
1501 }
1502 if (al.commonattr & ATTR_CMN_GRPUUID) {
1503 ATTR_UNPACK(va.va_guuid);
1504 VATTR_SET_ACTIVE(&va, va_guuid);
1505 }
1506
1507 /* volume */
1508 if (al.volattr & ATTR_VOL_INFO) {
1509 if (al.volattr & ATTR_VOL_NAME) {
1510 volname = cursor;
1511 ATTR_UNPACK(ar);
1512 volname += ar.attr_dataoffset;
1513 if ((volname + ar.attr_length) > bufend) {
1514 error = EINVAL;
1515 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
1516 goto out;
1517 }
1518 /* guarantee NUL termination */
1519 volname[ar.attr_length - 1] = 0;
1520 }
1521 }
1522
1523 /* file */
1524 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1525 /* XXX does it actually make any sense to change this? */
1526 error = EINVAL;
1527 VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
1528 goto out;
1529 }
1530
1531 /*
1532 * Validate and authorize.
1533 */
1534 action = 0;
1535 if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, &context)) != 0)) {
1536 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
1537 goto out;
1538 }
1539 /*
1540 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1541 * and will be auth'ed by the FS.
1542 */
1543 if (fndrinfo != NULL) {
1544 if (al.volattr & ATTR_VOL_INFO) {
1545 if (vp->v_tag != VT_HFS) {
1546 error = EINVAL;
1547 goto out;
1548 }
1549 } else {
1550 action |= KAUTH_VNODE_WRITE_ATTRIBUTES;
1551 }
1552 }
1553
1554 if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
1555 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
1556 goto out;
1557 }
1558
1559 /*
1560 * Write the attributes if we have any.
1561 */
1562 if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, &context)) != 0)) {
1563 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1564 goto out;
1565 }
1566
1567 /*
1568 * Write the Finder Info if we have any.
1569 */
1570 if (fndrinfo != NULL) {
1571 if (al.volattr & ATTR_VOL_INFO) {
1572 if (vp->v_tag == VT_HFS) {
1573 error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, &context);
1574 if (error != 0)
1575 goto out;
1576 } else {
1577 /* XXX should never get here */
1578 }
1579 } else {
1580 /* write Finder Info EA */
1581 uio_t auio;
1582 char uio_buf[UIO_SIZEOF(1)];
1583
1584 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
1585 error = ENOMEM;
1586 } else {
1587 uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
1588 error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, &context);
1589 uio_free(auio);
1590 }
1591
1592 if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
1593 add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
1594 }
1595
1596 if (error != 0) {
1597 goto out;
1598 }
1599 }
1600 }
1601
1602 /*
1603 * Set the volume name, if we have one
1604 */
1605 if (volname != NULL)
1606 {
1607 struct vfs_attr vs;
1608
1609 VFSATTR_INIT(&vs);
1610
1611 vs.f_vol_name = volname; /* References the setattrlist buffer directly */
1612 VFSATTR_WANTED(&vs, f_vol_name);
1613
1614 if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
1615 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
1616 goto out;
1617 }
1618
1619 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
1620 error = EINVAL;
1621 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
1622 goto out;
1623 }
1624 }
1625
1626 /* all done and successful */
1627
1628 out:
1629 if (vp != NULL)
1630 vnode_put(vp);
1631 if (user_buf != NULL)
1632 FREE(user_buf, M_TEMP);
1633 VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
1634 return(error);
1635 }