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