]>
Commit | Line | Data |
---|---|---|
9bccf70c | 1 | /* |
22ba694c | 2 | * Copyright (c) 2000-2014 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
9bccf70c | 5 | * |
2d21ac55 A |
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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
9bccf70c A |
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> | |
91447636 A |
42 | #include <sys/mount_internal.h> |
43 | #include <sys/kauth.h> | |
6d2010ae | 44 | #include <sys/fsctl.h> |
91447636 A |
45 | |
46 | #include <kern/locks.h> | |
9bccf70c A |
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" | |
2d21ac55 | 53 | #include "hfs_btreeio.h" |
9bccf70c A |
54 | |
55 | /* Packing routines: */ | |
56 | ||
2d21ac55 A |
57 | static void packnameattr(struct attrblock *abp, struct vnode *vp, |
58 | const u_int8_t *name, int namelen); | |
9bccf70c A |
59 | |
60 | static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp, | |
61 | struct vnode *vp, struct cat_desc * cdp, | |
b0d623f7 | 62 | struct cat_attr * cap, struct vfs_context *ctx); |
9bccf70c A |
63 | |
64 | static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp, | |
65 | struct cat_attr *cattrp, struct cat_fork *datafork, | |
b0d623f7 | 66 | struct cat_fork *rsrcfork, struct vnode *vp); |
9bccf70c A |
67 | |
68 | static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp, | |
69 | struct vnode *vp, struct cat_desc * descp, | |
91447636 | 70 | struct cat_attr * cattrp); |
9bccf70c | 71 | |
2d21ac55 | 72 | static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx); |
9bccf70c | 73 | |
fe8ab488 A |
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 | ||
9bccf70c A |
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. | |
9bccf70c | 102 | */ |
9bccf70c | 103 | int |
91447636 A |
104 | hfs_vnop_readdirattr(ap) |
105 | struct vnop_readdirattr_args /* { | |
9bccf70c A |
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; | |
91447636 | 114 | vfs_context_t a_context; |
9bccf70c A |
115 | } */ *ap; |
116 | { | |
fe8ab488 A |
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 | { | |
91447636 A |
192 | struct cnode *dcp; |
193 | struct hfsmount * hfsmp; | |
2d21ac55 A |
194 | u_int32_t fixedblocksize; |
195 | u_int32_t maxattrblocksize; | |
196 | u_int32_t currattrbufsize; | |
9bccf70c | 197 | void *attrbufptr = NULL; |
fe8ab488 A |
198 | void *attrptr = NULL; |
199 | void *varptr = NULL; | |
200 | caddr_t namebuf = NULL; | |
9bccf70c A |
201 | struct attrblock attrblk; |
202 | int error = 0; | |
2d21ac55 | 203 | int index = 0; |
fe8ab488 | 204 | int i = 0; |
9bccf70c | 205 | struct cat_desc *lastdescp = NULL; |
9bccf70c | 206 | struct cat_entrylist *ce_list = NULL; |
91447636 A |
207 | directoryhint_t *dirhint = NULL; |
208 | unsigned int tag; | |
2d21ac55 A |
209 | int maxentries; |
210 | int lockflags; | |
b0d623f7 | 211 | u_int32_t dirchg = 0; |
fe8ab488 | 212 | int reachedeof = 0; |
b4c24cb9 | 213 | |
fe8ab488 A |
214 | *(actualcount) = 0; |
215 | *(eofflag) = 0; | |
9bccf70c | 216 | |
fe8ab488 | 217 | if ((uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1)) |
9bccf70c | 218 | return (EINVAL); |
6d2010ae | 219 | |
316670eb | 220 | if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) { |
6d2010ae A |
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 | ||
2d21ac55 A |
231 | /* |
232 | * Take an exclusive directory lock since we manipulate the directory hints | |
233 | */ | |
39236c6e | 234 | if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { |
91447636 | 235 | return (error); |
2d21ac55 | 236 | } |
91447636 A |
237 | dcp = VTOC(dvp); |
238 | hfsmp = VTOHFS(dvp); | |
239 | ||
b0d623f7 | 240 | dirchg = dcp->c_dirchangecnt; |
9bccf70c | 241 | |
2d21ac55 | 242 | /* Extract directory index and tag (sequence number) from uio_offset */ |
91447636 A |
243 | index = uio_offset(uio) & HFS_INDEX_MASK; |
244 | tag = uio_offset(uio) & ~HFS_INDEX_MASK; | |
fe8ab488 A |
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 | */ | |
9bccf70c A |
253 | |
254 | /* Get a buffer to hold packed attributes. */ | |
2d21ac55 | 255 | fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */ |
9bccf70c | 256 | |
fe8ab488 A |
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 | } | |
2d21ac55 A |
279 | /* Get a detached directory hint (cnode must be locked exclusive) */ |
280 | dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE); | |
91447636 A |
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 | /* | |
2d21ac55 A |
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. | |
91447636 | 295 | */ |
2d21ac55 | 296 | maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE); |
fe8ab488 A |
297 | /* There is maxcount for the bulk vnop */ |
298 | if (!vap) | |
299 | maxentries = min(maxentries, maxcount); | |
2d21ac55 A |
300 | maxentries = min(maxentries, MAXCATENTRIES); |
301 | if (maxentries < 1) { | |
302 | error = EINVAL; | |
303 | goto exit2; | |
91447636 | 304 | } |
2d21ac55 A |
305 | |
306 | /* Initialize a catalog entry list. */ | |
307 | MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK); | |
b0d623f7 A |
308 | if (ce_list == NULL) { |
309 | error = ENOMEM; | |
310 | goto exit2; | |
311 | } | |
2d21ac55 A |
312 | bzero(ce_list, CE_LIST_SIZE(maxentries)); |
313 | ce_list->maxentries = maxentries; | |
314 | ||
9bccf70c | 315 | /* |
2d21ac55 | 316 | * Populate the ce_list from the catalog file. |
9bccf70c | 317 | */ |
2d21ac55 A |
318 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); |
319 | ||
fe8ab488 | 320 | error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof); |
2d21ac55 A |
321 | /* Don't forget to release the descriptors later! */ |
322 | ||
323 | hfs_systemfile_unlock(hfsmp, lockflags); | |
324 | ||
fe8ab488 A |
325 | if ((error == ENOENT) || (reachedeof != 0)) { |
326 | *(eofflag) = TRUE; | |
2d21ac55 A |
327 | error = 0; |
328 | } | |
329 | if (error) { | |
330 | goto exit1; | |
331 | } | |
332 | ||
fe8ab488 A |
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 | C_FORCEUPDATE); | |
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 | ||
2d21ac55 A |
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 | ||
fe8ab488 A |
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 | ) { | |
9bccf70c | 381 | /* |
2d21ac55 | 382 | * Obtain vnode for our vnode_authorize() calls. |
9bccf70c | 383 | */ |
6d2010ae | 384 | if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) { |
9bccf70c | 385 | vp = NULL; |
9bccf70c | 386 | } |
fe8ab488 | 387 | } else if (vap || !(options & FSOPT_NOINMEMUPDATE)) { |
2d21ac55 | 388 | /* Get in-memory cnode data (if any). */ |
6d2010ae | 389 | vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0); |
2d21ac55 A |
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; | |
9bccf70c | 404 | } |
2d21ac55 A |
405 | /* All done with cnode. */ |
406 | hfs_unlock(cp); | |
407 | cp = NULL; | |
408 | } | |
9bccf70c | 409 | |
fe8ab488 A |
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 | } | |
91447636 | 429 | |
fe8ab488 A |
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)) { | |
2d21ac55 | 434 | break; |
fe8ab488 A |
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 | } | |
2d21ac55 | 473 | } |
fe8ab488 A |
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 | ||
2d21ac55 A |
490 | /* Save the last valid catalog entry */ |
491 | lastdescp = &ce_list->entry[i].ce_desc; | |
492 | index++; | |
fe8ab488 | 493 | *actualcount += 1; |
2d21ac55 | 494 | |
fe8ab488 A |
495 | /* Do we have the bare minimum for the next entry ? */ |
496 | if (resid < sizeof(uint32_t)) | |
2d21ac55 | 497 | break; |
9bccf70c | 498 | } |
2d21ac55 | 499 | } /* for each catalog entry */ |
9bccf70c | 500 | |
bb59bff1 A |
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 | ||
2d21ac55 A |
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 | } | |
9bccf70c | 515 | |
fe8ab488 A |
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) { | |
2d21ac55 A |
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; | |
fe8ab488 A |
539 | } |
540 | ||
2d21ac55 A |
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 | ||
39236c6e | 546 | (void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS); |
2d21ac55 | 547 | dcp = VTOC(dvp); |
9bccf70c | 548 | |
2d21ac55 A |
549 | exit1: |
550 | /* Pack directory index and tag into uio_offset. */ | |
91447636 A |
551 | while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS; |
552 | uio_setoffset(uio, index | tag); | |
553 | dirhint->dh_index |= tag; | |
9bccf70c | 554 | |
2d21ac55 | 555 | exit2: |
fe8ab488 A |
556 | if (newstate) |
557 | *newstate = dirchg; | |
2d21ac55 | 558 | |
fe8ab488 A |
559 | /* |
560 | * Drop directory hint on error or if there are no more entries, | |
561 | * only if EOF was seen. | |
562 | */ | |
2d21ac55 | 563 | if (dirhint) { |
fe8ab488 | 564 | if ((error != 0) || *(eofflag)) |
2d21ac55 A |
565 | hfs_reldirhint(dcp, dirhint); |
566 | else | |
567 | hfs_insertdirhint(dcp, dirhint); | |
91447636 | 568 | } |
fe8ab488 A |
569 | if (namebuf) { |
570 | FREE(namebuf, M_TEMP); | |
571 | vap->va_name = NULL; | |
572 | } | |
9bccf70c A |
573 | if (attrbufptr) |
574 | FREE(attrbufptr, M_TEMP); | |
575 | if (ce_list) | |
576 | FREE(ce_list, M_TEMP); | |
2d21ac55 | 577 | |
fe8ab488 A |
578 | if (vap && *actualcount && error) |
579 | error = 0; | |
580 | ||
91447636 | 581 | hfs_unlock(dcp); |
9bccf70c A |
582 | return (error); |
583 | } | |
584 | ||
585 | ||
586 | /*==================== Attribute list support routines ====================*/ | |
587 | ||
588 | /* | |
589 | * Pack cnode attributes into an attribute block. | |
590 | */ | |
6d2010ae | 591 | __private_extern__ |
9bccf70c A |
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, | |
55e303ae | 599 | struct cat_fork *rsrcfork, |
b0d623f7 | 600 | struct vfs_context *ctx) |
2d21ac55 A |
601 | { |
602 | struct attrlist *attrlistp = abp->ab_attrlist; | |
91447636 | 603 | |
2d21ac55 | 604 | if (attrlistp->commonattr) |
b0d623f7 | 605 | packcommonattr(abp, hfsmp, vp, descp, attrp, ctx); |
9bccf70c | 606 | |
2d21ac55 A |
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)) | |
b0d623f7 | 611 | packfileattr(abp, hfsmp, attrp, datafork, rsrcfork, vp); |
9bccf70c A |
612 | } |
613 | ||
614 | ||
2d21ac55 A |
615 | static char* |
616 | mountpointname(struct mount *mp) | |
9bccf70c | 617 | { |
2d21ac55 A |
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 | } | |
9bccf70c | 641 | |
9bccf70c | 642 | |
2d21ac55 A |
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; | |
9bccf70c | 656 | |
2d21ac55 A |
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; | |
9bccf70c | 675 | } |
9bccf70c | 676 | } |
2d21ac55 A |
677 | if (name == NULL) { |
678 | name = ∅ | |
679 | namelen = 0; | |
9bccf70c | 680 | } |
2d21ac55 A |
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; | |
9bccf70c A |
697 | *abp->ab_varbufpp = varbufptr; |
698 | } | |
699 | ||
9bccf70c A |
700 | static void |
701 | packcommonattr( | |
702 | struct attrblock *abp, | |
703 | struct hfsmount *hfsmp, | |
704 | struct vnode *vp, | |
705 | struct cat_desc * cdp, | |
55e303ae | 706 | struct cat_attr * cap, |
b0d623f7 | 707 | struct vfs_context * ctx) |
9bccf70c A |
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; | |
b0d623f7 | 713 | boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx)); |
2d21ac55 A |
714 | uid_t cuid = 1; |
715 | int isroot = 0; | |
716 | ||
717 | if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) { | |
b0d623f7 | 718 | cuid = kauth_cred_getuid(vfs_context_ucred(ctx)); |
2d21ac55 A |
719 | isroot = cuid == 0; |
720 | } | |
9bccf70c A |
721 | |
722 | if (ATTR_CMN_NAME & attr) { | |
91447636 | 723 | packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen); |
9bccf70c A |
724 | attrbufptr = *abp->ab_attrbufpp; |
725 | varbufptr = *abp->ab_varbufpp; | |
726 | } | |
727 | if (ATTR_CMN_DEVID & attr) { | |
2d21ac55 A |
728 | *((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev; |
729 | attrbufptr = ((dev_t *)attrbufptr) + 1; | |
9bccf70c A |
730 | } |
731 | if (ATTR_CMN_FSID & attr) { | |
91447636 A |
732 | fsid_t fsid; |
733 | ||
39236c6e A |
734 | fsid.val[0] = hfsmp->hfs_raw_dev; |
735 | fsid.val[1] = vfs_typenum(mp); | |
91447636 | 736 | *((fsid_t *)attrbufptr) = fsid; |
2d21ac55 | 737 | attrbufptr = ((fsid_t *)attrbufptr) + 1; |
9bccf70c A |
738 | } |
739 | if (ATTR_CMN_OBJTYPE & attr) { | |
2d21ac55 A |
740 | *((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode); |
741 | attrbufptr = ((fsobj_type_t *)attrbufptr) + 1; | |
9bccf70c A |
742 | } |
743 | if (ATTR_CMN_OBJTAG & attr) { | |
2d21ac55 A |
744 | *((fsobj_tag_t *)attrbufptr) = VT_HFS; |
745 | attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1; | |
9bccf70c A |
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 | */ | |
2d21ac55 | 759 | if (ATTR_CMN_OBJID & attr) { |
9bccf70c A |
760 | ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; |
761 | ((fsobj_id_t *)attrbufptr)->fid_generation = 0; | |
2d21ac55 | 762 | attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; |
9bccf70c A |
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; | |
2d21ac55 | 767 | attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; |
9bccf70c A |
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; | |
2d21ac55 | 772 | attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; |
9bccf70c A |
773 | } |
774 | if (ATTR_CMN_SCRIPT & attr) { | |
2d21ac55 A |
775 | *((text_encoding_t *)attrbufptr) = cdp->cd_encoding; |
776 | attrbufptr = ((text_encoding_t *)attrbufptr) + 1; | |
9bccf70c A |
777 | } |
778 | if (ATTR_CMN_CRTIME & attr) { | |
91447636 | 779 | if (is_64_bit) { |
b0d623f7 A |
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; | |
91447636 A |
783 | } |
784 | else { | |
b0d623f7 A |
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; | |
91447636 | 788 | } |
9bccf70c A |
789 | } |
790 | if (ATTR_CMN_MODTIME & attr) { | |
91447636 | 791 | if (is_64_bit) { |
b0d623f7 A |
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; | |
91447636 A |
795 | } |
796 | else { | |
b0d623f7 A |
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; | |
91447636 | 800 | } |
9bccf70c A |
801 | } |
802 | if (ATTR_CMN_CHGTIME & attr) { | |
91447636 | 803 | if (is_64_bit) { |
b0d623f7 A |
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; | |
91447636 A |
807 | } |
808 | else { | |
b0d623f7 A |
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; | |
91447636 | 812 | } |
9bccf70c A |
813 | } |
814 | if (ATTR_CMN_ACCTIME & attr) { | |
91447636 | 815 | if (is_64_bit) { |
b0d623f7 A |
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; | |
91447636 A |
819 | } |
820 | else { | |
b0d623f7 A |
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; | |
91447636 | 824 | } |
9bccf70c A |
825 | } |
826 | if (ATTR_CMN_BKUPTIME & attr) { | |
91447636 | 827 | if (is_64_bit) { |
b0d623f7 A |
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; | |
91447636 A |
831 | } |
832 | else { | |
b0d623f7 A |
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; | |
91447636 | 836 | } |
9bccf70c A |
837 | } |
838 | if (ATTR_CMN_FNDRINFO & attr) { | |
6d2010ae | 839 | u_int8_t *finfo = NULL; |
9bccf70c | 840 | bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32); |
6d2010ae A |
841 | finfo = (u_int8_t*)attrbufptr; |
842 | ||
2d21ac55 A |
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 | } | |
6d2010ae A |
851 | |
852 | /* advance 16 bytes into the attrbuf */ | |
853 | finfo = finfo + 16; | |
39236c6e A |
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)) { | |
6d2010ae | 857 | struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo; |
22ba694c | 858 | extinfo->document_id = 0; |
6d2010ae | 859 | extinfo->date_added = 0; |
39236c6e | 860 | extinfo->write_gen_counter = 0; |
6d2010ae A |
861 | } |
862 | else if (S_ISDIR(cap->ca_mode)) { | |
863 | struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo; | |
22ba694c | 864 | extinfo->document_id = 0; |
6d2010ae | 865 | extinfo->date_added = 0; |
22ba694c | 866 | extinfo->write_gen_counter = 0; |
6d2010ae A |
867 | } |
868 | ||
2d21ac55 | 869 | attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32; |
9bccf70c A |
870 | } |
871 | if (ATTR_CMN_OWNERID & attr) { | |
2d21ac55 A |
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; | |
9bccf70c A |
883 | } |
884 | if (ATTR_CMN_GRPID & attr) { | |
2d21ac55 A |
885 | gid_t ngid = cap->ca_gid; |
886 | ||
887 | if (!isroot) { | |
b0d623f7 | 888 | gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx)); |
2d21ac55 A |
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; | |
9bccf70c A |
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 | */ | |
2d21ac55 A |
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; | |
9bccf70c A |
909 | } |
910 | if (ATTR_CMN_FLAGS & attr) { | |
2d21ac55 A |
911 | *((u_int32_t *)attrbufptr) = cap->ca_flags; |
912 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c A |
913 | } |
914 | if (ATTR_CMN_USERACCESS & attr) { | |
2d21ac55 A |
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, | |
39236c6e | 922 | cap->ca_mode, mp, vfs_context_ucred(ctx), 0); |
2d21ac55 A |
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; | |
9bccf70c A |
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, | |
91447636 | 956 | struct cat_attr * cattrp) |
9bccf70c A |
957 | { |
958 | attrgroup_t attr = abp->ab_attrlist->dirattr; | |
959 | void *attrbufptr = *abp->ab_attrbufpp; | |
2d21ac55 A |
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 | } | |
9bccf70c | 971 | if (ATTR_DIR_ENTRYCOUNT & attr) { |
2d21ac55 | 972 | entries = cattrp->ca_entries; |
9bccf70c | 973 | |
91447636 | 974 | if (descp->cd_parentcnid == kHFSRootParentID) { |
2d21ac55 A |
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) | |
b4c24cb9 | 978 | --entries; /* hide private dir */ |
2d21ac55 A |
979 | if (hfsmp->jnl || |
980 | ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && | |
981 | (hfsmp->hfs_flags & HFS_READ_ONLY))) | |
b4c24cb9 A |
982 | entries -= 2; /* hide the journal files */ |
983 | } | |
9bccf70c | 984 | |
2d21ac55 A |
985 | *((u_int32_t *)attrbufptr) = entries; |
986 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c A |
987 | } |
988 | if (ATTR_DIR_MOUNTSTATUS & attr) { | |
91447636 | 989 | if (vp != NULL && vnode_mountedhere(vp) != NULL) |
2d21ac55 | 990 | *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT; |
9bccf70c | 991 | else |
2d21ac55 A |
992 | *((u_int32_t *)attrbufptr) = 0; |
993 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c A |
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, | |
b0d623f7 A |
1004 | struct cat_fork *rsrcfork, |
1005 | struct vnode *vp) | |
9bccf70c | 1006 | { |
b0d623f7 A |
1007 | #if !HFS_COMPRESSION |
1008 | #pragma unused(vp) | |
1009 | #endif | |
9bccf70c A |
1010 | attrgroup_t attr = abp->ab_attrlist->fileattr; |
1011 | void *attrbufptr = *abp->ab_attrbufpp; | |
1012 | void *varbufptr = *abp->ab_varbufpp; | |
2d21ac55 | 1013 | u_int32_t allocblksize; |
9bccf70c A |
1014 | |
1015 | allocblksize = HFSTOVCB(hfsmp)->blockSize; | |
1016 | ||
b0d623f7 A |
1017 | off_t datasize = datafork->cf_size; |
1018 | off_t totalsize = datasize + rsrcfork->cf_size; | |
1019 | #if HFS_COMPRESSION | |
6d2010ae A |
1020 | int handle_compressed; |
1021 | handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1); | |
1022 | ||
1023 | if (handle_compressed) { | |
b0d623f7 A |
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 | ||
9bccf70c | 1033 | if (ATTR_FILE_LINKCOUNT & attr) { |
2d21ac55 A |
1034 | *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount; |
1035 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c A |
1036 | } |
1037 | if (ATTR_FILE_TOTALSIZE & attr) { | |
b0d623f7 | 1038 | *((off_t *)attrbufptr) = totalsize; |
2d21ac55 | 1039 | attrbufptr = ((off_t *)attrbufptr) + 1; |
9bccf70c A |
1040 | } |
1041 | if (ATTR_FILE_ALLOCSIZE & attr) { | |
2d21ac55 | 1042 | *((off_t *)attrbufptr) = |
9bccf70c | 1043 | (off_t)cattrp->ca_blocks * (off_t)allocblksize; |
2d21ac55 | 1044 | attrbufptr = ((off_t *)attrbufptr) + 1; |
9bccf70c A |
1045 | } |
1046 | if (ATTR_FILE_IOBLOCKSIZE & attr) { | |
2d21ac55 A |
1047 | *((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize; |
1048 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c A |
1049 | } |
1050 | if (ATTR_FILE_CLUMPSIZE & attr) { | |
2d21ac55 A |
1051 | *((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz; |
1052 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c A |
1053 | } |
1054 | if (ATTR_FILE_DEVTYPE & attr) { | |
1055 | if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode)) | |
2d21ac55 | 1056 | *((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev; |
9bccf70c | 1057 | else |
2d21ac55 A |
1058 | *((u_int32_t *)attrbufptr) = 0; |
1059 | attrbufptr = ((u_int32_t *)attrbufptr) + 1; | |
9bccf70c | 1060 | } |
b0d623f7 | 1061 | |
9bccf70c | 1062 | if (ATTR_FILE_DATALENGTH & attr) { |
b0d623f7 | 1063 | *((off_t *)attrbufptr) = datasize; |
2d21ac55 | 1064 | attrbufptr = ((off_t *)attrbufptr) + 1; |
9bccf70c | 1065 | } |
b0d623f7 A |
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 | ||
6d2010ae | 1074 | if ( handle_compressed ) { |
b0d623f7 A |
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 | } | |
9bccf70c | 1087 | } |
b0d623f7 A |
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 | } | |
9bccf70c A |
1103 | } |
1104 | *abp->ab_attrbufpp = attrbufptr; | |
1105 | *abp->ab_varbufpp = varbufptr; | |
1106 | } | |
1107 | ||
9bccf70c A |
1108 | /* |
1109 | * Calculate the total size of an attribute block. | |
1110 | */ | |
6d2010ae | 1111 | __private_extern__ |
9bccf70c A |
1112 | int |
1113 | hfs_attrblksize(struct attrlist *attrlist) | |
1114 | { | |
1115 | int size; | |
1116 | attrgroup_t a; | |
91447636 A |
1117 | int sizeof_timespec; |
1118 | boolean_t is_64_bit = proc_is64bit(current_proc()); | |
9bccf70c | 1119 | |
91447636 | 1120 | if (is_64_bit) |
b0d623f7 | 1121 | sizeof_timespec = sizeof(struct user64_timespec); |
91447636 | 1122 | else |
b0d623f7 | 1123 | sizeof_timespec = sizeof(struct user32_timespec); |
91447636 | 1124 | |
9bccf70c A |
1125 | DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0); |
1126 | ||
9bccf70c A |
1127 | DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0); |
1128 | ||
9bccf70c A |
1129 | DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0); |
1130 | ||
9bccf70c A |
1131 | DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0); |
1132 | ||
9bccf70c A |
1133 | DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0); |
1134 | ||
1135 | size = 0; | |
1136 | ||
1137 | if ((a = attrlist->commonattr) != 0) { | |
2d21ac55 | 1138 | if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference); |
9bccf70c A |
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); | |
2d21ac55 A |
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; | |
9bccf70c A |
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); | |
2d21ac55 A |
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 | } | |
9bccf70c | 1161 | if ((a = attrlist->dirattr) != 0) { |
2d21ac55 A |
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 | } | |
9bccf70c | 1166 | if ((a = attrlist->fileattr) != 0) { |
2d21ac55 | 1167 | if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t); |
9bccf70c A |
1168 | if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t); |
1169 | if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t); | |
2d21ac55 A |
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); | |
9bccf70c A |
1173 | if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t); |
1174 | if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t); | |
9bccf70c A |
1175 | if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t); |
1176 | if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t); | |
2d21ac55 | 1177 | } |
9bccf70c | 1178 | |
2d21ac55 | 1179 | return (size); |
9bccf70c A |
1180 | } |
1181 | ||
2d21ac55 A |
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 | ||
9bccf70c | 1223 | |
b0d623f7 | 1224 | u_int32_t |
9bccf70c | 1225 | DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, |
2d21ac55 | 1226 | struct mount *mp, kauth_cred_t cred, __unused struct proc *p) |
9bccf70c | 1227 | { |
b0d623f7 | 1228 | u_int32_t permissions; |
9bccf70c A |
1229 | |
1230 | if (obj_uid == UNKNOWNUID) | |
2d21ac55 | 1231 | obj_uid = kauth_cred_getuid(cred); |
9bccf70c A |
1232 | |
1233 | /* User id 0 (root) always gets access. */ | |
91447636 | 1234 | if (!suser(cred, NULL)) { |
9bccf70c A |
1235 | permissions = R_OK | W_OK | X_OK; |
1236 | goto Exit; | |
1237 | }; | |
1238 | ||
1239 | /* Otherwise, check the owner. */ | |
2d21ac55 | 1240 | if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) { |
b0d623f7 | 1241 | permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6; |
9bccf70c A |
1242 | goto Exit; |
1243 | } | |
1244 | ||
1245 | /* Otherwise, check the groups. */ | |
91447636 A |
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) { | |
b0d623f7 | 1250 | permissions = ((u_int32_t)obj_mode & S_IRWXG) >> 3; |
91447636 | 1251 | goto Exit; |
9bccf70c A |
1252 | } |
1253 | } | |
1254 | ||
1255 | /* Otherwise, settle for 'others' access. */ | |
b0d623f7 | 1256 | permissions = (u_int32_t)obj_mode & S_IRWXO; |
9bccf70c A |
1257 | |
1258 | Exit: | |
1259 | return (permissions); | |
1260 | } | |
1261 | ||
fe8ab488 A |
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 | } |