]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2014 Apple 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 | /* | |
30 | * hfs_attrlist.c - HFS attribute list processing | |
31 | * | |
32 | * Copyright (c) 1998-2002, Apple Computer, Inc. All Rights Reserved. | |
33 | */ | |
34 | ||
35 | #include <sys/param.h> | |
36 | #include <sys/systm.h> | |
37 | #include <sys/kernel.h> | |
38 | #include <sys/malloc.h> | |
39 | #include <sys/attr.h> | |
40 | #include <sys/stat.h> | |
41 | #include <sys/unistd.h> | |
42 | #include <sys/mount_internal.h> | |
43 | #include <sys/kauth.h> | |
44 | #include <sys/fsctl.h> | |
45 | ||
46 | #include <kern/locks.h> | |
47 | ||
48 | #include "hfs.h" | |
49 | #include "hfs_cnode.h" | |
50 | #include "hfs_mount.h" | |
51 | #include "hfs_dbg.h" | |
52 | #include "hfs_attrlist.h" | |
53 | #include "hfs_btreeio.h" | |
54 | ||
55 | /* Packing routines: */ | |
56 | ||
57 | static void packnameattr(struct attrblock *abp, struct vnode *vp, | |
58 | const u_int8_t *name, int namelen); | |
59 | ||
60 | static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp, | |
61 | struct vnode *vp, struct cat_desc * cdp, | |
62 | struct cat_attr * cap, struct vfs_context *ctx); | |
63 | ||
64 | static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp, | |
65 | struct cat_attr *cattrp, struct cat_fork *datafork, | |
66 | struct cat_fork *rsrcfork, struct vnode *vp); | |
67 | ||
68 | static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp, | |
69 | struct vnode *vp, struct cat_desc * descp, | |
70 | struct cat_attr * cattrp); | |
71 | ||
72 | static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx); | |
73 | ||
74 | static void get_vattr_data_for_attrs(struct attrlist *, struct vnode_attr *, | |
75 | struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *, | |
76 | struct cat_fork *, struct cat_fork *, vfs_context_t); | |
77 | ||
78 | static void vattr_data_for_common_attrs(struct attrlist *, struct vnode_attr *, | |
79 | struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *, | |
80 | vfs_context_t); | |
81 | ||
82 | static void vattr_data_for_dir_attrs(struct attrlist *, struct vnode_attr *, | |
83 | struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *); | |
84 | ||
85 | static void vattr_data_for_file_attrs(struct attrlist *, struct vnode_attr *, | |
86 | struct hfsmount *, struct cat_attr *, struct cat_fork *, struct cat_fork *, | |
87 | struct vnode *vp); | |
88 | ||
89 | static int hfs_readdirattr_internal(struct vnode *, struct attrlist *, | |
90 | struct vnode_attr *, uio_t, uint64_t, int, uint32_t *, int *, int *, | |
91 | vfs_context_t); | |
92 | ||
93 | /* | |
94 | * readdirattr operation will return attributes for the items in the | |
95 | * directory specified. | |
96 | * | |
97 | * It does not do . and .. entries. The problem is if you are at the root of the | |
98 | * hfs directory and go to .. you could be crossing a mountpoint into a | |
99 | * different (ufs) file system. The attributes that apply for it may not | |
100 | * apply for the file system you are doing the readdirattr on. To make life | |
101 | * simpler, this call will only return entries in its directory, hfs like. | |
102 | */ | |
103 | int | |
104 | hfs_vnop_readdirattr(ap) | |
105 | struct vnop_readdirattr_args /* { | |
106 | struct vnode *a_vp; | |
107 | struct attrlist *a_alist; | |
108 | struct uio *a_uio; | |
109 | u_long a_maxcount; | |
110 | u_long a_options; | |
111 | u_long *a_newstate; | |
112 | int *a_eofflag; | |
113 | u_long *a_actualcount; | |
114 | vfs_context_t a_context; | |
115 | } */ *ap; | |
116 | { | |
117 | int error; | |
118 | struct attrlist *alist = ap->a_alist; | |
119 | ||
120 | /* Check for invalid options and buffer space. */ | |
121 | if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) || | |
122 | (ap->a_maxcount <= 0)) { | |
123 | return (EINVAL); | |
124 | } | |
125 | /* | |
126 | * Reject requests for unsupported attributes. | |
127 | */ | |
128 | if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) || | |
129 | (alist->commonattr & ~HFS_ATTR_CMN_VALID) || | |
130 | (alist->volattr != 0) || | |
131 | (alist->dirattr & ~HFS_ATTR_DIR_VALID) || | |
132 | (alist->fileattr & ~HFS_ATTR_FILE_VALID) || | |
133 | (alist->forkattr != 0)) { | |
134 | return (EINVAL); | |
135 | } | |
136 | ||
137 | error = hfs_readdirattr_internal(ap->a_vp, alist, NULL, ap->a_uio, | |
138 | (uint64_t)ap->a_options, ap->a_maxcount, ap->a_newstate, | |
139 | ap->a_eofflag, (int *)ap->a_actualcount, ap->a_context); | |
140 | ||
141 | return (error); | |
142 | } | |
143 | ||
144 | ||
145 | /* | |
146 | * getattrlistbulk, like readdirattr, will return attributes for the items in | |
147 | * the directory specified. | |
148 | * | |
149 | * It does not do . and .. entries. The problem is if you are at the root of the | |
150 | * hfs directory and go to .. you could be crossing a mountpoint into a | |
151 | * different (ufs) file system. The attributes that apply for it may not | |
152 | * apply for the file system you are doing the readdirattr on. To make life | |
153 | * simpler, this call will only return entries in its directory, hfs like. | |
154 | */ | |
155 | int | |
156 | hfs_vnop_getattrlistbulk(ap) | |
157 | struct vnop_getattrlistbulk_args /* { | |
158 | struct vnodeop_desc *a_desc; | |
159 | vnode_t a_vp; | |
160 | struct attrlist *a_alist; | |
161 | struct vnode_attr *a_vap; | |
162 | struct uio *a_uio; | |
163 | void *a_private; | |
164 | uint64_t a_options; | |
165 | int32_t *a_eofflag; | |
166 | int32_t *a_actualcount; | |
167 | vfs_context_t a_context; | |
168 | } */ *ap; | |
169 | { | |
170 | int error = 0; | |
171 | ||
172 | error = hfs_readdirattr_internal(ap->a_vp, ap->a_alist, ap->a_vap, | |
173 | ap->a_uio, (uint64_t)ap->a_options, 0, NULL, ap->a_eofflag, | |
174 | (int *)ap->a_actualcount, ap->a_context); | |
175 | ||
176 | return (error); | |
177 | } | |
178 | ||
179 | /* | |
180 | * Common function for both hfs_vnop_readdirattr and hfs_vnop_getattrlistbulk. | |
181 | * This either fills in a vnode_attr structure or fills in an attrbute buffer | |
182 | * Currently the difference in behaviour required for the two vnops is keyed | |
183 | * on whether the passed in vnode_attr pointer is null or not. If the pointer | |
184 | * is null we fill in buffer passed and if it is not null we fill in the fields | |
185 | * of the vnode_attr structure. | |
186 | */ | |
187 | int | |
188 | hfs_readdirattr_internal(struct vnode *dvp, struct attrlist *alist, | |
189 | struct vnode_attr *vap, uio_t uio, uint64_t options, int maxcount, | |
190 | uint32_t *newstate, int *eofflag, int *actualcount, vfs_context_t ctx) | |
191 | { | |
192 | struct cnode *dcp; | |
193 | struct hfsmount * hfsmp; | |
194 | u_int32_t fixedblocksize; | |
195 | u_int32_t maxattrblocksize; | |
196 | u_int32_t currattrbufsize; | |
197 | void *attrbufptr = NULL; | |
198 | void *attrptr = NULL; | |
199 | void *varptr = NULL; | |
200 | caddr_t namebuf = NULL; | |
201 | struct attrblock attrblk; | |
202 | int error = 0; | |
203 | int index = 0; | |
204 | int i = 0; | |
205 | struct cat_desc *lastdescp = NULL; | |
206 | struct cat_entrylist *ce_list = NULL; | |
207 | directoryhint_t *dirhint = NULL; | |
208 | unsigned int tag; | |
209 | int maxentries; | |
210 | int lockflags; | |
211 | u_int32_t dirchg = 0; | |
212 | int reachedeof = 0; | |
213 | ||
214 | *(actualcount) = 0; | |
215 | *(eofflag) = 0; | |
216 | ||
217 | if ((uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1)) | |
218 | return (EINVAL); | |
219 | ||
220 | if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) { | |
221 | int compressed = hfs_file_is_compressed(VTOC(dvp), 0); /* 0 == take the cnode lock */ | |
222 | ||
223 | if (!compressed) { | |
224 | error = check_for_dataless_file(dvp, NAMESPACE_HANDLER_READ_OP); | |
225 | if (error) { | |
226 | return error; | |
227 | } | |
228 | } | |
229 | } | |
230 | ||
231 | /* | |
232 | * Take an exclusive directory lock since we manipulate the directory hints | |
233 | */ | |
234 | if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { | |
235 | return (error); | |
236 | } | |
237 | dcp = VTOC(dvp); | |
238 | hfsmp = VTOHFS(dvp); | |
239 | ||
240 | dirchg = dcp->c_dirchangecnt; | |
241 | ||
242 | /* Extract directory index and tag (sequence number) from uio_offset */ | |
243 | index = uio_offset(uio) & HFS_INDEX_MASK; | |
244 | tag = uio_offset(uio) & ~HFS_INDEX_MASK; | |
245 | ||
246 | /* | |
247 | * We can't just use the valence as an optimization to avoid | |
248 | * going to the catalog. It might be wrong (== 0), and that would | |
249 | * cause us to avoid iterating the directory when it might actually have | |
250 | * contents. Instead, use the catalog to tell us when we've hit EOF | |
251 | * for this directory | |
252 | */ | |
253 | ||
254 | /* Get a buffer to hold packed attributes. */ | |
255 | fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */ | |
256 | ||
257 | if (!vap) { | |
258 | maxattrblocksize = fixedblocksize; | |
259 | if (alist->commonattr & ATTR_CMN_NAME) | |
260 | maxattrblocksize += kHFSPlusMaxFileNameBytes + 1; | |
261 | ||
262 | MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK); | |
263 | if (attrbufptr == NULL) { | |
264 | error = ENOMEM; | |
265 | goto exit2; | |
266 | } | |
267 | attrptr = attrbufptr; | |
268 | varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */ | |
269 | } else { | |
270 | if ((alist->commonattr & ATTR_CMN_NAME) && !vap->va_name) { | |
271 | MALLOC(namebuf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK); | |
272 | if (!namebuf) { | |
273 | error = ENOMEM; | |
274 | goto exit2; | |
275 | } | |
276 | vap->va_name = namebuf; | |
277 | } | |
278 | } | |
279 | /* Get a detached directory hint (cnode must be locked exclusive) */ | |
280 | dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE); | |
281 | ||
282 | /* Hide tag from catalog layer. */ | |
283 | dirhint->dh_index &= HFS_INDEX_MASK; | |
284 | if (dirhint->dh_index == HFS_INDEX_MASK) { | |
285 | dirhint->dh_index = -1; | |
286 | } | |
287 | ||
288 | /* | |
289 | * Obtain a list of catalog entries and pack their attributes until | |
290 | * the output buffer is full or maxcount entries have been packed. | |
291 | */ | |
292 | ||
293 | /* | |
294 | * Constrain our list size. | |
295 | */ | |
296 | maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE); | |
297 | /* There is maxcount for the bulk vnop */ | |
298 | if (!vap) | |
299 | maxentries = min(maxentries, maxcount); | |
300 | maxentries = min(maxentries, MAXCATENTRIES); | |
301 | if (maxentries < 1) { | |
302 | error = EINVAL; | |
303 | goto exit2; | |
304 | } | |
305 | ||
306 | /* Initialize a catalog entry list. */ | |
307 | MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK); | |
308 | if (ce_list == NULL) { | |
309 | error = ENOMEM; | |
310 | goto exit2; | |
311 | } | |
312 | bzero(ce_list, CE_LIST_SIZE(maxentries)); | |
313 | ce_list->maxentries = maxentries; | |
314 | ||
315 | /* | |
316 | * Populate the ce_list from the catalog file. | |
317 | */ | |
318 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
319 | ||
320 | error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof); | |
321 | /* Don't forget to release the descriptors later! */ | |
322 | ||
323 | hfs_systemfile_unlock(hfsmp, lockflags); | |
324 | ||
325 | if ((error == ENOENT) || (reachedeof != 0)) { | |
326 | *(eofflag) = TRUE; | |
327 | error = 0; | |
328 | } | |
329 | if (error) { | |
330 | goto exit1; | |
331 | } | |
332 | ||
333 | /* | |
334 | * Check for a FS corruption in the valence. We're holding the cnode lock | |
335 | * exclusive since we need to serialize the directory hints, so if we found | |
336 | * that the valence reported 0, but we actually found some items here, then | |
337 | * silently minimally self-heal and bump the valence to 1. | |
338 | */ | |
339 | if ((dcp->c_entries == 0) && (ce_list->realentries > 0)) { | |
340 | dcp->c_entries++; | |
341 | dcp->c_flag |= C_MODIFIED; | |
342 | printf("hfs_vnop_readdirattr: repairing valence to non-zero! \n"); | |
343 | /* force an update on dcp while we're still holding the lock. */ | |
344 | hfs_update(dvp, 0); | |
345 | } | |
346 | ||
347 | /* | |
348 | * Drop the directory lock so we don't deadlock when we: | |
349 | * - acquire a child cnode lock | |
350 | * - make calls to vnode_authorize() | |
351 | * - make calls to kauth_cred_ismember_gid() | |
352 | */ | |
353 | hfs_unlock(dcp); | |
354 | dcp = NULL; | |
355 | ||
356 | /* Process the catalog entries. */ | |
357 | for (i = 0; i < (int)ce_list->realentries; ++i) { | |
358 | struct cnode *cp = NULL; | |
359 | struct vnode *vp = NULL; | |
360 | struct cat_desc * cdescp; | |
361 | struct cat_attr * cattrp; | |
362 | struct cat_fork c_datafork; | |
363 | struct cat_fork c_rsrcfork; | |
364 | ||
365 | bzero(&c_datafork, sizeof(c_datafork)); | |
366 | bzero(&c_rsrcfork, sizeof(c_rsrcfork)); | |
367 | cdescp = &ce_list->entry[i].ce_desc; | |
368 | cattrp = &ce_list->entry[i].ce_attr; | |
369 | c_datafork.cf_size = ce_list->entry[i].ce_datasize; | |
370 | c_datafork.cf_blocks = ce_list->entry[i].ce_datablks; | |
371 | c_rsrcfork.cf_size = ce_list->entry[i].ce_rsrcsize; | |
372 | c_rsrcfork.cf_blocks = ce_list->entry[i].ce_rsrcblks; | |
373 | ||
374 | if (((alist->commonattr & ATTR_CMN_USERACCESS) && | |
375 | (cattrp->ca_recflags & kHFSHasSecurityMask)) | |
376 | #if CONFIG_PROTECT | |
377 | || | |
378 | ((alist->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) && (vap)) | |
379 | #endif | |
380 | ) { | |
381 | /* | |
382 | * Obtain vnode for our vnode_authorize() calls. | |
383 | */ | |
384 | if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) { | |
385 | vp = NULL; | |
386 | } | |
387 | } else if (vap || !(options & FSOPT_NOINMEMUPDATE)) { | |
388 | /* Get in-memory cnode data (if any). */ | |
389 | vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0); | |
390 | } | |
391 | if (vp != NULL) { | |
392 | cp = VTOC(vp); | |
393 | /* Only use cnode's decriptor for non-hardlinks */ | |
394 | if (!(cp->c_flag & C_HARDLINK)) | |
395 | cdescp = &cp->c_desc; | |
396 | cattrp = &cp->c_attr; | |
397 | if (cp->c_datafork) { | |
398 | c_datafork.cf_size = cp->c_datafork->ff_size; | |
399 | c_datafork.cf_blocks = cp->c_datafork->ff_blocks; | |
400 | } | |
401 | if (cp->c_rsrcfork) { | |
402 | c_rsrcfork.cf_size = cp->c_rsrcfork->ff_size; | |
403 | c_rsrcfork.cf_blocks = cp->c_rsrcfork->ff_blocks; | |
404 | } | |
405 | /* All done with cnode. */ | |
406 | hfs_unlock(cp); | |
407 | cp = NULL; | |
408 | } | |
409 | ||
410 | if (!vap) { | |
411 | *((u_int32_t *)attrptr) = 0; | |
412 | attrptr = ((u_int32_t *)attrptr) + 1; | |
413 | attrblk.ab_attrlist = alist; | |
414 | attrblk.ab_attrbufpp = &attrptr; | |
415 | attrblk.ab_varbufpp = &varptr; | |
416 | attrblk.ab_flags = 0; | |
417 | attrblk.ab_blocksize = maxattrblocksize; | |
418 | attrblk.ab_context = ctx; | |
419 | ||
420 | /* Pack catalog entries into attribute buffer. */ | |
421 | hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, ctx); | |
422 | currattrbufsize = ((char *)varptr - (char *)attrbufptr); | |
423 | ||
424 | /* All done with vnode. */ | |
425 | if (vp != NULL) { | |
426 | vnode_put(vp); | |
427 | vp = NULL; | |
428 | } | |
429 | ||
430 | /* Make sure there's enough buffer space remaining. */ | |
431 | // LP64todo - fix this! | |
432 | if (uio_resid(uio) < 0 || | |
433 | currattrbufsize > (u_int32_t)uio_resid(uio)) { | |
434 | break; | |
435 | } else { | |
436 | *((u_int32_t *)attrbufptr) = currattrbufsize; | |
437 | error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio); | |
438 | if (error != E_NONE) { | |
439 | break; | |
440 | } | |
441 | attrptr = attrbufptr; | |
442 | /* Point to variable-length storage */ | |
443 | varptr = (char *)attrbufptr + fixedblocksize; | |
444 | /* Save the last valid catalog entry */ | |
445 | lastdescp = &ce_list->entry[i].ce_desc; | |
446 | index++; | |
447 | *actualcount += 1; | |
448 | ||
449 | /* Termination checks */ | |
450 | if ((--maxcount <= 0) || | |
451 | // LP64todo - fix this! | |
452 | uio_resid(uio) < 0 || | |
453 | ((u_int32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE))){ | |
454 | break; | |
455 | } | |
456 | } | |
457 | } else { | |
458 | size_t orig_resid = (size_t)uio_resid(uio); | |
459 | size_t resid; | |
460 | ||
461 | get_vattr_data_for_attrs(alist, vap, hfsmp, vp, cdescp, | |
462 | cattrp, &c_datafork, &c_rsrcfork, ctx); | |
463 | ||
464 | #if CONFIG_PROTECT | |
465 | if ((alist->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) && | |
466 | vp) { | |
467 | int class; | |
468 | ||
469 | if (!cp_vnode_getclass(vp, &class)) { | |
470 | VATTR_RETURN(vap, va_dataprotect_class, | |
471 | (uint32_t)class); | |
472 | } | |
473 | } | |
474 | #endif | |
475 | error = vfs_attr_pack(vp, uio, alist, options, vap, | |
476 | NULL, ctx); | |
477 | ||
478 | /* All done with vnode. */ | |
479 | if (vp) { | |
480 | vnode_put(vp); | |
481 | vp = NULL; | |
482 | } | |
483 | ||
484 | resid = uio_resid(uio); | |
485 | ||
486 | /* Was this entry succesful ? */ | |
487 | if (error || resid == orig_resid) | |
488 | break; | |
489 | ||
490 | /* Save the last valid catalog entry */ | |
491 | lastdescp = &ce_list->entry[i].ce_desc; | |
492 | index++; | |
493 | *actualcount += 1; | |
494 | ||
495 | /* Do we have the bare minimum for the next entry ? */ | |
496 | if (resid < sizeof(uint32_t)) | |
497 | break; | |
498 | } | |
499 | } /* for each catalog entry */ | |
500 | ||
501 | /* | |
502 | * If we couldn't fit all the entries requested in the user's buffer, | |
503 | * it's not EOF. | |
504 | */ | |
505 | if (*eofflag && (*actualcount < (int)ce_list->realentries)) | |
506 | *eofflag = 0; | |
507 | ||
508 | /* If we skipped catalog entries for reserved files that should | |
509 | * not be listed in namespace, update the index accordingly. | |
510 | */ | |
511 | if (ce_list->skipentries) { | |
512 | index += ce_list->skipentries; | |
513 | ce_list->skipentries = 0; | |
514 | } | |
515 | ||
516 | /* | |
517 | * If there are more entries then save the last name. | |
518 | * Key this behavior based on whether or not we observed EOFFLAG. | |
519 | * | |
520 | * Do not use the valence as a way to determine if we hit EOF, since | |
521 | * it can be wrong. Use the catalog's output only. | |
522 | */ | |
523 | if ((*(eofflag) == 0) && lastdescp != NULL) { | |
524 | ||
525 | /* Remember last entry */ | |
526 | if ((dirhint->dh_desc.cd_flags & CD_HASBUF) && | |
527 | (dirhint->dh_desc.cd_nameptr != NULL)) { | |
528 | dirhint->dh_desc.cd_flags &= ~CD_HASBUF; | |
529 | vfs_removename((const char *)dirhint->dh_desc.cd_nameptr); | |
530 | } | |
531 | dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen; | |
532 | dirhint->dh_desc.cd_nameptr = (const u_int8_t *) | |
533 | vfs_addname((const char *)lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0); | |
534 | dirhint->dh_desc.cd_flags |= CD_HASBUF; | |
535 | dirhint->dh_index = index - 1; | |
536 | dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid; | |
537 | dirhint->dh_desc.cd_hint = lastdescp->cd_hint; | |
538 | dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding; | |
539 | } | |
540 | ||
541 | /* All done with the catalog descriptors. */ | |
542 | for (i = 0; i < (int)ce_list->realentries; ++i) | |
543 | cat_releasedesc(&ce_list->entry[i].ce_desc); | |
544 | ce_list->realentries = 0; | |
545 | ||
546 | (void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS); | |
547 | dcp = VTOC(dvp); | |
548 | ||
549 | exit1: | |
550 | /* Pack directory index and tag into uio_offset. */ | |
551 | while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS; | |
552 | uio_setoffset(uio, index | tag); | |
553 | dirhint->dh_index |= tag; | |
554 | ||
555 | exit2: | |
556 | if (newstate) | |
557 | *newstate = dirchg; | |
558 | ||
559 | /* | |
560 | * Drop directory hint on error or if there are no more entries, | |
561 | * only if EOF was seen. | |
562 | */ | |
563 | if (dirhint) { | |
564 | if ((error != 0) || *(eofflag)) | |
565 | hfs_reldirhint(dcp, dirhint); | |
566 | else | |
567 | hfs_insertdirhint(dcp, dirhint); | |
568 | } | |
569 | if (namebuf) { | |
570 | FREE(namebuf, M_TEMP); | |
571 | vap->va_name = NULL; | |
572 | } | |
573 | if (attrbufptr) | |
574 | FREE(attrbufptr, M_TEMP); | |
575 | if (ce_list) | |
576 | FREE(ce_list, M_TEMP); | |
577 | ||
578 | if (vap && *actualcount && error) | |
579 | error = 0; | |
580 | ||
581 | hfs_unlock(dcp); | |
582 | return (error); | |
583 | } | |
584 | ||
585 | ||
586 | /*==================== Attribute list support routines ====================*/ | |
587 | ||
588 | /* | |
589 | * Pack cnode attributes into an attribute block. | |
590 | */ | |
591 | __private_extern__ | |
592 | void | |
593 | hfs_packattrblk(struct attrblock *abp, | |
594 | struct hfsmount *hfsmp, | |
595 | struct vnode *vp, | |
596 | struct cat_desc *descp, | |
597 | struct cat_attr *attrp, | |
598 | struct cat_fork *datafork, | |
599 | struct cat_fork *rsrcfork, | |
600 | struct vfs_context *ctx) | |
601 | { | |
602 | struct attrlist *attrlistp = abp->ab_attrlist; | |
603 | ||
604 | if (attrlistp->commonattr) | |
605 | packcommonattr(abp, hfsmp, vp, descp, attrp, ctx); | |
606 | ||
607 | if (attrlistp->dirattr && S_ISDIR(attrp->ca_mode)) | |
608 | packdirattr(abp, hfsmp, vp, descp,attrp); | |
609 | ||
610 | if (attrlistp->fileattr && !S_ISDIR(attrp->ca_mode)) | |
611 | packfileattr(abp, hfsmp, attrp, datafork, rsrcfork, vp); | |
612 | } | |
613 | ||
614 | ||
615 | static char* | |
616 | mountpointname(struct mount *mp) | |
617 | { | |
618 | size_t namelength = strlen(mp->mnt_vfsstat.f_mntonname); | |
619 | int foundchars = 0; | |
620 | char *c; | |
621 | ||
622 | if (namelength == 0) | |
623 | return (NULL); | |
624 | ||
625 | /* | |
626 | * Look backwards through the name string, looking for | |
627 | * the first slash encountered (which must precede the | |
628 | * last part of the pathname). | |
629 | */ | |
630 | for (c = mp->mnt_vfsstat.f_mntonname + namelength - 1; | |
631 | namelength > 0; --c, --namelength) { | |
632 | if (*c != '/') { | |
633 | foundchars = 1; | |
634 | } else if (foundchars) { | |
635 | return (c + 1); | |
636 | } | |
637 | } | |
638 | ||
639 | return (mp->mnt_vfsstat.f_mntonname); | |
640 | } | |
641 | ||
642 | ||
643 | static void | |
644 | packnameattr( | |
645 | struct attrblock *abp, | |
646 | struct vnode *vp, | |
647 | const u_int8_t *name, | |
648 | int namelen) | |
649 | { | |
650 | void *varbufptr; | |
651 | struct attrreference * attr_refptr; | |
652 | char *mpname; | |
653 | size_t mpnamelen; | |
654 | u_int32_t attrlength; | |
655 | u_int8_t empty = 0; | |
656 | ||
657 | /* A cnode's name may be incorrect for the root of a mounted | |
658 | * filesystem (it can be mounted on a different directory name | |
659 | * than the name of the volume, such as "blah-1"). So for the | |
660 | * root directory, it's best to return the last element of the | |
661 | location where the volume's mounted: | |
662 | */ | |
663 | if ((vp != NULL) && vnode_isvroot(vp) && | |
664 | (mpname = mountpointname(vnode_mount(vp)))) { | |
665 | mpnamelen = strlen(mpname); | |
666 | ||
667 | /* Trim off any trailing slashes: */ | |
668 | while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) | |
669 | --mpnamelen; | |
670 | ||
671 | /* If there's anything left, use it instead of the volume's name */ | |
672 | if (mpnamelen > 0) { | |
673 | name = (u_int8_t *)mpname; | |
674 | namelen = mpnamelen; | |
675 | } | |
676 | } | |
677 | if (name == NULL) { | |
678 | name = ∅ | |
679 | namelen = 0; | |
680 | } | |
681 | ||
682 | varbufptr = *abp->ab_varbufpp; | |
683 | attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp); | |
684 | ||
685 | attrlength = namelen + 1; | |
686 | attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr; | |
687 | attr_refptr->attr_length = attrlength; | |
688 | (void) strncpy((char *)varbufptr, (const char *) name, attrlength); | |
689 | /* | |
690 | * Advance beyond the space just allocated and | |
691 | * round up to the next 4-byte boundary: | |
692 | */ | |
693 | varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3); | |
694 | ++attr_refptr; | |
695 | ||
696 | *abp->ab_attrbufpp = attr_refptr; | |
697 | *abp->ab_varbufpp = varbufptr; | |
698 | } | |
699 | ||
700 | static void | |
701 | packcommonattr( | |
702 | struct attrblock *abp, | |
703 | struct hfsmount *hfsmp, | |
704 | struct vnode *vp, | |
705 | struct cat_desc * cdp, | |
706 | struct cat_attr * cap, | |
707 | struct vfs_context * ctx) | |
708 | { | |
709 | attrgroup_t attr = abp->ab_attrlist->commonattr; | |
710 | struct mount *mp = HFSTOVFS(hfsmp); | |
711 | void *attrbufptr = *abp->ab_attrbufpp; | |
712 | void *varbufptr = *abp->ab_varbufpp; | |
713 | boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx)); | |
714 | uid_t cuid = 1; | |
715 | int isroot = 0; | |
716 | ||
717 | if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) { | |
718 | cuid = kauth_cred_getuid(vfs_context_ucred(ctx)); | |
719 | isroot = cuid == 0; | |
720 | } | |
721 | ||
722 | if (ATTR_CMN_NAME & attr) { | |
723 | packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen); | |
724 | attrbufptr = *abp->ab_attrbufpp; | |
725 | varbufptr = *abp->ab_varbufpp; | |
726 | } | |
727 | if (ATTR_CMN_DEVID & attr) { | |
728 | *((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev; | |
729 | attrbufptr = ((dev_t *)attrbufptr) + 1; | |
730 | } | |
731 | if (ATTR_CMN_FSID & attr) { | |
732 | fsid_t fsid; | |
733 | ||
734 | fsid.val[0] = hfsmp->hfs_raw_dev; | |
735 | fsid.val[1] = vfs_typenum(mp); | |
736 | *((fsid_t *)attrbufptr) = fsid; | |
737 | attrbufptr = ((fsid_t *)attrbufptr) + 1; | |
738 | } | |
739 | if (ATTR_CMN_OBJTYPE & attr) { | |
740 | *((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode); | |
741 | attrbufptr = ((fsobj_type_t *)attrbufptr) + 1; | |
742 | } | |
743 | if (ATTR_CMN_OBJTAG & attr) { | |
744 | *((fsobj_tag_t *)attrbufptr) = VT_HFS; | |
745 | attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1; | |
746 | } | |
747 | /* | |
748 | * Exporting file IDs from HFS Plus: | |
749 | * | |
750 | * For "normal" files the c_fileid is the same value as the | |
751 | * c_cnid. But for hard link files, they are different - the | |
752 | * c_cnid belongs to the active directory entry (ie the link) | |
753 | * and the c_fileid is for the actual inode (ie the data file). | |
754 | * | |
755 | * The stat call (getattr) will always return the c_fileid | |
756 | * and Carbon APIs, which are hardlink-ignorant, will always | |
757 | * receive the c_cnid (from getattrlist). | |
758 | */ | |
759 | if (ATTR_CMN_OBJID & attr) { | |
760 | ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; | |
761 | ((fsobj_id_t *)attrbufptr)->fid_generation = 0; | |
762 | attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; | |
763 | } | |
764 | if (ATTR_CMN_OBJPERMANENTID & attr) { | |
765 | ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; | |
766 | ((fsobj_id_t *)attrbufptr)->fid_generation = 0; | |
767 | attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; | |
768 | } | |
769 | if (ATTR_CMN_PAROBJID & attr) { | |
770 | ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid; | |
771 | ((fsobj_id_t *)attrbufptr)->fid_generation = 0; | |
772 | attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; | |
773 | } | |
774 | if (ATTR_CMN_SCRIPT & attr) { | |
775 | *((text_encoding_t *)attrbufptr) = cdp->cd_encoding; | |
776 | attrbufptr = ((text_encoding_t *)attrbufptr) + 1; | |
777 | } | |
778 | if (ATTR_CMN_CRTIME & attr) { | |
779 | if (is_64_bit) { | |
780 | ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_itime; | |
781 | ((struct user64_timespec *)attrbufptr)->tv_nsec = 0; | |
782 | attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; | |
783 | } | |
784 | else { | |
785 | ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_itime; | |
786 | ((struct user32_timespec *)attrbufptr)->tv_nsec = 0; | |
787 | attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; | |
788 | } | |
789 | } | |
790 | if (ATTR_CMN_MODTIME & attr) { | |
791 | if (is_64_bit) { | |
792 | ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_mtime; | |
793 | ((struct user64_timespec *)attrbufptr)->tv_nsec = 0; | |
794 | attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; | |
795 | } | |
796 | else { | |
797 | ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_mtime; | |
798 | ((struct user32_timespec *)attrbufptr)->tv_nsec = 0; | |
799 | attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; | |
800 | } | |
801 | } | |
802 | if (ATTR_CMN_CHGTIME & attr) { | |
803 | if (is_64_bit) { | |
804 | ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_ctime; | |
805 | ((struct user64_timespec *)attrbufptr)->tv_nsec = 0; | |
806 | attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; | |
807 | } | |
808 | else { | |
809 | ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_ctime; | |
810 | ((struct user32_timespec *)attrbufptr)->tv_nsec = 0; | |
811 | attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; | |
812 | } | |
813 | } | |
814 | if (ATTR_CMN_ACCTIME & attr) { | |
815 | if (is_64_bit) { | |
816 | ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_atime; | |
817 | ((struct user64_timespec *)attrbufptr)->tv_nsec = 0; | |
818 | attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; | |
819 | } | |
820 | else { | |
821 | ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_atime; | |
822 | ((struct user32_timespec *)attrbufptr)->tv_nsec = 0; | |
823 | attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; | |
824 | } | |
825 | } | |
826 | if (ATTR_CMN_BKUPTIME & attr) { | |
827 | if (is_64_bit) { | |
828 | ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_btime; | |
829 | ((struct user64_timespec *)attrbufptr)->tv_nsec = 0; | |
830 | attrbufptr = ((struct user64_timespec *)attrbufptr) + 1; | |
831 | } | |
832 | else { | |
833 | ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_btime; | |
834 | ((struct user32_timespec *)attrbufptr)->tv_nsec = 0; | |
835 | attrbufptr = ((struct user32_timespec *)attrbufptr) + 1; | |
836 | } | |
837 | } | |
838 | if (ATTR_CMN_FNDRINFO & attr) { | |
839 | u_int8_t *finfo = NULL; | |
840 | bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32); | |
841 | finfo = (u_int8_t*)attrbufptr; | |
842 | ||
843 | /* Don't expose a symlink's private type/creator. */ | |
844 | if (S_ISLNK(cap->ca_mode)) { | |
845 | struct FndrFileInfo *fip; | |
846 | ||
847 | fip = (struct FndrFileInfo *)attrbufptr; | |
848 | fip->fdType = 0; | |
849 | fip->fdCreator = 0; | |
850 | } | |
851 | ||
852 | /* advance 16 bytes into the attrbuf */ | |
853 | finfo = finfo + 16; | |
854 | ||
855 | /* also don't expose the date_added or write_gen_counter fields */ | |
856 | if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) { | |
857 | struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo; | |
858 | extinfo->document_id = 0; | |
859 | extinfo->date_added = 0; | |
860 | extinfo->write_gen_counter = 0; | |
861 | } | |
862 | else if (S_ISDIR(cap->ca_mode)) { | |
863 | struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo; | |
864 | extinfo->document_id = 0; | |
865 | extinfo->date_added = 0; | |
866 | extinfo->write_gen_counter = 0; | |
867 | } | |
868 | ||
869 | attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32; | |
870 | } | |
871 | if (ATTR_CMN_OWNERID & attr) { | |
872 | uid_t nuid = cap->ca_uid; | |
873 | ||
874 | if (!isroot) { | |
875 | if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) | |
876 | nuid = cuid; | |
877 | else if (nuid == UNKNOWNUID) | |
878 | nuid = cuid; | |
879 | } | |
880 | ||
881 | *((uid_t *)attrbufptr) = nuid; | |
882 | attrbufptr = ((uid_t *)attrbufptr) + 1; | |
883 | } | |
884 | if (ATTR_CMN_GRPID & attr) { | |
885 | gid_t ngid = cap->ca_gid; | |
886 | ||
887 | if (!isroot) { | |
888 | gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx)); | |
889 | if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) | |
890 | ngid = cgid; | |
891 | else if (ngid == UNKNOWNUID) | |
892 | ngid = cgid; | |
893 | } | |
894 | ||
895 | *((gid_t *)attrbufptr) = ngid; | |
896 | attrbufptr = ((gid_t *)attrbufptr) + 1; | |
897 | } | |
898 | if (ATTR_CMN_ACCESSMASK & attr) { | |
899 | /* | |
900 | * [2856576] Since we are dynamically changing the owner, also | |
901 | * effectively turn off the set-user-id and set-group-id bits, | |
902 | * just like chmod(2) would when changing ownership. This prevents | |
903 | * a security hole where set-user-id programs run as whoever is | |
904 | * logged on (or root if nobody is logged in yet!) | |
905 | */ | |
906 | *((u_int32_t *)attrbufptr) = (cap->ca_uid == UNKNOWNUID) ? | |
907 | cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode; | |
908 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
909 | } | |
910 | if (ATTR_CMN_FLAGS & attr) { | |
911 | *((u_int32_t *)attrbufptr) = cap->ca_flags; | |
912 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
913 | } | |
914 | if (ATTR_CMN_USERACCESS & attr) { | |
915 | u_int32_t user_access; | |
916 | ||
917 | /* Take the long path when we have an ACL */ | |
918 | if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) { | |
919 | user_access = hfs_real_user_access(vp, abp->ab_context); | |
920 | } else { | |
921 | user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid, | |
922 | cap->ca_mode, mp, vfs_context_ucred(ctx), 0); | |
923 | } | |
924 | /* Also consider READ-ONLY file system. */ | |
925 | if (vfs_flags(mp) & MNT_RDONLY) { | |
926 | user_access &= ~W_OK; | |
927 | } | |
928 | /* Locked objects are not writable either */ | |
929 | if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) != 0)) | |
930 | user_access &= ~W_OK; | |
931 | if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) == 0)) | |
932 | user_access &= ~W_OK; | |
933 | ||
934 | *((u_int32_t *)attrbufptr) = user_access; | |
935 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
936 | } | |
937 | if (ATTR_CMN_FILEID & attr) { | |
938 | *((u_int64_t *)attrbufptr) = cap->ca_fileid; | |
939 | attrbufptr = ((u_int64_t *)attrbufptr) + 1; | |
940 | } | |
941 | if (ATTR_CMN_PARENTID & attr) { | |
942 | *((u_int64_t *)attrbufptr) = cdp->cd_parentcnid; | |
943 | attrbufptr = ((u_int64_t *)attrbufptr) + 1; | |
944 | } | |
945 | ||
946 | *abp->ab_attrbufpp = attrbufptr; | |
947 | *abp->ab_varbufpp = varbufptr; | |
948 | } | |
949 | ||
950 | static void | |
951 | packdirattr( | |
952 | struct attrblock *abp, | |
953 | struct hfsmount *hfsmp, | |
954 | struct vnode *vp, | |
955 | struct cat_desc * descp, | |
956 | struct cat_attr * cattrp) | |
957 | { | |
958 | attrgroup_t attr = abp->ab_attrlist->dirattr; | |
959 | void *attrbufptr = *abp->ab_attrbufpp; | |
960 | u_int32_t entries; | |
961 | ||
962 | /* | |
963 | * The DIR_LINKCOUNT is the count of real directory hard links. | |
964 | * (i.e. its not the sum of the implied "." and ".." references | |
965 | * typically used in stat's st_nlink field) | |
966 | */ | |
967 | if (ATTR_DIR_LINKCOUNT & attr) { | |
968 | *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount; | |
969 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
970 | } | |
971 | if (ATTR_DIR_ENTRYCOUNT & attr) { | |
972 | entries = cattrp->ca_entries; | |
973 | ||
974 | if (descp->cd_parentcnid == kHFSRootParentID) { | |
975 | if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) | |
976 | --entries; /* hide private dir */ | |
977 | if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) | |
978 | --entries; /* hide private dir */ | |
979 | if (hfsmp->jnl || | |
980 | ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && | |
981 | (hfsmp->hfs_flags & HFS_READ_ONLY))) | |
982 | entries -= 2; /* hide the journal files */ | |
983 | } | |
984 | ||
985 | *((u_int32_t *)attrbufptr) = entries; | |
986 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
987 | } | |
988 | if (ATTR_DIR_MOUNTSTATUS & attr) { | |
989 | if (vp != NULL && vnode_mountedhere(vp) != NULL) | |
990 | *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT; | |
991 | else | |
992 | *((u_int32_t *)attrbufptr) = 0; | |
993 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
994 | } | |
995 | *abp->ab_attrbufpp = attrbufptr; | |
996 | } | |
997 | ||
998 | static void | |
999 | packfileattr( | |
1000 | struct attrblock *abp, | |
1001 | struct hfsmount *hfsmp, | |
1002 | struct cat_attr *cattrp, | |
1003 | struct cat_fork *datafork, | |
1004 | struct cat_fork *rsrcfork, | |
1005 | struct vnode *vp) | |
1006 | { | |
1007 | #if !HFS_COMPRESSION | |
1008 | #pragma unused(vp) | |
1009 | #endif | |
1010 | attrgroup_t attr = abp->ab_attrlist->fileattr; | |
1011 | void *attrbufptr = *abp->ab_attrbufpp; | |
1012 | void *varbufptr = *abp->ab_varbufpp; | |
1013 | u_int32_t allocblksize; | |
1014 | ||
1015 | allocblksize = HFSTOVCB(hfsmp)->blockSize; | |
1016 | ||
1017 | off_t datasize = datafork->cf_size; | |
1018 | off_t totalsize = datasize + rsrcfork->cf_size; | |
1019 | #if HFS_COMPRESSION | |
1020 | int handle_compressed; | |
1021 | handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1); | |
1022 | ||
1023 | if (handle_compressed) { | |
1024 | if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) { | |
1025 | if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) { /* 1 == don't take the cnode lock */ | |
1026 | /* total size of a compressed file is just the data size */ | |
1027 | totalsize = datasize; | |
1028 | } | |
1029 | } | |
1030 | } | |
1031 | #endif | |
1032 | ||
1033 | if (ATTR_FILE_LINKCOUNT & attr) { | |
1034 | *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount; | |
1035 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
1036 | } | |
1037 | if (ATTR_FILE_TOTALSIZE & attr) { | |
1038 | *((off_t *)attrbufptr) = totalsize; | |
1039 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1040 | } | |
1041 | if (ATTR_FILE_ALLOCSIZE & attr) { | |
1042 | *((off_t *)attrbufptr) = | |
1043 | (off_t)cattrp->ca_blocks * (off_t)allocblksize; | |
1044 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1045 | } | |
1046 | if (ATTR_FILE_IOBLOCKSIZE & attr) { | |
1047 | *((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize; | |
1048 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
1049 | } | |
1050 | if (ATTR_FILE_CLUMPSIZE & attr) { | |
1051 | *((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz; | |
1052 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
1053 | } | |
1054 | if (ATTR_FILE_DEVTYPE & attr) { | |
1055 | if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode)) | |
1056 | *((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev; | |
1057 | else | |
1058 | *((u_int32_t *)attrbufptr) = 0; | |
1059 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
1060 | } | |
1061 | ||
1062 | if (ATTR_FILE_DATALENGTH & attr) { | |
1063 | *((off_t *)attrbufptr) = datasize; | |
1064 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1065 | } | |
1066 | ||
1067 | #if HFS_COMPRESSION | |
1068 | /* fake the data fork size on a decmpfs compressed file to reflect the | |
1069 | * uncompressed size. This ensures proper reading and copying of these files. | |
1070 | * NOTE: we may need to get the vnode here because the vnode parameter | |
1071 | * passed by hfs_vnop_readdirattr() may be null. | |
1072 | */ | |
1073 | ||
1074 | if ( handle_compressed ) { | |
1075 | if (attr & ATTR_FILE_DATAALLOCSIZE) { | |
1076 | *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize; | |
1077 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1078 | } | |
1079 | if (attr & ATTR_FILE_RSRCLENGTH) { | |
1080 | *((off_t *)attrbufptr) = 0; | |
1081 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1082 | } | |
1083 | if (attr & ATTR_FILE_RSRCALLOCSIZE) { | |
1084 | *((off_t *)attrbufptr) = 0; | |
1085 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1086 | } | |
1087 | } | |
1088 | else | |
1089 | #endif | |
1090 | { | |
1091 | if (ATTR_FILE_DATAALLOCSIZE & attr) { | |
1092 | *((off_t *)attrbufptr) = (off_t)datafork->cf_blocks * (off_t)allocblksize; | |
1093 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1094 | } | |
1095 | if (ATTR_FILE_RSRCLENGTH & attr) { | |
1096 | *((off_t *)attrbufptr) = rsrcfork->cf_size; | |
1097 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1098 | } | |
1099 | if (ATTR_FILE_RSRCALLOCSIZE & attr) { | |
1100 | *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize; | |
1101 | attrbufptr = ((off_t *)attrbufptr) + 1; | |
1102 | } | |
1103 | } | |
1104 | *abp->ab_attrbufpp = attrbufptr; | |
1105 | *abp->ab_varbufpp = varbufptr; | |
1106 | } | |
1107 | ||
1108 | /* | |
1109 | * Calculate the total size of an attribute block. | |
1110 | */ | |
1111 | __private_extern__ | |
1112 | int | |
1113 | hfs_attrblksize(struct attrlist *attrlist) | |
1114 | { | |
1115 | int size; | |
1116 | attrgroup_t a; | |
1117 | int sizeof_timespec; | |
1118 | boolean_t is_64_bit = proc_is64bit(current_proc()); | |
1119 | ||
1120 | if (is_64_bit) | |
1121 | sizeof_timespec = sizeof(struct user64_timespec); | |
1122 | else | |
1123 | sizeof_timespec = sizeof(struct user32_timespec); | |
1124 | ||
1125 | DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0); | |
1126 | ||
1127 | DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0); | |
1128 | ||
1129 | DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0); | |
1130 | ||
1131 | DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0); | |
1132 | ||
1133 | DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0); | |
1134 | ||
1135 | size = 0; | |
1136 | ||
1137 | if ((a = attrlist->commonattr) != 0) { | |
1138 | if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference); | |
1139 | if (a & ATTR_CMN_DEVID) size += sizeof(dev_t); | |
1140 | if (a & ATTR_CMN_FSID) size += sizeof(fsid_t); | |
1141 | if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t); | |
1142 | if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t); | |
1143 | if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t); | |
1144 | if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t); | |
1145 | if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t); | |
1146 | if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t); | |
1147 | if (a & ATTR_CMN_CRTIME) size += sizeof_timespec; | |
1148 | if (a & ATTR_CMN_MODTIME) size += sizeof_timespec; | |
1149 | if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec; | |
1150 | if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec; | |
1151 | if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec; | |
1152 | if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t); | |
1153 | if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t); | |
1154 | if (a & ATTR_CMN_GRPID) size += sizeof(gid_t); | |
1155 | if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_int32_t); | |
1156 | if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t); | |
1157 | if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t); | |
1158 | if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t); | |
1159 | if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t); | |
1160 | } | |
1161 | if ((a = attrlist->dirattr) != 0) { | |
1162 | if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t); | |
1163 | if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t); | |
1164 | if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t); | |
1165 | } | |
1166 | if ((a = attrlist->fileattr) != 0) { | |
1167 | if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t); | |
1168 | if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t); | |
1169 | if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t); | |
1170 | if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(u_int32_t); | |
1171 | if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t); | |
1172 | if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t); | |
1173 | if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t); | |
1174 | if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t); | |
1175 | if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t); | |
1176 | if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t); | |
1177 | } | |
1178 | ||
1179 | return (size); | |
1180 | } | |
1181 | ||
1182 | #define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \ | |
1183 | KAUTH_VNODE_ADD_SUBDIRECTORY | \ | |
1184 | KAUTH_VNODE_DELETE_CHILD) | |
1185 | ||
1186 | #define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY) | |
1187 | ||
1188 | #define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH) | |
1189 | ||
1190 | #define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA) | |
1191 | ||
1192 | #define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA) | |
1193 | ||
1194 | #define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE) | |
1195 | ||
1196 | ||
1197 | /* | |
1198 | * Compute the same [expensive] user_access value as getattrlist does | |
1199 | */ | |
1200 | static u_int32_t | |
1201 | hfs_real_user_access(vnode_t vp, vfs_context_t ctx) | |
1202 | { | |
1203 | u_int32_t user_access = 0; | |
1204 | ||
1205 | if (vnode_isdir(vp)) { | |
1206 | if (vnode_authorize(vp, NULLVP, KAUTH_DIR_WRITE_RIGHTS, ctx) == 0) | |
1207 | user_access |= W_OK; | |
1208 | if (vnode_authorize(vp, NULLVP, KAUTH_DIR_READ_RIGHTS, ctx) == 0) | |
1209 | user_access |= R_OK; | |
1210 | if (vnode_authorize(vp, NULLVP, KAUTH_DIR_EXECUTE_RIGHTS, ctx) == 0) | |
1211 | user_access |= X_OK; | |
1212 | } else { | |
1213 | if (vnode_authorize(vp, NULLVP, KAUTH_FILE_WRITE_RIGHTS, ctx) == 0) | |
1214 | user_access |= W_OK; | |
1215 | if (vnode_authorize(vp, NULLVP, KAUTH_FILE_READRIGHTS, ctx) == 0) | |
1216 | user_access |= R_OK; | |
1217 | if (vnode_authorize(vp, NULLVP, KAUTH_FILE_EXECUTE_RIGHTS, ctx) == 0) | |
1218 | user_access |= X_OK; | |
1219 | } | |
1220 | return (user_access); | |
1221 | } | |
1222 | ||
1223 | ||
1224 | u_int32_t | |
1225 | DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, | |
1226 | struct mount *mp, kauth_cred_t cred, __unused struct proc *p) | |
1227 | { | |
1228 | u_int32_t permissions; | |
1229 | ||
1230 | if (obj_uid == UNKNOWNUID) | |
1231 | obj_uid = kauth_cred_getuid(cred); | |
1232 | ||
1233 | /* User id 0 (root) always gets access. */ | |
1234 | if (!suser(cred, NULL)) { | |
1235 | permissions = R_OK | W_OK | X_OK; | |
1236 | goto Exit; | |
1237 | }; | |
1238 | ||
1239 | /* Otherwise, check the owner. */ | |
1240 | if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) { | |
1241 | permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6; | |
1242 | goto Exit; | |
1243 | } | |
1244 | ||
1245 | /* Otherwise, check the groups. */ | |
1246 | if (! (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)) { | |
1247 | int is_member; | |
1248 | ||
1249 | if (kauth_cred_ismember_gid(cred, obj_gid, &is_member) == 0 && is_member) { | |
1250 | permissions = ((u_int32_t)obj_mode & S_IRWXG) >> 3; | |
1251 | goto Exit; | |
1252 | } | |
1253 | } | |
1254 | ||
1255 | /* Otherwise, settle for 'others' access. */ | |
1256 | permissions = (u_int32_t)obj_mode & S_IRWXO; | |
1257 | ||
1258 | Exit: | |
1259 | return (permissions); | |
1260 | } | |
1261 | ||
1262 | ||
1263 | /* | |
1264 | * =========================================================================== | |
1265 | * Support functions for filling up a vnode_attr structure based on attributes | |
1266 | * requested. | |
1267 | * =========================================================================== | |
1268 | */ | |
1269 | void | |
1270 | get_vattr_data_for_attrs(struct attrlist *alp, struct vnode_attr *vap, | |
1271 | struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *descp, | |
1272 | struct cat_attr *atrp, struct cat_fork *datafork, struct cat_fork *rsrcfork, | |
1273 | vfs_context_t ctx) | |
1274 | { | |
1275 | if (alp->commonattr) | |
1276 | vattr_data_for_common_attrs(alp, vap, hfsmp, vp, descp, atrp, | |
1277 | ctx); | |
1278 | ||
1279 | if (alp->dirattr && S_ISDIR(atrp->ca_mode)) | |
1280 | vattr_data_for_dir_attrs(alp, vap, hfsmp, vp, descp, atrp); | |
1281 | ||
1282 | if (alp->fileattr && !S_ISDIR(atrp->ca_mode)) { | |
1283 | vattr_data_for_file_attrs(alp, vap, hfsmp, atrp, datafork, | |
1284 | rsrcfork, vp); | |
1285 | } | |
1286 | } | |
1287 | ||
1288 | static void | |
1289 | copy_name_attr(struct vnode_attr *vap, struct vnode *vp, const u_int8_t *name, | |
1290 | int namelen) | |
1291 | { | |
1292 | char *mpname; | |
1293 | size_t mpnamelen; | |
1294 | u_int32_t attrlength; | |
1295 | u_int8_t empty = 0; | |
1296 | ||
1297 | /* A cnode's name may be incorrect for the root of a mounted | |
1298 | * filesystem (it can be mounted on a different directory name | |
1299 | * than the name of the volume, such as "blah-1"). So for the | |
1300 | * root directory, it's best to return the last element of the | |
1301 | location where the volume's mounted: | |
1302 | */ | |
1303 | if ((vp != NULL) && vnode_isvroot(vp) && | |
1304 | (mpname = mountpointname(vnode_mount(vp)))) { | |
1305 | mpnamelen = strlen(mpname); | |
1306 | ||
1307 | /* Trim off any trailing slashes: */ | |
1308 | while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) | |
1309 | --mpnamelen; | |
1310 | ||
1311 | /* If there's anything left, use it instead of the volume's name */ | |
1312 | if (mpnamelen > 0) { | |
1313 | name = (u_int8_t *)mpname; | |
1314 | namelen = mpnamelen; | |
1315 | } | |
1316 | } | |
1317 | ||
1318 | if (name == NULL) { | |
1319 | name = ∅ | |
1320 | namelen = 0; | |
1321 | } | |
1322 | ||
1323 | attrlength = namelen + 1; | |
1324 | (void) strncpy((char *)vap->va_name, (const char *) name, attrlength); | |
1325 | /* | |
1326 | * round upto 8 and zero out the rounded up bytes. | |
1327 | */ | |
1328 | attrlength = min(kHFSPlusMaxFileNameBytes, ((attrlength + 7) & ~0x07)); | |
1329 | bzero(vap->va_name + attrlength, kHFSPlusMaxFileNameBytes - attrlength); | |
1330 | } | |
1331 | ||
1332 | static void | |
1333 | vattr_data_for_common_attrs( struct attrlist *alp, struct vnode_attr *vap, | |
1334 | struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *cdp, | |
1335 | struct cat_attr *cap, vfs_context_t ctx) | |
1336 | { | |
1337 | attrgroup_t attr = alp->commonattr; | |
1338 | struct mount *mp = HFSTOVFS(hfsmp); | |
1339 | uid_t cuid = 1; | |
1340 | int isroot = 0; | |
1341 | ||
1342 | if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) { | |
1343 | cuid = kauth_cred_getuid(vfs_context_ucred(ctx)); | |
1344 | isroot = cuid == 0; | |
1345 | } | |
1346 | ||
1347 | if (ATTR_CMN_NAME & attr) { | |
1348 | if (vap->va_name) { | |
1349 | copy_name_attr(vap, vp, cdp->cd_nameptr, | |
1350 | cdp->cd_namelen); | |
1351 | VATTR_SET_SUPPORTED(vap, va_name); | |
1352 | } else { | |
1353 | VATTR_CLEAR_SUPPORTED(vap, va_name); | |
1354 | } | |
1355 | } | |
1356 | ||
1357 | if (ATTR_CMN_DEVID & attr) { | |
1358 | vap->va_devid = hfsmp->hfs_raw_dev; | |
1359 | VATTR_SET_SUPPORTED(vap, va_devid); | |
1360 | } | |
1361 | ||
1362 | if (ATTR_CMN_FSID & attr) { | |
1363 | vap->va_fsid64.val[0] = hfsmp->hfs_raw_dev; | |
1364 | vap->va_fsid64.val[1] = vfs_typenum(mp); | |
1365 | VATTR_SET_SUPPORTED(vap, va_fsid64); | |
1366 | } | |
1367 | /* | |
1368 | * We always provide the objtype even if not asked because VFS helper | |
1369 | * functions depend on knowing the object's type. | |
1370 | */ | |
1371 | vap->va_objtype = IFTOVT(cap->ca_mode); | |
1372 | VATTR_SET_SUPPORTED(vap, va_objtype); | |
1373 | ||
1374 | if (ATTR_CMN_OBJTAG & attr) { | |
1375 | vap->va_objtag = VT_HFS; | |
1376 | VATTR_SET_SUPPORTED(vap, va_objtag); | |
1377 | } | |
1378 | /* | |
1379 | * Exporting file IDs from HFS Plus: | |
1380 | * | |
1381 | * For "normal" files the c_fileid is the same value as the | |
1382 | * c_cnid. But for hard link files, they are different - the | |
1383 | * c_cnid belongs to the active directory entry (ie the link) | |
1384 | * and the c_fileid is for the actual inode (ie the data file). | |
1385 | * | |
1386 | * The stat call (getattr) will always return the c_fileid | |
1387 | * and Carbon APIs, which are hardlink-ignorant, will always | |
1388 | * receive the c_cnid (from getattrlist). | |
1389 | */ | |
1390 | if ((ATTR_CMN_OBJID & attr) || | |
1391 | (ATTR_CMN_OBJPERMANENTID & attr)) { | |
1392 | vap->va_linkid = cdp->cd_cnid; | |
1393 | VATTR_SET_SUPPORTED(vap, va_linkid); | |
1394 | } | |
1395 | ||
1396 | if (ATTR_CMN_PAROBJID & attr) { | |
1397 | vap->va_parentid = cdp->cd_parentcnid; | |
1398 | VATTR_SET_SUPPORTED(vap, va_parentid); | |
1399 | } | |
1400 | ||
1401 | if (ATTR_CMN_SCRIPT & attr) { | |
1402 | vap->va_encoding = cdp->cd_encoding; | |
1403 | VATTR_SET_SUPPORTED(vap, va_encoding); | |
1404 | } | |
1405 | ||
1406 | if (ATTR_CMN_CRTIME & attr) { | |
1407 | vap->va_create_time.tv_sec = cap->ca_itime; | |
1408 | vap->va_create_time.tv_nsec = 0; | |
1409 | VATTR_SET_SUPPORTED(vap, va_create_time); | |
1410 | } | |
1411 | ||
1412 | if (ATTR_CMN_MODTIME & attr) { | |
1413 | vap->va_modify_time.tv_sec = cap->ca_mtime; | |
1414 | vap->va_modify_time.tv_nsec = 0; | |
1415 | VATTR_SET_SUPPORTED(vap, va_modify_time); | |
1416 | } | |
1417 | ||
1418 | if (ATTR_CMN_CHGTIME & attr) { | |
1419 | vap->va_change_time.tv_sec = cap->ca_ctime; | |
1420 | vap->va_change_time.tv_nsec = 0; | |
1421 | VATTR_SET_SUPPORTED(vap, va_change_time); | |
1422 | } | |
1423 | ||
1424 | if (ATTR_CMN_ACCTIME & attr) { | |
1425 | vap->va_access_time.tv_sec = cap->ca_atime; | |
1426 | vap->va_access_time.tv_nsec = 0; | |
1427 | VATTR_SET_SUPPORTED(vap, va_access_time); | |
1428 | } | |
1429 | ||
1430 | if (ATTR_CMN_BKUPTIME & attr) { | |
1431 | vap->va_backup_time.tv_sec = cap->ca_btime; | |
1432 | vap->va_backup_time.tv_nsec = 0; | |
1433 | VATTR_SET_SUPPORTED(vap, va_backup_time); | |
1434 | } | |
1435 | ||
1436 | if (ATTR_CMN_FNDRINFO & attr) { | |
1437 | u_int8_t *finfo = NULL; | |
1438 | ||
1439 | bcopy(&cap->ca_finderinfo, &vap->va_finderinfo[0], | |
1440 | sizeof(u_int8_t) * 32); | |
1441 | finfo = (u_int8_t*)(&vap->va_finderinfo[0]); | |
1442 | ||
1443 | /* Don't expose a symlink's private type/creator. */ | |
1444 | if (S_ISLNK(cap->ca_mode)) { | |
1445 | struct FndrFileInfo *fip; | |
1446 | ||
1447 | fip = (struct FndrFileInfo *)finfo; | |
1448 | fip->fdType = 0; | |
1449 | fip->fdCreator = 0; | |
1450 | } | |
1451 | ||
1452 | /* advance 16 bytes into the attrbuf */ | |
1453 | finfo = finfo + 16; | |
1454 | ||
1455 | /* also don't expose the date_added or write_gen_counter fields */ | |
1456 | if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) { | |
1457 | struct FndrExtendedFileInfo *extinfo = | |
1458 | (struct FndrExtendedFileInfo *)finfo; | |
1459 | extinfo->document_id = 0; | |
1460 | extinfo->date_added = 0; | |
1461 | extinfo->write_gen_counter = 0; | |
1462 | } else if (S_ISDIR(cap->ca_mode)) { | |
1463 | struct FndrExtendedDirInfo *extinfo = | |
1464 | (struct FndrExtendedDirInfo *)finfo; | |
1465 | extinfo->document_id = 0; | |
1466 | extinfo->date_added = 0; | |
1467 | extinfo->write_gen_counter = 0; | |
1468 | } | |
1469 | ||
1470 | VATTR_SET_SUPPORTED(vap, va_finderinfo); | |
1471 | } | |
1472 | ||
1473 | if (ATTR_CMN_OWNERID & attr) { | |
1474 | uid_t nuid = cap->ca_uid; | |
1475 | ||
1476 | if (!isroot) { | |
1477 | if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) | |
1478 | nuid = cuid; | |
1479 | else if (nuid == UNKNOWNUID) | |
1480 | nuid = cuid; | |
1481 | } | |
1482 | ||
1483 | vap->va_uid = nuid; | |
1484 | VATTR_SET_SUPPORTED(vap, va_uid); | |
1485 | } | |
1486 | ||
1487 | if (ATTR_CMN_GRPID & attr) { | |
1488 | gid_t ngid = cap->ca_gid; | |
1489 | ||
1490 | if (!isroot) { | |
1491 | gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx)); | |
1492 | if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) | |
1493 | ngid = cgid; | |
1494 | else if (ngid == UNKNOWNUID) | |
1495 | ngid = cgid; | |
1496 | } | |
1497 | ||
1498 | vap->va_gid = ngid; | |
1499 | VATTR_SET_SUPPORTED(vap, va_gid); | |
1500 | } | |
1501 | ||
1502 | if (ATTR_CMN_ACCESSMASK & attr) { | |
1503 | uint32_t nmode; | |
1504 | /* | |
1505 | * [2856576] Since we are dynamically changing the owner, also | |
1506 | * effectively turn off the set-user-id and set-group-id bits, | |
1507 | * just like chmod(2) would when changing ownership. This prevents | |
1508 | * a security hole where set-user-id programs run as whoever is | |
1509 | * logged on (or root if nobody is logged in yet!) | |
1510 | */ | |
1511 | nmode = (cap->ca_uid == UNKNOWNUID) ? | |
1512 | cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode; | |
1513 | ||
1514 | vap->va_mode = nmode; | |
1515 | VATTR_SET_SUPPORTED(vap, va_mode); | |
1516 | } | |
1517 | ||
1518 | if (ATTR_CMN_FLAGS & attr) { | |
1519 | vap->va_flags = cap->ca_flags; | |
1520 | VATTR_SET_SUPPORTED(vap, va_flags); | |
1521 | } | |
1522 | ||
1523 | if (ATTR_CMN_GEN_COUNT & attr) { | |
1524 | vap->va_write_gencount = hfs_get_gencount_from_blob( | |
1525 | (const uint8_t *)cap->ca_finderinfo, cap->ca_mode); | |
1526 | VATTR_SET_SUPPORTED(vap, va_write_gencount); | |
1527 | } | |
1528 | ||
1529 | if (ATTR_CMN_DOCUMENT_ID & attr) { | |
1530 | vap->va_document_id = hfs_get_document_id_from_blob( | |
1531 | (const uint8_t *)cap->ca_finderinfo, cap->ca_mode); | |
1532 | VATTR_SET_SUPPORTED(vap, va_document_id); | |
1533 | } | |
1534 | ||
1535 | if (ATTR_CMN_USERACCESS & attr) { | |
1536 | u_int32_t user_access; | |
1537 | ||
1538 | /* Take the long path when we have an ACL */ | |
1539 | if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) { | |
1540 | user_access = hfs_real_user_access(vp, ctx); | |
1541 | } else { | |
1542 | user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid, | |
1543 | cap->ca_mode, mp, vfs_context_ucred(ctx), 0); | |
1544 | } | |
1545 | /* Also consider READ-ONLY file system. */ | |
1546 | if (vfs_flags(mp) & MNT_RDONLY) { | |
1547 | user_access &= ~W_OK; | |
1548 | } | |
1549 | /* Locked objects are not writable either */ | |
1550 | if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(ctx) != 0)) | |
1551 | user_access &= ~W_OK; | |
1552 | if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(ctx) == 0)) | |
1553 | user_access &= ~W_OK; | |
1554 | ||
1555 | vap->va_user_access = user_access; | |
1556 | VATTR_SET_SUPPORTED(vap, va_user_access); | |
1557 | } | |
1558 | ||
1559 | /* | |
1560 | * Right now the best we can do is tell if we *don't* have extended | |
1561 | * security (like hfs_vnop_getattr). | |
1562 | */ | |
1563 | if (ATTR_CMN_EXTENDED_SECURITY & attr) { | |
1564 | if (!(cap->ca_recflags & kHFSHasSecurityMask)) { | |
1565 | vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE; | |
1566 | VATTR_SET_SUPPORTED(vap, va_acl); | |
1567 | } | |
1568 | } | |
1569 | ||
1570 | if (ATTR_CMN_FILEID & attr) { | |
1571 | vap->va_fileid = cap->ca_fileid; | |
1572 | VATTR_SET_SUPPORTED(vap, va_fileid); | |
1573 | } | |
1574 | ||
1575 | if (ATTR_CMN_PARENTID & attr) { | |
1576 | vap->va_parentid = cdp->cd_parentcnid; | |
1577 | VATTR_SET_SUPPORTED(vap, va_parentid); | |
1578 | } | |
1579 | ||
1580 | if (ATTR_CMN_ADDEDTIME & attr) { | |
1581 | if (cap->ca_recflags & kHFSHasDateAddedMask) { | |
1582 | vap->va_addedtime.tv_sec = hfs_get_dateadded_from_blob( | |
1583 | (const uint8_t *)cap->ca_finderinfo, cap->ca_mode); | |
1584 | vap->va_addedtime.tv_nsec = 0; | |
1585 | VATTR_SET_SUPPORTED(vap, va_addedtime); | |
1586 | } | |
1587 | } | |
1588 | } | |
1589 | ||
1590 | static void | |
1591 | vattr_data_for_dir_attrs(struct attrlist *alp, struct vnode_attr *vap, | |
1592 | struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * descp, | |
1593 | struct cat_attr * cattrp) | |
1594 | { | |
1595 | attrgroup_t attr = alp->dirattr; | |
1596 | u_int32_t entries; | |
1597 | ||
1598 | /* | |
1599 | * The DIR_LINKCOUNT is the count of real directory hard links. | |
1600 | * (i.e. its not the sum of the implied "." and ".." references | |
1601 | * typically used in stat's st_nlink field) | |
1602 | */ | |
1603 | if (ATTR_DIR_LINKCOUNT & attr) { | |
1604 | vap->va_dirlinkcount = cattrp->ca_linkcount; | |
1605 | VATTR_SET_SUPPORTED(vap, va_dirlinkcount); | |
1606 | } | |
1607 | if (ATTR_DIR_ENTRYCOUNT & attr) { | |
1608 | entries = cattrp->ca_entries; | |
1609 | ||
1610 | if (descp->cd_parentcnid == kHFSRootParentID) { | |
1611 | if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) | |
1612 | --entries; /* hide private dir */ | |
1613 | if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) | |
1614 | --entries; /* hide private dir */ | |
1615 | if (hfsmp->jnl || | |
1616 | ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && | |
1617 | (hfsmp->hfs_flags & HFS_READ_ONLY))) | |
1618 | entries -= 2; /* hide the journal files */ | |
1619 | } | |
1620 | ||
1621 | vap->va_nchildren = entries; | |
1622 | VATTR_SET_SUPPORTED(vap, va_nchildren); | |
1623 | } | |
1624 | ||
1625 | if (ATTR_DIR_MOUNTSTATUS & attr) { | |
1626 | /* | |
1627 | * There is not vnode_attr for mount point status. | |
1628 | * XXX. Should there be ? | |
1629 | */ | |
1630 | u_int32_t mstatus = 0; | |
1631 | ||
1632 | if (vp != NULL && vnode_mountedhere(vp) != NULL) | |
1633 | mstatus = DIR_MNTSTATUS_MNTPOINT; | |
1634 | } | |
1635 | } | |
1636 | ||
1637 | static void | |
1638 | vattr_data_for_file_attrs(struct attrlist *alp, struct vnode_attr *vap, | |
1639 | struct hfsmount *hfsmp, struct cat_attr *cattrp, struct cat_fork *datafork, | |
1640 | struct cat_fork *rsrcfork, struct vnode *vp) | |
1641 | { | |
1642 | #if !HFS_COMPRESSION | |
1643 | #pragma unused(vp) | |
1644 | #endif | |
1645 | attrgroup_t attr = alp->fileattr; | |
1646 | off_t da_size, rsrc_len, rsrc_alloc; | |
1647 | u_int32_t allocblksize; | |
1648 | ||
1649 | allocblksize = HFSTOVCB(hfsmp)->blockSize; | |
1650 | ||
1651 | off_t datasize = datafork->cf_size; | |
1652 | off_t totalsize = datasize + rsrcfork->cf_size; | |
1653 | #if HFS_COMPRESSION | |
1654 | int handle_compressed; | |
1655 | handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1); | |
1656 | ||
1657 | if (handle_compressed) { | |
1658 | if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) { | |
1659 | if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) { /* 1 == don't take the cnode lock */ | |
1660 | /* total size of a compressed file is just the data size */ | |
1661 | totalsize = datasize; | |
1662 | } | |
1663 | } | |
1664 | } | |
1665 | #endif | |
1666 | ||
1667 | if (ATTR_FILE_LINKCOUNT & attr) { | |
1668 | vap->va_nlink = cattrp->ca_linkcount; | |
1669 | VATTR_SET_SUPPORTED(vap, va_nlink); | |
1670 | } | |
1671 | if (ATTR_FILE_TOTALSIZE & attr) { | |
1672 | VATTR_RETURN(vap, va_total_size, totalsize); | |
1673 | } | |
1674 | if (ATTR_FILE_ALLOCSIZE & attr) { | |
1675 | VATTR_RETURN(vap, va_total_alloc, | |
1676 | (off_t)cattrp->ca_blocks * (off_t)allocblksize ); | |
1677 | } | |
1678 | if (ATTR_FILE_IOBLOCKSIZE & attr) { | |
1679 | VATTR_RETURN(vap, va_iosize, hfsmp->hfs_logBlockSize); | |
1680 | } | |
1681 | ||
1682 | /* ATTR_FILE_CLUMPSIZE is obsolete */ | |
1683 | ||
1684 | if (ATTR_FILE_DEVTYPE & attr) { | |
1685 | dev_t dev = 0; | |
1686 | ||
1687 | if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode)) | |
1688 | dev = (u_int32_t)cattrp->ca_rdev; | |
1689 | ||
1690 | VATTR_RETURN(vap, va_rdev, dev); | |
1691 | } | |
1692 | ||
1693 | if (ATTR_FILE_DATALENGTH & attr) { | |
1694 | VATTR_RETURN(vap, va_data_size, datasize); | |
1695 | } | |
1696 | #if HFS_COMPRESSION | |
1697 | /* fake the data fork size on a decmpfs compressed file to reflect the | |
1698 | * uncompressed size. This ensures proper reading and copying of these | |
1699 | * files. | |
1700 | * NOTE: we may need to get the vnode here because the vnode parameter | |
1701 | * passed by hfs_vnop_readdirattr() may be null. | |
1702 | */ | |
1703 | ||
1704 | if (handle_compressed) { | |
1705 | da_size = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize; | |
1706 | rsrc_len = 0; | |
1707 | rsrc_alloc = 0; | |
1708 | } | |
1709 | else | |
1710 | #endif | |
1711 | { | |
1712 | da_size = (off_t)datafork->cf_blocks * (off_t)allocblksize; | |
1713 | rsrc_len = rsrcfork->cf_size; | |
1714 | rsrc_alloc = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize; | |
1715 | } | |
1716 | ||
1717 | if (ATTR_FILE_DATAALLOCSIZE & attr) { | |
1718 | VATTR_RETURN(vap, va_data_alloc, da_size); | |
1719 | } | |
1720 | ||
1721 | if (ATTR_FILE_RSRCLENGTH & attr) { | |
1722 | VATTR_RETURN(vap, va_rsrc_length, rsrc_len); | |
1723 | } | |
1724 | ||
1725 | if (ATTR_FILE_RSRCALLOCSIZE & attr) { | |
1726 | VATTR_RETURN(vap, va_rsrc_alloc, rsrc_alloc); | |
1727 | } | |
1728 | } |