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