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