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