]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_xattr.c
74c19e403f0792d1351fb714568560f4bdbc024b
[apple/xnu.git] / bsd / hfs / hfs_xattr.c
1 /*
2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/utfconv.h>
36 #include <sys/vnode.h>
37 #include <sys/xattr.h>
38
39 #include "hfs.h"
40 #include "hfs_cnode.h"
41 #include "hfs_mount.h"
42 #include "hfs_format.h"
43 #include "hfs_endian.h"
44
45 #include "hfscommon/headers/BTreesInternal.h"
46
47
48 #define ATTRIBUTE_FILE_NODE_SIZE 8192
49
50
51 /* State information for the listattr_callback callback function. */
52 struct listattr_callback_state {
53 u_int32_t fileID;
54 int result;
55 uio_t uio;
56 size_t size;
57 };
58
59 #define HFS_MAXATTRIBUTESIZE (1024*1024)
60
61 /* HFS Internal Names */
62 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
63
64
65 #define RESOURCE_FORK_EXISTS(VP) \
66 ((VTOC((VP))->c_blocks - VTOF((VP))->ff_blocks) > 0)
67
68 static u_int32_t emptyfinfo[8] = {0};
69
70
71 extern int hfs_create_attr_btree(struct hfsmount *hfsmp, uint32_t nodesize, uint32_t nodecnt);
72
73
74 int hfs_vnop_getxattr(struct vnop_getxattr_args *ap);
75 int hfs_vnop_setxattr(struct vnop_setxattr_args *ap);
76 int hfs_vnop_removexattr(struct vnop_removexattr_args *ap);
77 int hfs_vnop_listxattr(struct vnop_listxattr_args *ap);
78 int hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey);
79
80
81
82 static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data,
83 struct listattr_callback_state *state);
84
85 static int buildkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key);
86
87 static int getnodecount(struct hfsmount *hfsmp, size_t nodesize);
88
89 static size_t getmaxinlineattrsize(struct vnode * attrvp);
90
91 /*
92 * Retrieve the data of an extended attribute.
93 */
94 __private_extern__
95 int
96 hfs_vnop_getxattr(struct vnop_getxattr_args *ap)
97 /*
98 struct vnop_getxattr_args {
99 struct vnodeop_desc *a_desc;
100 vnode_t a_vp;
101 char * a_name;
102 uio_t a_uio;
103 size_t *a_size;
104 int a_options;
105 vfs_context_t a_context;
106 };
107 */
108 {
109 struct vnode *vp = ap->a_vp;
110 struct hfsmount *hfsmp;
111 uio_t uio = ap->a_uio;
112 struct BTreeIterator * iterator = NULL;
113 struct filefork *btfile;
114 FSBufferDescriptor btdata;
115 HFSPlusAttrData * datap = NULL;
116 size_t bufsize;
117 UInt16 datasize;
118 int lockflags;
119 int result;
120
121 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
122 return (EINVAL); /* invalid name */
123 }
124 hfsmp = VTOHFS(vp);
125
126 if (!VNODE_IS_RSRC(vp)) {
127 /* Get the Finder Info. */
128 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
129 bufsize = 32;
130
131 /* If Finder Info is empty then it doesn't exist. */
132 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
133 return (ENOATTR);
134 }
135 if (uio == NULL) {
136 *ap->a_size = bufsize;
137 return (0);
138 }
139 if (uio_resid(uio) < bufsize)
140 return (ERANGE);
141
142 result = uiomove((caddr_t) &VTOC(vp)->c_finderinfo , bufsize, uio);
143
144 return (result);
145 }
146 /* Read the Resource Fork. */
147 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
148 struct vnode *rvp = NULL;
149
150 if ( !vnode_isreg(vp) ) {
151 return (EPERM);
152 }
153 if ( !RESOURCE_FORK_EXISTS(vp)) {
154 return (ENOATTR);
155 }
156 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
157 return (result);
158 }
159 result = hfs_vgetrsrc(hfsmp, vp, &rvp, vfs_context_proc(ap->a_context));
160 hfs_unlock(VTOC(vp));
161 if (result) {
162 return (result);
163 }
164 if (uio == NULL) {
165 *ap->a_size = (size_t)VTOF(rvp)->ff_size;
166 } else {
167 result = VNOP_READ(rvp, uio, 0, ap->a_context);
168 }
169 vnode_put(rvp);
170 return (result);
171 }
172 }
173 /*
174 * Standard HFS only supports native FinderInfo and Resource Forks.
175 */
176 if (hfsmp->hfs_flags & HFS_STANDARD) {
177 return (EPERM);
178 }
179 /* Bail if we don't have any extended attributes. */
180 if ((hfsmp->hfs_attribute_vp == NULL) ||
181 (VTOC(vp)->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
182 return (ENOATTR);
183 }
184 btfile = VTOF(hfsmp->hfs_attribute_vp);
185
186 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
187 bzero(iterator, sizeof(*iterator));
188
189 bufsize = sizeof(HFSPlusAttrData) - 2;
190 if (uio)
191 bufsize += uio_resid(uio);
192 MALLOC(datap, HFSPlusAttrData *, bufsize, M_TEMP, M_WAITOK);
193 btdata.bufferAddress = datap;
194 btdata.itemSize = bufsize;
195 btdata.itemCount = 1;
196
197 result = buildkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
198 if (result)
199 goto exit;
200
201 /* Lookup the attribute. */
202 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
203 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
204 hfs_systemfile_unlock(hfsmp, lockflags);
205
206 if (result) {
207 if (result == btNotFound)
208 result = ENOATTR;
209 goto exit;
210 }
211
212 *ap->a_size = datap->attrSize;
213
214 /* Copy out the attribute data. */
215 if (uio) {
216 if (datap->attrSize > uio_resid(uio))
217 result = ERANGE;
218 else
219 result = uiomove((caddr_t) &datap->attrData , datap->attrSize, uio);
220 }
221 exit:
222 FREE(datap, M_TEMP);
223 FREE(iterator, M_TEMP);
224
225 return MacToVFSError(result);
226 }
227
228 /*
229 * Set the data of an extended attribute.
230 */
231 __private_extern__
232 int
233 hfs_vnop_setxattr(struct vnop_setxattr_args *ap)
234 /*
235 struct vnop_setxattr_args {
236 struct vnodeop_desc *a_desc;
237 vnode_t a_vp;
238 char * a_name;
239 uio_t a_uio;
240 int a_options;
241 vfs_context_t a_context;
242 };
243 */
244 {
245 struct vnode *vp = ap->a_vp;
246 struct hfsmount *hfsmp;
247 uio_t uio = ap->a_uio;
248 struct BTreeIterator * iterator = NULL;
249 struct filefork *btfile;
250 size_t attrsize;
251 FSBufferDescriptor btdata;
252 HFSPlusAttrData * datap = NULL;
253 UInt16 datasize;
254 int lockflags;
255 int result;
256
257 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
258 return (EINVAL); /* invalid name */
259 }
260 hfsmp = VTOHFS(vp);
261 if (VNODE_IS_RSRC(vp)) {
262 return (EPERM);
263 }
264 /* Set the Finder Info. */
265 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
266 attrsize = 32;
267
268 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo))) {
269 /* attr exists and "create" was specified. */
270 if (ap->a_options & XATTR_CREATE) {
271 return (EEXIST);
272 }
273 } else {
274 /* attr doesn't exists and "replace" was specified. */
275 if (ap->a_options & XATTR_REPLACE) {
276 return (ENOATTR);
277 }
278 }
279 if (uio_resid(uio) != attrsize)
280 return (ERANGE);
281
282 result = uiomove((caddr_t) &VTOC(vp)->c_finderinfo , attrsize, uio);
283 if (result == 0) {
284 VTOC(vp)->c_touch_chgtime = TRUE;
285 VTOC(vp)->c_flag |= C_MODIFIED;
286 result = hfs_update(vp, FALSE);
287 }
288 return (result);
289 }
290 /* Write the Resource Fork. */
291 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
292 struct vnode *rvp = NULL;
293
294 if (!vnode_isreg(vp)) {
295 return (EPERM);
296 }
297 if (RESOURCE_FORK_EXISTS(vp)) {
298 /* attr exists and "create" was specified. */
299 if (ap->a_options & XATTR_CREATE) {
300 return (EEXIST);
301 }
302 } else {
303 /* attr doesn't exists and "replace" was specified. */
304 if (ap->a_options & XATTR_REPLACE) {
305 return (ENOATTR);
306 }
307 }
308 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
309 return (result);
310 }
311 result = hfs_vgetrsrc(hfsmp, vp, &rvp, vfs_context_proc(ap->a_context));
312 hfs_unlock(VTOC(vp));
313 if (result) {
314 return (result);
315 }
316 result = VNOP_WRITE(rvp, uio, 0, ap->a_context);
317 vnode_put(rvp);
318 return (result);
319 }
320 /*
321 * Standard HFS only supports native FinderInfo and Resource Forks.
322 */
323 if (hfsmp->hfs_flags & HFS_STANDARD) {
324 return (EPERM);
325 }
326 if (hfsmp->hfs_max_inline_attrsize == 0) {
327 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
328 }
329 attrsize = uio_resid(uio);
330 if (attrsize > hfsmp->hfs_max_inline_attrsize) {
331 /*
332 * XXX Need to support extent-based attributes XXX
333 */
334 return (E2BIG);
335 }
336 /* Calculate size of record rounded up to multiple of 2 bytes. */
337 datasize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
338
339 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
340 bzero(iterator, sizeof(*iterator));
341
342 MALLOC(datap, HFSPlusAttrData *, datasize, M_TEMP, M_WAITOK);
343 btdata.bufferAddress = datap;
344 btdata.itemSize = datasize;
345 btdata.itemCount = 1;
346 datap->recordType = kHFSPlusAttrInlineData;
347 datap->reserved[0] = 0;
348 datap->reserved[1] = 0;
349 datap->attrSize = attrsize;
350
351 /* Copy in the attribute data. */
352 result = uiomove((caddr_t) &datap->attrData , attrsize, uio);
353 if (result) {
354 goto exit2;
355 }
356 /* Build a b-tree key. */
357 result = buildkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
358 if (result) {
359 goto exit2;
360 }
361 /* Start a transaction for our changes. */
362 if (hfs_start_transaction(hfsmp) != 0) {
363 result = EINVAL;
364 goto exit2;
365 }
366
367 /* once we started the transaction, nobody can compete with us, so make sure this file is still there */
368 struct cnode *cp;
369 cp = VTOC(vp);
370 if (cp->c_flag & C_NOEXISTS) { /* this file has already been removed */
371 result = ENOENT;
372 goto exit1;
373 }
374
375 /*
376 * If there isn't an attributes b-tree then create one.
377 */
378 if (hfsmp->hfs_attribute_vp == NULL) {
379 lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
380 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
381 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
382 hfs_systemfile_unlock(hfsmp, lockflags);
383 if (result) {
384 goto exit1;
385 }
386 }
387 btfile = VTOF(hfsmp->hfs_attribute_vp);
388
389 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
390
391 if (ap->a_options & XATTR_REPLACE) {
392 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
393 if (result)
394 goto exit0;
395 else
396 goto exit;
397 }
398
399 /* Insert the attribute. */
400 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
401 if (result) {
402 if (result != btExists) {
403 goto exit0;
404 }
405
406 // if it exists and XATTR_CREATE was specified,
407 // the spec says to return EEXIST
408 if (ap->a_options & XATTR_CREATE) {
409 result = EEXIST;
410 goto exit0;
411 }
412 /* XXX need to account for old size in c_attrblks */
413 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
414 }
415 exit:
416 (void) BTFlushPath(btfile);
417 exit0:
418 hfs_systemfile_unlock(hfsmp, lockflags);
419 if (result == 0) {
420 struct cnode * cp;
421
422 cp = VTOC(vp);
423 cp->c_touch_chgtime = TRUE;
424 if ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
425 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
426 (void) hfs_update(vp, 0);
427 }
428 HFS_KNOTE(vp, NOTE_ATTRIB);
429 }
430 exit1:
431 /* Finish the transaction of our changes. */
432 hfs_end_transaction(hfsmp);
433 exit2:
434 FREE(datap, M_TEMP);
435 FREE(iterator, M_TEMP);
436
437 if (result == btNotFound)
438 result = ENOATTR;
439 else
440 result = MacToVFSError(result);
441
442 return (result);
443 }
444
445 /*
446 * Remove an extended attribute.
447 */
448 __private_extern__
449 int
450 hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
451 /*
452 struct vnop_removexattr_args {
453 struct vnodeop_desc *a_desc;
454 vnode_t a_vp;
455 char * a_name;
456 int a_options;
457 vfs_context_t a_context;
458 };
459 */
460 {
461 struct vnode *vp = ap->a_vp;
462 struct hfsmount *hfsmp;
463 struct BTreeIterator * iterator = NULL;
464 struct filefork *btfile;
465 struct proc *p = vfs_context_proc(ap->a_context);
466 FSBufferDescriptor btdata;
467 HFSPlusAttrData attrdata;
468 int lockflags;
469 int result;
470
471 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
472 return (EINVAL); /* invalid name */
473 }
474 hfsmp = VTOHFS(vp);
475 if (VNODE_IS_RSRC(vp)) {
476 return (EPERM);
477 }
478
479 /* If Resource Fork is non-empty then truncate it. */
480 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
481 struct vnode *rvp = NULL;
482
483 if ( !vnode_isreg(vp) ) {
484 return (EPERM);
485 }
486 if ( !RESOURCE_FORK_EXISTS(vp) ) {
487 return (ENOATTR);
488 }
489 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
490 return (result);
491 }
492 result = hfs_vgetrsrc(hfsmp, vp, &rvp, p);
493 hfs_unlock(VTOC(vp));
494 if (result) {
495 return (result);
496 }
497 hfs_lock_truncate(VTOC(rvp), TRUE);
498 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK))) {
499 hfs_unlock_truncate(VTOC(vp));
500 vnode_put(rvp);
501 return (result);
502 }
503 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
504
505 hfs_unlock_truncate(VTOC(rvp));
506 hfs_unlock(VTOC(rvp));
507
508 vnode_put(rvp);
509 return (result);
510 }
511 /* Clear out the Finder Info. */
512 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
513 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
514 return (ENOATTR);
515 }
516 bzero(VTOC(vp)->c_finderinfo, sizeof(emptyfinfo));
517 return (0);
518 }
519 /*
520 * Standard HFS only supports native FinderInfo and Resource Forks.
521 */
522 if (hfsmp->hfs_flags & HFS_STANDARD) {
523 return (EPERM);
524 }
525 if (hfsmp->hfs_attribute_vp == NULL) {
526 return (ENOATTR);
527 }
528 btfile = VTOF(hfsmp->hfs_attribute_vp);
529
530 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
531 bzero(iterator, sizeof(*iterator));
532
533 if (hfs_start_transaction(hfsmp) != 0) {
534 result = EINVAL;
535 goto exit2;
536 }
537
538 result = buildkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
539 if (result)
540 goto exit2;
541
542 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
543
544 btdata.bufferAddress = &attrdata;
545 btdata.itemSize = sizeof(attrdata);
546 btdata.itemCount = 1;
547 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL);
548 if (result)
549 goto exit1;
550
551 result = BTDeleteRecord(btfile, iterator);
552 (void) BTFlushPath(btfile);
553 exit1:
554 hfs_systemfile_unlock(hfsmp, lockflags);
555 if (result == 0) {
556 VTOC(vp)->c_touch_chgtime = TRUE;
557 HFS_KNOTE(vp, NOTE_ATTRIB);
558 }
559 exit2:
560 if (result == btNotFound) {
561 result = ENOATTR;
562 }
563 hfs_end_transaction(hfsmp);
564
565 FREE(iterator, M_TEMP);
566
567 return MacToVFSError(result);
568 }
569
570
571 /*
572 * Retrieve the list of extended attribute names.
573 */
574 __private_extern__
575 int
576 hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
577 /*
578 struct vnop_listxattr_args {
579 struct vnodeop_desc *a_desc;
580 vnode_t a_vp;
581 uio_t a_uio;
582 size_t *a_size;
583 int a_options;
584 vfs_context_t a_context;
585 */
586 {
587 struct vnode *vp = ap->a_vp;
588 struct hfsmount *hfsmp;
589 uio_t uio = ap->a_uio;
590 struct BTreeIterator * iterator = NULL;
591 struct filefork *btfile;
592 struct listattr_callback_state state;
593 int lockflags;
594 int result;
595
596 if (VNODE_IS_RSRC(vp)) {
597 return (EPERM);
598 }
599 hfsmp = VTOHFS(vp);
600 *ap->a_size = 0;
601
602 /* If Finder Info is non-empty then export it. */
603 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
604 if (uio == NULL) {
605 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
606 } else if (uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
607 return (ERANGE);
608 } else {
609 result = uiomove((caddr_t)XATTR_FINDERINFO_NAME,
610 sizeof(XATTR_FINDERINFO_NAME), uio);
611 if (result)
612 return (result);
613 }
614 }
615 /* If Resource Fork is non-empty then export it. */
616 if (vnode_isreg(vp) && RESOURCE_FORK_EXISTS(vp)) {
617 if (uio == NULL) {
618 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
619 } else if (uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
620 return (ERANGE);
621 } else {
622 result = uiomove((caddr_t)XATTR_RESOURCEFORK_NAME,
623 sizeof(XATTR_RESOURCEFORK_NAME), uio);
624 if (result)
625 return (result);
626 }
627 }
628 /*
629 * Standard HFS only supports native FinderInfo and Resource Forks.
630 * Return at this point.
631 */
632 if (hfsmp->hfs_flags & HFS_STANDARD) {
633 return (0);
634 }
635 /* Bail if we don't have any extended attributes. */
636 if ((hfsmp->hfs_attribute_vp == NULL) ||
637 (VTOC(vp)->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
638 return (0);
639 }
640 btfile = VTOF(hfsmp->hfs_attribute_vp);
641
642 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
643 bzero(iterator, sizeof(*iterator));
644 result = buildkey(VTOC(vp)->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
645 if (result)
646 goto exit;
647
648 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
649
650 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
651 if (result && result != btNotFound) {
652 hfs_systemfile_unlock(hfsmp, lockflags);
653 goto exit;
654 }
655
656 state.fileID = VTOC(vp)->c_fileid;
657 state.result = 0;
658 state.uio = uio;
659 state.size = 0;
660
661 /*
662 * Process entries starting just after iterator->key.
663 */
664 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
665 (IterateCallBackProcPtr)listattr_callback, &state);
666 hfs_systemfile_unlock(hfsmp, lockflags);
667 if (uio == NULL) {
668 *ap->a_size += state.size;
669 }
670 exit:
671 FREE(iterator, M_TEMP);
672
673 if (state.result || result == btNotFound)
674 result = state.result;
675
676 return MacToVFSError(result);
677 }
678
679
680 /*
681 * Callback - called for each attribute
682 */
683 static int
684 listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
685 {
686 char attrname[XATTR_MAXNAMELEN + 1];
687 size_t bytecount;
688 int result;
689
690 if (state->fileID != key->fileID) {
691 state->result = 0;
692 return (0); /* stop */
693 }
694 /*
695 * Skip over non-primary keys
696 */
697 if (key->startBlock != 0) {
698 return (1); /* continue */
699 }
700
701 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
702 attrname, &bytecount, sizeof(attrname), 0, 0);
703 if (result) {
704 state->result = result;
705 return (0); /* stop */
706 }
707 bytecount++; /* account for null termination char */
708
709 if (xattr_protected(attrname))
710 return (1); /* continue */
711
712 if (state->uio == NULL) {
713 state->size += bytecount;
714 } else {
715 if (bytecount > uio_resid(state->uio)) {
716 state->result = ERANGE;
717 return (0); /* stop */
718 }
719 result = uiomove((caddr_t) attrname, bytecount, state->uio);
720 if (result) {
721 state->result = result;
722 return (0); /* stop */
723 }
724 }
725 return (1); /* continue */
726 }
727
728
729 /*
730 * Remove all the attributes from a cnode.
731 *
732 * A jornal transaction must be already started.
733 * Attributes b-Tree must have exclusive lock held.
734 */
735 __private_extern__
736 int
737 hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid)
738 {
739 BTreeIterator *next_iterator, *del_iterator;
740 HFSPlusAttrKey *next_key;
741 struct filefork *btfile;
742 int result, iter_result;
743
744 if (hfsmp->hfs_attribute_vp == NULL) {
745 return (0);
746 }
747 btfile = VTOF(hfsmp->hfs_attribute_vp);
748
749 MALLOC(next_iterator, BTreeIterator *, sizeof(BTreeIterator) * 2, M_TEMP, M_WAITOK);
750 bzero(next_iterator, sizeof(BTreeIterator) * 2);
751 del_iterator = &next_iterator[1];
752 next_key = (HFSPlusAttrKey *)&next_iterator->key;
753
754 /*
755 * Go to first possible attribute key/record pair
756 */
757 (void) buildkey(fileid, NULL, next_key);
758 result = BTIterateRecord(btfile, kBTreeNextRecord, next_iterator, NULL, NULL);
759 if (result || next_key->fileID != fileid) {
760 goto exit;
761 }
762 /* Remember iterator of attribute to delete */
763 bcopy(next_iterator, del_iterator, sizeof(BTreeIterator));
764
765 /* Loop until there are no more attributes for this file id */
766 for(;;) {
767 iter_result = BTIterateRecord(btfile, kBTreeNextRecord, next_iterator, NULL, NULL);
768
769 /* XXX need to free and extents for record types 0x20 and 0x30 */
770 result = BTDeleteRecord(btfile, del_iterator);
771 if (result) {
772 goto exit;
773 }
774 if (iter_result) {
775 result = iter_result;
776 break;
777 }
778 if (iter_result || next_key->fileID != fileid) {
779 break; /* end of attributes for this file id */
780 }
781 bcopy(next_iterator, del_iterator, sizeof(BTreeIterator));
782 }
783 exit:
784 (void) BTFlushPath(btfile);
785
786 if (result == btNotFound) {
787 result = 0;
788 }
789 FREE(next_iterator, M_TEMP);
790 return (result);
791 }
792
793 /*
794 * Enable/Disable extended security (ACLs).
795 */
796 __private_extern__
797 int
798 hfs_setextendedsecurity(struct hfsmount *hfsmp, int state)
799 {
800 struct BTreeIterator * iterator = NULL;
801 struct filefork *btfile;
802 int lockflags;
803 int result;
804
805 if (hfsmp->hfs_flags & HFS_STANDARD) {
806 return (ENOTSUP);
807 }
808
809 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
810 bzero(iterator, sizeof(*iterator));
811
812 /*
813 * Build a b-tree key.
814 * We use the root's parent id (1) to hold this volume attribute.
815 */
816 (void) buildkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
817 (HFSPlusAttrKey *)&iterator->key);
818
819 /* Start a transaction for our changes. */
820 if (hfs_start_transaction(hfsmp) != 0) {
821 result = EINVAL;
822 goto exit2;
823 }
824 /*
825 * If there isn't an attributes b-tree then create one.
826 */
827 if (hfsmp->hfs_attribute_vp == NULL) {
828 lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
829 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
830 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
831 hfs_systemfile_unlock(hfsmp, lockflags);
832 if (result) {
833 goto exit1;
834 }
835 }
836 btfile = VTOF(hfsmp->hfs_attribute_vp);
837
838 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
839
840 if (state == 0) {
841 /* Remove the attribute. */
842 result = BTDeleteRecord(btfile, iterator);
843 if (result == btNotFound)
844 result = 0;
845 } else {
846 FSBufferDescriptor btdata;
847 HFSPlusAttrData attrdata;
848 UInt16 datasize;
849
850 datasize = sizeof(attrdata);
851 btdata.bufferAddress = &attrdata;
852 btdata.itemSize = datasize;
853 btdata.itemCount = 1;
854 attrdata.recordType = kHFSPlusAttrInlineData;
855 attrdata.reserved[0] = 0;
856 attrdata.reserved[1] = 0;
857 attrdata.attrSize = 2;
858 attrdata.attrData[0] = 0;
859 attrdata.attrData[1] = 0;
860
861 /* Insert the attribute. */
862 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
863 if (result == btExists)
864 result = 0;
865 }
866 (void) BTFlushPath(btfile);
867
868 hfs_systemfile_unlock(hfsmp, lockflags);
869 exit1:
870 /* Finish the transaction of our changes. */
871 hfs_end_transaction(hfsmp);
872 exit2:
873 FREE(iterator, M_TEMP);
874
875 if (result == 0) {
876 if (state == 0)
877 vfs_clearextendedsecurity(HFSTOVFS(hfsmp));
878 else
879 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
880 printf("hfs: %s extended security on %s\n",
881 state == 0 ? "disabling" : "enabling", hfsmp->vcbVN);
882 }
883
884 return MacToVFSError(result);
885 }
886
887 /*
888 * Check for extended security (ACLs).
889 */
890 __private_extern__
891 void
892 hfs_checkextendedsecurity(struct hfsmount *hfsmp)
893 {
894 struct BTreeIterator * iterator;
895 struct filefork *btfile;
896 int lockflags;
897 int result;
898
899 if (hfsmp->hfs_flags & HFS_STANDARD ||
900 hfsmp->hfs_attribute_vp == NULL) {
901 return;
902 }
903
904 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
905 bzero(iterator, sizeof(*iterator));
906
907 /*
908 * Build a b-tree key.
909 * We use the root's parent id (1) to hold this volume attribute.
910 */
911 (void) buildkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
912 (HFSPlusAttrKey *)&iterator->key);
913
914 btfile = VTOF(hfsmp->hfs_attribute_vp);
915
916 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
917
918 /* Check for our attribute. */
919 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
920
921 hfs_systemfile_unlock(hfsmp, lockflags);
922 FREE(iterator, M_TEMP);
923
924 if (result == 0) {
925 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
926 printf("hfs mount: enabling extended security on %s\n", hfsmp->vcbVN);
927 }
928 }
929
930
931 /*
932 * hfs_attrkeycompare - compare two attribute b-tree keys.
933 *
934 * The name portion of the key is compared using a 16-bit binary comparison.
935 * This is called from the b-tree code.
936 */
937 __private_extern__
938 int
939 hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
940 {
941 u_int32_t searchFileID, trialFileID;
942 int result;
943
944 searchFileID = searchKey->fileID;
945 trialFileID = trialKey->fileID;
946 result = 0;
947
948 if (searchFileID > trialFileID) {
949 ++result;
950 } else if (searchFileID < trialFileID) {
951 --result;
952 } else {
953 u_int16_t * str1 = &searchKey->attrName[0];
954 u_int16_t * str2 = &trialKey->attrName[0];
955 int length1 = searchKey->attrNameLen;
956 int length2 = trialKey->attrNameLen;
957 u_int16_t c1, c2;
958 int length;
959
960 if (length1 < length2) {
961 length = length1;
962 --result;
963 } else if (length1 > length2) {
964 length = length2;
965 ++result;
966 } else {
967 length = length1;
968 }
969
970 while (length--) {
971 c1 = *(str1++);
972 c2 = *(str2++);
973
974 if (c1 > c2) {
975 result = 1;
976 break;
977 }
978 if (c1 < c2) {
979 result = -1;
980 break;
981 }
982 }
983 if (result)
984 return (result);
985 /*
986 * Names are equal; compare startBlock
987 */
988 if (searchKey->startBlock == trialKey->startBlock)
989 return (0);
990 else
991 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
992 }
993
994 return result;
995 }
996
997
998 /*
999 * buildkey - build an Attribute b-tree key
1000 */
1001 static int
1002 buildkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
1003 {
1004 int result = 0;
1005 size_t unicodeBytes = 0;
1006
1007 if (attrname != NULL) {
1008 /*
1009 * Convert filename from UTF-8 into Unicode
1010 */
1011 result = utf8_decodestr(attrname, strlen(attrname), key->attrName,
1012 &unicodeBytes, sizeof(key->attrName), 0, 0);
1013 if (result) {
1014 if (result != ENAMETOOLONG)
1015 result = EINVAL; /* name has invalid characters */
1016 return (result);
1017 }
1018 key->attrNameLen = unicodeBytes / sizeof(UniChar);
1019 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
1020 } else {
1021 key->attrNameLen = 0;
1022 key->keyLength = kHFSPlusAttrKeyMinimumLength;
1023 }
1024 key->pad = 0;
1025 key->fileID = fileID;
1026 key->startBlock = 0;
1027
1028 return (0);
1029 }
1030
1031 /*
1032 * getnodecount - calculate starting node count for attributes b-tree.
1033 */
1034 static int
1035 getnodecount(struct hfsmount *hfsmp, size_t nodesize)
1036 {
1037 int avedatasize;
1038 int recpernode;
1039 int count;
1040
1041 avedatasize = sizeof(u_int16_t); /* index slot */
1042 avedatasize += kHFSPlusAttrKeyMinimumLength + HFS_AVERAGE_NAME_SIZE * sizeof(u_int16_t);
1043 avedatasize += sizeof(HFSPlusAttrData) + 32;
1044
1045 recpernode = (nodesize - sizeof(BTNodeDescriptor)) / avedatasize;
1046
1047 count = (hfsmp->hfs_filecount + hfsmp->hfs_dircount) / 8;
1048 count /= recpernode;
1049
1050 /* XXX should also consider volume size XXX */
1051
1052 return (MAX(count, (int)(1024 * 1024) / (int)nodesize));
1053 }
1054
1055
1056 /*
1057 * getmaxinlineattrsize - calculate maximum inline attribute size.
1058 *
1059 * This yields 3,802 bytes for an 8K node size.
1060 */
1061 static size_t
1062 getmaxinlineattrsize(struct vnode * attrvp)
1063 {
1064 struct BTreeInfoRec btinfo;
1065 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
1066 size_t maxsize;
1067
1068 if (attrvp != NULL) {
1069 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK);
1070 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
1071 nodesize = btinfo.nodeSize;
1072 hfs_unlock(VTOC(attrvp));
1073 }
1074 maxsize = nodesize;
1075 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */
1076 maxsize -= 3 * sizeof(UInt16); /* minus 3 index slots */
1077 maxsize /= 2; /* 2 key/rec pairs minumum */
1078 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */
1079 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */
1080 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */
1081
1082 return (maxsize);
1083 }
1084
1085