]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_xattr.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_xattr.c
1 /*
2 * Copyright (c) 2004-2013 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 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/ubc.h>
34 #include <sys/utfconv.h>
35 #include <sys/vnode.h>
36 #include <sys/xattr.h>
37 #include <sys/fcntl.h>
38 #include <sys/fsctl.h>
39 #include <sys/vnode_internal.h>
40 #include <sys/kauth.h>
41
42 #include "hfs.h"
43 #include "hfs_cnode.h"
44 #include "hfs_mount.h"
45 #include "hfs_format.h"
46 #include "hfs_endian.h"
47 #include "hfs_btreeio.h"
48 #include "hfs_fsctl.h"
49
50 #include "hfscommon/headers/BTreesInternal.h"
51
52 #define HFS_XATTR_VERBOSE 0
53
54 #define ATTRIBUTE_FILE_NODE_SIZE 8192
55
56
57 /* State information for the listattr_callback callback function. */
58 struct listattr_callback_state {
59 u_int32_t fileID;
60 int result;
61 uio_t uio;
62 size_t size;
63 #if HFS_COMPRESSION
64 int showcompressed;
65 vfs_context_t ctx;
66 vnode_t vp;
67 #endif /* HFS_COMPRESSION */
68 };
69
70
71 /* HFS Internal Names */
72 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
73 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
74
75 /* Faster version if we already know this is the data fork. */
76 #define RSRC_FORK_EXISTS(CP) \
77 (((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0)
78
79 static u_int32_t emptyfinfo[8] = {0};
80
81 static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo);
82
83 const char hfs_attrdatafilename[] = "Attribute Data";
84
85 static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data,
86 struct listattr_callback_state *state);
87
88 static int remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator);
89
90 static int getnodecount(struct hfsmount *hfsmp, size_t nodesize);
91
92 static size_t getmaxinlineattrsize(struct vnode * attrvp);
93
94 static int read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents);
95
96 static int write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents);
97
98 static int alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks);
99
100 static void free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents);
101
102 static int has_overflow_extents(HFSPlusForkData *forkdata);
103
104 static int count_extent_blocks(int maxblks, HFSPlusExtentRecord extents);
105
106 #if NAMEDSTREAMS
107 /*
108 * Obtain the vnode for a stream.
109 */
110 int
111 hfs_vnop_getnamedstream(struct vnop_getnamedstream_args* ap)
112 {
113 vnode_t vp = ap->a_vp;
114 vnode_t *svpp = ap->a_svpp;
115 struct cnode *cp;
116 int error = 0;
117
118 *svpp = NULL;
119
120 /*
121 * We only support the "com.apple.ResourceFork" stream.
122 */
123 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
124 return (ENOATTR);
125 }
126 cp = VTOC(vp);
127 if ( !S_ISREG(cp->c_mode) ) {
128 return (EPERM);
129 }
130 #if HFS_COMPRESSION
131 int hide_rsrc = hfs_hides_rsrc(ap->a_context, VTOC(vp), 1);
132 #endif /* HFS_COMPRESSION */
133 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
134 return (error);
135 }
136 if ((!RSRC_FORK_EXISTS(cp)
137 #if HFS_COMPRESSION
138 || hide_rsrc
139 #endif /* HFS_COMPRESSION */
140 ) && (ap->a_operation != NS_OPEN)) {
141 hfs_unlock(cp);
142 return (ENOATTR);
143 }
144 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE, FALSE);
145 hfs_unlock(cp);
146
147 return (error);
148 }
149
150 /*
151 * Create a stream.
152 */
153 int
154 hfs_vnop_makenamedstream(struct vnop_makenamedstream_args* ap)
155 {
156 vnode_t vp = ap->a_vp;
157 vnode_t *svpp = ap->a_svpp;
158 struct cnode *cp;
159 int error = 0;
160
161 *svpp = NULL;
162
163 /*
164 * We only support the "com.apple.ResourceFork" stream.
165 */
166 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
167 return (ENOATTR);
168 }
169 cp = VTOC(vp);
170 if ( !S_ISREG(cp->c_mode) ) {
171 return (EPERM);
172 }
173 #if HFS_COMPRESSION
174 if (hfs_hides_rsrc(ap->a_context, VTOC(vp), 1)) {
175 if (VNODE_IS_RSRC(vp)) {
176 return EINVAL;
177 } else {
178 error = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0);
179 if (error != 0)
180 return error;
181 }
182 }
183 #endif /* HFS_COMPRESSION */
184 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
185 return (error);
186 }
187 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE, FALSE);
188 hfs_unlock(cp);
189
190 return (error);
191 }
192
193 /*
194 * Remove a stream.
195 */
196 int
197 hfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap)
198 {
199 vnode_t svp = ap->a_svp;
200 struct cnode *scp;
201 int error = 0;
202
203 /*
204 * We only support the "com.apple.ResourceFork" stream.
205 */
206 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
207 return (ENOATTR);
208 }
209 #if HFS_COMPRESSION
210 if (hfs_hides_rsrc(ap->a_context, VTOC(svp), 1)) {
211 /* do nothing */
212 return 0;
213 }
214 #endif /* HFS_COMPRESSION */
215
216 scp = VTOC(svp);
217
218 /* Take truncate lock before taking cnode lock. */
219 hfs_lock_truncate(scp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
220 if ((error = hfs_lock(scp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
221 goto out;
222 }
223 if (VTOF(svp)->ff_size != 0) {
224 error = hfs_truncate(svp, 0, IO_NDELAY, 0, 0, ap->a_context);
225 }
226 hfs_unlock(scp);
227 out:
228 hfs_unlock_truncate(scp, HFS_LOCK_DEFAULT);
229 return (error);
230 }
231 #endif
232
233
234 /* Zero out the date added field for the specified cnode */
235 static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo)
236 {
237 u_int8_t *finfo = finderinfo;
238
239 /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */
240 finfo = finfo + 16;
241
242 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
243 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
244 extinfo->date_added = 0;
245 extinfo->write_gen_counter = 0;
246 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
247 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
248 extinfo->date_added = 0;
249 } else {
250 /* Return an error */
251 return -1;
252 }
253 return 0;
254
255 }
256
257 /*
258 * Retrieve the data of an extended attribute.
259 */
260 int
261 hfs_vnop_getxattr(struct vnop_getxattr_args *ap)
262 /*
263 struct vnop_getxattr_args {
264 struct vnodeop_desc *a_desc;
265 vnode_t a_vp;
266 char * a_name;
267 uio_t a_uio;
268 size_t *a_size;
269 int a_options;
270 vfs_context_t a_context;
271 };
272 */
273 {
274 struct vnode *vp = ap->a_vp;
275 struct cnode *cp;
276 struct hfsmount *hfsmp;
277 uio_t uio = ap->a_uio;
278 size_t bufsize;
279 int result;
280
281 cp = VTOC(vp);
282 if (vp == cp->c_vp) {
283 #if HFS_COMPRESSION
284 int decmpfs_hide = hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1); /* 1 == don't take the cnode lock */
285 if (decmpfs_hide && !(ap->a_options & XATTR_SHOWCOMPRESSION))
286 return ENOATTR;
287 #endif /* HFS_COMPRESSION */
288
289 /* Get the Finder Info. */
290 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
291 u_int8_t finderinfo[32];
292 bufsize = 32;
293
294 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
295 return (result);
296 }
297 /* Make a copy since we may not export all of it. */
298 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
299 hfs_unlock(cp);
300
301 /* Zero out the date added field in the local copy */
302 hfs_zero_hidden_fields (cp, finderinfo);
303
304 /* Don't expose a symlink's private type/creator. */
305 if (vnode_islnk(vp)) {
306 struct FndrFileInfo *fip;
307
308 fip = (struct FndrFileInfo *)&finderinfo;
309 fip->fdType = 0;
310 fip->fdCreator = 0;
311 }
312 /* If Finder Info is empty then it doesn't exist. */
313 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
314 return (ENOATTR);
315 }
316 if (uio == NULL) {
317 *ap->a_size = bufsize;
318 return (0);
319 }
320 if ((user_size_t)uio_resid(uio) < bufsize)
321 return (ERANGE);
322
323 result = uiomove((caddr_t)&finderinfo , bufsize, uio);
324
325 return (result);
326 }
327 /* Read the Resource Fork. */
328 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
329 struct vnode *rvp = NULL;
330 int openunlinked = 0;
331 int namelen = 0;
332
333 if ( !S_ISREG(cp->c_mode) ) {
334 return (EPERM);
335 }
336 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
337 return (result);
338 }
339 namelen = cp->c_desc.cd_namelen;
340
341 if ( !RSRC_FORK_EXISTS(cp)) {
342 hfs_unlock(cp);
343 return (ENOATTR);
344 }
345 hfsmp = VTOHFS(vp);
346 if ((cp->c_flag & C_DELETED) && (namelen == 0)) {
347 openunlinked = 1;
348 }
349
350 result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
351 hfs_unlock(cp);
352 if (result) {
353 return (result);
354 }
355 if (uio == NULL) {
356 *ap->a_size = (size_t)VTOF(rvp)->ff_size;
357 } else {
358 #if HFS_COMPRESSION
359 user_ssize_t uio_size = 0;
360 if (decmpfs_hide)
361 uio_size = uio_resid(uio);
362 #endif /* HFS_COMPRESSION */
363 result = VNOP_READ(rvp, uio, 0, ap->a_context);
364 #if HFS_COMPRESSION
365 if (decmpfs_hide &&
366 (result == 0) &&
367 (uio_resid(uio) == uio_size)) {
368 /*
369 * We intentionally make the above call to VNOP_READ so that
370 * it can return an authorization/permission/etc. Error
371 * based on ap->a_context and thus deny this operation;
372 * in that case, result != 0 and we won't proceed.
373 *
374 * However, if result == 0, it will have returned no data
375 * because hfs_vnop_read hid the resource fork
376 * (hence uio_resid(uio) == uio_size, i.e. the uio is untouched)
377 *
378 * In that case, we try again with the decmpfs_ctx context
379 * to get the actual data
380 */
381 result = VNOP_READ(rvp, uio, 0, decmpfs_ctx);
382 }
383 #endif /* HFS_COMPRESSION */
384 }
385 /* force the rsrc fork vnode to recycle right away */
386 if (openunlinked) {
387 int vref;
388 vref = vnode_ref (rvp);
389 if (vref == 0) {
390 vnode_rele (rvp);
391 }
392 vnode_recycle(rvp);
393 }
394 vnode_put(rvp);
395 return (result);
396 }
397 }
398 hfsmp = VTOHFS(vp);
399 /*
400 * Standard HFS only supports native FinderInfo and Resource Forks.
401 */
402 if (hfsmp->hfs_flags & HFS_STANDARD) {
403 return (EPERM);
404 }
405
406 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
407 return (result);
408 }
409
410 /* Check for non-rsrc, non-finderinfo EAs */
411 result = hfs_getxattr_internal (cp, ap, VTOHFS(cp->c_vp), 0);
412
413 hfs_unlock(cp);
414
415 return MacToVFSError(result);
416 }
417
418
419
420 /*
421 * getxattr_internal
422 *
423 * We break out this internal function which searches the attributes B-Tree and the
424 * overflow extents file to find non-resource, non-finderinfo EAs. There may be cases
425 * where we need to get EAs in contexts where we are already holding the cnode lock,
426 * and to re-enter hfs_vnop_getxattr would cause us to double-lock the cnode. Instead,
427 * we can just directly call this function.
428 *
429 * We pass the hfsmp argument directly here because we may not necessarily have a cnode to
430 * operate on. Under normal conditions, we have a file or directory to query, but if we
431 * are operating on the root directory (id 1), then we may not have a cnode. In this case, if hte
432 * 'cp' argument is NULL, then we need to use the 'fileid' argument as the entry to manipulate
433 *
434 * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared.
435 */
436 int hfs_getxattr_internal (struct cnode *cp, struct vnop_getxattr_args *ap,
437 struct hfsmount *hfsmp, u_int32_t fileid)
438 {
439
440 struct filefork *btfile;
441 struct BTreeIterator * iterator = NULL;
442 size_t bufsize = 0;
443 HFSPlusAttrRecord *recp = NULL;
444 FSBufferDescriptor btdata;
445 int lockflags = 0;
446 int result = 0;
447 u_int16_t datasize = 0;
448 uio_t uio = ap->a_uio;
449 u_int32_t target_id = 0;
450
451 if (cp) {
452 target_id = cp->c_fileid;
453 } else {
454 target_id = fileid;
455 }
456
457
458 /* Bail if we don't have an EA B-Tree. */
459 if ((hfsmp->hfs_attribute_vp == NULL) ||
460 ((cp) && (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0)) {
461 result = ENOATTR;
462 goto exit;
463 }
464
465 /* Initialize the B-Tree iterator for searching for the proper EA */
466 btfile = VTOF(hfsmp->hfs_attribute_vp);
467
468 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
469 if (iterator == NULL) {
470 result = ENOMEM;
471 goto exit;
472 }
473 bzero(iterator, sizeof(*iterator));
474
475 /* Allocate memory for reading in the attribute record. This buffer is
476 * big enough to read in all types of attribute records. It is not big
477 * enough to read inline attribute data which is read in later.
478 */
479 MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK);
480 if (recp == NULL) {
481 result = ENOMEM;
482 goto exit;
483 }
484 btdata.bufferAddress = recp;
485 btdata.itemSize = sizeof(HFSPlusAttrRecord);
486 btdata.itemCount = 1;
487
488 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
489 if (result) {
490 goto exit;
491 }
492
493 /* Lookup the attribute in the Attribute B-Tree */
494 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
495 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
496 hfs_systemfile_unlock(hfsmp, lockflags);
497
498 if (result) {
499 if (result == btNotFound) {
500 result = ENOATTR;
501 }
502 goto exit;
503 }
504
505 /*
506 * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if
507 * we have extent based EAs.
508 */
509 switch (recp->recordType) {
510
511 /* Attribute fits in the Attribute B-Tree */
512 case kHFSPlusAttrInlineData: {
513 /*
514 * Sanity check record size. It's not required to have any
515 * user data, so the minimum size is 2 bytes less that the
516 * size of HFSPlusAttrData (since HFSPlusAttrData struct
517 * has 2 bytes set aside for attribute data).
518 */
519 if (datasize < (sizeof(HFSPlusAttrData) - 2)) {
520 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
521 hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrData));
522 result = ENOATTR;
523 break;
524 }
525 *ap->a_size = recp->attrData.attrSize;
526 if (uio && recp->attrData.attrSize != 0) {
527 if (*ap->a_size > (user_size_t)uio_resid(uio)) {
528 /* User provided buffer is not large enough for the xattr data */
529 result = ERANGE;
530 } else {
531 /* Previous BTreeSearchRecord() read in only the attribute record,
532 * and not the attribute data. Now allocate enough memory for
533 * both attribute record and data, and read the attribute record again.
534 */
535 bufsize = sizeof(HFSPlusAttrData) - 2 + recp->attrData.attrSize;
536 FREE(recp, M_TEMP);
537 MALLOC(recp, HFSPlusAttrRecord *, bufsize, M_TEMP, M_WAITOK);
538 if (recp == NULL) {
539 result = ENOMEM;
540 goto exit;
541 }
542
543 btdata.bufferAddress = recp;
544 btdata.itemSize = bufsize;
545 btdata.itemCount = 1;
546
547 bzero(iterator, sizeof(*iterator));
548 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
549 if (result) {
550 goto exit;
551 }
552
553 /* Lookup the attribute record and inline data */
554 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
555 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
556 hfs_systemfile_unlock(hfsmp, lockflags);
557 if (result) {
558 if (result == btNotFound) {
559 result = ENOATTR;
560 }
561 goto exit;
562 }
563
564 /* Copy-out the attribute data to the user buffer */
565 *ap->a_size = recp->attrData.attrSize;
566 result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio);
567 }
568 }
569 break;
570 }
571
572 /* Extent-Based EAs */
573 case kHFSPlusAttrForkData: {
574 if (datasize < sizeof(HFSPlusAttrForkData)) {
575 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
576 hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrForkData));
577 result = ENOATTR;
578 break;
579 }
580 *ap->a_size = recp->forkData.theFork.logicalSize;
581 if (uio == NULL) {
582 break;
583 }
584 if (*ap->a_size > (user_size_t)uio_resid(uio)) {
585 result = ERANGE;
586 break;
587 }
588 /* Process overflow extents if necessary. */
589 if (has_overflow_extents(&recp->forkData.theFork)) {
590 HFSPlusExtentDescriptor *extentbuf;
591 HFSPlusExtentDescriptor *extentptr;
592 size_t extentbufsize;
593 u_int32_t totalblocks;
594 u_int32_t blkcnt;
595 u_int32_t attrlen;
596
597 totalblocks = recp->forkData.theFork.totalBlocks;
598 /* Ignore bogus block counts. */
599 if (totalblocks > howmany(HFS_XATTR_MAXSIZE, hfsmp->blockSize)) {
600 result = ERANGE;
601 break;
602 }
603 attrlen = recp->forkData.theFork.logicalSize;
604
605 /* Get a buffer to hold the worst case amount of extents. */
606 extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor);
607 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
608 MALLOC(extentbuf, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
609 if (extentbuf == NULL) {
610 result = ENOMEM;
611 break;
612 }
613 bzero(extentbuf, extentbufsize);
614 extentptr = extentbuf;
615
616 /* Grab the first 8 extents. */
617 bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
618 extentptr += kHFSPlusExtentDensity;
619 blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents);
620
621 /* Now lookup the overflow extents. */
622 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
623 while (blkcnt < totalblocks) {
624 ((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt;
625 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
626 if (result ||
627 (recp->recordType != kHFSPlusAttrExtents) ||
628 (datasize < sizeof(HFSPlusAttrExtents))) {
629 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
630 ap->a_name, blkcnt, totalblocks);
631 result = ENOATTR;
632 break; /* break from while */
633 }
634 /* Grab the next 8 extents. */
635 bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
636 extentptr += kHFSPlusExtentDensity;
637 blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents);
638 }
639
640 /* Release Attr B-Tree lock */
641 hfs_systemfile_unlock(hfsmp, lockflags);
642
643 if (blkcnt < totalblocks) {
644 result = ENOATTR;
645 } else {
646 result = read_attr_data(hfsmp, uio, attrlen, extentbuf);
647 }
648 FREE(extentbuf, M_TEMP);
649
650 } else { /* No overflow extents. */
651 result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents);
652 }
653 break;
654 }
655
656 default:
657 /* We only support Extent or inline EAs. Default to ENOATTR for anything else */
658 result = ENOATTR;
659 break;
660 }
661
662 exit:
663 if (iterator) {
664 FREE(iterator, M_TEMP);
665 }
666 if (recp) {
667 FREE(recp, M_TEMP);
668 }
669
670 return result;
671
672 }
673
674
675 /*
676 * Set the data of an extended attribute.
677 */
678 int
679 hfs_vnop_setxattr(struct vnop_setxattr_args *ap)
680 /*
681 struct vnop_setxattr_args {
682 struct vnodeop_desc *a_desc;
683 vnode_t a_vp;
684 char * a_name;
685 uio_t a_uio;
686 int a_options;
687 vfs_context_t a_context;
688 };
689 */
690 {
691 struct vnode *vp = ap->a_vp;
692 struct cnode *cp = NULL;
693 struct hfsmount *hfsmp;
694 uio_t uio = ap->a_uio;
695 size_t attrsize;
696 void * user_data_ptr = NULL;
697 int result;
698 time_t orig_ctime=VTOC(vp)->c_ctime;
699
700 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
701 return (EINVAL); /* invalid name */
702 }
703 hfsmp = VTOHFS(vp);
704 if (VNODE_IS_RSRC(vp)) {
705 return (EPERM);
706 }
707
708 #if HFS_COMPRESSION
709 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) ) { /* 1 == don't take the cnode lock */
710 result = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0);
711 if (result != 0)
712 return result;
713 }
714 #endif /* HFS_COMPRESSION */
715
716 check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_WRITE_OP, NSPACE_REARM_NO_ARG);
717
718 /* Set the Finder Info. */
719 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
720 u_int8_t finderinfo[32];
721 struct FndrFileInfo *fip;
722 void * finderinfo_start;
723 u_int8_t *finfo = NULL;
724 u_int16_t fdFlags;
725 u_int32_t dateadded = 0;
726 u_int32_t write_gen_counter = 0;
727
728 attrsize = sizeof(VTOC(vp)->c_finderinfo);
729
730 if ((user_size_t)uio_resid(uio) != attrsize) {
731 return (ERANGE);
732 }
733 /* Grab the new Finder Info data. */
734 if ((result = uiomove((caddr_t)&finderinfo , attrsize, uio))) {
735 return (result);
736 }
737 fip = (struct FndrFileInfo *)&finderinfo;
738
739 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
740 return (result);
741 }
742 cp = VTOC(vp);
743
744 /* Symlink's don't have an external type/creator. */
745 if (vnode_islnk(vp)) {
746 /* Skip over type/creator fields. */
747 finderinfo_start = &cp->c_finderinfo[8];
748 attrsize -= 8;
749 } else {
750 finderinfo_start = &cp->c_finderinfo[0];
751 /*
752 * Don't allow the external setting of
753 * file type to kHardLinkFileType.
754 */
755 if (fip->fdType == SWAP_BE32(kHardLinkFileType)) {
756 hfs_unlock(cp);
757 return (EPERM);
758 }
759 }
760
761 /* Grab the current date added from the cnode */
762 dateadded = hfs_get_dateadded (cp);
763 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
764 write_gen_counter = hfs_get_gencount(cp);
765 }
766
767 /* Zero out the date added field to ignore user's attempts to set it */
768 hfs_zero_hidden_fields(cp, finderinfo);
769
770 if (bcmp(finderinfo_start, emptyfinfo, attrsize)) {
771 /* attr exists and "create" was specified. */
772 if (ap->a_options & XATTR_CREATE) {
773 hfs_unlock(cp);
774 return (EEXIST);
775 }
776 } else { /* empty */
777 /* attr doesn't exists and "replace" was specified. */
778 if (ap->a_options & XATTR_REPLACE) {
779 hfs_unlock(cp);
780 return (ENOATTR);
781 }
782 }
783
784 /*
785 * Now restore the date added to the finderinfo to be written out.
786 * Advance to the 2nd half of the finderinfo to write out the date added
787 * into the buffer.
788 *
789 * Make sure to endian swap the date added back into big endian. When we used
790 * hfs_get_dateadded above to retrieve it, it swapped into local endianness
791 * for us. But now that we're writing it out, put it back into big endian.
792 */
793 finfo = &finderinfo[16];
794
795 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
796 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
797 extinfo->date_added = OSSwapHostToBigInt32(dateadded);
798 extinfo->write_gen_counter = write_gen_counter;
799 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
800 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
801 extinfo->date_added = OSSwapHostToBigInt32(dateadded);
802 }
803
804 /* Set the cnode's Finder Info. */
805 if (attrsize == sizeof(cp->c_finderinfo)) {
806 bcopy(&finderinfo[0], finderinfo_start, attrsize);
807 } else {
808 bcopy(&finderinfo[8], finderinfo_start, attrsize);
809 }
810
811 /* Updating finderInfo updates change time and modified time */
812 cp->c_touch_chgtime = TRUE;
813 cp->c_flag |= C_MODIFIED;
814
815 /*
816 * Mirror the invisible bit to the UF_HIDDEN flag.
817 *
818 * The fdFlags for files and frFlags for folders are both 8 bytes
819 * into the userInfo (the first 16 bytes of the Finder Info). They
820 * are both 16-bit fields.
821 */
822 fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]);
823 if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) {
824 cp->c_bsdflags |= UF_HIDDEN;
825 } else {
826 cp->c_bsdflags &= ~UF_HIDDEN;
827 }
828
829 result = hfs_update(vp, FALSE);
830
831 hfs_unlock(cp);
832 return (result);
833 }
834 /* Write the Resource Fork. */
835 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
836 struct vnode *rvp = NULL;
837 int namelen = 0;
838 int openunlinked = 0;
839
840 if (!vnode_isreg(vp)) {
841 return (EPERM);
842 }
843 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
844 return (result);
845 }
846 cp = VTOC(vp);
847 namelen = cp->c_desc.cd_namelen;
848
849 if (RSRC_FORK_EXISTS(cp)) {
850 /* attr exists and "create" was specified. */
851 if (ap->a_options & XATTR_CREATE) {
852 hfs_unlock(cp);
853 return (EEXIST);
854 }
855 } else {
856 /* attr doesn't exists and "replace" was specified. */
857 if (ap->a_options & XATTR_REPLACE) {
858 hfs_unlock(cp);
859 return (ENOATTR);
860 }
861 }
862
863 /*
864 * Note that we could be called on to grab the rsrc fork vnode
865 * for a file that has become open-unlinked.
866 */
867 if ((cp->c_flag & C_DELETED) && (namelen == 0)) {
868 openunlinked = 1;
869 }
870
871 result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
872 hfs_unlock(cp);
873 if (result) {
874 return (result);
875 }
876 /* VNOP_WRITE marks cnode as needing a modtime update */
877 result = VNOP_WRITE(rvp, uio, 0, ap->a_context);
878
879 /* if open unlinked, force it inactive */
880 if (openunlinked) {
881 int vref;
882 vref = vnode_ref (rvp);
883 if (vref == 0) {
884 vnode_rele(rvp);
885 }
886 vnode_recycle (rvp);
887 } else {
888 /* cnode is not open-unlinked, so re-lock cnode to sync */
889 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
890 vnode_recycle (rvp);
891 vnode_put(rvp);
892 return result;
893 }
894
895 /* hfs fsync rsrc fork to force to disk and update modtime */
896 result = hfs_fsync (rvp, MNT_NOWAIT, 0, vfs_context_proc (ap->a_context));
897 hfs_unlock (cp);
898 }
899
900 vnode_put(rvp);
901 return (result);
902 }
903 /*
904 * Standard HFS only supports native FinderInfo and Resource Forks.
905 */
906 if (hfsmp->hfs_flags & HFS_STANDARD) {
907 return (EPERM);
908 }
909 attrsize = uio_resid(uio);
910
911 /* Enforce an upper limit. */
912 if (attrsize > HFS_XATTR_MAXSIZE) {
913 result = E2BIG;
914 goto exit;
915 }
916
917 /*
918 * Attempt to copy the users attr data before taking any locks,
919 * only if it will be an inline attribute. For larger attributes,
920 * the data will be directly read from the uio.
921 */
922 if (attrsize > 0 &&
923 hfsmp->hfs_max_inline_attrsize != 0 &&
924 attrsize < hfsmp->hfs_max_inline_attrsize) {
925 MALLOC(user_data_ptr, void *, attrsize, M_TEMP, M_WAITOK);
926 if (user_data_ptr == NULL) {
927 result = ENOMEM;
928 goto exit;
929 }
930
931 result = uiomove((caddr_t)user_data_ptr, attrsize, uio);
932 if (result) {
933 goto exit;
934 }
935 }
936
937 result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
938 if (result) {
939 goto exit;
940 }
941 cp = VTOC(vp);
942
943 /*
944 * If we're trying to set a non-finderinfo, non-resourcefork EA, then
945 * call the breakout function.
946 */
947 result = hfs_setxattr_internal (cp, user_data_ptr, attrsize, ap, VTOHFS(vp), 0);
948
949 exit:
950 if (cp) {
951 hfs_unlock(cp);
952 }
953 if (user_data_ptr) {
954 FREE(user_data_ptr, M_TEMP);
955 }
956
957 return (result == btNotFound ? ENOATTR : MacToVFSError(result));
958 }
959
960
961 /*
962 * hfs_setxattr_internal
963 *
964 * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or
965 * extent-based EAs.
966 *
967 * See comments from hfs_getxattr_internal on why we need to pass 'hfsmp' and fileid here.
968 * The gist is that we could end up writing to the root folder which may not have a cnode.
969 *
970 * Assumptions:
971 * 1. cnode 'cp' is locked EXCLUSIVE before calling this function.
972 * 2. data_ptr contains data to be written. If gathering data from userland, this must be
973 * done before calling this function.
974 * 3. If data originates entirely in-kernel, use a null UIO, and ensure the size is less than
975 * hfsmp->hfs_max_inline_attrsize bytes long.
976 */
977 int hfs_setxattr_internal (struct cnode *cp, caddr_t data_ptr, size_t attrsize,
978 struct vnop_setxattr_args *ap, struct hfsmount *hfsmp,
979 u_int32_t fileid)
980 {
981 uio_t uio = ap->a_uio;
982 struct vnode *vp = ap->a_vp;
983 int started_transaction = 0;
984 struct BTreeIterator * iterator = NULL;
985 struct filefork *btfile = NULL;
986 FSBufferDescriptor btdata;
987 HFSPlusAttrRecord attrdata; /* 90 bytes */
988 HFSPlusAttrRecord *recp = NULL;
989 HFSPlusExtentDescriptor *extentptr = NULL;
990 int result = 0;
991 int lockflags = 0;
992 int exists = 0;
993 int allocatedblks = 0;
994 u_int32_t target_id;
995 int takelock = 1;
996
997 if (cp) {
998 target_id = cp->c_fileid;
999 } else {
1000 target_id = fileid;
1001 if (target_id != 1) {
1002 /*
1003 * If we are manipulating something other than
1004 * the root folder (id 1), and do not have a cnode-in-hand,
1005 * then we must already hold the requisite b-tree locks from
1006 * earlier up the call stack. (See hfs_makenode)
1007 */
1008 takelock = 0;
1009 }
1010 }
1011
1012 /* Start a transaction for our changes. */
1013 if (hfs_start_transaction(hfsmp) != 0) {
1014 result = EINVAL;
1015 goto exit;
1016 }
1017 started_transaction = 1;
1018
1019 /*
1020 * Once we started the transaction, nobody can compete
1021 * with us, so make sure this file is still there.
1022 */
1023 if ((cp) && (cp->c_flag & C_NOEXISTS)) {
1024 result = ENOENT;
1025 goto exit;
1026 }
1027
1028 /*
1029 * If there isn't an attributes b-tree then create one.
1030 */
1031 if (hfsmp->hfs_attribute_vp == NULL) {
1032 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
1033 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
1034 if (result) {
1035 goto exit;
1036 }
1037 }
1038 if (hfsmp->hfs_max_inline_attrsize == 0) {
1039 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
1040 }
1041
1042 if (takelock) {
1043 /* Take exclusive access to the attributes b-tree. */
1044 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1045 }
1046
1047 /* Build the b-tree key. */
1048 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1049 if (iterator == NULL) {
1050 result = ENOMEM;
1051 goto exit;
1052 }
1053 bzero(iterator, sizeof(*iterator));
1054 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1055 if (result) {
1056 goto exit;
1057 }
1058
1059 /* Preflight for replace/create semantics. */
1060 btfile = VTOF(hfsmp->hfs_attribute_vp);
1061 btdata.bufferAddress = &attrdata;
1062 btdata.itemSize = sizeof(attrdata);
1063 btdata.itemCount = 1;
1064 exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0;
1065
1066 /* Replace requires that the attribute already exists. */
1067 if ((ap->a_options & XATTR_REPLACE) && !exists) {
1068 result = ENOATTR;
1069 goto exit;
1070 }
1071 /* Create requires that the attribute doesn't exist. */
1072 if ((ap->a_options & XATTR_CREATE) && exists) {
1073 result = EEXIST;
1074 goto exit;
1075 }
1076
1077 /* If it won't fit inline then use extent-based attributes. */
1078 if (attrsize > hfsmp->hfs_max_inline_attrsize) {
1079 size_t extentbufsize;
1080 int blkcnt;
1081 int extentblks;
1082 u_int32_t *keystartblk;
1083 int i;
1084
1085 if (uio == NULL) {
1086 /*
1087 * setxattrs originating from in-kernel are not supported if they are bigger
1088 * than the inline max size. Just return ENOATTR and force them to do it with a
1089 * smaller EA.
1090 */
1091 result = EPERM;
1092 goto exit;
1093 }
1094
1095 /* Get some blocks. */
1096 blkcnt = howmany(attrsize, hfsmp->blockSize);
1097 extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor);
1098 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
1099 MALLOC(extentptr, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
1100 if (extentptr == NULL) {
1101 result = ENOMEM;
1102 goto exit;
1103 }
1104 bzero(extentptr, extentbufsize);
1105 result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks);
1106 if (result) {
1107 allocatedblks = 0;
1108 goto exit; /* no more space */
1109 }
1110 /* Copy data into the blocks. */
1111 result = write_attr_data(hfsmp, uio, attrsize, extentptr);
1112 if (result) {
1113 if (vp) {
1114 const char *name = vnode_getname(vp);
1115 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
1116 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1117 if (name)
1118 vnode_putname(name);
1119 }
1120 goto exit;
1121 }
1122
1123 /* Now remove any previous attribute. */
1124 if (exists) {
1125 result = remove_attribute_records(hfsmp, iterator);
1126 if (result) {
1127 if (vp) {
1128 const char *name = vnode_getname(vp);
1129 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1130 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1131 if (name)
1132 vnode_putname(name);
1133 }
1134 goto exit;
1135 }
1136 }
1137 /* Create attribute fork data record. */
1138 MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK);
1139 if (recp == NULL) {
1140 result = ENOMEM;
1141 goto exit;
1142 }
1143 btdata.bufferAddress = recp;
1144 btdata.itemCount = 1;
1145 btdata.itemSize = sizeof(HFSPlusAttrForkData);
1146
1147 recp->recordType = kHFSPlusAttrForkData;
1148 recp->forkData.reserved = 0;
1149 recp->forkData.theFork.logicalSize = attrsize;
1150 recp->forkData.theFork.clumpSize = 0;
1151 recp->forkData.theFork.totalBlocks = blkcnt;
1152 bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord));
1153
1154 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1155
1156 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1157 if (result) {
1158 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1159 hfsmp->vcbVN, target_id, ap->a_name, result);
1160 goto exit;
1161 }
1162 extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents);
1163 blkcnt -= extentblks;
1164 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1165 i = 0;
1166
1167 /* Create overflow extents as needed. */
1168 while (blkcnt > 0) {
1169 /* Initialize the key and record. */
1170 *keystartblk += (u_int32_t)extentblks;
1171 btdata.itemSize = sizeof(HFSPlusAttrExtents);
1172 recp->recordType = kHFSPlusAttrExtents;
1173 recp->overflowExtents.reserved = 0;
1174
1175 /* Copy the next set of extents. */
1176 i += kHFSPlusExtentDensity;
1177 bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord));
1178
1179 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1180 if (result) {
1181 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1182 hfsmp->vcbVN, target_id, ap->a_name, result);
1183 goto exit;
1184 }
1185 extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents);
1186 blkcnt -= extentblks;
1187 }
1188 } else { /* Inline data */
1189 if (exists) {
1190 result = remove_attribute_records(hfsmp, iterator);
1191 if (result) {
1192 goto exit;
1193 }
1194 }
1195
1196 /* Calculate size of record rounded up to multiple of 2 bytes. */
1197 btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
1198 MALLOC(recp, HFSPlusAttrRecord *, btdata.itemSize, M_TEMP, M_WAITOK);
1199 if (recp == NULL) {
1200 result = ENOMEM;
1201 goto exit;
1202 }
1203 recp->recordType = kHFSPlusAttrInlineData;
1204 recp->attrData.reserved[0] = 0;
1205 recp->attrData.reserved[1] = 0;
1206 recp->attrData.attrSize = attrsize;
1207
1208 /* Copy in the attribute data (if any). */
1209 if (attrsize > 0) {
1210 if (data_ptr) {
1211 bcopy(data_ptr, &recp->attrData.attrData, attrsize);
1212 } else {
1213 /*
1214 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1215 * then deny the copy operation.
1216 */
1217 if (uio == NULL) {
1218 result = EPERM;
1219 goto exit;
1220 }
1221 result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio);
1222 }
1223
1224 if (result) {
1225 goto exit;
1226 }
1227 }
1228
1229 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1230
1231 btdata.bufferAddress = recp;
1232 btdata.itemCount = 1;
1233 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1234 }
1235
1236 exit:
1237 if (btfile && started_transaction) {
1238 (void) BTFlushPath(btfile);
1239 }
1240 if (lockflags) {
1241 hfs_systemfile_unlock(hfsmp, lockflags);
1242 }
1243 if (result == 0) {
1244 if (vp) {
1245 cp = VTOC(vp);
1246 /* Setting an attribute only updates change time and not
1247 * modified time of the file.
1248 */
1249 cp->c_touch_chgtime = TRUE;
1250 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
1251 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
1252 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
1253 }
1254 (void) hfs_update(vp, 0);
1255 }
1256 }
1257 if (started_transaction) {
1258 if (result && allocatedblks) {
1259 free_attr_blks(hfsmp, allocatedblks, extentptr);
1260 }
1261 hfs_end_transaction(hfsmp);
1262 }
1263
1264 if (recp) {
1265 FREE(recp, M_TEMP);
1266 }
1267 if (extentptr) {
1268 FREE(extentptr, M_TEMP);
1269 }
1270 if (iterator) {
1271 FREE(iterator, M_TEMP);
1272 }
1273
1274 return result;
1275 }
1276
1277
1278
1279
1280 /*
1281 * Remove an extended attribute.
1282 */
1283 int
1284 hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
1285 /*
1286 struct vnop_removexattr_args {
1287 struct vnodeop_desc *a_desc;
1288 vnode_t a_vp;
1289 char * a_name;
1290 int a_options;
1291 vfs_context_t a_context;
1292 };
1293 */
1294 {
1295 struct vnode *vp = ap->a_vp;
1296 struct cnode *cp = VTOC(vp);
1297 struct hfsmount *hfsmp;
1298 struct BTreeIterator * iterator = NULL;
1299 int lockflags;
1300 int result;
1301 time_t orig_ctime=VTOC(vp)->c_ctime;
1302
1303 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
1304 return (EINVAL); /* invalid name */
1305 }
1306 hfsmp = VTOHFS(vp);
1307 if (VNODE_IS_RSRC(vp)) {
1308 return (EPERM);
1309 }
1310
1311 #if HFS_COMPRESSION
1312 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) {
1313 return ENOATTR;
1314 }
1315 #endif /* HFS_COMPRESSION */
1316
1317 check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NSPACE_REARM_NO_ARG);
1318
1319 /* If Resource Fork is non-empty then truncate it. */
1320 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1321 struct vnode *rvp = NULL;
1322
1323 if ( !vnode_isreg(vp) ) {
1324 return (EPERM);
1325 }
1326 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1327 return (result);
1328 }
1329 if ( !RSRC_FORK_EXISTS(cp)) {
1330 hfs_unlock(cp);
1331 return (ENOATTR);
1332 }
1333 result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
1334 hfs_unlock(cp);
1335 if (result) {
1336 return (result);
1337 }
1338
1339 hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1340 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1341 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1342 vnode_put(rvp);
1343 return (result);
1344 }
1345
1346 /* Start a transaction for encapsulating changes in
1347 * hfs_truncate() and hfs_update()
1348 */
1349 if ((result = hfs_start_transaction(hfsmp))) {
1350 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1351 hfs_unlock(cp);
1352 vnode_put(rvp);
1353 return (result);
1354 }
1355
1356 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, 0, ap->a_context);
1357 if (result == 0) {
1358 cp->c_touch_chgtime = TRUE;
1359 cp->c_flag |= C_MODIFIED;
1360 result = hfs_update(vp, FALSE);
1361 }
1362
1363 hfs_end_transaction(hfsmp);
1364 hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT);
1365 hfs_unlock(VTOC(rvp));
1366
1367 vnode_put(rvp);
1368 return (result);
1369 }
1370 /* Clear out the Finder Info. */
1371 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1372 void * finderinfo_start;
1373 int finderinfo_size;
1374 u_int8_t finderinfo[32];
1375 u_int32_t date_added, write_gen_counter;
1376 u_int8_t *finfo = NULL;
1377
1378 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1379 return (result);
1380 }
1381
1382 /* Use the local copy to store our temporary changes. */
1383 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1384
1385
1386 /* Zero out the date added field in the local copy */
1387 hfs_zero_hidden_fields (cp, finderinfo);
1388
1389 /* Don't expose a symlink's private type/creator. */
1390 if (vnode_islnk(vp)) {
1391 struct FndrFileInfo *fip;
1392
1393 fip = (struct FndrFileInfo *)&finderinfo;
1394 fip->fdType = 0;
1395 fip->fdCreator = 0;
1396 }
1397
1398 /* Do the byte compare against the local copy */
1399 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
1400 hfs_unlock(cp);
1401 return (ENOATTR);
1402 }
1403
1404 /*
1405 * If there was other content, zero out everything except
1406 * type/creator and date added. First, save the date added.
1407 */
1408 finfo = cp->c_finderinfo;
1409 finfo = finfo + 16;
1410 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1411 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1412 date_added = extinfo->date_added;
1413 write_gen_counter = extinfo->write_gen_counter;
1414 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1415 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1416 date_added = extinfo->date_added;
1417 }
1418
1419 if (vnode_islnk(vp)) {
1420 /* Ignore type/creator */
1421 finderinfo_start = &cp->c_finderinfo[8];
1422 finderinfo_size = sizeof(cp->c_finderinfo) - 8;
1423 } else {
1424 finderinfo_start = &cp->c_finderinfo[0];
1425 finderinfo_size = sizeof(cp->c_finderinfo);
1426 }
1427 bzero(finderinfo_start, finderinfo_size);
1428
1429
1430 /* Now restore the date added */
1431 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1432 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1433 extinfo->date_added = date_added;
1434 extinfo->write_gen_counter = write_gen_counter;
1435 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1436 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1437 extinfo->date_added = date_added;
1438 }
1439
1440 /* Updating finderInfo updates change time and modified time */
1441 cp->c_touch_chgtime = TRUE;
1442 cp->c_flag |= C_MODIFIED;
1443 hfs_update(vp, FALSE);
1444
1445 hfs_unlock(cp);
1446
1447 return (0);
1448 }
1449 /*
1450 * Standard HFS only supports native FinderInfo and Resource Forks.
1451 */
1452 if (hfsmp->hfs_flags & HFS_STANDARD) {
1453 return (EPERM);
1454 }
1455 if (hfsmp->hfs_attribute_vp == NULL) {
1456 return (ENOATTR);
1457 }
1458
1459 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1460 if (iterator == NULL) {
1461 return (ENOMEM);
1462 }
1463 bzero(iterator, sizeof(*iterator));
1464
1465 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1466 goto exit_nolock;
1467 }
1468
1469 result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1470 if (result) {
1471 goto exit;
1472 }
1473
1474 if (hfs_start_transaction(hfsmp) != 0) {
1475 result = EINVAL;
1476 goto exit;
1477 }
1478 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1479
1480 result = remove_attribute_records(hfsmp, iterator);
1481
1482 hfs_systemfile_unlock(hfsmp, lockflags);
1483
1484 if (result == 0) {
1485 cp->c_touch_chgtime = TRUE;
1486
1487 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1488
1489 /* If no more attributes exist, clear attribute bit */
1490 result = file_attribute_exist(hfsmp, cp->c_fileid);
1491 if (result == 0) {
1492 cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
1493 }
1494 if (result == EEXIST) {
1495 result = 0;
1496 }
1497
1498 hfs_systemfile_unlock(hfsmp, lockflags);
1499
1500 /* If ACL was removed, clear security bit */
1501 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
1502 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
1503 }
1504 (void) hfs_update(vp, 0);
1505 }
1506
1507 hfs_end_transaction(hfsmp);
1508 exit:
1509 hfs_unlock(cp);
1510 exit_nolock:
1511 FREE(iterator, M_TEMP);
1512 return MacToVFSError(result);
1513 }
1514
1515 /* Check if any attribute record exist for given fileID. This function
1516 * is called by hfs_vnop_removexattr to determine if it should clear the
1517 * attribute bit in the catalog record or not.
1518 *
1519 * Note - you must acquire a shared lock on the attribute btree before
1520 * calling this function.
1521 *
1522 * Output:
1523 * EEXIST - If attribute record was found
1524 * 0 - Attribute was not found
1525 * (other) - Other error (such as EIO)
1526 */
1527 int
1528 file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID)
1529 {
1530 HFSPlusAttrKey *key;
1531 struct BTreeIterator * iterator = NULL;
1532 struct filefork *btfile;
1533 int result = 0;
1534
1535 // if there's no attribute b-tree we sure as heck
1536 // can't have any attributes!
1537 if (hfsmp->hfs_attribute_vp == NULL) {
1538 return false;
1539 }
1540
1541 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1542 if (iterator == NULL) {
1543 result = ENOMEM;
1544 goto out;
1545 }
1546 bzero(iterator, sizeof(*iterator));
1547 key = (HFSPlusAttrKey *)&iterator->key;
1548
1549 result = hfs_buildattrkey(fileID, NULL, key);
1550 if (result) {
1551 goto out;
1552 }
1553
1554 btfile = VTOF(hfsmp->hfs_attribute_vp);
1555 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1556 if (result && (result != btNotFound)) {
1557 goto out;
1558 }
1559
1560 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1561 /* If no next record was found or fileID for next record did not match,
1562 * no more attributes exist for this fileID
1563 */
1564 if ((result && (result == btNotFound)) || (key->fileID != fileID)) {
1565 result = 0;
1566 } else {
1567 result = EEXIST;
1568 }
1569
1570 out:
1571 if (iterator) {
1572 FREE(iterator, M_TEMP);
1573 }
1574 return result;
1575 }
1576
1577
1578 /*
1579 * Remove all the records for a given attribute.
1580 *
1581 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1582 * - A transaction must have been started.
1583 * - The Attribute b-tree file must be locked exclusive.
1584 * - The Allocation Bitmap file must be locked exclusive.
1585 * - The iterator key must be initialized.
1586 */
1587 int
1588 remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator)
1589 {
1590 struct filefork *btfile;
1591 FSBufferDescriptor btdata;
1592 HFSPlusAttrRecord attrdata; /* 90 bytes */
1593 u_int16_t datasize;
1594 int result;
1595
1596 btfile = VTOF(hfsmp->hfs_attribute_vp);
1597
1598 btdata.bufferAddress = &attrdata;
1599 btdata.itemSize = sizeof(attrdata);
1600 btdata.itemCount = 1;
1601 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1602 if (result) {
1603 goto exit; /* no records. */
1604 }
1605 /*
1606 * Free the blocks from extent based attributes.
1607 *
1608 * Note that the block references (btree records) are removed
1609 * before releasing the blocks in the allocation bitmap.
1610 */
1611 if (attrdata.recordType == kHFSPlusAttrForkData) {
1612 int totalblks;
1613 int extentblks;
1614 u_int32_t *keystartblk;
1615
1616 if (datasize < sizeof(HFSPlusAttrForkData)) {
1617 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize, sizeof(HFSPlusAttrForkData));
1618 }
1619 totalblks = attrdata.forkData.theFork.totalBlocks;
1620
1621 /* Process the first 8 extents. */
1622 extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents);
1623 if (extentblks > totalblks)
1624 panic("hfs: remove_attribute_records: corruption...");
1625 if (BTDeleteRecord(btfile, iterator) == 0) {
1626 free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents);
1627 }
1628 totalblks -= extentblks;
1629 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1630
1631 /* Process any overflow extents. */
1632 while (totalblks) {
1633 *keystartblk += (u_int32_t)extentblks;
1634
1635 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1636 if (result ||
1637 (attrdata.recordType != kHFSPlusAttrExtents) ||
1638 (datasize < sizeof(HFSPlusAttrExtents))) {
1639 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1640 hfsmp->vcbVN, MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
1641 result = ENOATTR;
1642 break; /* break from while */
1643 }
1644 /* Process the next 8 extents. */
1645 extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents);
1646 if (extentblks > totalblks)
1647 panic("hfs: remove_attribute_records: corruption...");
1648 if (BTDeleteRecord(btfile, iterator) == 0) {
1649 free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents);
1650 }
1651 totalblks -= extentblks;
1652 }
1653 } else {
1654 result = BTDeleteRecord(btfile, iterator);
1655 }
1656 (void) BTFlushPath(btfile);
1657 exit:
1658 return (result == btNotFound ? ENOATTR : MacToVFSError(result));
1659 }
1660
1661
1662 /*
1663 * Retrieve the list of extended attribute names.
1664 */
1665 int
1666 hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
1667 /*
1668 struct vnop_listxattr_args {
1669 struct vnodeop_desc *a_desc;
1670 vnode_t a_vp;
1671 uio_t a_uio;
1672 size_t *a_size;
1673 int a_options;
1674 vfs_context_t a_context;
1675 */
1676 {
1677 struct vnode *vp = ap->a_vp;
1678 struct cnode *cp = VTOC(vp);
1679 struct hfsmount *hfsmp;
1680 uio_t uio = ap->a_uio;
1681 struct BTreeIterator * iterator = NULL;
1682 struct filefork *btfile;
1683 struct listattr_callback_state state;
1684 user_addr_t user_start = 0;
1685 user_size_t user_len = 0;
1686 int lockflags;
1687 int result;
1688 u_int8_t finderinfo[32];
1689
1690
1691 if (VNODE_IS_RSRC(vp)) {
1692 return (EPERM);
1693 }
1694
1695 #if HFS_COMPRESSION
1696 int compressed = hfs_file_is_compressed(cp, 1); /* 1 == don't take the cnode lock */
1697 #endif /* HFS_COMPRESSION */
1698
1699 hfsmp = VTOHFS(vp);
1700 *ap->a_size = 0;
1701
1702 /*
1703 * Take the truncate lock; this serializes us against the ioctl
1704 * to truncate data & reset the decmpfs state
1705 * in the compressed file handler.
1706 */
1707 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
1708
1709 /* Now the regular cnode lock (shared) */
1710 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
1711 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1712 return (result);
1713 }
1714
1715 /*
1716 * Make a copy of the cnode's finderinfo to a local so we can
1717 * zero out the date added field. Also zero out the private type/creator
1718 * for symlinks.
1719 */
1720 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1721 hfs_zero_hidden_fields (cp, finderinfo);
1722
1723 /* Don't expose a symlink's private type/creator. */
1724 if (vnode_islnk(vp)) {
1725 struct FndrFileInfo *fip;
1726
1727 fip = (struct FndrFileInfo *)&finderinfo;
1728 fip->fdType = 0;
1729 fip->fdCreator = 0;
1730 }
1731
1732
1733 /* If Finder Info is non-empty then export it's name. */
1734 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
1735 if (uio == NULL) {
1736 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
1737 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
1738 result = ERANGE;
1739 goto exit;
1740 } else {
1741 result = uiomove(XATTR_FINDERINFO_NAME,
1742 sizeof(XATTR_FINDERINFO_NAME), uio);
1743 if (result)
1744 goto exit;
1745 }
1746 }
1747 /* If Resource Fork is non-empty then export it's name. */
1748 if (S_ISREG(cp->c_mode) && RSRC_FORK_EXISTS(cp)) {
1749 #if HFS_COMPRESSION
1750 if ((ap->a_options & XATTR_SHOWCOMPRESSION) ||
1751 !compressed ||
1752 !hfs_hides_rsrc(ap->a_context, VTOC(vp), 1) /* 1 == don't take the cnode lock */
1753 )
1754 #endif /* HFS_COMPRESSION */
1755 {
1756 if (uio == NULL) {
1757 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
1758 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
1759 result = ERANGE;
1760 goto exit;
1761 } else {
1762 result = uiomove(XATTR_RESOURCEFORK_NAME,
1763 sizeof(XATTR_RESOURCEFORK_NAME), uio);
1764 if (result)
1765 goto exit;
1766 }
1767 }
1768 }
1769 /*
1770 * Standard HFS only supports native FinderInfo and Resource Forks.
1771 * Return at this point.
1772 */
1773 if (hfsmp->hfs_flags & HFS_STANDARD) {
1774 result = 0;
1775 goto exit;
1776 }
1777 /* Bail if we don't have any extended attributes. */
1778 if ((hfsmp->hfs_attribute_vp == NULL) ||
1779 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
1780 result = 0;
1781 goto exit;
1782 }
1783 btfile = VTOF(hfsmp->hfs_attribute_vp);
1784
1785 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1786 if (iterator == NULL) {
1787 result = ENOMEM;
1788 goto exit;
1789 }
1790 bzero(iterator, sizeof(*iterator));
1791 result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
1792 if (result)
1793 goto exit;
1794
1795 /*
1796 * Lock the user's buffer here so that we won't fault on
1797 * it in uiomove while holding the attributes b-tree lock.
1798 */
1799 if (uio && uio_isuserspace(uio)) {
1800 user_start = uio_curriovbase(uio);
1801 user_len = uio_curriovlen(uio);
1802
1803 if ((result = vslock(user_start, user_len)) != 0) {
1804 user_start = 0;
1805 goto exit;
1806 }
1807 }
1808 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1809
1810 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1811 if (result && result != btNotFound) {
1812 hfs_systemfile_unlock(hfsmp, lockflags);
1813 goto exit;
1814 }
1815
1816 state.fileID = cp->c_fileid;
1817 state.result = 0;
1818 state.uio = uio;
1819 state.size = 0;
1820 #if HFS_COMPRESSION
1821 state.showcompressed = !compressed || ap->a_options & XATTR_SHOWCOMPRESSION;
1822 state.ctx = ap->a_context;
1823 state.vp = vp;
1824 #endif /* HFS_COMPRESSION */
1825
1826 /*
1827 * Process entries starting just after iterator->key.
1828 */
1829 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
1830 (IterateCallBackProcPtr)listattr_callback, &state);
1831 hfs_systemfile_unlock(hfsmp, lockflags);
1832 if (uio == NULL) {
1833 *ap->a_size += state.size;
1834 }
1835
1836 if (state.result || result == btNotFound)
1837 result = state.result;
1838
1839 exit:
1840 if (user_start) {
1841 vsunlock(user_start, user_len, TRUE);
1842 }
1843 if (iterator) {
1844 FREE(iterator, M_TEMP);
1845 }
1846 hfs_unlock(cp);
1847 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1848
1849 return MacToVFSError(result);
1850 }
1851
1852
1853 /*
1854 * Callback - called for each attribute record
1855 */
1856 static int
1857 listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
1858 {
1859 char attrname[XATTR_MAXNAMELEN + 1];
1860 ssize_t bytecount;
1861 int result;
1862
1863 if (state->fileID != key->fileID) {
1864 state->result = 0;
1865 return (0); /* stop */
1866 }
1867 /*
1868 * Skip over non-primary keys
1869 */
1870 if (key->startBlock != 0) {
1871 return (1); /* continue */
1872 }
1873
1874 /* Convert the attribute name into UTF-8. */
1875 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
1876 (u_int8_t *)attrname, (size_t *)&bytecount, sizeof(attrname), '/', 0);
1877 if (result) {
1878 state->result = result;
1879 return (0); /* stop */
1880 }
1881 bytecount++; /* account for null termination char */
1882
1883 if (xattr_protected(attrname))
1884 return (1); /* continue */
1885
1886 #if HFS_COMPRESSION
1887 if (!state->showcompressed && hfs_hides_xattr(state->ctx, VTOC(state->vp), attrname, 1) ) /* 1 == don't take the cnode lock */
1888 return 1; /* continue */
1889 #endif /* HFS_COMPRESSION */
1890
1891 if (state->uio == NULL) {
1892 state->size += bytecount;
1893 } else {
1894 if (bytecount > uio_resid(state->uio)) {
1895 state->result = ERANGE;
1896 return (0); /* stop */
1897 }
1898 result = uiomove((caddr_t) attrname, bytecount, state->uio);
1899 if (result) {
1900 state->result = result;
1901 return (0); /* stop */
1902 }
1903 }
1904 return (1); /* continue */
1905 }
1906
1907 /*
1908 * Remove all the attributes from a cnode.
1909 *
1910 * This function creates/ends its own transaction so that each
1911 * attribute is deleted in its own transaction (to avoid having
1912 * a transaction grow too large).
1913 *
1914 * This function takes the necessary locks on the attribute
1915 * b-tree file and the allocation (bitmap) file.
1916 */
1917 int
1918 hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid)
1919 {
1920 BTreeIterator *iterator = NULL;
1921 HFSPlusAttrKey *key;
1922 struct filefork *btfile;
1923 int result, lockflags;
1924
1925 if (hfsmp->hfs_attribute_vp == NULL) {
1926 return (0);
1927 }
1928 btfile = VTOF(hfsmp->hfs_attribute_vp);
1929
1930 MALLOC(iterator, BTreeIterator *, sizeof(BTreeIterator), M_TEMP, M_WAITOK);
1931 if (iterator == NULL) {
1932 return (ENOMEM);
1933 }
1934 bzero(iterator, sizeof(BTreeIterator));
1935 key = (HFSPlusAttrKey *)&iterator->key;
1936
1937 /* Loop until there are no more attributes for this file id */
1938 for(;;) {
1939 if (hfs_start_transaction(hfsmp) != 0) {
1940 result = EINVAL;
1941 goto exit;
1942 }
1943
1944 /* Lock the attribute b-tree and the allocation (bitmap) files */
1945 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1946
1947 /*
1948 * Go to first possible attribute key/record pair
1949 */
1950 (void) hfs_buildattrkey(fileid, NULL, key);
1951 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1952 if (result || key->fileID != fileid) {
1953 hfs_systemfile_unlock(hfsmp, lockflags);
1954 hfs_end_transaction(hfsmp);
1955 goto exit;
1956 }
1957 result = remove_attribute_records(hfsmp, iterator);
1958
1959 #if HFS_XATTR_VERBOSE
1960 if (result) {
1961 printf("hfs_removeallattr: unexpected err %d\n", result);
1962 }
1963 #endif
1964 hfs_systemfile_unlock(hfsmp, lockflags);
1965 hfs_end_transaction(hfsmp);
1966 if (result)
1967 break;
1968 }
1969 exit:
1970 FREE(iterator, M_TEMP);
1971 return (result == btNotFound ? 0: MacToVFSError(result));
1972 }
1973
1974 __private_extern__
1975 void
1976 hfs_xattr_init(struct hfsmount * hfsmp)
1977 {
1978 /*
1979 * If there isn't an attributes b-tree then create one.
1980 */
1981 if (!(hfsmp->hfs_flags & HFS_STANDARD) &&
1982 (hfsmp->hfs_attribute_vp == NULL) &&
1983 !(hfsmp->hfs_flags & HFS_READ_ONLY)) {
1984 (void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
1985 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
1986 }
1987 if (hfsmp->hfs_attribute_vp)
1988 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
1989 }
1990
1991 /*
1992 * Enable/Disable volume attributes stored as EA for root file system.
1993 * Supported attributes are -
1994 * 1. Extent-based Extended Attributes
1995 */
1996 int
1997 hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
1998 {
1999 struct BTreeIterator * iterator = NULL;
2000 struct filefork *btfile;
2001 int lockflags;
2002 int result;
2003
2004 if (hfsmp->hfs_flags & HFS_STANDARD) {
2005 return (ENOTSUP);
2006 }
2007 if (xattrtype != HFS_SET_XATTREXTENTS_STATE) {
2008 return EINVAL;
2009 }
2010
2011 /*
2012 * If there isn't an attributes b-tree then create one.
2013 */
2014 if (hfsmp->hfs_attribute_vp == NULL) {
2015 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2016 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2017 if (result) {
2018 return (result);
2019 }
2020 }
2021
2022 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2023 if (iterator == NULL) {
2024 return (ENOMEM);
2025 }
2026 bzero(iterator, sizeof(*iterator));
2027
2028 /*
2029 * Build a b-tree key.
2030 * We use the root's parent id (1) to hold this volume attribute.
2031 */
2032 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
2033 (HFSPlusAttrKey *)&iterator->key);
2034
2035 /* Start a transaction for our changes. */
2036 if (hfs_start_transaction(hfsmp) != 0) {
2037 result = EINVAL;
2038 goto exit;
2039 }
2040 btfile = VTOF(hfsmp->hfs_attribute_vp);
2041
2042 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2043
2044 if (state == 0) {
2045 /* Remove the attribute. */
2046 result = BTDeleteRecord(btfile, iterator);
2047 if (result == btNotFound)
2048 result = 0;
2049 } else {
2050 FSBufferDescriptor btdata;
2051 HFSPlusAttrData attrdata;
2052 u_int16_t datasize;
2053
2054 datasize = sizeof(attrdata);
2055 btdata.bufferAddress = &attrdata;
2056 btdata.itemSize = datasize;
2057 btdata.itemCount = 1;
2058 attrdata.recordType = kHFSPlusAttrInlineData;
2059 attrdata.reserved[0] = 0;
2060 attrdata.reserved[1] = 0;
2061 attrdata.attrSize = 2;
2062 attrdata.attrData[0] = 0;
2063 attrdata.attrData[1] = 0;
2064
2065 /* Insert the attribute. */
2066 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
2067 if (result == btExists)
2068 result = 0;
2069 }
2070 (void) BTFlushPath(btfile);
2071
2072 hfs_systemfile_unlock(hfsmp, lockflags);
2073
2074 /* Finish the transaction of our changes. */
2075 hfs_end_transaction(hfsmp);
2076
2077 /* Update the state in the mount point */
2078 hfs_lock_mount (hfsmp);
2079 if (state == 0) {
2080 hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
2081 } else {
2082 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
2083 }
2084 hfs_unlock_mount (hfsmp);
2085
2086 exit:
2087 if (iterator) {
2088 FREE(iterator, M_TEMP);
2089 }
2090 return MacToVFSError(result);
2091 }
2092
2093
2094 /*
2095 * hfs_attrkeycompare - compare two attribute b-tree keys.
2096 *
2097 * The name portion of the key is compared using a 16-bit binary comparison.
2098 * This is called from the b-tree code.
2099 */
2100 __private_extern__
2101 int
2102 hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
2103 {
2104 u_int32_t searchFileID, trialFileID;
2105 int result;
2106
2107 searchFileID = searchKey->fileID;
2108 trialFileID = trialKey->fileID;
2109 result = 0;
2110
2111 if (searchFileID > trialFileID) {
2112 ++result;
2113 } else if (searchFileID < trialFileID) {
2114 --result;
2115 } else {
2116 u_int16_t * str1 = &searchKey->attrName[0];
2117 u_int16_t * str2 = &trialKey->attrName[0];
2118 int length1 = searchKey->attrNameLen;
2119 int length2 = trialKey->attrNameLen;
2120 u_int16_t c1, c2;
2121 int length;
2122
2123 if (length1 < length2) {
2124 length = length1;
2125 --result;
2126 } else if (length1 > length2) {
2127 length = length2;
2128 ++result;
2129 } else {
2130 length = length1;
2131 }
2132
2133 while (length--) {
2134 c1 = *(str1++);
2135 c2 = *(str2++);
2136
2137 if (c1 > c2) {
2138 result = 1;
2139 break;
2140 }
2141 if (c1 < c2) {
2142 result = -1;
2143 break;
2144 }
2145 }
2146 if (result)
2147 return (result);
2148 /*
2149 * Names are equal; compare startBlock
2150 */
2151 if (searchKey->startBlock == trialKey->startBlock) {
2152 return (0);
2153 } else {
2154 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
2155 }
2156 }
2157
2158 return result;
2159 }
2160
2161
2162 /*
2163 * hfs_buildattrkey - build an Attribute b-tree key
2164 */
2165 __private_extern__
2166 int
2167 hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
2168 {
2169 int result = 0;
2170 size_t unicodeBytes = 0;
2171
2172 if (attrname != NULL) {
2173 /*
2174 * Convert filename from UTF-8 into Unicode
2175 */
2176 result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName,
2177 &unicodeBytes, sizeof(key->attrName), 0, 0);
2178 if (result) {
2179 if (result != ENAMETOOLONG)
2180 result = EINVAL; /* name has invalid characters */
2181 return (result);
2182 }
2183 key->attrNameLen = unicodeBytes / sizeof(UniChar);
2184 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
2185 } else {
2186 key->attrNameLen = 0;
2187 key->keyLength = kHFSPlusAttrKeyMinimumLength;
2188 }
2189 key->pad = 0;
2190 key->fileID = fileID;
2191 key->startBlock = 0;
2192
2193 return (0);
2194 }
2195
2196 /*
2197 * getnodecount - calculate starting node count for attributes b-tree.
2198 */
2199 static int
2200 getnodecount(struct hfsmount *hfsmp, size_t nodesize)
2201 {
2202 u_int64_t freebytes;
2203 u_int64_t calcbytes;
2204
2205 /*
2206 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2207 * 10.5: Attempt to be as big as the catalog clump size.
2208 *
2209 * Use no more than 10 % of the remaining free space.
2210 */
2211 freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize;
2212
2213 calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024);
2214
2215 calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize);
2216
2217 calcbytes = MIN(calcbytes, freebytes / 10);
2218
2219 return (MAX(2, (int)(calcbytes / nodesize)));
2220 }
2221
2222
2223 /*
2224 * getmaxinlineattrsize - calculate maximum inline attribute size.
2225 *
2226 * This yields 3,802 bytes for an 8K node size.
2227 */
2228 static size_t
2229 getmaxinlineattrsize(struct vnode * attrvp)
2230 {
2231 struct BTreeInfoRec btinfo;
2232 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
2233 size_t maxsize;
2234
2235 if (attrvp != NULL) {
2236 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2237 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
2238 nodesize = btinfo.nodeSize;
2239 hfs_unlock(VTOC(attrvp));
2240 }
2241 maxsize = nodesize;
2242 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */
2243 maxsize -= 3 * sizeof(u_int16_t); /* minus 3 index slots */
2244 maxsize /= 2; /* 2 key/rec pairs minumum */
2245 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */
2246 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */
2247 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */
2248
2249 return (maxsize);
2250 }
2251
2252 /*
2253 * Initialize vnode for attribute data I/O.
2254 *
2255 * On success,
2256 * - returns zero
2257 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2258 * - an iocount is taken on the attrdata vnode which exists
2259 * for the entire duration of the mount. It is only dropped
2260 * during unmount
2261 * - the attrdata cnode is not locked
2262 *
2263 * On failure,
2264 * - returns non-zero value
2265 * - the caller does not have to worry about any locks or references
2266 */
2267 int init_attrdata_vnode(struct hfsmount *hfsmp)
2268 {
2269 vnode_t vp;
2270 int result = 0;
2271 struct cat_desc cat_desc;
2272 struct cat_attr cat_attr;
2273 struct cat_fork cat_fork;
2274 int newvnode_flags = 0;
2275
2276 bzero(&cat_desc, sizeof(cat_desc));
2277 cat_desc.cd_parentcnid = kHFSRootParentID;
2278 cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
2279 cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
2280 cat_desc.cd_cnid = kHFSAttributeDataFileID;
2281 /* Tag vnode as system file, note that we can still use cluster I/O */
2282 cat_desc.cd_flags |= CD_ISMETA;
2283
2284 bzero(&cat_attr, sizeof(cat_attr));
2285 cat_attr.ca_linkcount = 1;
2286 cat_attr.ca_mode = S_IFREG;
2287 cat_attr.ca_fileid = cat_desc.cd_cnid;
2288 cat_attr.ca_blocks = hfsmp->totalBlocks;
2289
2290 /*
2291 * The attribute data file is a virtual file that spans the
2292 * entire file system space.
2293 *
2294 * Each extent-based attribute occupies a unique portion of
2295 * in this virtual file. The cluster I/O is done using actual
2296 * allocation block offsets so no additional mapping is needed
2297 * for the VNOP_BLOCKMAP call.
2298 *
2299 * This approach allows the attribute data to be cached without
2300 * incurring the high cost of using a separate vnode per attribute.
2301 *
2302 * Since we need to acquire the attribute b-tree file lock anyways,
2303 * the virtual file doesn't introduce any additional serialization.
2304 */
2305 bzero(&cat_fork, sizeof(cat_fork));
2306 cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
2307 cat_fork.cf_blocks = hfsmp->totalBlocks;
2308 cat_fork.cf_extents[0].startBlock = 0;
2309 cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
2310
2311 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr,
2312 &cat_fork, &vp, &newvnode_flags);
2313 if (result == 0) {
2314 hfsmp->hfs_attrdata_vp = vp;
2315 hfs_unlock(VTOC(vp));
2316 }
2317 return (result);
2318 }
2319
2320 /*
2321 * Read an extent based attribute.
2322 */
2323 static int
2324 read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2325 {
2326 vnode_t evp = hfsmp->hfs_attrdata_vp;
2327 int bufsize;
2328 int64_t iosize;
2329 int attrsize;
2330 int blksize;
2331 int i;
2332 int result = 0;
2333
2334 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2335
2336 bufsize = (int)uio_resid(uio);
2337 attrsize = (int)datasize;
2338 blksize = (int)hfsmp->blockSize;
2339
2340 /*
2341 * Read the attribute data one extent at a time.
2342 * For the typical case there is only one extent.
2343 */
2344 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2345 iosize = extents[i].blockCount * blksize;
2346 iosize = MIN(iosize, attrsize);
2347 iosize = MIN(iosize, bufsize);
2348 uio_setresid(uio, iosize);
2349 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2350
2351 result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT);
2352
2353 #if HFS_XATTR_VERBOSE
2354 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2355 iosize, extents[i].startBlock, extents[i].blockCount, result);
2356 #endif
2357 if (result)
2358 break;
2359 attrsize -= iosize;
2360 bufsize -= iosize;
2361 }
2362 uio_setresid(uio, bufsize);
2363 uio_setoffset(uio, datasize);
2364
2365 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2366 return (result);
2367 }
2368
2369 /*
2370 * Write an extent based attribute.
2371 */
2372 static int
2373 write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2374 {
2375 vnode_t evp = hfsmp->hfs_attrdata_vp;
2376 off_t filesize;
2377 int bufsize;
2378 int attrsize;
2379 int64_t iosize;
2380 int blksize;
2381 int i;
2382 int result = 0;
2383
2384 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2385
2386 bufsize = uio_resid(uio);
2387 attrsize = (int) datasize;
2388 blksize = (int) hfsmp->blockSize;
2389 filesize = VTOF(evp)->ff_size;
2390
2391 /*
2392 * Write the attribute data one extent at a time.
2393 */
2394 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2395 iosize = extents[i].blockCount * blksize;
2396 iosize = MIN(iosize, attrsize);
2397 iosize = MIN(iosize, bufsize);
2398 uio_setresid(uio, iosize);
2399 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2400
2401 result = cluster_write(evp, uio, filesize, filesize, filesize,
2402 (off_t) 0, IO_SYNC | IO_UNIT);
2403 #if HFS_XATTR_VERBOSE
2404 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2405 iosize, extents[i].startBlock, extents[i].blockCount, result);
2406 #endif
2407 if (result)
2408 break;
2409 attrsize -= iosize;
2410 bufsize -= iosize;
2411 }
2412 uio_setresid(uio, bufsize);
2413 uio_setoffset(uio, datasize);
2414
2415 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2416 return (result);
2417 }
2418
2419 /*
2420 * Allocate blocks for an extent based attribute.
2421 */
2422 static int
2423 alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks)
2424 {
2425 int blkcnt;
2426 int startblk;
2427 int lockflags;
2428 int i;
2429 int maxextents;
2430 int result = 0;
2431
2432 startblk = hfsmp->hfs_metazone_end;
2433 blkcnt = howmany(attrsize, hfsmp->blockSize);
2434 if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) {
2435 return (ENOSPC);
2436 }
2437 *blocks = blkcnt;
2438 maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor);
2439
2440 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2441
2442 for (i = 0; (blkcnt > 0) && (i < maxextents); i++) {
2443 /* Try allocating and see if we find something decent */
2444 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0,
2445 &extents[i].startBlock, &extents[i].blockCount);
2446 /*
2447 * If we couldn't find anything, then re-try the allocation but allow
2448 * journal flushes.
2449 */
2450 if (result == dskFulErr) {
2451 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, HFS_ALLOC_FLUSHTXN,
2452 &extents[i].startBlock, &extents[i].blockCount);
2453 }
2454
2455
2456 #if HFS_XATTR_VERBOSE
2457 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2458 blkcnt, extents[i].startBlock, extents[i].blockCount, result);
2459 #endif
2460 if (result) {
2461 extents[i].startBlock = 0;
2462 extents[i].blockCount = 0;
2463 break;
2464 }
2465 blkcnt -= extents[i].blockCount;
2466 startblk = extents[i].startBlock + extents[i].blockCount;
2467 }
2468 /*
2469 * If it didn't fit in the extents buffer then bail.
2470 */
2471 if (blkcnt) {
2472 result = ENOSPC;
2473
2474 #if HFS_XATTR_VERBOSE
2475 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
2476 #endif
2477 for (; i >= 0; i--) {
2478 if ((blkcnt = extents[i].blockCount) != 0) {
2479 (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt, 0);
2480 extents[i].startBlock = 0;
2481 extents[i].blockCount = 0;
2482 }
2483 }
2484 }
2485
2486 hfs_systemfile_unlock(hfsmp, lockflags);
2487 return MacToVFSError(result);
2488 }
2489
2490 /*
2491 * Release blocks from an extent based attribute.
2492 */
2493 static void
2494 free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents)
2495 {
2496 vnode_t evp = hfsmp->hfs_attrdata_vp;
2497 int remblks = blkcnt;
2498 int lockflags;
2499 int i;
2500
2501 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2502
2503 for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) {
2504 if (extents[i].blockCount > (u_int32_t)blkcnt) {
2505 #if HFS_XATTR_VERBOSE
2506 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2507 extents[i].startBlock, extents[i].blockCount);
2508 #endif
2509 extents[i].blockCount = 0;
2510 continue;
2511 }
2512 if (extents[i].startBlock == 0) {
2513 break;
2514 }
2515 (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount, 0);
2516 remblks -= extents[i].blockCount;
2517 extents[i].startBlock = 0;
2518 extents[i].blockCount = 0;
2519
2520 #if HFS_XATTR_VERBOSE
2521 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2522 extents[i].startBlock, extents[i].blockCount);
2523 #endif
2524 /* Discard any resident pages for this block range. */
2525 if (evp) {
2526 off_t start, end;
2527
2528 start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize;
2529 end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize;
2530 (void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE);
2531 }
2532 }
2533
2534 hfs_systemfile_unlock(hfsmp, lockflags);
2535 }
2536
2537 static int
2538 has_overflow_extents(HFSPlusForkData *forkdata)
2539 {
2540 u_int32_t blocks;
2541
2542 if (forkdata->extents[7].blockCount == 0)
2543 return (0);
2544
2545 blocks = forkdata->extents[0].blockCount +
2546 forkdata->extents[1].blockCount +
2547 forkdata->extents[2].blockCount +
2548 forkdata->extents[3].blockCount +
2549 forkdata->extents[4].blockCount +
2550 forkdata->extents[5].blockCount +
2551 forkdata->extents[6].blockCount +
2552 forkdata->extents[7].blockCount;
2553
2554 return (forkdata->totalBlocks > blocks);
2555 }
2556
2557 static int
2558 count_extent_blocks(int maxblks, HFSPlusExtentRecord extents)
2559 {
2560 int blocks;
2561 int i;
2562
2563 for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) {
2564 /* Ignore obvious bogus extents. */
2565 if (extents[i].blockCount > (u_int32_t)maxblks)
2566 continue;
2567 if (extents[i].startBlock == 0 || extents[i].blockCount == 0)
2568 break;
2569 blocks += extents[i].blockCount;
2570 }
2571 return (blocks);
2572 }