]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_xattr.c
c63dce8ea8ba0286d2c7b82de48e1ea1d22e8d82
[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/uio_internal.h>
42
43 #include "hfs.h"
44 #include "hfs_cnode.h"
45 #include "hfs_mount.h"
46 #include "hfs_format.h"
47 #include "hfs_endian.h"
48 #include "hfs_btreeio.h"
49 #include "hfs_fsctl.h"
50 #include "hfs_cprotect.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, 0);
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
1036 if (cp) {
1037 target_id = cp->c_fileid;
1038 } else {
1039 target_id = fileid;
1040 }
1041
1042 /* Start a transaction for our changes. */
1043 if (hfs_start_transaction(hfsmp) != 0) {
1044 result = EINVAL;
1045 goto exit;
1046 }
1047 started_transaction = 1;
1048
1049 /*
1050 * Once we started the transaction, nobody can compete
1051 * with us, so make sure this file is still there.
1052 */
1053 if ((cp) && (cp->c_flag & C_NOEXISTS)) {
1054 result = ENOENT;
1055 goto exit;
1056 }
1057
1058 /*
1059 * If there isn't an attributes b-tree then create one.
1060 */
1061 if (hfsmp->hfs_attribute_vp == NULL) {
1062 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
1063 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
1064 if (result) {
1065 goto exit;
1066 }
1067 }
1068 if (hfsmp->hfs_max_inline_attrsize == 0) {
1069 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
1070 }
1071
1072 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1073
1074 /* Build the b-tree key. */
1075 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1076 if (iterator == NULL) {
1077 result = ENOMEM;
1078 goto exit;
1079 }
1080 bzero(iterator, sizeof(*iterator));
1081 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1082 if (result) {
1083 goto exit;
1084 }
1085
1086 /* Preflight for replace/create semantics. */
1087 btfile = VTOF(hfsmp->hfs_attribute_vp);
1088 btdata.bufferAddress = &attrdata;
1089 btdata.itemSize = sizeof(attrdata);
1090 btdata.itemCount = 1;
1091 exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0;
1092
1093 /* Replace requires that the attribute already exists. */
1094 if ((ap->a_options & XATTR_REPLACE) && !exists) {
1095 result = ENOATTR;
1096 goto exit;
1097 }
1098 /* Create requires that the attribute doesn't exist. */
1099 if ((ap->a_options & XATTR_CREATE) && exists) {
1100 result = EEXIST;
1101 goto exit;
1102 }
1103
1104 /* If it won't fit inline then use extent-based attributes. */
1105 if (attrsize > hfsmp->hfs_max_inline_attrsize) {
1106 size_t extentbufsize;
1107 int blkcnt;
1108 int extentblks;
1109 u_int32_t *keystartblk;
1110 int i;
1111
1112 if (uio == NULL) {
1113 /*
1114 * setxattrs originating from in-kernel are not supported if they are bigger
1115 * than the inline max size. Just return ENOATTR and force them to do it with a
1116 * smaller EA.
1117 */
1118 result = EPERM;
1119 goto exit;
1120 }
1121
1122 /* Get some blocks. */
1123 blkcnt = howmany(attrsize, hfsmp->blockSize);
1124 extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor);
1125 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
1126 MALLOC(extentptr, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
1127 if (extentptr == NULL) {
1128 result = ENOMEM;
1129 goto exit;
1130 }
1131 bzero(extentptr, extentbufsize);
1132 result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks);
1133 if (result) {
1134 allocatedblks = 0;
1135 goto exit; /* no more space */
1136 }
1137 /* Copy data into the blocks. */
1138 result = write_attr_data(hfsmp, uio, attrsize, extentptr);
1139 if (result) {
1140 if (vp) {
1141 const char *name = vnode_getname(vp);
1142 printf("hfs_setxattr: write_attr_data 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 /* Now remove any previous attribute. */
1151 if (exists) {
1152 result = remove_attribute_records(hfsmp, iterator);
1153 if (result) {
1154 if (vp) {
1155 const char *name = vnode_getname(vp);
1156 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1157 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1158 if (name)
1159 vnode_putname(name);
1160 }
1161 goto exit;
1162 }
1163 }
1164 /* Create attribute fork data record. */
1165 MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK);
1166 if (recp == NULL) {
1167 result = ENOMEM;
1168 goto exit;
1169 }
1170 btdata.bufferAddress = recp;
1171 btdata.itemCount = 1;
1172 btdata.itemSize = sizeof(HFSPlusAttrForkData);
1173
1174 recp->recordType = kHFSPlusAttrForkData;
1175 recp->forkData.reserved = 0;
1176 recp->forkData.theFork.logicalSize = attrsize;
1177 recp->forkData.theFork.clumpSize = 0;
1178 recp->forkData.theFork.totalBlocks = blkcnt;
1179 bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord));
1180
1181 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1182
1183 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1184 if (result) {
1185 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1186 hfsmp->vcbVN, target_id, ap->a_name, result);
1187 goto exit;
1188 }
1189 extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents);
1190 blkcnt -= extentblks;
1191 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1192 i = 0;
1193
1194 /* Create overflow extents as needed. */
1195 while (blkcnt > 0) {
1196 /* Initialize the key and record. */
1197 *keystartblk += (u_int32_t)extentblks;
1198 btdata.itemSize = sizeof(HFSPlusAttrExtents);
1199 recp->recordType = kHFSPlusAttrExtents;
1200 recp->overflowExtents.reserved = 0;
1201
1202 /* Copy the next set of extents. */
1203 i += kHFSPlusExtentDensity;
1204 bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord));
1205
1206 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1207 if (result) {
1208 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1209 hfsmp->vcbVN, target_id, ap->a_name, result);
1210 goto exit;
1211 }
1212 extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents);
1213 blkcnt -= extentblks;
1214 }
1215 } else { /* Inline data */
1216 if (exists) {
1217 result = remove_attribute_records(hfsmp, iterator);
1218 if (result) {
1219 goto exit;
1220 }
1221 }
1222
1223 /* Calculate size of record rounded up to multiple of 2 bytes. */
1224 btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
1225 MALLOC(recp, HFSPlusAttrRecord *, btdata.itemSize, M_TEMP, M_WAITOK);
1226 if (recp == NULL) {
1227 result = ENOMEM;
1228 goto exit;
1229 }
1230 recp->recordType = kHFSPlusAttrInlineData;
1231 recp->attrData.reserved[0] = 0;
1232 recp->attrData.reserved[1] = 0;
1233 recp->attrData.attrSize = attrsize;
1234
1235 /* Copy in the attribute data (if any). */
1236 if (attrsize > 0) {
1237 if (data_ptr) {
1238 bcopy(data_ptr, &recp->attrData.attrData, attrsize);
1239 } else {
1240 /*
1241 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1242 * then deny the copy operation.
1243 */
1244 if (uio == NULL) {
1245 result = EPERM;
1246 goto exit;
1247 }
1248 result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio);
1249 }
1250
1251 if (result) {
1252 goto exit;
1253 }
1254 }
1255
1256 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1257
1258 btdata.bufferAddress = recp;
1259 btdata.itemCount = 1;
1260 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1261 }
1262
1263 exit:
1264 if (btfile && started_transaction) {
1265 (void) BTFlushPath(btfile);
1266 }
1267 hfs_systemfile_unlock(hfsmp, lockflags);
1268 if (result == 0) {
1269 if (vp) {
1270 cp = VTOC(vp);
1271 /* Setting an attribute only updates change time and not
1272 * modified time of the file.
1273 */
1274 cp->c_touch_chgtime = TRUE;
1275 cp->c_flag |= C_MODIFIED;
1276 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
1277 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
1278 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
1279 }
1280 (void) hfs_update(vp, 0);
1281 }
1282 }
1283 if (started_transaction) {
1284 if (result && allocatedblks) {
1285 free_attr_blks(hfsmp, allocatedblks, extentptr);
1286 }
1287 hfs_end_transaction(hfsmp);
1288 }
1289
1290 if (recp) {
1291 FREE(recp, M_TEMP);
1292 }
1293 if (extentptr) {
1294 FREE(extentptr, M_TEMP);
1295 }
1296 if (iterator) {
1297 FREE(iterator, M_TEMP);
1298 }
1299
1300 return result;
1301 }
1302
1303
1304
1305
1306 /*
1307 * Remove an extended attribute.
1308 */
1309 int
1310 hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
1311 /*
1312 struct vnop_removexattr_args {
1313 struct vnodeop_desc *a_desc;
1314 vnode_t a_vp;
1315 char * a_name;
1316 int a_options;
1317 vfs_context_t a_context;
1318 };
1319 */
1320 {
1321 struct vnode *vp = ap->a_vp;
1322 struct cnode *cp = VTOC(vp);
1323 struct hfsmount *hfsmp;
1324 struct BTreeIterator * iterator = NULL;
1325 int lockflags;
1326 int result;
1327 time_t orig_ctime=VTOC(vp)->c_ctime;
1328
1329 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
1330 return (EINVAL); /* invalid name */
1331 }
1332 hfsmp = VTOHFS(vp);
1333 if (VNODE_IS_RSRC(vp)) {
1334 return (EPERM);
1335 }
1336
1337 #if HFS_COMPRESSION
1338 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) {
1339 return ENOATTR;
1340 }
1341 #endif /* HFS_COMPRESSION */
1342
1343 check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NSPACE_REARM_NO_ARG);
1344
1345 /* If Resource Fork is non-empty then truncate it. */
1346 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1347 struct vnode *rvp = NULL;
1348
1349 if ( !vnode_isreg(vp) ) {
1350 return (EPERM);
1351 }
1352 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1353 return (result);
1354 }
1355 if (!hfs_has_rsrc(cp)) {
1356 hfs_unlock(cp);
1357 return (ENOATTR);
1358 }
1359 result = hfs_vgetrsrc(hfsmp, vp, &rvp);
1360 hfs_unlock(cp);
1361 if (result) {
1362 return (result);
1363 }
1364
1365 hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1366
1367 // Tell UBC now before we take the cnode lock and start the transaction
1368 hfs_ubc_setsize(rvp, 0, false);
1369
1370 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1371 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1372 vnode_put(rvp);
1373 return (result);
1374 }
1375
1376 /* Start a transaction for encapsulating changes in
1377 * hfs_truncate() and hfs_update()
1378 */
1379 if ((result = hfs_start_transaction(hfsmp))) {
1380 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1381 hfs_unlock(cp);
1382 vnode_put(rvp);
1383 return (result);
1384 }
1385
1386 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
1387 if (result == 0) {
1388 cp->c_touch_chgtime = TRUE;
1389 cp->c_flag |= C_MODIFIED;
1390 result = hfs_update(vp, 0);
1391 }
1392
1393 hfs_end_transaction(hfsmp);
1394 hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT);
1395 hfs_unlock(VTOC(rvp));
1396
1397 vnode_put(rvp);
1398 return (result);
1399 }
1400 /* Clear out the Finder Info. */
1401 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1402 void * finderinfo_start;
1403 int finderinfo_size;
1404 u_int8_t finderinfo[32];
1405 u_int32_t date_added, write_gen_counter, document_id;
1406 u_int8_t *finfo = NULL;
1407
1408 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1409 return (result);
1410 }
1411
1412 /* Use the local copy to store our temporary changes. */
1413 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1414
1415
1416 /* Zero out the date added field in the local copy */
1417 hfs_zero_hidden_fields (cp, finderinfo);
1418
1419 /* Don't expose a symlink's private type/creator. */
1420 if (vnode_islnk(vp)) {
1421 struct FndrFileInfo *fip;
1422
1423 fip = (struct FndrFileInfo *)&finderinfo;
1424 fip->fdType = 0;
1425 fip->fdCreator = 0;
1426 }
1427
1428 /* Do the byte compare against the local copy */
1429 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
1430 hfs_unlock(cp);
1431 return (ENOATTR);
1432 }
1433
1434 /*
1435 * If there was other content, zero out everything except
1436 * type/creator and date added. First, save the date added.
1437 */
1438 finfo = cp->c_finderinfo;
1439 finfo = finfo + 16;
1440 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1441 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1442 date_added = extinfo->date_added;
1443 write_gen_counter = extinfo->write_gen_counter;
1444 document_id = extinfo->document_id;
1445 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1446 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1447 date_added = extinfo->date_added;
1448 write_gen_counter = extinfo->write_gen_counter;
1449 document_id = extinfo->document_id;
1450 }
1451
1452 if (vnode_islnk(vp)) {
1453 /* Ignore type/creator */
1454 finderinfo_start = &cp->c_finderinfo[8];
1455 finderinfo_size = sizeof(cp->c_finderinfo) - 8;
1456 } else {
1457 finderinfo_start = &cp->c_finderinfo[0];
1458 finderinfo_size = sizeof(cp->c_finderinfo);
1459 }
1460 bzero(finderinfo_start, finderinfo_size);
1461
1462
1463 /* Now restore the date added */
1464 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1465 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1466 extinfo->date_added = date_added;
1467 extinfo->write_gen_counter = write_gen_counter;
1468 extinfo->document_id = document_id;
1469 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1470 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1471 extinfo->date_added = date_added;
1472 extinfo->write_gen_counter = write_gen_counter;
1473 extinfo->document_id = document_id;
1474 }
1475
1476 /* Updating finderInfo updates change time and modified time */
1477 cp->c_touch_chgtime = TRUE;
1478 cp->c_flag |= C_MODIFIED;
1479 hfs_update(vp, 0);
1480
1481 hfs_unlock(cp);
1482
1483 return (0);
1484 }
1485 /*
1486 * Standard HFS only supports native FinderInfo and Resource Forks.
1487 */
1488 if (hfsmp->hfs_flags & HFS_STANDARD) {
1489 return (EPERM);
1490 }
1491 if (hfsmp->hfs_attribute_vp == NULL) {
1492 return (ENOATTR);
1493 }
1494
1495 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1496 if (iterator == NULL) {
1497 return (ENOMEM);
1498 }
1499 bzero(iterator, sizeof(*iterator));
1500
1501 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1502 goto exit_nolock;
1503 }
1504
1505 result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1506 if (result) {
1507 goto exit;
1508 }
1509
1510 if (hfs_start_transaction(hfsmp) != 0) {
1511 result = EINVAL;
1512 goto exit;
1513 }
1514 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1515
1516 result = remove_attribute_records(hfsmp, iterator);
1517
1518 hfs_systemfile_unlock(hfsmp, lockflags);
1519
1520 if (result == 0) {
1521 cp->c_touch_chgtime = TRUE;
1522
1523 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1524
1525 /* If no more attributes exist, clear attribute bit */
1526 result = file_attribute_exist(hfsmp, cp->c_fileid);
1527 if (result == 0) {
1528 cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
1529 cp->c_flag |= C_MODIFIED;
1530 }
1531 if (result == EEXIST) {
1532 result = 0;
1533 }
1534
1535 hfs_systemfile_unlock(hfsmp, lockflags);
1536
1537 /* If ACL was removed, clear security bit */
1538 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
1539 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
1540 cp->c_flag |= C_MODIFIED;
1541 }
1542 (void) hfs_update(vp, 0);
1543 }
1544
1545 hfs_end_transaction(hfsmp);
1546 exit:
1547 hfs_unlock(cp);
1548 exit_nolock:
1549 FREE(iterator, M_TEMP);
1550 return MacToVFSError(result);
1551 }
1552
1553 /* Check if any attribute record exist for given fileID. This function
1554 * is called by hfs_vnop_removexattr to determine if it should clear the
1555 * attribute bit in the catalog record or not.
1556 *
1557 * Note - you must acquire a shared lock on the attribute btree before
1558 * calling this function.
1559 *
1560 * Output:
1561 * EEXIST - If attribute record was found
1562 * 0 - Attribute was not found
1563 * (other) - Other error (such as EIO)
1564 */
1565 int
1566 file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID)
1567 {
1568 HFSPlusAttrKey *key;
1569 struct BTreeIterator * iterator = NULL;
1570 struct filefork *btfile;
1571 int result = 0;
1572
1573 // if there's no attribute b-tree we sure as heck
1574 // can't have any attributes!
1575 if (hfsmp->hfs_attribute_vp == NULL) {
1576 return false;
1577 }
1578
1579 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1580 if (iterator == NULL) {
1581 result = ENOMEM;
1582 goto out;
1583 }
1584 bzero(iterator, sizeof(*iterator));
1585 key = (HFSPlusAttrKey *)&iterator->key;
1586
1587 result = hfs_buildattrkey(fileID, NULL, key);
1588 if (result) {
1589 goto out;
1590 }
1591
1592 btfile = VTOF(hfsmp->hfs_attribute_vp);
1593 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1594 if (result && (result != btNotFound)) {
1595 goto out;
1596 }
1597
1598 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1599 /* If no next record was found or fileID for next record did not match,
1600 * no more attributes exist for this fileID
1601 */
1602 if ((result && (result == btNotFound)) || (key->fileID != fileID)) {
1603 result = 0;
1604 } else {
1605 result = EEXIST;
1606 }
1607
1608 out:
1609 if (iterator) {
1610 FREE(iterator, M_TEMP);
1611 }
1612 return result;
1613 }
1614
1615
1616 /*
1617 * Remove all the records for a given attribute.
1618 *
1619 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1620 * - A transaction must have been started.
1621 * - The Attribute b-tree file must be locked exclusive.
1622 * - The Allocation Bitmap file must be locked exclusive.
1623 * - The iterator key must be initialized.
1624 */
1625 int
1626 remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator)
1627 {
1628 struct filefork *btfile;
1629 FSBufferDescriptor btdata;
1630 HFSPlusAttrRecord attrdata; /* 90 bytes */
1631 u_int16_t datasize;
1632 int result;
1633
1634 btfile = VTOF(hfsmp->hfs_attribute_vp);
1635
1636 btdata.bufferAddress = &attrdata;
1637 btdata.itemSize = sizeof(attrdata);
1638 btdata.itemCount = 1;
1639 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1640 if (result) {
1641 goto exit; /* no records. */
1642 }
1643 /*
1644 * Free the blocks from extent based attributes.
1645 *
1646 * Note that the block references (btree records) are removed
1647 * before releasing the blocks in the allocation bitmap.
1648 */
1649 if (attrdata.recordType == kHFSPlusAttrForkData) {
1650 int totalblks;
1651 int extentblks;
1652 u_int32_t *keystartblk;
1653
1654 if (datasize < sizeof(HFSPlusAttrForkData)) {
1655 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize, sizeof(HFSPlusAttrForkData));
1656 }
1657 totalblks = attrdata.forkData.theFork.totalBlocks;
1658
1659 /* Process the first 8 extents. */
1660 extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents);
1661 if (extentblks > totalblks)
1662 panic("hfs: remove_attribute_records: corruption...");
1663 if (BTDeleteRecord(btfile, iterator) == 0) {
1664 free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents);
1665 }
1666 totalblks -= extentblks;
1667 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1668
1669 /* Process any overflow extents. */
1670 while (totalblks) {
1671 *keystartblk += (u_int32_t)extentblks;
1672
1673 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1674 if (result ||
1675 (attrdata.recordType != kHFSPlusAttrExtents) ||
1676 (datasize < sizeof(HFSPlusAttrExtents))) {
1677 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1678 hfsmp->vcbVN, MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
1679 result = ENOATTR;
1680 break; /* break from while */
1681 }
1682 /* Process the next 8 extents. */
1683 extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents);
1684 if (extentblks > totalblks)
1685 panic("hfs: remove_attribute_records: corruption...");
1686 if (BTDeleteRecord(btfile, iterator) == 0) {
1687 free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents);
1688 }
1689 totalblks -= extentblks;
1690 }
1691 } else {
1692 result = BTDeleteRecord(btfile, iterator);
1693 }
1694 (void) BTFlushPath(btfile);
1695 exit:
1696 return (result == btNotFound ? ENOATTR : MacToVFSError(result));
1697 }
1698
1699
1700 /*
1701 * Retrieve the list of extended attribute names.
1702 */
1703 int
1704 hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
1705 /*
1706 struct vnop_listxattr_args {
1707 struct vnodeop_desc *a_desc;
1708 vnode_t a_vp;
1709 uio_t a_uio;
1710 size_t *a_size;
1711 int a_options;
1712 vfs_context_t a_context;
1713 */
1714 {
1715 struct vnode *vp = ap->a_vp;
1716 struct cnode *cp = VTOC(vp);
1717 struct hfsmount *hfsmp;
1718 uio_t uio = ap->a_uio;
1719 struct BTreeIterator * iterator = NULL;
1720 struct filefork *btfile;
1721 struct listattr_callback_state state;
1722 user_addr_t user_start = 0;
1723 user_size_t user_len = 0;
1724 int lockflags;
1725 int result;
1726 u_int8_t finderinfo[32];
1727
1728
1729 if (VNODE_IS_RSRC(vp)) {
1730 return (EPERM);
1731 }
1732
1733 #if HFS_COMPRESSION
1734 int compressed = hfs_file_is_compressed(cp, 1); /* 1 == don't take the cnode lock */
1735 #endif /* HFS_COMPRESSION */
1736
1737 hfsmp = VTOHFS(vp);
1738 *ap->a_size = 0;
1739
1740 /*
1741 * Take the truncate lock; this serializes us against the ioctl
1742 * to truncate data & reset the decmpfs state
1743 * in the compressed file handler.
1744 */
1745 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
1746
1747 /* Now the regular cnode lock (shared) */
1748 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
1749 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1750 return (result);
1751 }
1752
1753 /*
1754 * Make a copy of the cnode's finderinfo to a local so we can
1755 * zero out the date added field. Also zero out the private type/creator
1756 * for symlinks.
1757 */
1758 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1759 hfs_zero_hidden_fields (cp, finderinfo);
1760
1761 /* Don't expose a symlink's private type/creator. */
1762 if (vnode_islnk(vp)) {
1763 struct FndrFileInfo *fip;
1764
1765 fip = (struct FndrFileInfo *)&finderinfo;
1766 fip->fdType = 0;
1767 fip->fdCreator = 0;
1768 }
1769
1770
1771 /* If Finder Info is non-empty then export it's name. */
1772 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
1773 if (uio == NULL) {
1774 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
1775 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
1776 result = ERANGE;
1777 goto exit;
1778 } else {
1779 result = uiomove(XATTR_FINDERINFO_NAME,
1780 sizeof(XATTR_FINDERINFO_NAME), uio);
1781 if (result)
1782 goto exit;
1783 }
1784 }
1785 /* If Resource Fork is non-empty then export it's name. */
1786 if (S_ISREG(cp->c_mode) && hfs_has_rsrc(cp)) {
1787 #if HFS_COMPRESSION
1788 if ((ap->a_options & XATTR_SHOWCOMPRESSION) ||
1789 !compressed ||
1790 !decmpfs_hides_rsrc(ap->a_context, VTOCMP(vp))
1791 )
1792 #endif /* HFS_COMPRESSION */
1793 {
1794 if (uio == NULL) {
1795 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
1796 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
1797 result = ERANGE;
1798 goto exit;
1799 } else {
1800 result = uiomove(XATTR_RESOURCEFORK_NAME,
1801 sizeof(XATTR_RESOURCEFORK_NAME), uio);
1802 if (result)
1803 goto exit;
1804 }
1805 }
1806 }
1807 /*
1808 * Standard HFS only supports native FinderInfo and Resource Forks.
1809 * Return at this point.
1810 */
1811 if (hfsmp->hfs_flags & HFS_STANDARD) {
1812 result = 0;
1813 goto exit;
1814 }
1815 /* Bail if we don't have any extended attributes. */
1816 if ((hfsmp->hfs_attribute_vp == NULL) ||
1817 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
1818 result = 0;
1819 goto exit;
1820 }
1821 btfile = VTOF(hfsmp->hfs_attribute_vp);
1822
1823 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1824 if (iterator == NULL) {
1825 result = ENOMEM;
1826 goto exit;
1827 }
1828 bzero(iterator, sizeof(*iterator));
1829 result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
1830 if (result)
1831 goto exit;
1832
1833 /*
1834 * Lock the user's buffer here so that we won't fault on
1835 * it in uiomove while holding the attributes b-tree lock.
1836 */
1837 if (uio && uio_isuserspace(uio)) {
1838 user_start = uio_curriovbase(uio);
1839 user_len = uio_curriovlen(uio);
1840
1841 if ((result = vslock(user_start, user_len)) != 0) {
1842 user_start = 0;
1843 goto exit;
1844 }
1845 }
1846 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1847
1848 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1849 if (result && result != btNotFound) {
1850 hfs_systemfile_unlock(hfsmp, lockflags);
1851 goto exit;
1852 }
1853
1854 state.fileID = cp->c_fileid;
1855 state.result = 0;
1856 state.uio = uio;
1857 state.size = 0;
1858 #if HFS_COMPRESSION
1859 state.showcompressed = !compressed || ap->a_options & XATTR_SHOWCOMPRESSION;
1860 state.ctx = ap->a_context;
1861 state.vp = vp;
1862 #endif /* HFS_COMPRESSION */
1863
1864 /*
1865 * Process entries starting just after iterator->key.
1866 */
1867 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
1868 (IterateCallBackProcPtr)listattr_callback, &state);
1869 hfs_systemfile_unlock(hfsmp, lockflags);
1870 if (uio == NULL) {
1871 *ap->a_size += state.size;
1872 }
1873
1874 if (state.result || result == btNotFound)
1875 result = state.result;
1876
1877 exit:
1878 if (user_start) {
1879 vsunlock(user_start, user_len, TRUE);
1880 }
1881 if (iterator) {
1882 FREE(iterator, M_TEMP);
1883 }
1884 hfs_unlock(cp);
1885 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1886
1887 return MacToVFSError(result);
1888 }
1889
1890
1891 /*
1892 * Callback - called for each attribute record
1893 */
1894 static int
1895 listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
1896 {
1897 char attrname[XATTR_MAXNAMELEN + 1];
1898 ssize_t bytecount;
1899 int result;
1900
1901 if (state->fileID != key->fileID) {
1902 state->result = 0;
1903 return (0); /* stop */
1904 }
1905 /*
1906 * Skip over non-primary keys
1907 */
1908 if (key->startBlock != 0) {
1909 return (1); /* continue */
1910 }
1911
1912 /* Convert the attribute name into UTF-8. */
1913 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
1914 (u_int8_t *)attrname, (size_t *)&bytecount, sizeof(attrname), '/', 0);
1915 if (result) {
1916 state->result = result;
1917 return (0); /* stop */
1918 }
1919 bytecount++; /* account for null termination char */
1920
1921 if (xattr_protected(attrname))
1922 return (1); /* continue */
1923
1924 #if HFS_COMPRESSION
1925 if (!state->showcompressed && decmpfs_hides_xattr(state->ctx, VTOCMP(state->vp), attrname) )
1926 return 1; /* continue */
1927 #endif /* HFS_COMPRESSION */
1928
1929 if (state->uio == NULL) {
1930 state->size += bytecount;
1931 } else {
1932 if (bytecount > uio_resid(state->uio)) {
1933 state->result = ERANGE;
1934 return (0); /* stop */
1935 }
1936 result = uiomove((caddr_t) attrname, bytecount, state->uio);
1937 if (result) {
1938 state->result = result;
1939 return (0); /* stop */
1940 }
1941 }
1942 return (1); /* continue */
1943 }
1944
1945 /*
1946 * Remove all the attributes from a cnode.
1947 *
1948 * This function creates/ends its own transaction so that each
1949 * attribute is deleted in its own transaction (to avoid having
1950 * a transaction grow too large).
1951 *
1952 * This function takes the necessary locks on the attribute
1953 * b-tree file and the allocation (bitmap) file.
1954 *
1955 * NOTE: Upon sucecss, this function will return with an open
1956 * transaction. The reason we do it this way is because when we
1957 * delete the last attribute, we must make sure the flag in the
1958 * catalog record that indicates there are no more records is cleared.
1959 * The caller is responsible for doing this and *must* do it before
1960 * ending the transaction.
1961 */
1962 int
1963 hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid,
1964 bool *open_transaction)
1965 {
1966 BTreeIterator *iterator = NULL;
1967 HFSPlusAttrKey *key;
1968 struct filefork *btfile;
1969 int result, lockflags = 0;
1970
1971 *open_transaction = false;
1972
1973 if (hfsmp->hfs_attribute_vp == NULL)
1974 return 0;
1975
1976 btfile = VTOF(hfsmp->hfs_attribute_vp);
1977
1978 MALLOC(iterator, BTreeIterator *, sizeof(BTreeIterator), M_TEMP, M_WAITOK);
1979 if (iterator == NULL) {
1980 return (ENOMEM);
1981 }
1982 bzero(iterator, sizeof(BTreeIterator));
1983 key = (HFSPlusAttrKey *)&iterator->key;
1984
1985 /* Loop until there are no more attributes for this file id */
1986 do {
1987 if (!*open_transaction)
1988 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1989
1990 (void) hfs_buildattrkey(fileid, NULL, key);
1991 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1992 if (result || key->fileID != fileid)
1993 goto exit;
1994
1995 hfs_systemfile_unlock(hfsmp, lockflags);
1996 lockflags = 0;
1997
1998 if (*open_transaction) {
1999 hfs_end_transaction(hfsmp);
2000 *open_transaction = false;
2001 }
2002
2003 if (hfs_start_transaction(hfsmp) != 0) {
2004 result = EINVAL;
2005 goto exit;
2006 }
2007
2008 *open_transaction = true;
2009
2010 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2011
2012 result = remove_attribute_records(hfsmp, iterator);
2013
2014 #if HFS_XATTR_VERBOSE
2015 if (result) {
2016 printf("hfs_removeallattr: unexpected err %d\n", result);
2017 }
2018 #endif
2019 } while (!result);
2020
2021 exit:
2022 FREE(iterator, M_TEMP);
2023
2024 if (lockflags)
2025 hfs_systemfile_unlock(hfsmp, lockflags);
2026
2027 result = result == btNotFound ? 0 : MacToVFSError(result);
2028
2029 if (result && *open_transaction) {
2030 hfs_end_transaction(hfsmp);
2031 *open_transaction = false;
2032 }
2033
2034 return result;
2035 }
2036
2037 __private_extern__
2038 void
2039 hfs_xattr_init(struct hfsmount * hfsmp)
2040 {
2041 /*
2042 * If there isn't an attributes b-tree then create one.
2043 */
2044 if (!(hfsmp->hfs_flags & HFS_STANDARD) &&
2045 (hfsmp->hfs_attribute_vp == NULL) &&
2046 !(hfsmp->hfs_flags & HFS_READ_ONLY)) {
2047 (void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2048 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2049 }
2050 if (hfsmp->hfs_attribute_vp)
2051 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
2052 }
2053
2054 /*
2055 * Enable/Disable volume attributes stored as EA for root file system.
2056 * Supported attributes are -
2057 * 1. Extent-based Extended Attributes
2058 */
2059 int
2060 hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
2061 {
2062 struct BTreeIterator * iterator = NULL;
2063 struct filefork *btfile;
2064 int lockflags;
2065 int result;
2066
2067 if (hfsmp->hfs_flags & HFS_STANDARD) {
2068 return (ENOTSUP);
2069 }
2070 if (xattrtype != HFS_SET_XATTREXTENTS_STATE) {
2071 return EINVAL;
2072 }
2073
2074 /*
2075 * If there isn't an attributes b-tree then create one.
2076 */
2077 if (hfsmp->hfs_attribute_vp == NULL) {
2078 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2079 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2080 if (result) {
2081 return (result);
2082 }
2083 }
2084
2085 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2086 if (iterator == NULL) {
2087 return (ENOMEM);
2088 }
2089 bzero(iterator, sizeof(*iterator));
2090
2091 /*
2092 * Build a b-tree key.
2093 * We use the root's parent id (1) to hold this volume attribute.
2094 */
2095 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
2096 (HFSPlusAttrKey *)&iterator->key);
2097
2098 /* Start a transaction for our changes. */
2099 if (hfs_start_transaction(hfsmp) != 0) {
2100 result = EINVAL;
2101 goto exit;
2102 }
2103 btfile = VTOF(hfsmp->hfs_attribute_vp);
2104
2105 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2106
2107 if (state == 0) {
2108 /* Remove the attribute. */
2109 result = BTDeleteRecord(btfile, iterator);
2110 if (result == btNotFound)
2111 result = 0;
2112 } else {
2113 FSBufferDescriptor btdata;
2114 HFSPlusAttrData attrdata;
2115 u_int16_t datasize;
2116
2117 datasize = sizeof(attrdata);
2118 btdata.bufferAddress = &attrdata;
2119 btdata.itemSize = datasize;
2120 btdata.itemCount = 1;
2121 attrdata.recordType = kHFSPlusAttrInlineData;
2122 attrdata.reserved[0] = 0;
2123 attrdata.reserved[1] = 0;
2124 attrdata.attrSize = 2;
2125 attrdata.attrData[0] = 0;
2126 attrdata.attrData[1] = 0;
2127
2128 /* Insert the attribute. */
2129 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
2130 if (result == btExists)
2131 result = 0;
2132 }
2133 (void) BTFlushPath(btfile);
2134
2135 hfs_systemfile_unlock(hfsmp, lockflags);
2136
2137 /* Finish the transaction of our changes. */
2138 hfs_end_transaction(hfsmp);
2139
2140 /* Update the state in the mount point */
2141 hfs_lock_mount (hfsmp);
2142 if (state == 0) {
2143 hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
2144 } else {
2145 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
2146 }
2147 hfs_unlock_mount (hfsmp);
2148
2149 exit:
2150 if (iterator) {
2151 FREE(iterator, M_TEMP);
2152 }
2153 return MacToVFSError(result);
2154 }
2155
2156
2157 /*
2158 * hfs_attrkeycompare - compare two attribute b-tree keys.
2159 *
2160 * The name portion of the key is compared using a 16-bit binary comparison.
2161 * This is called from the b-tree code.
2162 */
2163 __private_extern__
2164 int
2165 hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
2166 {
2167 u_int32_t searchFileID, trialFileID;
2168 int result;
2169
2170 searchFileID = searchKey->fileID;
2171 trialFileID = trialKey->fileID;
2172 result = 0;
2173
2174 if (searchFileID > trialFileID) {
2175 ++result;
2176 } else if (searchFileID < trialFileID) {
2177 --result;
2178 } else {
2179 u_int16_t * str1 = &searchKey->attrName[0];
2180 u_int16_t * str2 = &trialKey->attrName[0];
2181 int length1 = searchKey->attrNameLen;
2182 int length2 = trialKey->attrNameLen;
2183 u_int16_t c1, c2;
2184 int length;
2185
2186 if (length1 < length2) {
2187 length = length1;
2188 --result;
2189 } else if (length1 > length2) {
2190 length = length2;
2191 ++result;
2192 } else {
2193 length = length1;
2194 }
2195
2196 while (length--) {
2197 c1 = *(str1++);
2198 c2 = *(str2++);
2199
2200 if (c1 > c2) {
2201 result = 1;
2202 break;
2203 }
2204 if (c1 < c2) {
2205 result = -1;
2206 break;
2207 }
2208 }
2209 if (result)
2210 return (result);
2211 /*
2212 * Names are equal; compare startBlock
2213 */
2214 if (searchKey->startBlock == trialKey->startBlock) {
2215 return (0);
2216 } else {
2217 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
2218 }
2219 }
2220
2221 return result;
2222 }
2223
2224
2225 /*
2226 * hfs_buildattrkey - build an Attribute b-tree key
2227 */
2228 __private_extern__
2229 int
2230 hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
2231 {
2232 int result = 0;
2233 size_t unicodeBytes = 0;
2234
2235 if (attrname != NULL) {
2236 /*
2237 * Convert filename from UTF-8 into Unicode
2238 */
2239 result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName,
2240 &unicodeBytes, sizeof(key->attrName), 0, 0);
2241 if (result) {
2242 if (result != ENAMETOOLONG)
2243 result = EINVAL; /* name has invalid characters */
2244 return (result);
2245 }
2246 key->attrNameLen = unicodeBytes / sizeof(UniChar);
2247 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
2248 } else {
2249 key->attrNameLen = 0;
2250 key->keyLength = kHFSPlusAttrKeyMinimumLength;
2251 }
2252 key->pad = 0;
2253 key->fileID = fileID;
2254 key->startBlock = 0;
2255
2256 return (0);
2257 }
2258
2259 /*
2260 * getnodecount - calculate starting node count for attributes b-tree.
2261 */
2262 static int
2263 getnodecount(struct hfsmount *hfsmp, size_t nodesize)
2264 {
2265 u_int64_t freebytes;
2266 u_int64_t calcbytes;
2267
2268 /*
2269 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2270 * 10.5: Attempt to be as big as the catalog clump size.
2271 *
2272 * Use no more than 10 % of the remaining free space.
2273 */
2274 freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize;
2275
2276 calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024);
2277
2278 calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize);
2279
2280 calcbytes = MIN(calcbytes, freebytes / 10);
2281
2282 return (MAX(2, (int)(calcbytes / nodesize)));
2283 }
2284
2285
2286 /*
2287 * getmaxinlineattrsize - calculate maximum inline attribute size.
2288 *
2289 * This yields 3,802 bytes for an 8K node size.
2290 */
2291 static size_t
2292 getmaxinlineattrsize(struct vnode * attrvp)
2293 {
2294 struct BTreeInfoRec btinfo;
2295 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
2296 size_t maxsize;
2297
2298 if (attrvp != NULL) {
2299 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2300 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
2301 nodesize = btinfo.nodeSize;
2302 hfs_unlock(VTOC(attrvp));
2303 }
2304 maxsize = nodesize;
2305 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */
2306 maxsize -= 3 * sizeof(u_int16_t); /* minus 3 index slots */
2307 maxsize /= 2; /* 2 key/rec pairs minumum */
2308 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */
2309 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */
2310 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */
2311
2312 return (maxsize);
2313 }
2314
2315 /*
2316 * Initialize vnode for attribute data I/O.
2317 *
2318 * On success,
2319 * - returns zero
2320 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2321 * - an iocount is taken on the attrdata vnode which exists
2322 * for the entire duration of the mount. It is only dropped
2323 * during unmount
2324 * - the attrdata cnode is not locked
2325 *
2326 * On failure,
2327 * - returns non-zero value
2328 * - the caller does not have to worry about any locks or references
2329 */
2330 int init_attrdata_vnode(struct hfsmount *hfsmp)
2331 {
2332 vnode_t vp;
2333 int result = 0;
2334 struct cat_desc cat_desc;
2335 struct cat_attr cat_attr;
2336 struct cat_fork cat_fork;
2337 int newvnode_flags = 0;
2338
2339 bzero(&cat_desc, sizeof(cat_desc));
2340 cat_desc.cd_parentcnid = kHFSRootParentID;
2341 cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
2342 cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
2343 cat_desc.cd_cnid = kHFSAttributeDataFileID;
2344 /* Tag vnode as system file, note that we can still use cluster I/O */
2345 cat_desc.cd_flags |= CD_ISMETA;
2346
2347 bzero(&cat_attr, sizeof(cat_attr));
2348 cat_attr.ca_linkcount = 1;
2349 cat_attr.ca_mode = S_IFREG;
2350 cat_attr.ca_fileid = cat_desc.cd_cnid;
2351 cat_attr.ca_blocks = hfsmp->totalBlocks;
2352
2353 /*
2354 * The attribute data file is a virtual file that spans the
2355 * entire file system space.
2356 *
2357 * Each extent-based attribute occupies a unique portion of
2358 * in this virtual file. The cluster I/O is done using actual
2359 * allocation block offsets so no additional mapping is needed
2360 * for the VNOP_BLOCKMAP call.
2361 *
2362 * This approach allows the attribute data to be cached without
2363 * incurring the high cost of using a separate vnode per attribute.
2364 *
2365 * Since we need to acquire the attribute b-tree file lock anyways,
2366 * the virtual file doesn't introduce any additional serialization.
2367 */
2368 bzero(&cat_fork, sizeof(cat_fork));
2369 cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
2370 cat_fork.cf_blocks = hfsmp->totalBlocks;
2371 cat_fork.cf_extents[0].startBlock = 0;
2372 cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
2373
2374 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr,
2375 &cat_fork, &vp, &newvnode_flags);
2376 if (result == 0) {
2377 hfsmp->hfs_attrdata_vp = vp;
2378 hfs_unlock(VTOC(vp));
2379 }
2380 return (result);
2381 }
2382
2383 /*
2384 * Read an extent based attribute.
2385 */
2386 static int
2387 read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2388 {
2389 vnode_t evp = hfsmp->hfs_attrdata_vp;
2390 int bufsize;
2391 int64_t iosize;
2392 int attrsize;
2393 int blksize;
2394 int i;
2395 int result = 0;
2396
2397 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2398
2399 bufsize = (int)uio_resid(uio);
2400 attrsize = (int)datasize;
2401 blksize = (int)hfsmp->blockSize;
2402
2403 /*
2404 * Read the attribute data one extent at a time.
2405 * For the typical case there is only one extent.
2406 */
2407 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2408 iosize = extents[i].blockCount * blksize;
2409 iosize = MIN(iosize, attrsize);
2410 iosize = MIN(iosize, bufsize);
2411 uio_setresid(uio, iosize);
2412 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2413
2414 result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT);
2415
2416 #if HFS_XATTR_VERBOSE
2417 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2418 iosize, extents[i].startBlock, extents[i].blockCount, result);
2419 #endif
2420 if (result)
2421 break;
2422 attrsize -= iosize;
2423 bufsize -= iosize;
2424 }
2425 uio_setresid(uio, bufsize);
2426 uio_setoffset(uio, datasize);
2427
2428 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2429 return (result);
2430 }
2431
2432 /*
2433 * Write an extent based attribute.
2434 */
2435 static int
2436 write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2437 {
2438 vnode_t evp = hfsmp->hfs_attrdata_vp;
2439 off_t filesize;
2440 int bufsize;
2441 int attrsize;
2442 int64_t iosize;
2443 int blksize;
2444 int i;
2445 int result = 0;
2446
2447 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2448
2449 bufsize = uio_resid(uio);
2450 attrsize = (int) datasize;
2451 blksize = (int) hfsmp->blockSize;
2452 filesize = VTOF(evp)->ff_size;
2453
2454 /*
2455 * Write the attribute data one extent at a time.
2456 */
2457 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2458 iosize = extents[i].blockCount * blksize;
2459 iosize = MIN(iosize, attrsize);
2460 iosize = MIN(iosize, bufsize);
2461 uio_setresid(uio, iosize);
2462 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2463
2464 result = cluster_write(evp, uio, filesize, filesize, filesize,
2465 (off_t) 0, IO_SYNC | IO_UNIT);
2466 #if HFS_XATTR_VERBOSE
2467 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2468 iosize, extents[i].startBlock, extents[i].blockCount, result);
2469 #endif
2470 if (result)
2471 break;
2472 attrsize -= iosize;
2473 bufsize -= iosize;
2474 }
2475 uio_setresid(uio, bufsize);
2476 uio_setoffset(uio, datasize);
2477
2478 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2479 return (result);
2480 }
2481
2482 /*
2483 * Allocate blocks for an extent based attribute.
2484 */
2485 static int
2486 alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks)
2487 {
2488 int blkcnt;
2489 int startblk;
2490 int lockflags;
2491 int i;
2492 int maxextents;
2493 int result = 0;
2494
2495 startblk = hfsmp->hfs_metazone_end;
2496 blkcnt = howmany(attrsize, hfsmp->blockSize);
2497 if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) {
2498 return (ENOSPC);
2499 }
2500 *blocks = blkcnt;
2501 maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor);
2502
2503 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2504
2505 for (i = 0; (blkcnt > 0) && (i < maxextents); i++) {
2506 /* Try allocating and see if we find something decent */
2507 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0,
2508 &extents[i].startBlock, &extents[i].blockCount);
2509 /*
2510 * If we couldn't find anything, then re-try the allocation but allow
2511 * journal flushes.
2512 */
2513 if (result == dskFulErr) {
2514 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, HFS_ALLOC_FLUSHTXN,
2515 &extents[i].startBlock, &extents[i].blockCount);
2516 }
2517
2518
2519 #if HFS_XATTR_VERBOSE
2520 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2521 blkcnt, extents[i].startBlock, extents[i].blockCount, result);
2522 #endif
2523 if (result) {
2524 extents[i].startBlock = 0;
2525 extents[i].blockCount = 0;
2526 break;
2527 }
2528 blkcnt -= extents[i].blockCount;
2529 startblk = extents[i].startBlock + extents[i].blockCount;
2530 }
2531 /*
2532 * If it didn't fit in the extents buffer then bail.
2533 */
2534 if (blkcnt) {
2535 result = ENOSPC;
2536
2537 #if HFS_XATTR_VERBOSE
2538 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
2539 #endif
2540 for (; i >= 0; i--) {
2541 if ((blkcnt = extents[i].blockCount) != 0) {
2542 (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt, 0);
2543 extents[i].startBlock = 0;
2544 extents[i].blockCount = 0;
2545 }
2546 }
2547 }
2548
2549 hfs_systemfile_unlock(hfsmp, lockflags);
2550 return MacToVFSError(result);
2551 }
2552
2553 /*
2554 * Release blocks from an extent based attribute.
2555 */
2556 static void
2557 free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents)
2558 {
2559 vnode_t evp = hfsmp->hfs_attrdata_vp;
2560 int remblks = blkcnt;
2561 int lockflags;
2562 int i;
2563
2564 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2565
2566 for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) {
2567 if (extents[i].blockCount > (u_int32_t)blkcnt) {
2568 #if HFS_XATTR_VERBOSE
2569 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2570 extents[i].startBlock, extents[i].blockCount);
2571 #endif
2572 extents[i].blockCount = 0;
2573 continue;
2574 }
2575 if (extents[i].startBlock == 0) {
2576 break;
2577 }
2578 (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount, 0);
2579 remblks -= extents[i].blockCount;
2580 extents[i].startBlock = 0;
2581 extents[i].blockCount = 0;
2582
2583 #if HFS_XATTR_VERBOSE
2584 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2585 extents[i].startBlock, extents[i].blockCount);
2586 #endif
2587 /* Discard any resident pages for this block range. */
2588 if (evp) {
2589 off_t start, end;
2590
2591 start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize;
2592 end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize;
2593 (void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE);
2594 }
2595 }
2596
2597 hfs_systemfile_unlock(hfsmp, lockflags);
2598 }
2599
2600 static int
2601 has_overflow_extents(HFSPlusForkData *forkdata)
2602 {
2603 u_int32_t blocks;
2604
2605 if (forkdata->extents[7].blockCount == 0)
2606 return (0);
2607
2608 blocks = forkdata->extents[0].blockCount +
2609 forkdata->extents[1].blockCount +
2610 forkdata->extents[2].blockCount +
2611 forkdata->extents[3].blockCount +
2612 forkdata->extents[4].blockCount +
2613 forkdata->extents[5].blockCount +
2614 forkdata->extents[6].blockCount +
2615 forkdata->extents[7].blockCount;
2616
2617 return (forkdata->totalBlocks > blocks);
2618 }
2619
2620 static int
2621 count_extent_blocks(int maxblks, HFSPlusExtentRecord extents)
2622 {
2623 int blocks;
2624 int i;
2625
2626 for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) {
2627 /* Ignore obvious bogus extents. */
2628 if (extents[i].blockCount > (u_int32_t)maxblks)
2629 continue;
2630 if (extents[i].startBlock == 0 || extents[i].blockCount == 0)
2631 break;
2632 blocks += extents[i].blockCount;
2633 }
2634 return (blocks);
2635 }
2636