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