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