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