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