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