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