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