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