]>
Commit | Line | Data |
---|---|---|
91447636 | 1 | /* |
316670eb | 2 | * Copyright (c) 2004-2012 Apple Inc. All rights reserved. |
91447636 | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 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. | |
0a7de745 | 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. | |
0a7de745 | 17 | * |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
91447636 | 27 | */ |
2d21ac55 A |
28 | /* |
29 | * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce | |
30 | * support for mandatory and extensible security protections. This notice | |
31 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
32 | * Version 2.0. | |
33 | */ | |
34 | ||
91447636 A |
35 | #include <sys/param.h> |
36 | ||
37 | #include <sys/fcntl.h> | |
38 | #include <sys/fsevents.h> | |
39 | #include <sys/kernel.h> | |
40 | #include <sys/kauth.h> | |
f427ee49 | 41 | #include <kern/kalloc.h> |
2d21ac55 | 42 | #include <sys/mount_internal.h> |
91447636 A |
43 | #include <sys/namei.h> |
44 | #include <sys/proc_internal.h> | |
45 | #include <sys/stat.h> | |
46 | #include <sys/uio.h> | |
47 | #include <sys/utfconv.h> | |
48 | #include <sys/vnode.h> | |
49 | #include <sys/vnode_internal.h> | |
91447636 A |
50 | #include <sys/xattr.h> |
51 | ||
4452a7af | 52 | #include <libkern/OSByteOrder.h> |
91447636 A |
53 | #include <vm/vm_kern.h> |
54 | ||
2d21ac55 A |
55 | #if CONFIG_MACF |
56 | #include <security/mac_framework.h> | |
57 | #endif | |
58 | ||
59 | ||
60 | #if NAMEDSTREAMS | |
61 | ||
316670eb A |
62 | static int shadow_sequence; |
63 | ||
91447636 | 64 | /* |
b0d623f7 | 65 | * We use %p to prevent loss of precision for pointers on varying architectures. |
91447636 | 66 | */ |
316670eb | 67 | |
0a7de745 A |
68 | #define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p" |
69 | #define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x" | |
316670eb A |
70 | #define SHADOW_DIR_CONTAINER "/var/run" |
71 | ||
2d21ac55 | 72 | #define MAKE_SHADOW_NAME(VP, NAME) \ |
316670eb | 73 | snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \ |
0a7de745 A |
74 | ((void*)(VM_KERNEL_ADDRPERM(VP))), \ |
75 | (VP)->v_id, \ | |
76 | ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data)))) | |
2d21ac55 | 77 | |
316670eb | 78 | /* The full path to the shadow directory */ |
0a7de745 | 79 | #define MAKE_SHADOW_DIRNAME(VP, NAME) \ |
316670eb | 80 | snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \ |
0a7de745 | 81 | ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence) |
316670eb A |
82 | |
83 | /* The shadow directory as a 'leaf' entry */ | |
0a7de745 | 84 | #define MAKE_SHADOW_DIR_LEAF(VP, NAME) \ |
316670eb | 85 | snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \ |
0a7de745 | 86 | ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence) |
2d21ac55 | 87 | |
2d21ac55 A |
88 | static int default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context); |
89 | ||
90 | static int default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context); | |
91447636 | 91 | |
2d21ac55 | 92 | static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context); |
91447636 | 93 | |
2d21ac55 A |
94 | static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context); |
95 | ||
39236c6e | 96 | static int get_shadow_dir(vnode_t *sdvpp); |
2d21ac55 | 97 | |
39236c6e | 98 | #endif /* NAMEDSTREAMS */ |
2d21ac55 A |
99 | |
100 | /* | |
101 | * Default xattr support routines. | |
102 | */ | |
91447636 | 103 | |
39236c6e A |
104 | static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, int options, |
105 | vfs_context_t context); | |
106 | static int default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, | |
107 | vfs_context_t context); | |
91447636 | 108 | static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, |
39236c6e A |
109 | vfs_context_t context); |
110 | static int default_removexattr(vnode_t vp, const char *name, int options, | |
111 | vfs_context_t context); | |
91447636 A |
112 | |
113 | /* | |
114 | * Retrieve the data of an extended attribute. | |
115 | */ | |
116 | int | |
117 | vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, | |
0a7de745 | 118 | int options, vfs_context_t context) |
91447636 A |
119 | { |
120 | int error; | |
121 | ||
39236c6e | 122 | if (!XATTR_VNODE_SUPPORTED(vp)) { |
0a7de745 | 123 | return EPERM; |
91447636 | 124 | } |
2d21ac55 A |
125 | #if NAMEDSTREAMS |
126 | /* getxattr calls are not allowed for streams. */ | |
127 | if (vp->v_flag & VISNAMEDSTREAM) { | |
128 | error = EPERM; | |
91447636 | 129 | goto out; |
2d21ac55 A |
130 | } |
131 | #endif | |
132 | /* | |
133 | * Non-kernel request need extra checks performed. | |
134 | * | |
135 | * The XATTR_NOSECURITY flag implies a kernel request. | |
136 | */ | |
137 | if (!(options & XATTR_NOSECURITY)) { | |
138 | #if CONFIG_MACF | |
139 | error = mac_vnode_check_getextattr(context, vp, name, uio); | |
0a7de745 | 140 | if (error) { |
2d21ac55 | 141 | goto out; |
0a7de745 | 142 | } |
2d21ac55 A |
143 | #endif /* MAC */ |
144 | if ((error = xattr_validatename(name))) { | |
145 | goto out; | |
146 | } | |
147 | if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context))) { | |
148 | goto out; | |
149 | } | |
150 | /* The offset can only be non-zero for resource forks. */ | |
0a7de745 | 151 | if (uio != NULL && uio_offset(uio) != 0 && |
f427ee49 | 152 | strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { |
2d21ac55 A |
153 | error = EINVAL; |
154 | goto out; | |
155 | } | |
156 | } | |
91447636 A |
157 | |
158 | /* The offset can only be non-zero for resource forks. */ | |
0a7de745 | 159 | if (uio != NULL && uio_offset(uio) != 0 && |
f427ee49 | 160 | strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { |
91447636 A |
161 | error = EINVAL; |
162 | goto out; | |
163 | } | |
164 | ||
165 | error = VNOP_GETXATTR(vp, name, uio, size, options, context); | |
2d21ac55 | 166 | if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { |
91447636 A |
167 | /* |
168 | * A filesystem may keep some EAs natively and return ENOTSUP for others. | |
91447636 A |
169 | */ |
170 | error = default_getxattr(vp, name, uio, size, options, context); | |
171 | } | |
172 | out: | |
0a7de745 | 173 | return error; |
91447636 A |
174 | } |
175 | ||
176 | /* | |
177 | * Set the data of an extended attribute. | |
178 | */ | |
179 | int | |
180 | vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context) | |
181 | { | |
182 | int error; | |
183 | ||
39236c6e | 184 | if (!XATTR_VNODE_SUPPORTED(vp)) { |
0a7de745 | 185 | return EPERM; |
91447636 | 186 | } |
2d21ac55 A |
187 | #if NAMEDSTREAMS |
188 | /* setxattr calls are not allowed for streams. */ | |
189 | if (vp->v_flag & VISNAMEDSTREAM) { | |
190 | error = EPERM; | |
191 | goto out; | |
192 | } | |
193 | #endif | |
0a7de745 A |
194 | if ((options & (XATTR_REPLACE | XATTR_CREATE)) == (XATTR_REPLACE | XATTR_CREATE)) { |
195 | return EINVAL; | |
91447636 A |
196 | } |
197 | if ((error = xattr_validatename(name))) { | |
0a7de745 | 198 | return error; |
91447636 | 199 | } |
0a7de745 | 200 | if (!(options & XATTR_NOSECURITY)) { |
2d21ac55 A |
201 | #if CONFIG_MACF |
202 | error = mac_vnode_check_setextattr(context, vp, name, uio); | |
0a7de745 | 203 | if (error) { |
2d21ac55 | 204 | goto out; |
0a7de745 | 205 | } |
2d21ac55 A |
206 | #endif /* MAC */ |
207 | error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context); | |
0a7de745 | 208 | if (error) { |
2d21ac55 | 209 | goto out; |
0a7de745 | 210 | } |
2d21ac55 | 211 | } |
91447636 | 212 | /* The offset can only be non-zero for resource forks. */ |
0a7de745 | 213 | if (uio_offset(uio) != 0 && |
f427ee49 | 214 | strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { |
91447636 A |
215 | error = EINVAL; |
216 | goto out; | |
217 | } | |
218 | ||
219 | error = VNOP_SETXATTR(vp, name, uio, options, context); | |
220 | #ifdef DUAL_EAS | |
221 | /* | |
222 | * An EJUSTRETURN is from a filesystem which keeps this xattr | |
223 | * natively as well as in a dot-underscore file. In this case the | |
224 | * EJUSTRETURN means the filesytem has done nothing, but identifies the | |
225 | * EA as one which may be represented natively and/or in a DU, and | |
226 | * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in | |
227 | * in vn_setxattr can we do the getxattrs needed to ascertain whether | |
228 | * the XATTR_{CREATE,REPLACE} should yield an error. | |
229 | */ | |
230 | if (error == EJUSTRETURN) { | |
231 | int native = 0, dufile = 0; | |
0a7de745 | 232 | size_t sz; /* not used */ |
91447636 A |
233 | |
234 | native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1; | |
235 | dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1; | |
236 | if (options & XATTR_CREATE && (native || dufile)) { | |
237 | error = EEXIST; | |
238 | goto out; | |
239 | } | |
240 | if (options & XATTR_REPLACE && !(native || dufile)) { | |
241 | error = ENOATTR; | |
242 | goto out; | |
243 | } | |
244 | /* | |
245 | * Having determined no CREATE/REPLACE error should result, we | |
246 | * zero those bits, so both backing stores get written to. | |
247 | */ | |
248 | options &= ~(XATTR_CREATE | XATTR_REPLACE); | |
249 | error = VNOP_SETXATTR(vp, name, uio, options, context); | |
250 | /* the mainline path here is to have error==ENOTSUP ... */ | |
251 | } | |
252 | #endif /* DUAL_EAS */ | |
2d21ac55 | 253 | if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { |
91447636 A |
254 | /* |
255 | * A filesystem may keep some EAs natively and return ENOTSUP for others. | |
91447636 A |
256 | */ |
257 | error = default_setxattr(vp, name, uio, options, context); | |
258 | } | |
2d21ac55 | 259 | #if CONFIG_MACF |
39037602 A |
260 | if ((error == 0) && !(options & XATTR_NOSECURITY)) { |
261 | mac_vnode_notify_setextattr(context, vp, name, uio); | |
0a7de745 | 262 | if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) { |
39037602 | 263 | mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); |
0a7de745 | 264 | } |
39037602 | 265 | } |
2d21ac55 | 266 | #endif |
91447636 | 267 | out: |
0a7de745 | 268 | return error; |
91447636 A |
269 | } |
270 | ||
271 | /* | |
272 | * Remove an extended attribute. | |
273 | */ | |
274 | int | |
275 | vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context) | |
276 | { | |
277 | int error; | |
278 | ||
39236c6e | 279 | if (!XATTR_VNODE_SUPPORTED(vp)) { |
0a7de745 | 280 | return EPERM; |
91447636 | 281 | } |
2d21ac55 A |
282 | #if NAMEDSTREAMS |
283 | /* removexattr calls are not allowed for streams. */ | |
284 | if (vp->v_flag & VISNAMEDSTREAM) { | |
285 | error = EPERM; | |
286 | goto out; | |
287 | } | |
288 | #endif | |
91447636 | 289 | if ((error = xattr_validatename(name))) { |
0a7de745 | 290 | return error; |
91447636 | 291 | } |
2d21ac55 A |
292 | if (!(options & XATTR_NOSECURITY)) { |
293 | #if CONFIG_MACF | |
294 | error = mac_vnode_check_deleteextattr(context, vp, name); | |
0a7de745 | 295 | if (error) { |
2d21ac55 | 296 | goto out; |
0a7de745 | 297 | } |
2d21ac55 A |
298 | #endif /* MAC */ |
299 | error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context); | |
0a7de745 | 300 | if (error) { |
2d21ac55 | 301 | goto out; |
0a7de745 | 302 | } |
2d21ac55 | 303 | } |
91447636 | 304 | error = VNOP_REMOVEXATTR(vp, name, options, context); |
2d21ac55 | 305 | if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { |
91447636 A |
306 | /* |
307 | * A filesystem may keep some EAs natively and return ENOTSUP for others. | |
91447636 A |
308 | */ |
309 | error = default_removexattr(vp, name, options, context); | |
310 | #ifdef DUAL_EAS | |
311 | } else if (error == EJUSTRETURN) { | |
312 | /* | |
313 | * EJUSTRETURN is from a filesystem which keeps this xattr natively as well | |
314 | * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove | |
315 | * a native xattr, so failure to find it in a DU file during | |
316 | * default_removexattr should not be considered an error. | |
317 | */ | |
318 | error = default_removexattr(vp, name, options, context); | |
0a7de745 | 319 | if (error == ENOATTR) { |
91447636 | 320 | error = 0; |
0a7de745 | 321 | } |
91447636 A |
322 | #endif /* DUAL_EAS */ |
323 | } | |
2d21ac55 | 324 | #if CONFIG_MACF |
39037602 A |
325 | if ((error == 0) && !(options & XATTR_NOSECURITY)) { |
326 | mac_vnode_notify_deleteextattr(context, vp, name); | |
0a7de745 | 327 | if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) { |
39037602 | 328 | mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); |
0a7de745 | 329 | } |
39037602 | 330 | } |
2d21ac55 | 331 | #endif |
91447636 | 332 | out: |
0a7de745 | 333 | return error; |
91447636 A |
334 | } |
335 | ||
336 | /* | |
337 | * Retrieve the list of extended attribute names. | |
338 | */ | |
339 | int | |
340 | vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context) | |
341 | { | |
342 | int error; | |
343 | ||
39236c6e | 344 | if (!XATTR_VNODE_SUPPORTED(vp)) { |
0a7de745 | 345 | return EPERM; |
91447636 | 346 | } |
2d21ac55 A |
347 | #if NAMEDSTREAMS |
348 | /* listxattr calls are not allowed for streams. */ | |
349 | if (vp->v_flag & VISNAMEDSTREAM) { | |
0a7de745 | 350 | return EPERM; |
2d21ac55 A |
351 | } |
352 | #endif | |
353 | ||
354 | if (!(options & XATTR_NOSECURITY)) { | |
355 | #if CONFIG_MACF | |
356 | error = mac_vnode_check_listextattr(context, vp); | |
0a7de745 | 357 | if (error) { |
2d21ac55 | 358 | goto out; |
0a7de745 | 359 | } |
2d21ac55 A |
360 | #endif /* MAC */ |
361 | ||
362 | error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context); | |
0a7de745 | 363 | if (error) { |
2d21ac55 | 364 | goto out; |
0a7de745 | 365 | } |
2d21ac55 | 366 | } |
91447636 A |
367 | |
368 | error = VNOP_LISTXATTR(vp, uio, size, options, context); | |
2d21ac55 | 369 | if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { |
91447636 A |
370 | /* |
371 | * A filesystem may keep some but not all EAs natively, in which case | |
372 | * the native EA names will have been uiomove-d out (or *size updated) | |
0a7de745 | 373 | * and the default_listxattr here will finish the job. |
91447636 A |
374 | */ |
375 | error = default_listxattr(vp, uio, size, options, context); | |
376 | } | |
377 | out: | |
0a7de745 | 378 | return error; |
91447636 A |
379 | } |
380 | ||
381 | int | |
382 | xattr_validatename(const char *name) | |
383 | { | |
f427ee49 | 384 | size_t namelen; |
91447636 A |
385 | |
386 | if (name == NULL || name[0] == '\0') { | |
0a7de745 | 387 | return EINVAL; |
91447636 | 388 | } |
5ba3f43e | 389 | namelen = strlen(name); |
0a7de745 A |
390 | |
391 | if (utf8_validatestr((const unsigned char *)name, namelen) != 0) { | |
392 | return EINVAL; | |
393 | } | |
394 | ||
395 | return 0; | |
91447636 A |
396 | } |
397 | ||
398 | ||
399 | /* | |
400 | * Determine whether an EA is a protected system attribute. | |
401 | */ | |
402 | int | |
403 | xattr_protected(const char *attrname) | |
404 | { | |
0a7de745 | 405 | return !strncmp(attrname, "com.apple.system.", 17); |
91447636 A |
406 | } |
407 | ||
408 | ||
d9a64523 A |
409 | static void |
410 | vnode_setasnamedstream_internal(vnode_t vp, vnode_t svp) | |
411 | { | |
412 | uint32_t streamflags = VISNAMEDSTREAM; | |
413 | ||
414 | if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { | |
415 | streamflags |= VISSHADOW; | |
416 | } | |
417 | ||
418 | /* Tag the vnode. */ | |
419 | vnode_lock_spin(svp); | |
420 | svp->v_flag |= streamflags; | |
421 | vnode_unlock(svp); | |
422 | ||
423 | /* Tag the parent so we know to flush credentials for streams on setattr */ | |
424 | vnode_lock_spin(vp); | |
425 | vp->v_lflag |= VL_HASSTREAMS; | |
426 | vnode_unlock(vp); | |
427 | ||
428 | /* Make the file it's parent. | |
429 | * Note: This parent link helps us distinguish vnodes for | |
430 | * shadow stream files from vnodes for resource fork on file | |
431 | * systems that support namedstream natively (both have | |
432 | * VISNAMEDSTREAM set) by allowing access to mount structure | |
433 | * for checking MNTK_NAMED_STREAMS bit at many places in the | |
434 | * code. | |
435 | */ | |
436 | vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT); | |
437 | ||
f427ee49 A |
438 | if (vnode_isdyldsharedcache(vp)) { |
439 | vnode_lock_spin(svp); | |
440 | svp->v_flag |= VSHARED_DYLD; | |
441 | vnode_unlock(svp); | |
442 | } | |
443 | ||
d9a64523 A |
444 | return; |
445 | } | |
446 | ||
447 | errno_t | |
448 | vnode_setasnamedstream(vnode_t vp, vnode_t svp) | |
449 | { | |
0a7de745 A |
450 | if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { |
451 | return EINVAL; | |
452 | } | |
d9a64523 A |
453 | |
454 | vnode_setasnamedstream_internal(vp, svp); | |
0a7de745 | 455 | return 0; |
d9a64523 A |
456 | } |
457 | ||
2d21ac55 | 458 | #if NAMEDSTREAMS |
39236c6e | 459 | |
2d21ac55 A |
460 | /* |
461 | * Obtain a named stream from vnode vp. | |
462 | */ | |
463 | errno_t | |
464 | vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context) | |
465 | { | |
466 | int error; | |
467 | ||
5ba3f43e | 468 | if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) { |
2d21ac55 | 469 | error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context); |
5ba3f43e | 470 | } else { |
0a7de745 | 471 | if (flags) { |
5ba3f43e | 472 | error = ENOTSUP; |
0a7de745 | 473 | } else { |
5ba3f43e | 474 | error = default_getnamedstream(vp, svpp, name, op, context); |
0a7de745 | 475 | } |
5ba3f43e | 476 | } |
2d21ac55 A |
477 | |
478 | if (error == 0) { | |
d9a64523 A |
479 | vnode_setasnamedstream_internal(vp, *svpp); |
480 | } | |
2d21ac55 | 481 | |
0a7de745 | 482 | return error; |
2d21ac55 A |
483 | } |
484 | ||
485 | /* | |
486 | * Make a named stream for vnode vp. | |
487 | */ | |
0a7de745 | 488 | errno_t |
2d21ac55 A |
489 | vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context) |
490 | { | |
491 | int error; | |
492 | ||
0a7de745 | 493 | if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) { |
2d21ac55 | 494 | error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context); |
0a7de745 | 495 | } else { |
2d21ac55 | 496 | error = default_makenamedstream(vp, svpp, name, context); |
0a7de745 | 497 | } |
2d21ac55 A |
498 | |
499 | if (error == 0) { | |
d9a64523 | 500 | vnode_setasnamedstream_internal(vp, *svpp); |
2d21ac55 | 501 | } |
d9a64523 | 502 | |
0a7de745 | 503 | return error; |
2d21ac55 A |
504 | } |
505 | ||
506 | /* | |
507 | * Remove a named stream from vnode vp. | |
508 | */ | |
0a7de745 | 509 | errno_t |
2d21ac55 A |
510 | vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context) |
511 | { | |
512 | int error; | |
513 | ||
0a7de745 | 514 | if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) { |
2d21ac55 | 515 | error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context); |
0a7de745 | 516 | } else { |
2d21ac55 | 517 | error = default_removenamedstream(vp, name, context); |
0a7de745 | 518 | } |
2d21ac55 | 519 | |
0a7de745 | 520 | return error; |
2d21ac55 A |
521 | } |
522 | ||
523 | #define NS_IOBUFSIZE (128 * 1024) | |
524 | ||
525 | /* | |
526 | * Release a named stream shadow file. | |
cf7d32b8 | 527 | * |
0a7de745 A |
528 | * Note: This function is called from two places where we do not need |
529 | * to check if the vnode has any references held before deleting the | |
530 | * shadow file. Once from vclean() when the vnode is being reclaimed | |
531 | * and we do not hold any references on the vnode. Second time from | |
532 | * default_getnamedstream() when we get an error during shadow stream | |
533 | * file initialization so that other processes who are waiting for the | |
534 | * shadow stream file initialization by the creator will get opportunity | |
cf7d32b8 | 535 | * to create and initialize the file again. |
2d21ac55 A |
536 | */ |
537 | errno_t | |
0a7de745 A |
538 | vnode_relenamedstream(vnode_t vp, vnode_t svp) |
539 | { | |
2d21ac55 A |
540 | vnode_t dvp; |
541 | struct componentname cn; | |
b0d623f7 | 542 | char tmpname[80]; |
2d21ac55 | 543 | errno_t err; |
0a7de745 A |
544 | |
545 | /* | |
39236c6e A |
546 | * We need to use the kernel context here. If we used the supplied |
547 | * VFS context we have no clue whether or not it originated from userland | |
548 | * where it could be subject to a chroot jail. We need to ensure that all | |
549 | * filesystem access to shadow files is done on the same FS regardless of | |
550 | * userland process restrictions. | |
551 | */ | |
552 | vfs_context_t kernelctx = vfs_context_kernel(); | |
2d21ac55 | 553 | |
2d21ac55 A |
554 | cache_purge(svp); |
555 | ||
556 | vnode_lock(svp); | |
557 | MAKE_SHADOW_NAME(vp, tmpname); | |
558 | vnode_unlock(svp); | |
559 | ||
560 | cn.cn_nameiop = DELETE; | |
561 | cn.cn_flags = ISLASTCN; | |
39236c6e | 562 | cn.cn_context = kernelctx; |
2d21ac55 A |
563 | cn.cn_pnbuf = tmpname; |
564 | cn.cn_pnlen = sizeof(tmpname); | |
565 | cn.cn_nameptr = cn.cn_pnbuf; | |
f427ee49 | 566 | cn.cn_namelen = (int)strlen(tmpname); |
2d21ac55 | 567 | |
0a7de745 A |
568 | /* |
569 | * Obtain the vnode for the shadow files directory. Make sure to | |
39236c6e A |
570 | * use the kernel ctx as described above. |
571 | */ | |
572 | err = get_shadow_dir(&dvp); | |
2d21ac55 A |
573 | if (err != 0) { |
574 | return err; | |
575 | } | |
b0d623f7 | 576 | |
39236c6e | 577 | (void) VNOP_REMOVE(dvp, svp, &cn, 0, kernelctx); |
2d21ac55 A |
578 | vnode_put(dvp); |
579 | ||
0a7de745 | 580 | return 0; |
2d21ac55 A |
581 | } |
582 | ||
583 | /* | |
584 | * Flush a named stream shadow file. | |
0a7de745 | 585 | * |
39236c6e A |
586 | * 'vp' represents the AppleDouble file. |
587 | * 'svp' represents the shadow file. | |
2d21ac55 | 588 | */ |
0a7de745 | 589 | errno_t |
2d21ac55 A |
590 | vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) |
591 | { | |
592 | struct vnode_attr va; | |
593 | uio_t auio = NULL; | |
594 | caddr_t bufptr = NULL; | |
595 | size_t bufsize = 0; | |
596 | size_t offset; | |
597 | size_t iosize; | |
598 | size_t datasize; | |
599 | int error; | |
0a7de745 A |
600 | /* |
601 | * The kernel context must be used for all I/O to the shadow file | |
39236c6e A |
602 | * and its namespace operations |
603 | */ | |
604 | vfs_context_t kernelctx = vfs_context_kernel(); | |
605 | ||
606 | /* The supplied context is used for access to the AD file itself */ | |
2d21ac55 A |
607 | |
608 | VATTR_INIT(&va); | |
609 | VATTR_WANTED(&va, va_data_size); | |
0a7de745 A |
610 | if (VNOP_GETATTR(svp, &va, context) != 0 || |
611 | !VATTR_IS_SUPPORTED(&va, va_data_size)) { | |
612 | return 0; | |
2d21ac55 | 613 | } |
f427ee49 A |
614 | if (va.va_data_size > UINT32_MAX) { |
615 | return EINVAL; | |
616 | } | |
617 | datasize = (size_t)va.va_data_size; | |
6d2010ae | 618 | if (datasize == 0) { |
2d21ac55 | 619 | (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); |
0a7de745 | 620 | return 0; |
2d21ac55 A |
621 | } |
622 | ||
623 | iosize = bufsize = MIN(datasize, NS_IOBUFSIZE); | |
3e170ce0 | 624 | if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) { |
0a7de745 | 625 | return ENOMEM; |
2d21ac55 | 626 | } |
b0d623f7 | 627 | auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); |
2d21ac55 A |
628 | offset = 0; |
629 | ||
630 | /* | |
631 | * Copy the shadow stream file data into the resource fork. | |
632 | */ | |
39236c6e | 633 | error = VNOP_OPEN(svp, 0, kernelctx); |
2d21ac55 A |
634 | if (error) { |
635 | printf("vnode_flushnamedstream: err %d opening file\n", error); | |
636 | goto out; | |
637 | } | |
638 | while (offset < datasize) { | |
639 | iosize = MIN(datasize - offset, iosize); | |
640 | ||
b0d623f7 | 641 | uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ); |
2d21ac55 | 642 | uio_addiov(auio, (uintptr_t)bufptr, iosize); |
39236c6e | 643 | error = VNOP_READ(svp, auio, 0, kernelctx); |
2d21ac55 A |
644 | if (error) { |
645 | break; | |
646 | } | |
647 | /* Since there's no truncate xattr we must remove the resource fork. */ | |
648 | if (offset == 0) { | |
649 | error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); | |
650 | if ((error != 0) && (error != ENOATTR)) { | |
651 | break; | |
652 | } | |
653 | } | |
b0d623f7 | 654 | uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE); |
2d21ac55 A |
655 | uio_addiov(auio, (uintptr_t)bufptr, iosize); |
656 | error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context); | |
657 | if (error) { | |
658 | break; | |
659 | } | |
660 | offset += iosize; | |
661 | } | |
39236c6e A |
662 | |
663 | /* close shadowfile */ | |
664 | (void) VNOP_CLOSE(svp, 0, kernelctx); | |
2d21ac55 A |
665 | out: |
666 | if (bufptr) { | |
667 | kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize); | |
668 | } | |
669 | if (auio) { | |
670 | uio_free(auio); | |
671 | } | |
0a7de745 | 672 | return error; |
2d21ac55 A |
673 | } |
674 | ||
39236c6e | 675 | |
0a7de745 | 676 | /* |
4b17d6b6 A |
677 | * Verify that the vnode 'vp' is a vnode that lives in the shadow |
678 | * directory. We can't just query the parent pointer directly since | |
679 | * the shadowfile is hooked up to the actual file it's a stream for. | |
680 | */ | |
0a7de745 A |
681 | errno_t |
682 | vnode_verifynamedstream(vnode_t vp) | |
683 | { | |
4b17d6b6 A |
684 | int error; |
685 | struct vnode *shadow_dvp = NULL; | |
686 | struct vnode *shadowfile = NULL; | |
687 | struct componentname cn; | |
0a7de745 A |
688 | |
689 | /* | |
39236c6e A |
690 | * We need to use the kernel context here. If we used the supplied |
691 | * VFS context we have no clue whether or not it originated from userland | |
692 | * where it could be subject to a chroot jail. We need to ensure that all | |
693 | * filesystem access to shadow files is done on the same FS regardless of | |
694 | * userland process restrictions. | |
695 | */ | |
696 | vfs_context_t kernelctx = vfs_context_kernel(); | |
4b17d6b6 | 697 | char tmpname[80]; |
0a7de745 | 698 | |
4b17d6b6 A |
699 | |
700 | /* Get the shadow directory vnode */ | |
39236c6e | 701 | error = get_shadow_dir(&shadow_dvp); |
4b17d6b6 A |
702 | if (error) { |
703 | return error; | |
704 | } | |
705 | ||
706 | /* Re-generate the shadow name in the buffer */ | |
0a7de745 | 707 | MAKE_SHADOW_NAME(vp, tmpname); |
4b17d6b6 A |
708 | |
709 | /* Look up item in shadow dir */ | |
710 | bzero(&cn, sizeof(cn)); | |
711 | cn.cn_nameiop = LOOKUP; | |
712 | cn.cn_flags = ISLASTCN | CN_ALLOWRSRCFORK; | |
39236c6e | 713 | cn.cn_context = kernelctx; |
4b17d6b6 A |
714 | cn.cn_pnbuf = tmpname; |
715 | cn.cn_pnlen = sizeof(tmpname); | |
716 | cn.cn_nameptr = cn.cn_pnbuf; | |
f427ee49 | 717 | cn.cn_namelen = (int)strlen(tmpname); |
4b17d6b6 | 718 | |
0a7de745 | 719 | if (VNOP_LOOKUP(shadow_dvp, &shadowfile, &cn, kernelctx) == 0) { |
4b17d6b6 A |
720 | /* is the pointer the same? */ |
721 | if (shadowfile == vp) { | |
0a7de745 A |
722 | error = 0; |
723 | } else { | |
4b17d6b6 A |
724 | error = EPERM; |
725 | } | |
726 | /* drop the iocount acquired */ | |
0a7de745 A |
727 | vnode_put(shadowfile); |
728 | } | |
4b17d6b6 A |
729 | |
730 | /* Drop iocount on shadow dir */ | |
0a7de745 | 731 | vnode_put(shadow_dvp); |
4b17d6b6 | 732 | return error; |
0a7de745 | 733 | } |
2d21ac55 | 734 | |
0a7de745 A |
735 | /* |
736 | * Access or create the shadow file as needed. | |
737 | * | |
39236c6e A |
738 | * 'makestream' with non-zero value means that we need to guarantee we were the |
739 | * creator of the shadow file. | |
740 | * | |
741 | * 'context' is the user supplied context for the original VFS operation that | |
742 | * caused us to need a shadow file. | |
743 | * | |
744 | * int pointed to by 'creator' is nonzero if we created the shadowfile. | |
745 | */ | |
2d21ac55 A |
746 | static int |
747 | getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, | |
0a7de745 | 748 | int *creator, vfs_context_t context) |
2d21ac55 A |
749 | { |
750 | vnode_t dvp = NULLVP; | |
751 | vnode_t svp = NULLVP; | |
752 | struct componentname cn; | |
753 | struct vnode_attr va; | |
b0d623f7 | 754 | char tmpname[80]; |
2d21ac55 A |
755 | size_t datasize = 0; |
756 | int error = 0; | |
6d2010ae | 757 | int retries = 0; |
39236c6e | 758 | vfs_context_t kernelctx = vfs_context_kernel(); |
2d21ac55 | 759 | |
6d2010ae | 760 | retry_create: |
2d21ac55 | 761 | *creator = 0; |
2d21ac55 A |
762 | /* Establish a unique file name. */ |
763 | MAKE_SHADOW_NAME(vp, tmpname); | |
764 | bzero(&cn, sizeof(cn)); | |
765 | cn.cn_nameiop = LOOKUP; | |
766 | cn.cn_flags = ISLASTCN; | |
767 | cn.cn_context = context; | |
768 | cn.cn_pnbuf = tmpname; | |
769 | cn.cn_pnlen = sizeof(tmpname); | |
770 | cn.cn_nameptr = cn.cn_pnbuf; | |
f427ee49 | 771 | cn.cn_namelen = (int)strlen(tmpname); |
2d21ac55 A |
772 | |
773 | /* Pick up uid, gid, mode and date from original file. */ | |
774 | VATTR_INIT(&va); | |
775 | VATTR_WANTED(&va, va_uid); | |
776 | VATTR_WANTED(&va, va_gid); | |
777 | VATTR_WANTED(&va, va_mode); | |
778 | VATTR_WANTED(&va, va_create_time); | |
779 | VATTR_WANTED(&va, va_modify_time); | |
0a7de745 A |
780 | if (VNOP_GETATTR(vp, &va, context) != 0 || |
781 | !VATTR_IS_SUPPORTED(&va, va_uid) || | |
782 | !VATTR_IS_SUPPORTED(&va, va_gid) || | |
783 | !VATTR_IS_SUPPORTED(&va, va_mode)) { | |
2d21ac55 A |
784 | va.va_uid = KAUTH_UID_NONE; |
785 | va.va_gid = KAUTH_GID_NONE; | |
786 | va.va_mode = S_IRUSR | S_IWUSR; | |
787 | } | |
788 | va.va_vaflags = VA_EXCLUSIVE; | |
789 | VATTR_SET(&va, va_type, VREG); | |
790 | /* We no longer change the access, but we still hide it. */ | |
791 | VATTR_SET(&va, va_flags, UF_HIDDEN); | |
792 | ||
793 | /* Obtain the vnode for the shadow files directory. */ | |
39236c6e | 794 | if (get_shadow_dir(&dvp) != 0) { |
2d21ac55 A |
795 | error = ENOTDIR; |
796 | goto out; | |
797 | } | |
798 | if (!makestream) { | |
799 | /* See if someone else already has it open. */ | |
39236c6e | 800 | if (VNOP_LOOKUP(dvp, &svp, &cn, kernelctx) == 0) { |
2d21ac55 A |
801 | /* Double check existence by asking for size. */ |
802 | VATTR_INIT(&va); | |
803 | VATTR_WANTED(&va, va_data_size); | |
0a7de745 | 804 | if (VNOP_GETATTR(svp, &va, context) == 0 && |
2d21ac55 A |
805 | VATTR_IS_SUPPORTED(&va, va_data_size)) { |
806 | goto out; /* OK to use. */ | |
807 | } | |
808 | } | |
0a7de745 A |
809 | |
810 | /* | |
811 | * Otherwise make sure the resource fork data exists. | |
39236c6e A |
812 | * Use the supplied context for accessing the AD file. |
813 | */ | |
2d21ac55 | 814 | error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize, |
0a7de745 | 815 | XATTR_NOSECURITY, context); |
2d21ac55 A |
816 | /* |
817 | * To maintain binary compatibility with legacy Carbon | |
818 | * emulated resource fork support, if the resource fork | |
819 | * doesn't exist but the Finder Info does, then act as | |
820 | * if an empty resource fork is present (see 4724359). | |
821 | */ | |
822 | if ((error == ENOATTR) && | |
823 | (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize, | |
0a7de745 | 824 | XATTR_NOSECURITY, context) == 0)) { |
2d21ac55 A |
825 | datasize = 0; |
826 | error = 0; | |
827 | } else { | |
828 | if (error) { | |
829 | goto out; | |
830 | } | |
0a7de745 | 831 | |
2d21ac55 A |
832 | /* If the resource fork exists, its size is expected to be non-zero. */ |
833 | if (datasize == 0) { | |
834 | error = ENOATTR; | |
835 | goto out; | |
836 | } | |
837 | } | |
838 | } | |
839 | /* Create the shadow stream file. */ | |
39236c6e | 840 | error = VNOP_CREATE(dvp, &svp, &cn, &va, kernelctx); |
2d21ac55 | 841 | if (error == 0) { |
7e4a7d39 | 842 | vnode_recycle(svp); |
2d21ac55 | 843 | *creator = 1; |
0a7de745 | 844 | } else if ((error == EEXIST) && !makestream) { |
39236c6e | 845 | error = VNOP_LOOKUP(dvp, &svp, &cn, kernelctx); |
0a7de745 | 846 | } else if ((error == ENOENT) && !makestream) { |
6d2010ae A |
847 | /* |
848 | * We could have raced with a rmdir on the shadow directory | |
849 | * post-lookup. Retry from the beginning, 1x only, to | |
0a7de745 | 850 | * try and see if we need to re-create the shadow directory |
6d2010ae A |
851 | * in get_shadow_dir. |
852 | */ | |
853 | if (retries == 0) { | |
854 | retries++; | |
855 | if (dvp) { | |
0a7de745 | 856 | vnode_put(dvp); |
6d2010ae A |
857 | dvp = NULLVP; |
858 | } | |
859 | if (svp) { | |
0a7de745 | 860 | vnode_put(svp); |
6d2010ae A |
861 | svp = NULLVP; |
862 | } | |
863 | goto retry_create; | |
864 | } | |
865 | /* Otherwise, just error out normally below */ | |
866 | } | |
0a7de745 | 867 | |
2d21ac55 A |
868 | out: |
869 | if (dvp) { | |
870 | vnode_put(dvp); | |
871 | } | |
872 | if (error) { | |
873 | /* On errors, clean up shadow stream file. */ | |
874 | if (svp) { | |
875 | vnode_put(svp); | |
876 | svp = NULLVP; | |
877 | } | |
878 | } | |
879 | *svpp = svp; | |
880 | if (rsrcsize) { | |
881 | *rsrcsize = datasize; | |
882 | } | |
0a7de745 | 883 | return error; |
2d21ac55 A |
884 | } |
885 | ||
886 | ||
887 | static int | |
888 | default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context) | |
889 | { | |
890 | vnode_t svp = NULLVP; | |
891 | uio_t auio = NULL; | |
892 | caddr_t bufptr = NULL; | |
893 | size_t bufsize = 0; | |
894 | size_t datasize = 0; | |
895 | int creator; | |
896 | int error; | |
897 | ||
39236c6e A |
898 | /* need the kernel context for accessing the shadowfile */ |
899 | vfs_context_t kernelctx = vfs_context_kernel(); | |
900 | ||
2d21ac55 A |
901 | /* |
902 | * Only the "com.apple.ResourceFork" stream is supported here. | |
903 | */ | |
f427ee49 | 904 | if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { |
2d21ac55 | 905 | *svpp = NULLVP; |
0a7de745 | 906 | return ENOATTR; |
2d21ac55 A |
907 | } |
908 | retry: | |
909 | /* | |
910 | * Obtain a shadow file for the resource fork I/O. | |
0a7de745 | 911 | * |
39236c6e A |
912 | * Need to pass along the supplied context so that getshadowfile |
913 | * can access the AD file as needed, using it. | |
2d21ac55 A |
914 | */ |
915 | error = getshadowfile(vp, &svp, 0, &datasize, &creator, context); | |
916 | if (error) { | |
917 | *svpp = NULLVP; | |
0a7de745 | 918 | return error; |
2d21ac55 A |
919 | } |
920 | ||
921 | /* | |
922 | * The creator of the shadow file provides its file data, | |
0a7de745 | 923 | * all other threads should wait until its ready. In order to |
b0d623f7 | 924 | * prevent a deadlock during error codepaths, we need to check if the |
0a7de745 | 925 | * vnode is being created, or if it has failed out. Regardless of success or |
b0d623f7 A |
926 | * failure, we set the VISSHADOW bit on the vnode, so we check that |
927 | * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't, | |
928 | * then we can infer the creator isn't done yet. If it's there, but | |
929 | * VISNAMEDSTREAM is not set, then we can infer it errored out and we should | |
930 | * try again. | |
2d21ac55 A |
931 | */ |
932 | if (!creator) { | |
933 | vnode_lock(svp); | |
934 | if (svp->v_flag & VISNAMEDSTREAM) { | |
935 | /* data is ready, go use it */ | |
936 | vnode_unlock(svp); | |
937 | goto out; | |
938 | } else { | |
b0d623f7 A |
939 | /* It's not ready, wait for it (sleep using v_parent as channel) */ |
940 | if ((svp->v_flag & VISSHADOW)) { | |
0a7de745 | 941 | /* |
b0d623f7 A |
942 | * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other |
943 | * thread is done with this vnode. Just unlock the vnode and try again | |
944 | */ | |
945 | vnode_unlock(svp); | |
0a7de745 | 946 | } else { |
b0d623f7 A |
947 | /* Otherwise, sleep if the shadow file is not created yet */ |
948 | msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP, | |
0a7de745 | 949 | "getnamedstream", NULL); |
b0d623f7 | 950 | } |
2d21ac55 A |
951 | vnode_put(svp); |
952 | svp = NULLVP; | |
953 | goto retry; | |
954 | } | |
955 | } | |
956 | ||
957 | /* | |
958 | * Copy the real resource fork data into shadow stream file. | |
959 | */ | |
960 | if (op == NS_OPEN && datasize != 0) { | |
961 | size_t offset; | |
0a7de745 | 962 | size_t iosize; |
2d21ac55 A |
963 | |
964 | iosize = bufsize = MIN(datasize, NS_IOBUFSIZE); | |
3e170ce0 | 965 | if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) { |
2d21ac55 A |
966 | error = ENOMEM; |
967 | goto out; | |
968 | } | |
969 | ||
b0d623f7 | 970 | auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); |
2d21ac55 A |
971 | offset = 0; |
972 | ||
39236c6e A |
973 | /* open the shadow file */ |
974 | error = VNOP_OPEN(svp, 0, kernelctx); | |
2d21ac55 A |
975 | if (error) { |
976 | goto out; | |
977 | } | |
978 | while (offset < datasize) { | |
0a7de745 | 979 | size_t tmpsize; |
2d21ac55 A |
980 | |
981 | iosize = MIN(datasize - offset, iosize); | |
982 | ||
b0d623f7 | 983 | uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ); |
2d21ac55 | 984 | uio_addiov(auio, (uintptr_t)bufptr, iosize); |
39236c6e | 985 | /* use supplied ctx for AD file */ |
2d21ac55 | 986 | error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize, |
0a7de745 | 987 | XATTR_NOSECURITY, context); |
2d21ac55 A |
988 | if (error) { |
989 | break; | |
990 | } | |
0a7de745 | 991 | |
b0d623f7 | 992 | uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE); |
2d21ac55 | 993 | uio_addiov(auio, (uintptr_t)bufptr, iosize); |
39236c6e A |
994 | /* kernel context for writing shadowfile */ |
995 | error = VNOP_WRITE(svp, auio, 0, kernelctx); | |
2d21ac55 A |
996 | if (error) { |
997 | break; | |
998 | } | |
999 | offset += iosize; | |
1000 | } | |
39236c6e A |
1001 | |
1002 | /* close shadow file */ | |
1003 | (void) VNOP_CLOSE(svp, 0, kernelctx); | |
2d21ac55 A |
1004 | } |
1005 | out: | |
1006 | /* Wake up anyone waiting for svp file content */ | |
1007 | if (creator) { | |
1008 | if (error == 0) { | |
1009 | vnode_lock(svp); | |
b0d623f7 A |
1010 | /* VISSHADOW would be set later on anyway, so we set it now */ |
1011 | svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW); | |
2d21ac55 A |
1012 | wakeup((caddr_t)&svp->v_parent); |
1013 | vnode_unlock(svp); | |
1014 | } else { | |
0a7de745 A |
1015 | /* On post create errors, get rid of the shadow file. This |
1016 | * way if there is another process waiting for initialization | |
1017 | * of the shadowfile by the current process will wake up and | |
b0d623f7 A |
1018 | * retry by creating and initializing the shadow file again. |
1019 | * Also add the VISSHADOW bit here to indicate we're done operating | |
1020 | * on this vnode. | |
cf7d32b8 | 1021 | */ |
39236c6e | 1022 | (void)vnode_relenamedstream(vp, svp); |
0a7de745 | 1023 | vnode_lock(svp); |
b0d623f7 | 1024 | svp->v_flag |= VISSHADOW; |
2d21ac55 | 1025 | wakeup((caddr_t)&svp->v_parent); |
b0d623f7 | 1026 | vnode_unlock(svp); |
2d21ac55 A |
1027 | } |
1028 | } | |
1029 | ||
1030 | if (bufptr) { | |
1031 | kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize); | |
1032 | } | |
1033 | if (auio) { | |
1034 | uio_free(auio); | |
1035 | } | |
1036 | if (error) { | |
1037 | /* On errors, clean up shadow stream file. */ | |
1038 | if (svp) { | |
1039 | vnode_put(svp); | |
1040 | svp = NULLVP; | |
1041 | } | |
1042 | } | |
1043 | *svpp = svp; | |
0a7de745 | 1044 | return error; |
2d21ac55 A |
1045 | } |
1046 | ||
1047 | static int | |
1048 | default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context) | |
1049 | { | |
1050 | int creator; | |
1051 | int error; | |
1052 | ||
1053 | /* | |
1054 | * Only the "com.apple.ResourceFork" stream is supported here. | |
1055 | */ | |
f427ee49 | 1056 | if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { |
2d21ac55 | 1057 | *svpp = NULLVP; |
0a7de745 | 1058 | return ENOATTR; |
2d21ac55 | 1059 | } |
39236c6e A |
1060 | |
1061 | /* Supply the context to getshadowfile so it can manipulate the AD file */ | |
2d21ac55 A |
1062 | error = getshadowfile(vp, svpp, 1, NULL, &creator, context); |
1063 | ||
1064 | /* | |
1065 | * Wake up any waiters over in default_getnamedstream(). | |
1066 | */ | |
1067 | if ((error == 0) && (*svpp != NULL) && creator) { | |
1068 | vnode_t svp = *svpp; | |
1069 | ||
1070 | vnode_lock(svp); | |
b0d623f7 A |
1071 | /* If we're the creator, mark it as a named stream */ |
1072 | svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW); | |
2d21ac55 A |
1073 | /* Wakeup any waiters on the v_parent channel */ |
1074 | wakeup((caddr_t)&svp->v_parent); | |
1075 | vnode_unlock(svp); | |
1076 | } | |
b0d623f7 | 1077 | |
0a7de745 | 1078 | return error; |
2d21ac55 A |
1079 | } |
1080 | ||
0a7de745 | 1081 | static int |
2d21ac55 A |
1082 | default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context) |
1083 | { | |
1084 | /* | |
1085 | * Only the "com.apple.ResourceFork" stream is supported here. | |
1086 | */ | |
f427ee49 | 1087 | if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { |
0a7de745 | 1088 | return ENOATTR; |
2d21ac55 A |
1089 | } |
1090 | /* | |
1091 | * XXX - what about other opened instances? | |
1092 | */ | |
1093 | return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); | |
1094 | } | |
1095 | ||
1096 | static int | |
0a7de745 A |
1097 | get_shadow_dir(vnode_t *sdvpp) |
1098 | { | |
2d21ac55 A |
1099 | vnode_t dvp = NULLVP; |
1100 | vnode_t sdvp = NULLVP; | |
1101 | struct componentname cn; | |
1102 | struct vnode_attr va; | |
b0d623f7 | 1103 | char tmpname[80]; |
2d21ac55 A |
1104 | uint32_t tmp_fsid; |
1105 | int error; | |
39236c6e | 1106 | vfs_context_t kernelctx = vfs_context_kernel(); |
6d2010ae A |
1107 | |
1108 | bzero(tmpname, sizeof(tmpname)); | |
316670eb | 1109 | MAKE_SHADOW_DIRNAME(rootvnode, tmpname); |
0a7de745 A |
1110 | /* |
1111 | * Look up the shadow directory to ensure that it still exists. | |
6d2010ae A |
1112 | * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues |
1113 | * in caching it when multiple threads may be trying to manipulate the pointers. | |
0a7de745 | 1114 | * |
39236c6e A |
1115 | * Make sure to use the kernel context. We want a singular view of |
1116 | * the shadow dir regardless of chrooted processes. | |
6d2010ae | 1117 | */ |
39236c6e | 1118 | error = vnode_lookup(tmpname, 0, &sdvp, kernelctx); |
6d2010ae A |
1119 | if (error == 0) { |
1120 | /* | |
0a7de745 | 1121 | * If we get here, then we have successfully looked up the shadow dir, |
6d2010ae A |
1122 | * and it has an iocount from the lookup. Return the vp in the output argument. |
1123 | */ | |
1124 | *sdvpp = sdvp; | |
0a7de745 | 1125 | return 0; |
2d21ac55 | 1126 | } |
6d2010ae A |
1127 | /* In the failure case, no iocount is acquired */ |
1128 | sdvp = NULLVP; | |
0a7de745 | 1129 | bzero(tmpname, sizeof(tmpname)); |
2d21ac55 | 1130 | |
0a7de745 | 1131 | /* |
39236c6e A |
1132 | * Obtain the vnode for "/var/run" directory using the kernel |
1133 | * context. | |
1134 | * | |
316670eb A |
1135 | * This is defined in the SHADOW_DIR_CONTAINER macro |
1136 | */ | |
39236c6e | 1137 | if (vnode_lookup(SHADOW_DIR_CONTAINER, 0, &dvp, kernelctx) != 0) { |
2d21ac55 A |
1138 | error = ENOTSUP; |
1139 | goto out; | |
1140 | } | |
1141 | ||
0a7de745 | 1142 | /* |
316670eb | 1143 | * Create the shadow stream directory. |
0a7de745 | 1144 | * 'dvp' below suggests the parent directory so |
316670eb A |
1145 | * we only need to provide the leaf entry name |
1146 | */ | |
1147 | MAKE_SHADOW_DIR_LEAF(rootvnode, tmpname); | |
2d21ac55 A |
1148 | bzero(&cn, sizeof(cn)); |
1149 | cn.cn_nameiop = LOOKUP; | |
1150 | cn.cn_flags = ISLASTCN; | |
39236c6e | 1151 | cn.cn_context = kernelctx; |
2d21ac55 A |
1152 | cn.cn_pnbuf = tmpname; |
1153 | cn.cn_pnlen = sizeof(tmpname); | |
1154 | cn.cn_nameptr = cn.cn_pnbuf; | |
f427ee49 | 1155 | cn.cn_namelen = (int)strlen(tmpname); |
2d21ac55 A |
1156 | |
1157 | /* | |
1158 | * owned by root, only readable by root, hidden | |
1159 | */ | |
1160 | VATTR_INIT(&va); | |
1161 | VATTR_SET(&va, va_uid, 0); | |
1162 | VATTR_SET(&va, va_gid, 0); | |
1163 | VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR); | |
1164 | VATTR_SET(&va, va_type, VDIR); | |
1165 | VATTR_SET(&va, va_flags, UF_HIDDEN); | |
1166 | va.va_vaflags = VA_EXCLUSIVE; | |
1167 | ||
39236c6e | 1168 | error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, kernelctx); |
0a7de745 | 1169 | |
2d21ac55 A |
1170 | /* |
1171 | * There can be only one winner for an exclusive create. | |
1172 | */ | |
6d2010ae | 1173 | if (error == EEXIST) { |
2d21ac55 | 1174 | /* loser has to look up directory */ |
39236c6e | 1175 | error = VNOP_LOOKUP(dvp, &sdvp, &cn, kernelctx); |
2d21ac55 A |
1176 | if (error == 0) { |
1177 | /* Make sure its in fact a directory */ | |
1178 | if (sdvp->v_type != VDIR) { | |
1179 | goto baddir; | |
1180 | } | |
39236c6e | 1181 | /* Obtain the fsid for /var/run directory */ |
2d21ac55 A |
1182 | VATTR_INIT(&va); |
1183 | VATTR_WANTED(&va, va_fsid); | |
0a7de745 | 1184 | if (VNOP_GETATTR(dvp, &va, kernelctx) != 0 || |
2d21ac55 A |
1185 | !VATTR_IS_SUPPORTED(&va, va_fsid)) { |
1186 | goto baddir; | |
1187 | } | |
1188 | tmp_fsid = va.va_fsid; | |
1189 | ||
1190 | VATTR_INIT(&va); | |
1191 | VATTR_WANTED(&va, va_uid); | |
1192 | VATTR_WANTED(&va, va_gid); | |
1193 | VATTR_WANTED(&va, va_mode); | |
1194 | VATTR_WANTED(&va, va_fsid); | |
1195 | VATTR_WANTED(&va, va_dirlinkcount); | |
1196 | VATTR_WANTED(&va, va_acl); | |
1197 | /* Provide defaults for attrs that may not be supported */ | |
1198 | va.va_dirlinkcount = 1; | |
1199 | va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE; | |
1200 | ||
0a7de745 A |
1201 | if (VNOP_GETATTR(sdvp, &va, kernelctx) != 0 || |
1202 | !VATTR_IS_SUPPORTED(&va, va_uid) || | |
1203 | !VATTR_IS_SUPPORTED(&va, va_gid) || | |
1204 | !VATTR_IS_SUPPORTED(&va, va_mode) || | |
2d21ac55 A |
1205 | !VATTR_IS_SUPPORTED(&va, va_fsid)) { |
1206 | goto baddir; | |
1207 | } | |
1208 | /* | |
0a7de745 A |
1209 | * Make sure its what we want: |
1210 | * - owned by root | |
2d21ac55 | 1211 | * - not writable by anyone |
39236c6e | 1212 | * - on same file system as /var/run |
2d21ac55 A |
1213 | * - not a hard-linked directory |
1214 | * - no ACLs (they might grant write access) | |
1215 | */ | |
1216 | if ((va.va_uid != 0) || (va.va_gid != 0) || | |
1217 | (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) || | |
1218 | (va.va_fsid != tmp_fsid) || | |
1219 | (va.va_dirlinkcount != 1) || | |
0a7de745 | 1220 | (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) { |
2d21ac55 A |
1221 | goto baddir; |
1222 | } | |
1223 | } | |
1224 | } | |
1225 | out: | |
1226 | if (dvp) { | |
1227 | vnode_put(dvp); | |
1228 | } | |
1229 | if (error) { | |
1230 | /* On errors, clean up shadow stream directory. */ | |
1231 | if (sdvp) { | |
1232 | vnode_put(sdvp); | |
1233 | sdvp = NULLVP; | |
1234 | } | |
1235 | } | |
1236 | *sdvpp = sdvp; | |
0a7de745 | 1237 | return error; |
2d21ac55 A |
1238 | |
1239 | baddir: | |
1240 | /* This is not the dir we're looking for, move along */ | |
1241 | ++shadow_sequence; /* try something else next time */ | |
1242 | error = ENOTDIR; | |
1243 | goto out; | |
1244 | } | |
39236c6e | 1245 | #endif /* NAMEDSTREAMS */ |
2d21ac55 A |
1246 | |
1247 | ||
39236c6e | 1248 | #if CONFIG_APPLEDOUBLE |
91447636 | 1249 | /* |
0a7de745 | 1250 | * Default Implementation (Non-native EA) |
91447636 A |
1251 | */ |
1252 | ||
1253 | ||
1254 | /* | |
0a7de745 A |
1255 | * Typical "._" AppleDouble Header File layout: |
1256 | * ------------------------------------------------------------ | |
1257 | * MAGIC 0x00051607 | |
1258 | * VERSION 0x00020000 | |
1259 | * FILLER 0 | |
1260 | * COUNT 2 | |
1261 | * .-- AD ENTRY[0] Finder Info Entry (must be first) | |
1262 | * .--+-- AD ENTRY[1] Resource Fork Entry (must be last) | |
1263 | * | '-> FINDER INFO | |
1264 | * | ///////////// Fixed Size Data (32 bytes) | |
1265 | * | EXT ATTR HDR | |
1266 | * | ///////////// | |
1267 | * | ATTR ENTRY[0] --. | |
1268 | * | ATTR ENTRY[1] --+--. | |
1269 | * | ATTR ENTRY[2] --+--+--. | |
1270 | * | ... | | | | |
1271 | * | ATTR ENTRY[N] --+--+--+--. | |
1272 | * | ATTR DATA 0 <-' | | | | |
1273 | * | //////////// | | | | |
1274 | * | ATTR DATA 1 <----' | | | |
1275 | * | ///////////// | | | |
1276 | * | ATTR DATA 2 <-------' | | |
1277 | * | ///////////// | | |
1278 | * | ... | | |
1279 | * | ATTR DATA N <----------' | |
1280 | * | ///////////// | |
1281 | * | Attribute Free Space | |
1282 | * | | |
1283 | * '----> RESOURCE FORK | |
1284 | * ///////////// Variable Sized Data | |
1285 | * ///////////// | |
1286 | * ///////////// | |
1287 | * ///////////// | |
1288 | * ///////////// | |
1289 | * ///////////// | |
1290 | * ... | |
1291 | * ///////////// | |
1292 | * | |
1293 | * ------------------------------------------------------------ | |
1294 | * | |
1295 | * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are | |
1296 | * stored as part of the Finder Info. The length in the Finder | |
1297 | * Info AppleDouble entry includes the length of the extended | |
1298 | * attribute header, attribute entries, and attribute data. | |
1299 | */ | |
91447636 A |
1300 | |
1301 | /* | |
1302 | * On Disk Data Structures | |
1303 | * | |
1304 | * Note: Motorola 68K alignment and big-endian. | |
1305 | * | |
1306 | * See RFC 1740 for additional information about the AppleDouble file format. | |
1307 | * | |
1308 | */ | |
1309 | ||
1310 | #define ADH_MAGIC 0x00051607 | |
1311 | #define ADH_VERSION 0x00020000 | |
1312 | #define ADH_MACOSX "Mac OS X " | |
1313 | ||
1314 | /* | |
1315 | * AppleDouble Entry ID's | |
1316 | */ | |
1317 | #define AD_DATA 1 /* Data fork */ | |
1318 | #define AD_RESOURCE 2 /* Resource fork */ | |
0a7de745 | 1319 | #define AD_REALNAME 3 /* File's name on home file system */ |
91447636 A |
1320 | #define AD_COMMENT 4 /* Standard Mac comment */ |
1321 | #define AD_ICONBW 5 /* Mac black & white icon */ | |
1322 | #define AD_ICONCOLOR 6 /* Mac color icon */ | |
1323 | #define AD_UNUSED 7 /* Not used */ | |
1324 | #define AD_FILEDATES 8 /* File dates; create, modify, etc */ | |
1325 | #define AD_FINDERINFO 9 /* Mac Finder info & extended info */ | |
1326 | #define AD_MACINFO 10 /* Mac file info, attributes, etc */ | |
1327 | #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */ | |
1328 | #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */ | |
1329 | #define AD_AFPNAME 13 /* Short name on AFP server */ | |
1330 | #define AD_AFPINFO 14 /* AFP file info, attrib., etc */ | |
0a7de745 | 1331 | #define AD_AFPDIRID 15 /* AFP directory ID */ |
91447636 A |
1332 | #define AD_ATTRIBUTES AD_FINDERINFO |
1333 | ||
1334 | ||
1335 | #define ATTR_FILE_PREFIX "._" | |
1336 | #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */ | |
1337 | ||
1338 | #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */ | |
1339 | ||
1340 | /* Implementation Limits */ | |
6d2010ae | 1341 | #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE |
91447636 A |
1342 | #define ATTR_MAX_HDR_SIZE 65536 |
1343 | /* | |
1344 | * Note: ATTR_MAX_HDR_SIZE is the largest attribute header | |
1345 | * size supported (including the attribute entries). All of | |
1346 | * the attribute entries must reside within this limit. If | |
1347 | * any of the attribute data crosses the ATTR_MAX_HDR_SIZE | |
1348 | * boundry, then all of the attribute data I/O is performed | |
6601e61a A |
1349 | * separately from the attribute header I/O. |
1350 | * | |
1351 | * In particular, all of the attr_entry structures must lie | |
1352 | * completely within the first ATTR_MAX_HDR_SIZE bytes of the | |
1353 | * AppleDouble file. However, the attribute data (i.e. the | |
1354 | * contents of the extended attributes) may extend beyond the | |
1355 | * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this | |
1356 | * limit is to allow the implementation to optimize by reading | |
1357 | * the first ATTR_MAX_HDR_SIZE bytes of the file. | |
91447636 A |
1358 | */ |
1359 | ||
1360 | ||
0a7de745 | 1361 | #define FINDERINFOSIZE 32 |
91447636 A |
1362 | |
1363 | typedef struct apple_double_entry { | |
0a7de745 | 1364 | u_int32_t type; /* entry type: see list, 0 invalid */ |
91447636 | 1365 | u_int32_t offset; /* entry data offset from the beginning of the file. */ |
0a7de745 | 1366 | u_int32_t length; /* entry data length in bytes. */ |
b0d623f7 | 1367 | } __attribute__((aligned(2), packed)) apple_double_entry_t; |
91447636 A |
1368 | |
1369 | ||
1370 | typedef struct apple_double_header { | |
1371 | u_int32_t magic; /* == ADH_MAGIC */ | |
0a7de745 | 1372 | u_int32_t version; /* format version: 2 = 0x00020000 */ |
91447636 | 1373 | u_int32_t filler[4]; |
0a7de745 | 1374 | u_int16_t numEntries; /* number of entries which follow */ |
91447636 A |
1375 | apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */ |
1376 | u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */ | |
1377 | u_int8_t pad[2]; /* get better alignment inside attr_header */ | |
b0d623f7 | 1378 | } __attribute__((aligned(2), packed)) apple_double_header_t; |
91447636 A |
1379 | |
1380 | #define ADHDRSIZE (4+4+16+2) | |
1381 | ||
1382 | /* Entries are aligned on 4 byte boundaries */ | |
1383 | typedef struct attr_entry { | |
1384 | u_int32_t offset; /* file offset to data */ | |
1385 | u_int32_t length; /* size of attribute data */ | |
1386 | u_int16_t flags; | |
1387 | u_int8_t namelen; | |
1388 | u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */ | |
b0d623f7 | 1389 | } __attribute__((aligned(2), packed)) attr_entry_t; |
91447636 A |
1390 | |
1391 | ||
2d21ac55 | 1392 | /* Header + entries must fit into 64K. Data may extend beyond 64K. */ |
91447636 A |
1393 | typedef struct attr_header { |
1394 | apple_double_header_t appledouble; | |
1395 | u_int32_t magic; /* == ATTR_HDR_MAGIC */ | |
1396 | u_int32_t debug_tag; /* for debugging == file id of owning file */ | |
0a7de745 | 1397 | u_int32_t total_size; /* file offset of end of attribute header + entries + data */ |
91447636 A |
1398 | u_int32_t data_start; /* file offset to attribute data area */ |
1399 | u_int32_t data_length; /* length of attribute data area */ | |
1400 | u_int32_t reserved[3]; | |
1401 | u_int16_t flags; | |
1402 | u_int16_t num_attrs; | |
b0d623f7 | 1403 | } __attribute__((aligned(2), packed)) attr_header_t; |
91447636 A |
1404 | |
1405 | ||
1406 | /* Empty Resource Fork Header */ | |
1407 | typedef struct rsrcfork_header { | |
1408 | u_int32_t fh_DataOffset; | |
1409 | u_int32_t fh_MapOffset; | |
1410 | u_int32_t fh_DataLength; | |
1411 | u_int32_t fh_MapLength; | |
1412 | u_int8_t systemData[112]; | |
1413 | u_int8_t appData[128]; | |
1414 | u_int32_t mh_DataOffset; | |
1415 | u_int32_t mh_MapOffset; | |
1416 | u_int32_t mh_DataLength; | |
1417 | u_int32_t mh_MapLength; | |
1418 | u_int32_t mh_Next; | |
1419 | u_int16_t mh_RefNum; | |
1420 | u_int8_t mh_Attr; | |
1421 | u_int8_t mh_InMemoryAttr; | |
1422 | u_int16_t mh_Types; | |
1423 | u_int16_t mh_Names; | |
1424 | u_int16_t typeCount; | |
b0d623f7 | 1425 | } __attribute__((aligned(2), packed)) rsrcfork_header_t; |
91447636 A |
1426 | |
1427 | #define RF_FIRST_RESOURCE 256 | |
1428 | #define RF_NULL_MAP_LENGTH 30 | |
1429 | #define RF_EMPTY_TAG "This resource fork intentionally left blank " | |
1430 | ||
91447636 A |
1431 | /* Runtime information about the attribute file. */ |
1432 | typedef struct attr_info { | |
1433 | vfs_context_t context; | |
1434 | vnode_t filevp; | |
1435 | size_t filesize; | |
1436 | size_t iosize; | |
1437 | u_int8_t *rawdata; | |
2d21ac55 | 1438 | size_t rawsize; /* minimum of filesize or ATTR_MAX_HDR_SIZE */ |
91447636 A |
1439 | apple_double_header_t *filehdr; |
1440 | apple_double_entry_t *finderinfo; | |
1441 | apple_double_entry_t *rsrcfork; | |
1442 | attr_header_t *attrhdr; | |
1443 | attr_entry_t *attr_entry; | |
1444 | u_int8_t readonly; | |
1445 | u_int8_t emptyfinderinfo; | |
1446 | } attr_info_t; | |
1447 | ||
1448 | ||
1449 | #define ATTR_SETTING 1 | |
1450 | ||
1451 | #define ATTR_ALIGN 3L /* Use four-byte alignment */ | |
1452 | ||
1453 | #define ATTR_ENTRY_LENGTH(namelen) \ | |
0a7de745 | 1454 | ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) |
91447636 A |
1455 | |
1456 | #define ATTR_NEXT(ae) \ | |
1457 | (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) | |
1458 | ||
1459 | #define ATTR_VALID(ae, ai) \ | |
1460 | ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize)) | |
1461 | ||
4452a7af A |
1462 | #define SWAP16(x) OSSwapBigToHostInt16((x)) |
1463 | #define SWAP32(x) OSSwapBigToHostInt32((x)) | |
1464 | #define SWAP64(x) OSSwapBigToHostInt64((x)) | |
91447636 A |
1465 | |
1466 | ||
1467 | static u_int32_t emptyfinfo[8] = {0}; | |
1468 | ||
1469 | ||
1470 | /* | |
1471 | * Local support routines | |
1472 | */ | |
1473 | static void close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context); | |
1474 | ||
1475 | static int open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context); | |
1476 | ||
1477 | static int create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context); | |
1478 | ||
1479 | static int remove_xattrfile(vnode_t xvp, vfs_context_t context); | |
1480 | ||
1481 | static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context); | |
1482 | ||
1483 | static void rel_xattrinfo(attr_info_t *ainfop); | |
1484 | ||
1485 | static int write_xattrinfo(attr_info_t *ainfop); | |
1486 | ||
1487 | static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr); | |
1488 | ||
1489 | static int lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context); | |
1490 | ||
1491 | static int unlock_xattrfile(vnode_t xvp, vfs_context_t context); | |
1492 | ||
1493 | ||
1494 | #if BYTE_ORDER == LITTLE_ENDIAN | |
0a7de745 A |
1495 | static void swap_adhdr(apple_double_header_t *adh); |
1496 | static void swap_attrhdr(attr_header_t *ah, attr_info_t* info); | |
91447636 A |
1497 | |
1498 | #else | |
1499 | #define swap_adhdr(x) | |
2d21ac55 | 1500 | #define swap_attrhdr(x, y) |
91447636 A |
1501 | #endif |
1502 | ||
6601e61a | 1503 | static int check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop); |
91447636 A |
1504 | static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context); |
1505 | static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context); | |
2d21ac55 A |
1506 | |
1507 | ||
6601e61a A |
1508 | /* |
1509 | * Sanity check and swap the header of an AppleDouble file. Assumes the buffer | |
1510 | * is in big endian (as it would exist on disk). Verifies the following: | |
1511 | * - magic field | |
1512 | * - version field | |
1513 | * - number of entries | |
1514 | * - that each entry fits within the file size | |
1515 | * | |
1516 | * If the header is invalid, ENOATTR is returned. | |
1517 | * | |
1518 | * NOTE: Does not attempt to validate the extended attributes header that | |
1519 | * may be embedded in the Finder Info entry. | |
1520 | */ | |
0a7de745 A |
1521 | static int |
1522 | check_and_swap_apple_double_header(attr_info_t *ainfop) | |
6601e61a A |
1523 | { |
1524 | int i, j; | |
1525 | u_int32_t header_end; | |
1526 | u_int32_t entry_end; | |
1527 | size_t rawsize; | |
1528 | apple_double_header_t *header; | |
0a7de745 | 1529 | |
6601e61a A |
1530 | rawsize = ainfop->rawsize; |
1531 | header = (apple_double_header_t *) ainfop->rawdata; | |
0a7de745 | 1532 | |
6601e61a | 1533 | /* Is the file big enough to contain an AppleDouble header? */ |
0a7de745 | 1534 | if (rawsize < offsetof(apple_double_header_t, entries)) { |
6601e61a | 1535 | return ENOATTR; |
0a7de745 A |
1536 | } |
1537 | ||
6601e61a A |
1538 | /* Swap the AppleDouble header fields to native order */ |
1539 | header->magic = SWAP32(header->magic); | |
1540 | header->version = SWAP32(header->version); | |
1541 | header->numEntries = SWAP16(header->numEntries); | |
0a7de745 | 1542 | |
6601e61a A |
1543 | /* Sanity check the AppleDouble header fields */ |
1544 | if (header->magic != ADH_MAGIC || | |
2d21ac55 A |
1545 | header->version != ADH_VERSION || |
1546 | header->numEntries < 1 || | |
1547 | header->numEntries > 15) { | |
6601e61a A |
1548 | return ENOATTR; |
1549 | } | |
0a7de745 | 1550 | |
6601e61a A |
1551 | /* Calculate where the entries[] array ends */ |
1552 | header_end = offsetof(apple_double_header_t, entries) + | |
0a7de745 A |
1553 | header->numEntries * sizeof(apple_double_entry_t); |
1554 | ||
6601e61a A |
1555 | /* Is the file big enough to contain the AppleDouble entries? */ |
1556 | if (rawsize < header_end) { | |
0a7de745 | 1557 | return ENOATTR; |
6601e61a | 1558 | } |
0a7de745 | 1559 | |
6601e61a | 1560 | /* Swap and sanity check each AppleDouble entry */ |
0a7de745 | 1561 | for (i = 0; i < header->numEntries; i++) { |
6601e61a A |
1562 | /* Swap the per-entry fields to native order */ |
1563 | header->entries[i].type = SWAP32(header->entries[i].type); | |
1564 | header->entries[i].offset = SWAP32(header->entries[i].offset); | |
1565 | header->entries[i].length = SWAP32(header->entries[i].length); | |
0a7de745 | 1566 | |
6601e61a | 1567 | entry_end = header->entries[i].offset + header->entries[i].length; |
0a7de745 | 1568 | |
6601e61a A |
1569 | /* |
1570 | * Does the entry's content start within the header itself, | |
1571 | * did the addition overflow, or does the entry's content | |
1572 | * extend past the end of the file? | |
1573 | */ | |
1574 | if (header->entries[i].offset < header_end || | |
0a7de745 | 1575 | entry_end < header->entries[i].offset || |
2d21ac55 | 1576 | entry_end > ainfop->filesize) { |
6601e61a A |
1577 | return ENOATTR; |
1578 | } | |
0a7de745 | 1579 | |
6601e61a A |
1580 | /* |
1581 | * Does the current entry's content overlap with a previous | |
1582 | * entry's content? | |
1583 | * | |
1584 | * Yes, this is O(N**2), and there are more efficient algorithms | |
1585 | * for testing pairwise overlap of N ranges when N is large. | |
1586 | * But we have already ensured N < 16, and N is almost always 2. | |
1587 | * So there's no point in using a more complex algorithm. | |
1588 | */ | |
0a7de745 A |
1589 | |
1590 | for (j = 0; j < i; j++) { | |
6601e61a | 1591 | if (entry_end > header->entries[j].offset && |
2d21ac55 | 1592 | header->entries[j].offset + header->entries[j].length > header->entries[i].offset) { |
6601e61a A |
1593 | return ENOATTR; |
1594 | } | |
1595 | } | |
1596 | } | |
0a7de745 | 1597 | |
6601e61a A |
1598 | return 0; |
1599 | } | |
91447636 | 1600 | |
2d21ac55 A |
1601 | |
1602 | ||
91447636 A |
1603 | /* |
1604 | * Retrieve the data of an extended attribute. | |
1605 | */ | |
39236c6e | 1606 | static int |
91447636 | 1607 | default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, |
0a7de745 | 1608 | __unused int options, vfs_context_t context) |
91447636 A |
1609 | { |
1610 | vnode_t xvp = NULL; | |
1611 | attr_info_t ainfo; | |
1612 | attr_header_t *header; | |
1613 | attr_entry_t *entry; | |
1614 | u_int8_t *attrdata; | |
f427ee49 A |
1615 | u_int32_t datalen; |
1616 | size_t namelen; | |
91447636 A |
1617 | int isrsrcfork; |
1618 | int fileflags; | |
1619 | int i; | |
1620 | int error; | |
1621 | ||
1622 | fileflags = FREAD; | |
f427ee49 | 1623 | if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { |
91447636 A |
1624 | isrsrcfork = 1; |
1625 | /* | |
1626 | * Open the file locked (shared) since the Carbon | |
1627 | * File Manager may have the Apple Double file open | |
1628 | * and could be changing the resource fork. | |
1629 | */ | |
1630 | fileflags |= O_SHLOCK; | |
1631 | } else { | |
1632 | isrsrcfork = 0; | |
1633 | } | |
1634 | ||
1635 | if ((error = open_xattrfile(vp, fileflags, &xvp, context))) { | |
0a7de745 | 1636 | return error; |
91447636 A |
1637 | } |
1638 | if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { | |
1639 | close_xattrfile(xvp, fileflags, context); | |
0a7de745 | 1640 | return error; |
91447636 A |
1641 | } |
1642 | ||
1643 | /* Get the Finder Info. */ | |
f427ee49 | 1644 | if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { |
91447636 A |
1645 | if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) { |
1646 | error = ENOATTR; | |
1647 | } else if (uio == NULL) { | |
1648 | *size = FINDERINFOSIZE; | |
1649 | error = 0; | |
1650 | } else if (uio_offset(uio) != 0) { | |
1651 | error = EINVAL; | |
1652 | } else if (uio_resid(uio) < FINDERINFOSIZE) { | |
1653 | error = ERANGE; | |
1654 | } else { | |
1655 | attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset; | |
1656 | error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio); | |
1657 | } | |
1658 | goto out; | |
1659 | } | |
1660 | ||
1661 | /* Read the Resource Fork. */ | |
1662 | if (isrsrcfork) { | |
1663 | if (!vnode_isreg(vp)) { | |
1664 | error = EPERM; | |
1665 | } else if (ainfo.rsrcfork == NULL) { | |
1666 | error = ENOATTR; | |
1667 | } else if (uio == NULL) { | |
1668 | *size = (size_t)ainfo.rsrcfork->length; | |
1669 | } else { | |
1670 | uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); | |
1671 | error = VNOP_READ(xvp, uio, 0, context); | |
0a7de745 | 1672 | if (error == 0) { |
91447636 | 1673 | uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset); |
0a7de745 | 1674 | } |
91447636 A |
1675 | } |
1676 | goto out; | |
1677 | } | |
0a7de745 | 1678 | |
91447636 A |
1679 | if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) { |
1680 | error = ENOATTR; | |
1681 | goto out; | |
1682 | } | |
1683 | if (uio_offset(uio) != 0) { | |
1684 | error = EINVAL; | |
1685 | goto out; | |
1686 | } | |
1687 | error = ENOATTR; | |
1688 | namelen = strlen(name) + 1; | |
1689 | header = ainfo.attrhdr; | |
1690 | entry = ainfo.attr_entry; | |
1691 | /* | |
1692 | * Search for attribute name in the header. | |
1693 | */ | |
1694 | for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { | |
2d21ac55 | 1695 | if (strncmp((const char *)entry->name, name, namelen) == 0) { |
f427ee49 | 1696 | datalen = entry->length; |
91447636 A |
1697 | if (uio == NULL) { |
1698 | *size = datalen; | |
1699 | error = 0; | |
1700 | break; | |
1701 | } | |
2d21ac55 | 1702 | if (uio_resid(uio) < (user_ssize_t)datalen) { |
91447636 A |
1703 | error = ERANGE; |
1704 | break; | |
1705 | } | |
1706 | if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) { | |
1707 | attrdata = ((u_int8_t *)header + entry->offset); | |
1708 | error = uiomove((caddr_t)attrdata, datalen, uio); | |
1709 | } else { | |
1710 | uio_setoffset(uio, entry->offset); | |
1711 | error = VNOP_READ(xvp, uio, 0, context); | |
1712 | uio_setoffset(uio, 0); | |
1713 | } | |
1714 | break; | |
1715 | } | |
1716 | entry = ATTR_NEXT(entry); | |
1717 | } | |
0a7de745 | 1718 | out: |
91447636 A |
1719 | rel_xattrinfo(&ainfo); |
1720 | close_xattrfile(xvp, fileflags, context); | |
1721 | ||
0a7de745 | 1722 | return error; |
91447636 A |
1723 | } |
1724 | ||
1725 | /* | |
1726 | * Set the data of an extended attribute. | |
1727 | */ | |
39236c6e | 1728 | static int |
91447636 A |
1729 | default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context) |
1730 | { | |
1731 | vnode_t xvp = NULL; | |
1732 | attr_info_t ainfo; | |
1733 | attr_header_t *header; | |
1734 | attr_entry_t *entry; | |
1735 | attr_entry_t *lastentry; | |
1736 | u_int8_t *attrdata; | |
1737 | size_t datalen; | |
1738 | size_t entrylen; | |
1739 | size_t datafreespace; | |
1740 | int namelen; | |
1741 | int found = 0; | |
1742 | int i; | |
1743 | int splitdata; | |
1744 | int fileflags; | |
1745 | int error; | |
2d21ac55 | 1746 | char finfo[FINDERINFOSIZE]; |
0a7de745 | 1747 | |
91447636 | 1748 | datalen = uio_resid(uio); |
f427ee49 A |
1749 | if (datalen > XATTR_MAXSIZE) { |
1750 | return EINVAL; | |
1751 | } | |
1752 | namelen = (int)strlen(name) + 1; | |
1753 | if (namelen > UINT8_MAX) { | |
1754 | return EINVAL; | |
1755 | } | |
91447636 A |
1756 | entrylen = ATTR_ENTRY_LENGTH(namelen); |
1757 | ||
2d21ac55 A |
1758 | /* |
1759 | * By convention, Finder Info that is all zeroes is equivalent to not | |
1760 | * having a Finder Info EA. So if we're trying to set the Finder Info | |
1761 | * to all zeroes, then delete it instead. If a file didn't have an | |
1762 | * AppleDouble file before, this prevents creating an AppleDouble file | |
1763 | * with no useful content. | |
1764 | * | |
1765 | * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check | |
1766 | * for all zeroes Finder Info before opening the AppleDouble file. | |
1767 | * But if either of those options were specified, we need to open the | |
1768 | * AppleDouble file to see whether there was already Finder Info (so we | |
1769 | * can return an error if needed); this case is handled further below. | |
1770 | * | |
1771 | * NOTE: this copies the Finder Info data into the "finfo" local. | |
1772 | */ | |
f427ee49 | 1773 | if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { |
2d21ac55 A |
1774 | /* |
1775 | * TODO: check the XATTR_CREATE and XATTR_REPLACE flags. | |
1776 | * That means we probably have to open_xattrfile and get_xattrinfo. | |
1777 | */ | |
1778 | if (uio_offset(uio) != 0 || datalen != FINDERINFOSIZE) { | |
1779 | return EINVAL; | |
1780 | } | |
f427ee49 | 1781 | error = uiomove(finfo, (int)datalen, uio); |
0a7de745 | 1782 | if (error) { |
2d21ac55 | 1783 | return error; |
0a7de745 A |
1784 | } |
1785 | if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 && | |
2d21ac55 A |
1786 | bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) { |
1787 | error = default_removexattr(vp, name, 0, context); | |
0a7de745 | 1788 | if (error == ENOATTR) { |
2d21ac55 | 1789 | error = 0; |
0a7de745 | 1790 | } |
2d21ac55 A |
1791 | return error; |
1792 | } | |
91447636 | 1793 | } |
0a7de745 | 1794 | |
91447636 A |
1795 | start: |
1796 | /* | |
1797 | * Open the file locked since setting an attribute | |
1798 | * can change the layout of the Apple Double file. | |
1799 | */ | |
1800 | fileflags = FREAD | FWRITE | O_EXLOCK; | |
1801 | if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xvp, context))) { | |
0a7de745 | 1802 | return error; |
91447636 A |
1803 | } |
1804 | if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) { | |
1805 | close_xattrfile(xvp, fileflags, context); | |
0a7de745 | 1806 | return error; |
91447636 A |
1807 | } |
1808 | ||
1809 | /* Set the Finder Info. */ | |
f427ee49 | 1810 | if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { |
91447636 A |
1811 | if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { |
1812 | /* attr exists and "create" was specified? */ | |
1813 | if (options & XATTR_CREATE) { | |
1814 | error = EEXIST; | |
1815 | goto out; | |
1816 | } | |
1817 | } else { | |
1818 | /* attr doesn't exists and "replace" was specified? */ | |
1819 | if (options & XATTR_REPLACE) { | |
1820 | error = ENOATTR; | |
1821 | goto out; | |
1822 | } | |
1823 | } | |
2d21ac55 A |
1824 | if (options != 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) { |
1825 | /* | |
1826 | * Setting the Finder Info to all zeroes is equivalent to | |
1827 | * removing it. Close the xattr file and let | |
1828 | * default_removexattr do the work (including deleting | |
1829 | * the xattr file if there are no other xattrs). | |
1830 | * | |
1831 | * Note that we have to handle the case where the | |
1832 | * Finder Info was already all zeroes, and we ignore | |
1833 | * ENOATTR. | |
1834 | * | |
1835 | * The common case where options == 0 was handled above. | |
1836 | */ | |
1837 | rel_xattrinfo(&ainfo); | |
1838 | close_xattrfile(xvp, fileflags, context); | |
1839 | error = default_removexattr(vp, name, 0, context); | |
0a7de745 | 1840 | if (error == ENOATTR) { |
2d21ac55 | 1841 | error = 0; |
0a7de745 | 1842 | } |
2d21ac55 | 1843 | return error; |
91447636 A |
1844 | } |
1845 | if (ainfo.finderinfo) { | |
1846 | attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset; | |
2d21ac55 | 1847 | bcopy(finfo, attrdata, datalen); |
91447636 A |
1848 | ainfo.iosize = sizeof(attr_header_t); |
1849 | error = write_xattrinfo(&ainfo); | |
1850 | goto out; | |
1851 | } | |
1852 | error = ENOATTR; | |
1853 | goto out; | |
1854 | } | |
1855 | ||
1856 | /* Write the Resource Fork. */ | |
f427ee49 A |
1857 | if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { |
1858 | off_t endoffset; | |
91447636 A |
1859 | |
1860 | if (!vnode_isreg(vp)) { | |
1861 | error = EPERM; | |
1862 | goto out; | |
1863 | } | |
39236c6e A |
1864 | /* Make sure we have a rsrc fork pointer.. */ |
1865 | if (ainfo.rsrcfork == NULL) { | |
1866 | error = ENOATTR; | |
1867 | goto out; | |
1868 | } | |
1869 | if (ainfo.rsrcfork) { | |
1870 | if (ainfo.rsrcfork->length != 0) { | |
1871 | if (options & XATTR_CREATE) { | |
1872 | /* attr exists, and create specified ? */ | |
1873 | error = EEXIST; | |
1874 | goto out; | |
0a7de745 A |
1875 | } |
1876 | } else { | |
39236c6e A |
1877 | /* Zero length AD rsrc fork */ |
1878 | if (options & XATTR_REPLACE) { | |
1879 | /* attr doesn't exist (0-length), but replace specified ? */ | |
1880 | error = ENOATTR; | |
1881 | goto out; | |
1882 | } | |
91447636 | 1883 | } |
0a7de745 | 1884 | } else { |
39236c6e A |
1885 | /* We can't do much if we somehow didn't get an AD rsrc pointer */ |
1886 | error = ENOATTR; | |
1887 | goto out; | |
1888 | } | |
1889 | ||
91447636 | 1890 | endoffset = uio_resid(uio) + uio_offset(uio); /* new size */ |
f427ee49 A |
1891 | if (endoffset > UINT32_MAX || endoffset < 0) { |
1892 | error = EINVAL; | |
1893 | goto out; | |
1894 | } | |
91447636 A |
1895 | uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); |
1896 | error = VNOP_WRITE(xvp, uio, 0, context); | |
0a7de745 | 1897 | if (error) { |
91447636 | 1898 | goto out; |
0a7de745 | 1899 | } |
91447636 A |
1900 | uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset); |
1901 | if (endoffset > ainfo.rsrcfork->length) { | |
f427ee49 | 1902 | ainfo.rsrcfork->length = (u_int32_t)endoffset; |
91447636 A |
1903 | ainfo.iosize = sizeof(attr_header_t); |
1904 | error = write_xattrinfo(&ainfo); | |
1905 | goto out; | |
1906 | } | |
1907 | goto out; | |
1908 | } | |
1909 | ||
2d21ac55 | 1910 | if (datalen > ATTR_MAX_SIZE) { |
0a7de745 | 1911 | return E2BIG; /* EINVAL instead ? */ |
2d21ac55 A |
1912 | } |
1913 | ||
91447636 A |
1914 | if (ainfo.attrhdr == NULL) { |
1915 | error = ENOATTR; | |
1916 | goto out; | |
1917 | } | |
1918 | header = ainfo.attrhdr; | |
1919 | entry = ainfo.attr_entry; | |
1920 | ||
1921 | /* Check if data area crosses the maximum header size. */ | |
0a7de745 | 1922 | if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) { |
91447636 | 1923 | splitdata = 1; /* do data I/O separately */ |
0a7de745 | 1924 | } else { |
91447636 | 1925 | splitdata = 0; |
0a7de745 A |
1926 | } |
1927 | ||
91447636 A |
1928 | /* |
1929 | * See if attribute already exists. | |
1930 | */ | |
1931 | for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { | |
2d21ac55 | 1932 | if (strncmp((const char *)entry->name, name, namelen) == 0) { |
91447636 A |
1933 | found = 1; |
1934 | break; | |
1935 | } | |
1936 | entry = ATTR_NEXT(entry); | |
1937 | } | |
1938 | ||
1939 | if (found) { | |
1940 | if (options & XATTR_CREATE) { | |
1941 | error = EEXIST; | |
1942 | goto out; | |
1943 | } | |
1944 | if (datalen == entry->length) { | |
1945 | if (splitdata) { | |
1946 | uio_setoffset(uio, entry->offset); | |
1947 | error = VNOP_WRITE(xvp, uio, 0, context); | |
1948 | uio_setoffset(uio, 0); | |
1949 | if (error) { | |
1950 | printf("setxattr: VNOP_WRITE error %d\n", error); | |
1951 | } | |
1952 | } else { | |
1953 | attrdata = (u_int8_t *)header + entry->offset; | |
f427ee49 | 1954 | error = uiomove((caddr_t)attrdata, (int)datalen, uio); |
0a7de745 | 1955 | if (error) { |
91447636 | 1956 | goto out; |
0a7de745 | 1957 | } |
91447636 A |
1958 | ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; |
1959 | error = write_xattrinfo(&ainfo); | |
1960 | if (error) { | |
1961 | printf("setxattr: write_xattrinfo error %d\n", error); | |
1962 | } | |
1963 | } | |
1964 | goto out; | |
1965 | } else { | |
1966 | /* | |
1967 | * Brute force approach - just remove old entry and set new entry. | |
1968 | */ | |
1969 | found = 0; | |
1970 | rel_xattrinfo(&ainfo); | |
1971 | close_xattrfile(xvp, fileflags, context); | |
1972 | error = default_removexattr(vp, name, options, context); | |
1973 | if (error) { | |
0a7de745 | 1974 | return error; |
91447636 | 1975 | } |
2d21ac55 A |
1976 | /* Clear XATTR_REPLACE option since we just removed the attribute. */ |
1977 | options &= ~XATTR_REPLACE; | |
91447636 A |
1978 | goto start; /* start over */ |
1979 | } | |
91447636 A |
1980 | } |
1981 | ||
1982 | if (options & XATTR_REPLACE) { | |
1983 | error = ENOATTR; /* nothing there to replace */ | |
1984 | goto out; | |
1985 | } | |
1986 | /* Check if header size limit has been reached. */ | |
1987 | if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) { | |
1988 | error = ENOSPC; | |
1989 | goto out; | |
1990 | } | |
1991 | ||
1992 | datafreespace = header->total_size - (header->data_start + header->data_length); | |
1993 | ||
1994 | /* Check if we need more space. */ | |
1995 | if ((datalen + entrylen) > datafreespace) { | |
1996 | size_t growsize; | |
1997 | ||
1998 | growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE); | |
0a7de745 | 1999 | |
91447636 A |
2000 | /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */ |
2001 | if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) { | |
2002 | growsize = ATTR_MAX_HDR_SIZE - header->total_size; | |
2003 | } | |
2004 | ||
2005 | ainfo.filesize += growsize; | |
2006 | error = vnode_setsize(xvp, ainfo.filesize, 0, context); | |
2007 | if (error) { | |
2008 | printf("setxattr: VNOP_TRUNCATE error %d\n", error); | |
2009 | } | |
0a7de745 | 2010 | if (error) { |
91447636 | 2011 | goto out; |
0a7de745 | 2012 | } |
91447636 A |
2013 | |
2014 | /* | |
2015 | * Move the resource fork out of the way. | |
2016 | */ | |
2017 | if (ainfo.rsrcfork) { | |
2018 | if (ainfo.rsrcfork->length != 0) { | |
2019 | shift_data_down(xvp, | |
0a7de745 A |
2020 | ainfo.rsrcfork->offset, |
2021 | ainfo.rsrcfork->length, | |
2022 | growsize, context); | |
91447636 A |
2023 | } |
2024 | ainfo.rsrcfork->offset += growsize; | |
2025 | } | |
2026 | ainfo.finderinfo->length += growsize; | |
2027 | header->total_size += growsize; | |
2028 | } | |
2029 | ||
2030 | /* Make space for a new entry. */ | |
2031 | if (splitdata) { | |
2032 | shift_data_down(xvp, | |
0a7de745 A |
2033 | header->data_start, |
2034 | header->data_length, | |
2035 | entrylen, context); | |
91447636 A |
2036 | } else { |
2037 | bcopy((u_int8_t *)header + header->data_start, | |
0a7de745 A |
2038 | (u_int8_t *)header + header->data_start + entrylen, |
2039 | header->data_length); | |
91447636 A |
2040 | } |
2041 | header->data_start += entrylen; | |
2042 | ||
2043 | /* Fix up entry data offsets. */ | |
2044 | lastentry = entry; | |
2045 | for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) { | |
2046 | entry->offset += entrylen; | |
2047 | } | |
0a7de745 | 2048 | |
91447636 A |
2049 | /* |
2050 | * If the attribute data area is entirely within | |
2051 | * the header buffer, then just update the buffer, | |
2052 | * otherwise we'll write it separately to the file. | |
2053 | */ | |
2054 | if (splitdata) { | |
2055 | off_t offset; | |
2056 | ||
2057 | /* Write new attribute data after the end of existing data. */ | |
2058 | offset = header->data_start + header->data_length; | |
2059 | uio_setoffset(uio, offset); | |
2060 | error = VNOP_WRITE(xvp, uio, 0, context); | |
2061 | uio_setoffset(uio, 0); | |
2062 | if (error) { | |
2063 | printf("setxattr: VNOP_WRITE error %d\n", error); | |
2064 | goto out; | |
2065 | } | |
2066 | } else { | |
2067 | attrdata = (u_int8_t *)header + header->data_start + header->data_length; | |
0a7de745 | 2068 | |
f427ee49 | 2069 | error = uiomove((caddr_t)attrdata, (int)datalen, uio); |
91447636 A |
2070 | if (error) { |
2071 | printf("setxattr: uiomove error %d\n", error); | |
2072 | goto out; | |
2073 | } | |
2074 | } | |
2075 | ||
2076 | /* Create the attribute entry. */ | |
f427ee49 | 2077 | lastentry->length = (u_int32_t)datalen; |
91447636 | 2078 | lastentry->offset = header->data_start + header->data_length; |
f427ee49 | 2079 | lastentry->namelen = (u_int8_t)namelen; |
91447636 A |
2080 | lastentry->flags = 0; |
2081 | bcopy(name, &lastentry->name[0], namelen); | |
2082 | ||
2083 | /* Update the attributes header. */ | |
2084 | header->num_attrs++; | |
2085 | header->data_length += datalen; | |
2086 | ||
2087 | if (splitdata) { | |
2088 | /* Only write the entries, since the data was written separately. */ | |
2089 | ainfo.iosize = ainfo.attrhdr->data_start; | |
2090 | } else { | |
0a7de745 | 2091 | /* The entry and data are both in the header; write them together. */ |
91447636 A |
2092 | ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; |
2093 | } | |
2094 | error = write_xattrinfo(&ainfo); | |
2095 | if (error) { | |
2096 | printf("setxattr: write_xattrinfo error %d\n", error); | |
2097 | } | |
2098 | ||
0a7de745 | 2099 | out: |
91447636 A |
2100 | rel_xattrinfo(&ainfo); |
2101 | close_xattrfile(xvp, fileflags, context); | |
2102 | ||
2103 | /* Touch the change time if we changed an attribute. */ | |
2104 | if (error == 0) { | |
2105 | struct vnode_attr va; | |
2106 | ||
2107 | /* Re-write the mtime to cause a ctime change. */ | |
2108 | VATTR_INIT(&va); | |
2109 | VATTR_WANTED(&va, va_modify_time); | |
2110 | if (vnode_getattr(vp, &va, context) == 0) { | |
2111 | VATTR_INIT(&va); | |
2112 | VATTR_SET(&va, va_modify_time, va.va_modify_time); | |
2113 | (void) vnode_setattr(vp, &va, context); | |
2114 | } | |
2115 | } | |
0a7de745 | 2116 | |
b0d623f7 A |
2117 | post_event_if_success(vp, error, NOTE_ATTRIB); |
2118 | ||
0a7de745 | 2119 | return error; |
91447636 A |
2120 | } |
2121 | ||
2122 | ||
2123 | /* | |
2124 | * Remove an extended attribute. | |
2125 | */ | |
39236c6e | 2126 | static int |
91447636 A |
2127 | default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_context_t context) |
2128 | { | |
2129 | vnode_t xvp = NULL; | |
2130 | attr_info_t ainfo; | |
2131 | attr_header_t *header; | |
2132 | attr_entry_t *entry; | |
2133 | attr_entry_t *oldslot; | |
2134 | u_int8_t *attrdata; | |
2135 | u_int32_t dataoff; | |
2136 | size_t datalen; | |
2137 | size_t entrylen; | |
2138 | int namelen; | |
2139 | int found = 0, lastone = 0; | |
2140 | int i; | |
2141 | int splitdata; | |
2142 | int attrcount = 0; | |
2143 | int isrsrcfork; | |
2144 | int fileflags; | |
2145 | int error; | |
2146 | ||
2147 | fileflags = FREAD | FWRITE; | |
5ba3f43e | 2148 | if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { |
91447636 A |
2149 | isrsrcfork = 1; |
2150 | /* | |
2151 | * Open the file locked (exclusive) since the Carbon | |
2152 | * File Manager may have the Apple Double file open | |
2153 | * and could be changing the resource fork. | |
2154 | */ | |
2155 | fileflags |= O_EXLOCK; | |
2156 | } else { | |
2157 | isrsrcfork = 0; | |
2158 | } | |
2159 | ||
2160 | if ((error = open_xattrfile(vp, fileflags, &xvp, context))) { | |
0a7de745 | 2161 | return error; |
91447636 A |
2162 | } |
2163 | if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { | |
2164 | close_xattrfile(xvp, fileflags, context); | |
0a7de745 | 2165 | return error; |
91447636 | 2166 | } |
0a7de745 | 2167 | if (ainfo.attrhdr) { |
91447636 | 2168 | attrcount += ainfo.attrhdr->num_attrs; |
0a7de745 A |
2169 | } |
2170 | if (ainfo.rsrcfork) { | |
91447636 | 2171 | ++attrcount; |
0a7de745 A |
2172 | } |
2173 | if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { | |
91447636 | 2174 | ++attrcount; |
0a7de745 | 2175 | } |
91447636 A |
2176 | |
2177 | /* Clear the Finder Info. */ | |
5ba3f43e | 2178 | if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { |
91447636 A |
2179 | if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) { |
2180 | error = ENOATTR; | |
2181 | goto out; | |
2182 | } | |
2183 | /* On removal of last attribute the ._ file is removed. */ | |
0a7de745 | 2184 | if (--attrcount == 0) { |
91447636 | 2185 | goto out; |
0a7de745 | 2186 | } |
91447636 A |
2187 | attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset; |
2188 | bzero((caddr_t)attrdata, FINDERINFOSIZE); | |
2189 | ainfo.iosize = sizeof(attr_header_t); | |
2190 | error = write_xattrinfo(&ainfo); | |
2191 | goto out; | |
2192 | } | |
2193 | ||
2194 | /* Clear the Resource Fork. */ | |
2195 | if (isrsrcfork) { | |
2196 | if (!vnode_isreg(vp)) { | |
2197 | error = EPERM; | |
2198 | goto out; | |
2199 | } | |
2200 | if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) { | |
2201 | error = ENOATTR; | |
2202 | goto out; | |
2203 | } | |
2204 | /* On removal of last attribute the ._ file is removed. */ | |
0a7de745 | 2205 | if (--attrcount == 0) { |
91447636 | 2206 | goto out; |
0a7de745 | 2207 | } |
91447636 A |
2208 | /* |
2209 | * XXX | |
2210 | * If the resource fork isn't the last AppleDouble | |
2211 | * entry then the space needs to be reclaimed by | |
2212 | * shifting the entries after the resource fork. | |
2213 | */ | |
2214 | if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) { | |
2215 | ainfo.filesize -= ainfo.rsrcfork->length; | |
2216 | error = vnode_setsize(xvp, ainfo.filesize, 0, context); | |
2217 | } | |
2218 | if (error == 0) { | |
2219 | ainfo.rsrcfork->length = 0; | |
2220 | ainfo.iosize = sizeof(attr_header_t); | |
2221 | error = write_xattrinfo(&ainfo); | |
2222 | } | |
2223 | goto out; | |
2224 | } | |
2225 | ||
2226 | if (ainfo.attrhdr == NULL) { | |
2227 | error = ENOATTR; | |
2228 | goto out; | |
2229 | } | |
f427ee49 | 2230 | namelen = (int)strlen(name) + 1; |
91447636 A |
2231 | header = ainfo.attrhdr; |
2232 | entry = ainfo.attr_entry; | |
2233 | ||
2234 | /* | |
2235 | * See if this attribute exists. | |
2236 | */ | |
2237 | for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { | |
2d21ac55 | 2238 | if (strncmp((const char *)entry->name, name, namelen) == 0) { |
91447636 | 2239 | found = 1; |
0a7de745 | 2240 | if ((i + 1) == header->num_attrs) { |
91447636 | 2241 | lastone = 1; |
0a7de745 | 2242 | } |
91447636 A |
2243 | break; |
2244 | } | |
2245 | entry = ATTR_NEXT(entry); | |
2246 | } | |
2247 | if (!found) { | |
2248 | error = ENOATTR; | |
2249 | goto out; | |
2250 | } | |
2251 | /* On removal of last attribute the ._ file is removed. */ | |
0a7de745 | 2252 | if (--attrcount == 0) { |
91447636 | 2253 | goto out; |
0a7de745 | 2254 | } |
91447636 A |
2255 | |
2256 | datalen = entry->length; | |
2257 | dataoff = entry->offset; | |
2258 | entrylen = ATTR_ENTRY_LENGTH(namelen); | |
0a7de745 | 2259 | if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) { |
91447636 | 2260 | splitdata = 1; |
0a7de745 | 2261 | } else { |
91447636 | 2262 | splitdata = 0; |
0a7de745 | 2263 | } |
91447636 A |
2264 | |
2265 | /* Remove the attribute entry. */ | |
2266 | if (!lastone) { | |
2267 | bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry, | |
0a7de745 | 2268 | ((size_t)header + header->data_start) - ((size_t)entry + entrylen)); |
91447636 A |
2269 | } |
2270 | ||
2271 | /* Adjust the attribute data. */ | |
2272 | if (splitdata) { | |
2273 | shift_data_up(xvp, | |
0a7de745 A |
2274 | header->data_start, |
2275 | dataoff - header->data_start, | |
2276 | entrylen, | |
2277 | context); | |
91447636 A |
2278 | if (!lastone) { |
2279 | shift_data_up(xvp, | |
0a7de745 A |
2280 | dataoff + datalen, |
2281 | (header->data_start + header->data_length) - (dataoff + datalen), | |
2282 | datalen + entrylen, | |
2283 | context); | |
91447636 A |
2284 | } |
2285 | /* XXX write zeros to freed space ? */ | |
2286 | ainfo.iosize = ainfo.attrhdr->data_start - entrylen; | |
2287 | } else { | |
91447636 | 2288 | bcopy((u_int8_t *)header + header->data_start, |
0a7de745 A |
2289 | (u_int8_t *)header + header->data_start - entrylen, |
2290 | dataoff - header->data_start); | |
91447636 A |
2291 | if (!lastone) { |
2292 | bcopy((u_int8_t *)header + dataoff + datalen, | |
0a7de745 A |
2293 | (u_int8_t *)header + dataoff - entrylen, |
2294 | (header->data_start + header->data_length) - (dataoff + datalen)); | |
91447636 | 2295 | } |
0a7de745 | 2296 | bzero(((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen)); |
91447636 A |
2297 | ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; |
2298 | } | |
2299 | ||
2300 | /* Adjust the header values and entry offsets. */ | |
2301 | header->num_attrs--; | |
2302 | header->data_start -= entrylen; | |
2303 | header->data_length -= datalen; | |
2304 | ||
2305 | oldslot = entry; | |
2306 | entry = ainfo.attr_entry; | |
2307 | for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { | |
2308 | entry->offset -= entrylen; | |
0a7de745 | 2309 | if (entry >= oldslot) { |
91447636 | 2310 | entry->offset -= datalen; |
0a7de745 | 2311 | } |
91447636 A |
2312 | entry = ATTR_NEXT(entry); |
2313 | } | |
2314 | error = write_xattrinfo(&ainfo); | |
2315 | if (error) { | |
2316 | printf("removexattr: write_xattrinfo error %d\n", error); | |
2317 | } | |
2318 | out: | |
2319 | rel_xattrinfo(&ainfo); | |
2320 | ||
2321 | /* When there are no more attributes remove the ._ file. */ | |
2322 | if (attrcount == 0) { | |
0a7de745 | 2323 | if (fileflags & O_EXLOCK) { |
91447636 | 2324 | (void) unlock_xattrfile(xvp, context); |
0a7de745 | 2325 | } |
91447636 A |
2326 | VNOP_CLOSE(xvp, fileflags, context); |
2327 | vnode_rele(xvp); | |
2328 | error = remove_xattrfile(xvp, context); | |
2329 | vnode_put(xvp); | |
2330 | } else { | |
2331 | close_xattrfile(xvp, fileflags, context); | |
2332 | } | |
2333 | /* Touch the change time if we changed an attribute. */ | |
2334 | if (error == 0) { | |
2335 | struct vnode_attr va; | |
2336 | ||
2337 | /* Re-write the mtime to cause a ctime change. */ | |
2338 | VATTR_INIT(&va); | |
2339 | VATTR_WANTED(&va, va_modify_time); | |
2340 | if (vnode_getattr(vp, &va, context) == 0) { | |
2341 | VATTR_INIT(&va); | |
2342 | VATTR_SET(&va, va_modify_time, va.va_modify_time); | |
2343 | (void) vnode_setattr(vp, &va, context); | |
2344 | } | |
2345 | } | |
b0d623f7 A |
2346 | |
2347 | post_event_if_success(vp, error, NOTE_ATTRIB); | |
2348 | ||
0a7de745 | 2349 | return error; |
91447636 A |
2350 | } |
2351 | ||
2352 | ||
2353 | /* | |
2354 | * Retrieve the list of extended attribute names. | |
2355 | */ | |
2356 | static int | |
2357 | default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context) | |
2358 | { | |
2359 | vnode_t xvp = NULL; | |
2360 | attr_info_t ainfo; | |
2361 | attr_entry_t *entry; | |
2362 | int i, count; | |
2363 | int error; | |
2364 | ||
2365 | /* | |
2366 | * We do not zero "*size" here as we don't want to stomp a size set when | |
2367 | * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the | |
2368 | * system call layer, up in listxattr or flistxattr. | |
2369 | */ | |
2370 | ||
2371 | if ((error = open_xattrfile(vp, FREAD, &xvp, context))) { | |
0a7de745 | 2372 | if (error == ENOATTR) { |
91447636 | 2373 | error = 0; |
0a7de745 A |
2374 | } |
2375 | return error; | |
91447636 A |
2376 | } |
2377 | if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { | |
0a7de745 | 2378 | if (error == ENOATTR) { |
b0d623f7 | 2379 | error = 0; |
0a7de745 | 2380 | } |
91447636 | 2381 | close_xattrfile(xvp, FREAD, context); |
0a7de745 | 2382 | return error; |
91447636 A |
2383 | } |
2384 | ||
2385 | /* Check for Finder Info. */ | |
2386 | if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { | |
2387 | if (uio == NULL) { | |
2388 | *size += sizeof(XATTR_FINDERINFO_NAME); | |
2d21ac55 | 2389 | } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_FINDERINFO_NAME)) { |
91447636 A |
2390 | error = ERANGE; |
2391 | goto out; | |
2392 | } else { | |
2d21ac55 | 2393 | error = uiomove(XATTR_FINDERINFO_NAME, |
0a7de745 | 2394 | sizeof(XATTR_FINDERINFO_NAME), uio); |
91447636 A |
2395 | if (error) { |
2396 | error = ERANGE; | |
2397 | goto out; | |
2398 | } | |
2399 | } | |
2400 | } | |
2401 | ||
2402 | /* Check for Resource Fork. */ | |
2403 | if (vnode_isreg(vp) && ainfo.rsrcfork) { | |
2404 | if (uio == NULL) { | |
2405 | *size += sizeof(XATTR_RESOURCEFORK_NAME); | |
2d21ac55 | 2406 | } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_RESOURCEFORK_NAME)) { |
91447636 A |
2407 | error = ERANGE; |
2408 | goto out; | |
2409 | } else { | |
2d21ac55 | 2410 | error = uiomove(XATTR_RESOURCEFORK_NAME, |
0a7de745 | 2411 | sizeof(XATTR_RESOURCEFORK_NAME), uio); |
91447636 A |
2412 | if (error) { |
2413 | error = ERANGE; | |
2414 | goto out; | |
2415 | } | |
2416 | } | |
2417 | } | |
2418 | ||
2419 | /* Check for attributes. */ | |
2420 | if (ainfo.attrhdr) { | |
2421 | count = ainfo.attrhdr->num_attrs; | |
2422 | for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) { | |
2d21ac55 | 2423 | if (xattr_protected((const char *)entry->name) || |
5ba3f43e | 2424 | ((entry->namelen < XATTR_MAXNAMELEN) && |
0a7de745 A |
2425 | (entry->name[entry->namelen] == '\0') && |
2426 | (xattr_validatename((const char *)entry->name) != 0))) { | |
91447636 A |
2427 | entry = ATTR_NEXT(entry); |
2428 | continue; | |
2429 | } | |
2430 | if (uio == NULL) { | |
2431 | *size += entry->namelen; | |
2432 | entry = ATTR_NEXT(entry); | |
2433 | continue; | |
2434 | } | |
2435 | if (uio_resid(uio) < entry->namelen) { | |
2436 | error = ERANGE; | |
2437 | break; | |
2438 | } | |
2439 | error = uiomove((caddr_t) entry->name, entry->namelen, uio); | |
2440 | if (error) { | |
0a7de745 | 2441 | if (error != EFAULT) { |
91447636 | 2442 | error = ERANGE; |
0a7de745 | 2443 | } |
91447636 | 2444 | break; |
0a7de745 | 2445 | } |
91447636 A |
2446 | entry = ATTR_NEXT(entry); |
2447 | } | |
2448 | } | |
0a7de745 | 2449 | out: |
91447636 A |
2450 | rel_xattrinfo(&ainfo); |
2451 | close_xattrfile(xvp, FREAD, context); | |
2452 | ||
0a7de745 | 2453 | return error; |
91447636 A |
2454 | } |
2455 | ||
2456 | static int | |
2457 | open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context) | |
2458 | { | |
2459 | vnode_t xvp = NULLVP; | |
2460 | vnode_t dvp = NULLVP; | |
f427ee49 A |
2461 | struct vnode_attr *va = NULL; |
2462 | struct nameidata *nd = NULL; | |
91447636 A |
2463 | char smallname[64]; |
2464 | char *filename = NULL; | |
2d21ac55 | 2465 | const char *basename = NULL; |
a991bd8d A |
2466 | size_t alloc_len; |
2467 | size_t copy_len; | |
91447636 A |
2468 | errno_t error; |
2469 | int opened = 0; | |
2470 | int referenced = 0; | |
2471 | ||
2472 | if (vnode_isvroot(vp) && vnode_isdir(vp)) { | |
2473 | /* | |
2474 | * For the root directory use "._." to hold the attributes. | |
2475 | */ | |
2476 | filename = &smallname[0]; | |
2d21ac55 | 2477 | snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, "."); |
91447636 A |
2478 | dvp = vp; /* the "._." file resides in the root dir */ |
2479 | goto lookup; | |
2480 | } | |
0a7de745 | 2481 | if ((dvp = vnode_getparent(vp)) == NULLVP) { |
91447636 A |
2482 | error = ENOATTR; |
2483 | goto out; | |
2484 | } | |
0a7de745 | 2485 | if ((basename = vnode_getname(vp)) == NULL) { |
91447636 A |
2486 | error = ENOATTR; |
2487 | goto out; | |
2488 | } | |
2489 | ||
2490 | /* "._" Attribute files cannot have attributes */ | |
2491 | if (vp->v_type == VREG && strlen(basename) > 2 && | |
2492 | basename[0] == '.' && basename[1] == '_') { | |
2493 | error = EPERM; | |
2494 | goto out; | |
2495 | } | |
2496 | filename = &smallname[0]; | |
a991bd8d A |
2497 | alloc_len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename); |
2498 | if (alloc_len >= sizeof(smallname)) { | |
2499 | alloc_len++; /* snprintf result doesn't include '\0' */ | |
2500 | filename = kheap_alloc(KHEAP_TEMP, alloc_len, Z_WAITOK); | |
2501 | copy_len = snprintf(filename, alloc_len, "%s%s", ATTR_FILE_PREFIX, basename); | |
91447636 A |
2502 | } |
2503 | /* | |
2504 | * Note that the lookup here does not authorize. Since we are looking | |
2505 | * up in the same directory that we already have the file vnode in, | |
2506 | * we must have been given the file vnode legitimately. Read/write | |
2507 | * access has already been authorized in layers above for calls from | |
2508 | * userspace, and the authorization code using this path to read | |
2509 | * file security from the EA must always get access | |
2510 | */ | |
2511 | lookup: | |
f427ee49 A |
2512 | nd = kheap_alloc(KHEAP_TEMP, sizeof(struct nameidata), Z_WAITOK); |
2513 | NDINIT(nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH, | |
0a7de745 | 2514 | UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context); |
f427ee49 A |
2515 | nd->ni_dvp = dvp; |
2516 | ||
2517 | va = kheap_alloc(KHEAP_TEMP, sizeof(struct vnode_attr), Z_WAITOK); | |
91447636 A |
2518 | |
2519 | if (fileflags & O_CREAT) { | |
f427ee49 | 2520 | nd->ni_cnd.cn_nameiop = CREATE; |
6d2010ae | 2521 | #if CONFIG_TRIGGERS |
f427ee49 | 2522 | nd->ni_op = OP_LINK; |
6d2010ae | 2523 | #endif |
743b1565 | 2524 | if (dvp != vp) { |
f427ee49 | 2525 | nd->ni_cnd.cn_flags |= LOCKPARENT; |
743b1565 | 2526 | } |
f427ee49 A |
2527 | if ((error = namei(nd))) { |
2528 | nd->ni_dvp = NULLVP; | |
91447636 A |
2529 | error = ENOATTR; |
2530 | goto out; | |
2531 | } | |
f427ee49 | 2532 | if ((xvp = nd->ni_vp) == NULLVP) { |
91447636 A |
2533 | uid_t uid; |
2534 | gid_t gid; | |
2535 | mode_t umode; | |
0a7de745 | 2536 | |
91447636 A |
2537 | /* |
2538 | * Pick up uid/gid/mode from target file. | |
2539 | */ | |
f427ee49 A |
2540 | VATTR_INIT(va); |
2541 | VATTR_WANTED(va, va_uid); | |
2542 | VATTR_WANTED(va, va_gid); | |
2543 | VATTR_WANTED(va, va_mode); | |
2544 | if (VNOP_GETATTR(vp, va, context) == 0 && | |
2545 | VATTR_IS_SUPPORTED(va, va_uid) && | |
2546 | VATTR_IS_SUPPORTED(va, va_gid) && | |
2547 | VATTR_IS_SUPPORTED(va, va_mode)) { | |
2548 | uid = va->va_uid; | |
2549 | gid = va->va_gid; | |
2550 | umode = va->va_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); | |
0a7de745 | 2551 | } else { /* fallback values */ |
91447636 A |
2552 | uid = KAUTH_UID_NONE; |
2553 | gid = KAUTH_GID_NONE; | |
0a7de745 | 2554 | umode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; |
91447636 A |
2555 | } |
2556 | ||
f427ee49 A |
2557 | VATTR_INIT(va); |
2558 | VATTR_SET(va, va_type, VREG); | |
2559 | VATTR_SET(va, va_mode, umode); | |
0a7de745 | 2560 | if (uid != KAUTH_UID_NONE) { |
f427ee49 | 2561 | VATTR_SET(va, va_uid, uid); |
0a7de745 A |
2562 | } |
2563 | if (gid != KAUTH_GID_NONE) { | |
f427ee49 | 2564 | VATTR_SET(va, va_gid, gid); |
0a7de745 | 2565 | } |
91447636 | 2566 | |
f427ee49 | 2567 | error = vn_create(dvp, &nd->ni_vp, nd, va, |
0a7de745 A |
2568 | VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL, |
2569 | 0, NULL, | |
2570 | context); | |
2571 | if (error) { | |
b0d623f7 | 2572 | error = ENOATTR; |
0a7de745 | 2573 | } else { |
f427ee49 | 2574 | xvp = nd->ni_vp; |
0a7de745 | 2575 | } |
91447636 | 2576 | } |
f427ee49 | 2577 | nameidone(nd); |
743b1565 A |
2578 | if (dvp != vp) { |
2579 | vnode_put(dvp); /* drop iocount from LOCKPARENT request above */ | |
2580 | } | |
0a7de745 A |
2581 | if (error) { |
2582 | goto out; | |
2583 | } | |
91447636 | 2584 | } else { |
f427ee49 A |
2585 | if ((error = namei(nd))) { |
2586 | nd->ni_dvp = NULLVP; | |
91447636 | 2587 | error = ENOATTR; |
b0d623f7 | 2588 | goto out; |
91447636 | 2589 | } |
f427ee49 A |
2590 | xvp = nd->ni_vp; |
2591 | nameidone(nd); | |
91447636 | 2592 | } |
f427ee49 | 2593 | nd->ni_dvp = NULLVP; |
91447636 A |
2594 | |
2595 | if (xvp->v_type != VREG) { | |
2596 | error = ENOATTR; | |
2597 | goto out; | |
2598 | } | |
2599 | /* | |
2600 | * Owners must match. | |
2601 | */ | |
f427ee49 A |
2602 | VATTR_INIT(va); |
2603 | VATTR_WANTED(va, va_uid); | |
2604 | if (VNOP_GETATTR(vp, va, context) == 0 && VATTR_IS_SUPPORTED(va, va_uid)) { | |
2605 | uid_t owner = va->va_uid; | |
2606 | ||
2607 | VATTR_INIT(va); | |
2608 | VATTR_WANTED(va, va_uid); | |
2609 | if (VNOP_GETATTR(xvp, va, context) == 0 && (owner != va->va_uid)) { | |
91447636 A |
2610 | error = ENOATTR; /* don't use this "._" file */ |
2611 | goto out; | |
2612 | } | |
2613 | } | |
0a7de745 A |
2614 | |
2615 | if ((error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) { | |
91447636 A |
2616 | error = ENOATTR; |
2617 | goto out; | |
2618 | } | |
2619 | opened = 1; | |
2620 | ||
2621 | if ((error = vnode_ref(xvp))) { | |
2622 | goto out; | |
2623 | } | |
2624 | referenced = 1; | |
2625 | ||
2626 | /* If create was requested, make sure file header exists. */ | |
2627 | if (fileflags & O_CREAT) { | |
f427ee49 A |
2628 | VATTR_INIT(va); |
2629 | VATTR_WANTED(va, va_data_size); | |
2630 | VATTR_WANTED(va, va_fileid); | |
2631 | VATTR_WANTED(va, va_nlink); | |
2632 | if ((error = vnode_getattr(xvp, va, context)) != 0) { | |
91447636 A |
2633 | error = EPERM; |
2634 | goto out; | |
2635 | } | |
0a7de745 | 2636 | |
91447636 | 2637 | /* If the file is empty then add a default header. */ |
f427ee49 | 2638 | if (va->va_data_size == 0) { |
91447636 | 2639 | /* Don't adopt hard-linked "._" files. */ |
f427ee49 | 2640 | if (VATTR_IS_SUPPORTED(va, va_nlink) && va->va_nlink > 1) { |
91447636 A |
2641 | error = EPERM; |
2642 | goto out; | |
2643 | } | |
f427ee49 | 2644 | if ((error = create_xattrfile(xvp, (u_int32_t)va->va_fileid, context))) { |
91447636 | 2645 | goto out; |
0a7de745 | 2646 | } |
91447636 A |
2647 | } |
2648 | } | |
0a7de745 | 2649 | /* Apply file locking if requested. */ |
91447636 A |
2650 | if (fileflags & (O_EXLOCK | O_SHLOCK)) { |
2651 | short locktype; | |
2652 | ||
2653 | locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK; | |
2654 | error = lock_xattrfile(xvp, locktype, context); | |
0a7de745 | 2655 | if (error) { |
b0d623f7 | 2656 | error = ENOATTR; |
0a7de745 | 2657 | } |
91447636 A |
2658 | } |
2659 | out: | |
91447636 A |
2660 | if (error) { |
2661 | if (xvp != NULLVP) { | |
2662 | if (opened) { | |
2663 | (void) VNOP_CLOSE(xvp, fileflags, context); | |
2664 | } | |
39236c6e A |
2665 | |
2666 | if (fileflags & O_CREAT) { | |
2667 | /* Delete the xattr file if we encountered any errors */ | |
0a7de745 | 2668 | (void) remove_xattrfile(xvp, context); |
39236c6e A |
2669 | } |
2670 | ||
91447636 A |
2671 | if (referenced) { |
2672 | (void) vnode_rele(xvp); | |
2673 | } | |
2674 | (void) vnode_put(xvp); | |
2675 | xvp = NULLVP; | |
2676 | } | |
2677 | if ((error == ENOATTR) && (fileflags & O_CREAT)) { | |
2678 | error = EPERM; | |
2679 | } | |
2680 | } | |
39236c6e | 2681 | /* Release resources after error-handling */ |
f427ee49 A |
2682 | kheap_free(KHEAP_TEMP, nd, sizeof(struct nameidata)); |
2683 | kheap_free(KHEAP_TEMP, va, sizeof(struct vnode_attr)); | |
39236c6e A |
2684 | if (dvp && (dvp != vp)) { |
2685 | vnode_put(dvp); | |
2686 | } | |
2687 | if (basename) { | |
2688 | vnode_putname(basename); | |
2689 | } | |
2690 | if (filename && filename != &smallname[0]) { | |
a991bd8d | 2691 | kheap_free(KHEAP_TEMP, filename, alloc_len); |
39236c6e A |
2692 | } |
2693 | ||
91447636 | 2694 | *xvpp = xvp; /* return a referenced vnode */ |
0a7de745 | 2695 | return error; |
91447636 A |
2696 | } |
2697 | ||
2698 | static void | |
2699 | close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context) | |
2700 | { | |
2701 | // if (fileflags & FWRITE) | |
2702 | // (void) VNOP_FSYNC(xvp, MNT_WAIT, context); | |
2703 | ||
0a7de745 | 2704 | if (fileflags & (O_EXLOCK | O_SHLOCK)) { |
91447636 | 2705 | (void) unlock_xattrfile(xvp, context); |
0a7de745 | 2706 | } |
91447636 A |
2707 | |
2708 | (void) VNOP_CLOSE(xvp, fileflags, context); | |
2709 | (void) vnode_rele(xvp); | |
2710 | (void) vnode_put(xvp); | |
2711 | } | |
2712 | ||
2713 | static int | |
2714 | remove_xattrfile(vnode_t xvp, vfs_context_t context) | |
2715 | { | |
2716 | vnode_t dvp; | |
2717 | struct nameidata nd; | |
2d21ac55 | 2718 | char *path = NULL; |
91447636 A |
2719 | int pathlen; |
2720 | int error = 0; | |
2721 | ||
f427ee49 | 2722 | path = zalloc(ZV_NAMEI); |
91447636 | 2723 | pathlen = MAXPATHLEN; |
2d21ac55 A |
2724 | error = vn_getpath(xvp, path, &pathlen); |
2725 | if (error) { | |
f427ee49 | 2726 | zfree(ZV_NAMEI, path); |
0a7de745 | 2727 | return error; |
2d21ac55 | 2728 | } |
91447636 | 2729 | |
6d2010ae | 2730 | NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH, |
0a7de745 | 2731 | UIO_SYSSPACE, CAST_USER_ADDR_T(path), context); |
91447636 | 2732 | error = namei(&nd); |
f427ee49 | 2733 | zfree(ZV_NAMEI, path); |
91447636 | 2734 | if (error) { |
0a7de745 | 2735 | return error; |
91447636 A |
2736 | } |
2737 | dvp = nd.ni_dvp; | |
2738 | xvp = nd.ni_vp; | |
2739 | ||
2740 | error = VNOP_REMOVE(dvp, xvp, &nd.ni_cnd, 0, context); | |
2741 | nameidone(&nd); | |
2742 | vnode_put(dvp); | |
2743 | vnode_put(xvp); | |
2744 | ||
0a7de745 | 2745 | return error; |
91447636 | 2746 | } |
2d21ac55 | 2747 | |
6601e61a A |
2748 | /* |
2749 | * Read in and parse the AppleDouble header and entries, and the extended | |
2750 | * attribute header and entries if any. Populates the fields of ainfop | |
2751 | * based on the headers and entries found. | |
2752 | * | |
2753 | * The basic idea is to: | |
2754 | * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All | |
2755 | * AppleDouble entries, the extended attribute header, and extended | |
2756 | * attribute entries must lie within this part of the file; the rest of | |
2757 | * the AppleDouble handling code assumes this. Plus it allows us to | |
2758 | * somewhat optimize by doing a smaller number of larger I/Os. | |
2759 | * - Swap and sanity check the AppleDouble header (including the AppleDouble | |
2760 | * entries). | |
2761 | * - Find the Finder Info and Resource Fork entries, if any. | |
2762 | * - If we're going to be writing, try to make sure the Finder Info entry has | |
2763 | * room to store the extended attribute header, plus some space for extended | |
2764 | * attributes. | |
2765 | * - Swap and sanity check the extended attribute header and entries (if any). | |
2766 | */ | |
91447636 A |
2767 | static int |
2768 | get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context) | |
2769 | { | |
2770 | uio_t auio = NULL; | |
2771 | void * buffer = NULL; | |
2772 | apple_double_header_t *filehdr; | |
91447636 | 2773 | struct vnode_attr va; |
f427ee49 | 2774 | size_t iosize = 0; |
91447636 A |
2775 | int i; |
2776 | int error; | |
2777 | ||
2778 | bzero(ainfop, sizeof(attr_info_t)); | |
2779 | ainfop->filevp = xvp; | |
2780 | ainfop->context = context; | |
2781 | VATTR_INIT(&va); | |
2782 | VATTR_WANTED(&va, va_data_size); | |
2783 | VATTR_WANTED(&va, va_fileid); | |
2784 | if ((error = vnode_getattr(xvp, &va, context))) { | |
2785 | goto bail; | |
2786 | } | |
2787 | ainfop->filesize = va.va_data_size; | |
2788 | ||
2789 | /* When setting attributes, allow room for the header to grow. */ | |
0a7de745 | 2790 | if (setting) { |
91447636 | 2791 | iosize = ATTR_MAX_HDR_SIZE; |
0a7de745 | 2792 | } else { |
91447636 | 2793 | iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize); |
0a7de745 | 2794 | } |
91447636 A |
2795 | |
2796 | if (iosize == 0) { | |
2797 | error = ENOATTR; | |
2798 | goto bail; | |
2799 | } | |
2800 | ainfop->iosize = iosize; | |
f427ee49 | 2801 | buffer = kheap_alloc(KHEAP_DATA_BUFFERS, iosize, Z_WAITOK); |
0a7de745 | 2802 | if (buffer == NULL) { |
6601e61a A |
2803 | error = ENOMEM; |
2804 | goto bail; | |
2805 | } | |
2806 | ||
b0d623f7 | 2807 | auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); |
91447636 A |
2808 | uio_addiov(auio, (uintptr_t)buffer, iosize); |
2809 | ||
2810 | /* Read the file header. */ | |
2811 | error = VNOP_READ(xvp, auio, 0, context); | |
2812 | if (error) { | |
2813 | goto bail; | |
2814 | } | |
2815 | ainfop->rawsize = iosize - uio_resid(auio); | |
2816 | ainfop->rawdata = (u_int8_t *)buffer; | |
0a7de745 | 2817 | |
91447636 A |
2818 | filehdr = (apple_double_header_t *)buffer; |
2819 | ||
6601e61a | 2820 | error = check_and_swap_apple_double_header(ainfop); |
0a7de745 | 2821 | if (error) { |
91447636 | 2822 | goto bail; |
0a7de745 A |
2823 | } |
2824 | ||
91447636 | 2825 | ainfop->filehdr = filehdr; /* valid AppleDouble header */ |
6601e61a | 2826 | |
91447636 A |
2827 | /* rel_xattrinfo is responsible for freeing the header buffer */ |
2828 | buffer = NULL; | |
2829 | ||
6601e61a | 2830 | /* Find the Finder Info and Resource Fork entries, if any */ |
91447636 A |
2831 | for (i = 0; i < filehdr->numEntries; ++i) { |
2832 | if (filehdr->entries[i].type == AD_FINDERINFO && | |
2d21ac55 | 2833 | filehdr->entries[i].length >= FINDERINFOSIZE) { |
6601e61a | 2834 | /* We found the Finder Info entry. */ |
91447636 | 2835 | ainfop->finderinfo = &filehdr->entries[i]; |
0a7de745 | 2836 | |
4ba76501 A |
2837 | /* At this point check_and_swap_apple_double_header() call above |
2838 | * verified that all apple double entires are valid: | |
2839 | * they point somewhere within the file. | |
6601e61a | 2840 | * |
4ba76501 A |
2841 | * Now for finderinfo make sure that the fixed portion |
2842 | * is within the buffer we read in. | |
6601e61a | 2843 | */ |
4ba76501 A |
2844 | if (((ainfop->finderinfo->offset + FINDERINFOSIZE) > ainfop->finderinfo->offset) && |
2845 | ((ainfop->finderinfo->offset + FINDERINFOSIZE) <= ainfop->rawsize)) { | |
2846 | /* | |
2847 | * Is the Finder Info "empty" (all zeroes)? If so, | |
2848 | * we'll pretend like the Finder Info extended attribute | |
2849 | * does not exist. | |
2850 | */ | |
2851 | if (bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) { | |
2852 | ainfop->emptyfinderinfo = 1; | |
2853 | } | |
2854 | } else { | |
2855 | error = ENOATTR; | |
2856 | goto bail; | |
91447636 | 2857 | } |
6601e61a | 2858 | } |
6601e61a A |
2859 | if (filehdr->entries[i].type == AD_RESOURCE) { |
2860 | /* | |
2861 | * Ignore zero-length resource forks when getting. If setting, | |
2862 | * we need to remember the resource fork entry so it can be | |
2863 | * updated once the new content has been written. | |
2864 | */ | |
0a7de745 | 2865 | if (filehdr->entries[i].length == 0 && !setting) { |
91447636 | 2866 | continue; |
0a7de745 A |
2867 | } |
2868 | ||
6601e61a A |
2869 | /* |
2870 | * Check to see if any "empty" resource fork is ours (i.e. is ignorable). | |
2871 | * | |
2872 | * The "empty" resource headers we created have a system data tag of: | |
2873 | * "This resource fork intentionally left blank " | |
2874 | */ | |
2875 | if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) { | |
2876 | uio_t rf_uio; | |
2877 | u_int8_t systemData[64]; | |
2878 | int rf_err; | |
2879 | ||
2d21ac55 | 2880 | |
6601e61a | 2881 | /* Read the system data which starts at byte 16 */ |
b0d623f7 | 2882 | rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); |
6601e61a A |
2883 | uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData)); |
2884 | uio_setoffset(rf_uio, filehdr->entries[i].offset + 16); | |
2885 | rf_err = VNOP_READ(xvp, rf_uio, 0, context); | |
2886 | uio_free(rf_uio); | |
2887 | ||
2888 | if (rf_err != 0 || | |
2d21ac55 | 2889 | bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) { |
6601e61a | 2890 | continue; /* skip this resource fork */ |
91447636 | 2891 | } |
91447636 | 2892 | } |
91447636 A |
2893 | ainfop->rsrcfork = &filehdr->entries[i]; |
2894 | if (i != (filehdr->numEntries - 1)) { | |
2895 | printf("get_xattrinfo: resource fork not last entry\n"); | |
2896 | ainfop->readonly = 1; | |
2897 | } | |
2898 | continue; | |
2899 | } | |
2900 | } | |
0a7de745 | 2901 | |
6601e61a A |
2902 | /* |
2903 | * See if this file looks like it is laid out correctly to contain | |
2904 | * extended attributes. If so, then do the following: | |
2905 | * | |
2906 | * - If we're going to be writing, try to make sure the Finder Info | |
2907 | * entry has room to store the extended attribute header, plus some | |
2908 | * space for extended attributes. | |
2909 | * | |
2910 | * - Swap and sanity check the extended attribute header and entries | |
2911 | * (if any). | |
2912 | */ | |
2913 | if (filehdr->numEntries == 2 && | |
2d21ac55 A |
2914 | ainfop->finderinfo == &filehdr->entries[0] && |
2915 | ainfop->rsrcfork == &filehdr->entries[1] && | |
2916 | ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) { | |
6601e61a A |
2917 | attr_header_t *attrhdr; |
2918 | attrhdr = (attr_header_t *)filehdr; | |
2919 | /* | |
2920 | * If we're going to be writing, try to make sure the Finder | |
2921 | * Info entry has room to store the extended attribute header, | |
2922 | * plus some space for extended attributes. | |
2923 | */ | |
2924 | if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) { | |
2925 | size_t delta; | |
2926 | size_t writesize; | |
0a7de745 | 2927 | |
6601e61a A |
2928 | delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE); |
2929 | if (ainfop->rsrcfork && filehdr->entries[1].length) { | |
2930 | /* Make some room before existing resource fork. */ | |
2931 | shift_data_down(xvp, | |
0a7de745 A |
2932 | filehdr->entries[1].offset, |
2933 | filehdr->entries[1].length, | |
2934 | delta, context); | |
6601e61a A |
2935 | writesize = sizeof(attr_header_t); |
2936 | } else { | |
2937 | /* Create a new, empty resource fork. */ | |
2938 | rsrcfork_header_t *rsrcforkhdr; | |
0a7de745 | 2939 | |
6601e61a | 2940 | vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context); |
0a7de745 | 2941 | |
6601e61a A |
2942 | /* Steal some space for an empty RF header. */ |
2943 | delta -= sizeof(rsrcfork_header_t); | |
0a7de745 | 2944 | |
6601e61a A |
2945 | bzero(&attrhdr->appledouble.pad[0], delta); |
2946 | rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta); | |
0a7de745 | 2947 | |
6601e61a A |
2948 | /* Fill in Empty Resource Fork Header. */ |
2949 | init_empty_resource_fork(rsrcforkhdr); | |
0a7de745 | 2950 | |
6601e61a A |
2951 | filehdr->entries[1].length = sizeof(rsrcfork_header_t); |
2952 | writesize = ATTR_BUF_SIZE; | |
2953 | } | |
2954 | filehdr->entries[0].length += delta; | |
2955 | filehdr->entries[1].offset += delta; | |
0a7de745 | 2956 | |
6601e61a A |
2957 | /* Fill in Attribute Header. */ |
2958 | attrhdr->magic = ATTR_HDR_MAGIC; | |
2959 | attrhdr->debug_tag = (u_int32_t)va.va_fileid; | |
2960 | attrhdr->total_size = filehdr->entries[1].offset; | |
2961 | attrhdr->data_start = sizeof(attr_header_t); | |
2962 | attrhdr->data_length = 0; | |
2963 | attrhdr->reserved[0] = 0; | |
2964 | attrhdr->reserved[1] = 0; | |
2965 | attrhdr->reserved[2] = 0; | |
2966 | attrhdr->flags = 0; | |
2967 | attrhdr->num_attrs = 0; | |
0a7de745 | 2968 | |
6601e61a | 2969 | /* Push out new header */ |
b0d623f7 | 2970 | uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE); |
6601e61a | 2971 | uio_addiov(auio, (uintptr_t)filehdr, writesize); |
0a7de745 A |
2972 | |
2973 | swap_adhdr(filehdr); /* to big endian */ | |
2974 | swap_attrhdr(attrhdr, ainfop); /* to big endian */ | |
6601e61a | 2975 | error = VNOP_WRITE(xvp, auio, 0, context); |
0a7de745 | 2976 | swap_adhdr(filehdr); /* back to native */ |
6601e61a A |
2977 | /* The attribute header gets swapped below. */ |
2978 | } | |
2979 | } | |
6601e61a A |
2980 | /* |
2981 | * Swap and sanity check the extended attribute header and | |
2982 | * entries (if any). The Finder Info content must be big enough | |
2983 | * to include the extended attribute header; if not, we just | |
2984 | * ignore it. | |
2985 | * | |
2986 | * Note that we're passing the offset + length (i.e. the end) | |
2987 | * of the Finder Info instead of rawsize to validate_attrhdr. | |
2988 | * This ensures that all extended attributes lie within the | |
2989 | * Finder Info content according to the AppleDouble entry. | |
2990 | * | |
2991 | * Sets ainfop->attrhdr and ainfop->attr_entry if a valid | |
2992 | * header was found. | |
2993 | */ | |
2994 | if (ainfop->finderinfo && | |
0a7de745 A |
2995 | ainfop->finderinfo == &filehdr->entries[0] && |
2996 | ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) { | |
6601e61a | 2997 | attr_header_t *attrhdr = (attr_header_t*)filehdr; |
2d21ac55 | 2998 | |
6601e61a A |
2999 | if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) { |
3000 | ainfop->attrhdr = attrhdr; /* valid attribute header */ | |
3001 | /* First attr_entry starts immediately following attribute header */ | |
3002 | ainfop->attr_entry = (attr_entry_t *)&attrhdr[1]; | |
3003 | } | |
3004 | } | |
3005 | ||
91447636 A |
3006 | error = 0; |
3007 | bail: | |
0a7de745 | 3008 | if (auio != NULL) { |
91447636 | 3009 | uio_free(auio); |
0a7de745 | 3010 | } |
f427ee49 | 3011 | kheap_free(KHEAP_DATA_BUFFERS, buffer, iosize); |
0a7de745 | 3012 | return error; |
91447636 A |
3013 | } |
3014 | ||
3015 | ||
3016 | static int | |
3017 | create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context) | |
3018 | { | |
3019 | attr_header_t *xah; | |
3020 | rsrcfork_header_t *rsrcforkhdr; | |
3021 | void * buffer; | |
3022 | uio_t auio; | |
3023 | int rsrcforksize; | |
3024 | int error; | |
3025 | ||
f427ee49 | 3026 | buffer = kheap_alloc(KHEAP_TEMP, ATTR_BUF_SIZE, Z_WAITOK); |
91447636 A |
3027 | bzero(buffer, ATTR_BUF_SIZE); |
3028 | ||
3029 | xah = (attr_header_t *)buffer; | |
b0d623f7 | 3030 | auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE); |
91447636 A |
3031 | uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE); |
3032 | rsrcforksize = sizeof(rsrcfork_header_t); | |
3033 | rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize); | |
3034 | ||
3035 | /* Fill in Apple Double Header. */ | |
0a7de745 A |
3036 | xah->appledouble.magic = SWAP32(ADH_MAGIC); |
3037 | xah->appledouble.version = SWAP32(ADH_VERSION); | |
3038 | xah->appledouble.numEntries = SWAP16(2); | |
3039 | xah->appledouble.entries[0].type = SWAP32(AD_FINDERINFO); | |
3040 | xah->appledouble.entries[0].offset = SWAP32(offsetof(apple_double_header_t, finfo)); | |
3041 | xah->appledouble.entries[0].length = SWAP32(ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize); | |
3042 | xah->appledouble.entries[1].type = SWAP32(AD_RESOURCE); | |
3043 | xah->appledouble.entries[1].offset = SWAP32(ATTR_BUF_SIZE - rsrcforksize); | |
3044 | xah->appledouble.entries[1].length = SWAP32(rsrcforksize); | |
91447636 A |
3045 | bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler)); |
3046 | ||
3047 | /* Fill in Attribute Header. */ | |
0a7de745 A |
3048 | xah->magic = SWAP32(ATTR_HDR_MAGIC); |
3049 | xah->debug_tag = SWAP32(fileid); | |
3050 | xah->total_size = SWAP32(ATTR_BUF_SIZE - rsrcforksize); | |
3051 | xah->data_start = SWAP32(sizeof(attr_header_t)); | |
91447636 A |
3052 | |
3053 | /* Fill in Empty Resource Fork Header. */ | |
3054 | init_empty_resource_fork(rsrcforkhdr); | |
3055 | ||
3056 | /* Push it out. */ | |
39236c6e A |
3057 | error = VNOP_WRITE(xvp, auio, IO_UNIT, context); |
3058 | ||
3059 | /* Did we write out the full uio? */ | |
3060 | if (uio_resid(auio) > 0) { | |
3061 | error = ENOSPC; | |
3062 | } | |
91447636 A |
3063 | |
3064 | uio_free(auio); | |
f427ee49 | 3065 | kheap_free(KHEAP_TEMP, buffer, ATTR_BUF_SIZE); |
91447636 | 3066 | |
0a7de745 | 3067 | return error; |
91447636 A |
3068 | } |
3069 | ||
3070 | static void | |
3071 | init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr) | |
3072 | { | |
3073 | bzero(rsrcforkhdr, sizeof(rsrcfork_header_t)); | |
0a7de745 A |
3074 | rsrcforkhdr->fh_DataOffset = SWAP32(RF_FIRST_RESOURCE); |
3075 | rsrcforkhdr->fh_MapOffset = SWAP32(RF_FIRST_RESOURCE); | |
3076 | rsrcforkhdr->fh_MapLength = SWAP32(RF_NULL_MAP_LENGTH); | |
3077 | rsrcforkhdr->mh_DataOffset = SWAP32(RF_FIRST_RESOURCE); | |
3078 | rsrcforkhdr->mh_MapOffset = SWAP32(RF_FIRST_RESOURCE); | |
3079 | rsrcforkhdr->mh_MapLength = SWAP32(RF_NULL_MAP_LENGTH); | |
3080 | rsrcforkhdr->mh_Types = SWAP16(RF_NULL_MAP_LENGTH - 2 ); | |
3081 | rsrcforkhdr->mh_Names = SWAP16(RF_NULL_MAP_LENGTH); | |
3082 | rsrcforkhdr->typeCount = SWAP16(-1); | |
91447636 A |
3083 | bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG)); |
3084 | } | |
3085 | ||
3086 | static void | |
3087 | rel_xattrinfo(attr_info_t *ainfop) | |
3088 | { | |
f427ee49 | 3089 | kheap_free_addr(KHEAP_DATA_BUFFERS, ainfop->filehdr); |
91447636 A |
3090 | bzero(ainfop, sizeof(attr_info_t)); |
3091 | } | |
3092 | ||
3093 | static int | |
3094 | write_xattrinfo(attr_info_t *ainfop) | |
3095 | { | |
3096 | uio_t auio; | |
3097 | int error; | |
3098 | ||
b0d623f7 | 3099 | auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE); |
91447636 A |
3100 | uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize); |
3101 | ||
3102 | swap_adhdr(ainfop->filehdr); | |
2d21ac55 A |
3103 | if (ainfop->attrhdr != NULL) { |
3104 | swap_attrhdr(ainfop->attrhdr, ainfop); | |
3105 | } | |
91447636 A |
3106 | |
3107 | error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context); | |
3108 | ||
3109 | swap_adhdr(ainfop->filehdr); | |
2d21ac55 A |
3110 | if (ainfop->attrhdr != NULL) { |
3111 | swap_attrhdr(ainfop->attrhdr, ainfop); | |
3112 | } | |
0a7de745 | 3113 | uio_free(auio); |
6601e61a | 3114 | |
0a7de745 | 3115 | return error; |
91447636 A |
3116 | } |
3117 | ||
3118 | #if BYTE_ORDER == LITTLE_ENDIAN | |
3119 | /* | |
0a7de745 | 3120 | * Endian swap apple double header |
91447636 A |
3121 | */ |
3122 | static void | |
3123 | swap_adhdr(apple_double_header_t *adh) | |
3124 | { | |
3125 | int count; | |
3126 | int i; | |
3127 | ||
3128 | count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries); | |
3129 | ||
0a7de745 A |
3130 | adh->magic = SWAP32(adh->magic); |
3131 | adh->version = SWAP32(adh->version); | |
3132 | adh->numEntries = SWAP16(adh->numEntries); | |
91447636 A |
3133 | |
3134 | for (i = 0; i < count; i++) { | |
0a7de745 A |
3135 | adh->entries[i].type = SWAP32(adh->entries[i].type); |
3136 | adh->entries[i].offset = SWAP32(adh->entries[i].offset); | |
3137 | adh->entries[i].length = SWAP32(adh->entries[i].length); | |
91447636 A |
3138 | } |
3139 | } | |
3140 | ||
3141 | /* | |
0a7de745 | 3142 | * Endian swap extended attributes header |
91447636 A |
3143 | */ |
3144 | static void | |
6601e61a | 3145 | swap_attrhdr(attr_header_t *ah, attr_info_t* info) |
91447636 A |
3146 | { |
3147 | attr_entry_t *ae; | |
3148 | int count; | |
3149 | int i; | |
3150 | ||
3151 | count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); | |
3152 | ||
0a7de745 A |
3153 | ah->magic = SWAP32(ah->magic); |
3154 | ah->debug_tag = SWAP32(ah->debug_tag); | |
3155 | ah->total_size = SWAP32(ah->total_size); | |
3156 | ah->data_start = SWAP32(ah->data_start); | |
3157 | ah->data_length = SWAP32(ah->data_length); | |
3158 | ah->flags = SWAP16(ah->flags); | |
3159 | ah->num_attrs = SWAP16(ah->num_attrs); | |
91447636 A |
3160 | |
3161 | ae = (attr_entry_t *)(&ah[1]); | |
6601e61a | 3162 | for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) { |
0a7de745 A |
3163 | ae->offset = SWAP32(ae->offset); |
3164 | ae->length = SWAP32(ae->length); | |
3165 | ae->flags = SWAP16(ae->flags); | |
91447636 A |
3166 | } |
3167 | } | |
3168 | #endif | |
3169 | ||
3170 | /* | |
6601e61a A |
3171 | * Validate and swap the attributes header contents, and each attribute's |
3172 | * attr_entry_t. | |
3173 | * | |
3174 | * Note: Assumes the caller has verified that the Finder Info content is large | |
3175 | * enough to contain the attr_header structure itself. Therefore, we can | |
3176 | * swap the header fields before sanity checking them. | |
91447636 | 3177 | */ |
2d21ac55 | 3178 | static int |
6601e61a | 3179 | check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop) |
91447636 A |
3180 | { |
3181 | attr_entry_t *ae; | |
6601e61a A |
3182 | u_int8_t *buf_end; |
3183 | u_int32_t end; | |
91447636 A |
3184 | int count; |
3185 | int i; | |
3186 | ||
0a7de745 | 3187 | if (ah == NULL) { |
6601e61a | 3188 | return EINVAL; |
0a7de745 | 3189 | } |
91447636 | 3190 | |
0a7de745 | 3191 | if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) { |
6601e61a | 3192 | return EINVAL; |
0a7de745 A |
3193 | } |
3194 | ||
6601e61a | 3195 | /* Swap the basic header fields */ |
0a7de745 A |
3196 | ah->magic = SWAP32(ah->magic); |
3197 | ah->debug_tag = SWAP32(ah->debug_tag); | |
3198 | ah->total_size = SWAP32(ah->total_size); | |
3199 | ah->data_start = SWAP32(ah->data_start); | |
3200 | ah->data_length = SWAP32(ah->data_length); | |
3201 | ah->flags = SWAP16(ah->flags); | |
3202 | ah->num_attrs = SWAP16(ah->num_attrs); | |
6601e61a A |
3203 | |
3204 | /* | |
3205 | * Make sure the total_size fits within the Finder Info area, and the | |
3206 | * extended attribute data area fits within total_size. | |
3207 | */ | |
3208 | end = ah->data_start + ah->data_length; | |
3209 | if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length || | |
c3c9b80d | 3210 | ah->data_start < sizeof(attr_header_t) || |
2d21ac55 A |
3211 | end < ah->data_start || |
3212 | end > ah->total_size) { | |
6601e61a A |
3213 | return EINVAL; |
3214 | } | |
0a7de745 | 3215 | |
6601e61a A |
3216 | /* |
3217 | * Make sure each of the attr_entry_t's fits within total_size. | |
3218 | */ | |
3219 | buf_end = ainfop->rawdata + ah->total_size; | |
3220 | count = ah->num_attrs; | |
91447636 | 3221 | ae = (attr_entry_t *)(&ah[1]); |
0a7de745 A |
3222 | |
3223 | for (i = 0; i < count; i++) { | |
6601e61a | 3224 | /* Make sure the fixed-size part of this attr_entry_t fits. */ |
0a7de745 | 3225 | if ((u_int8_t *) &ae[1] > buf_end) { |
6601e61a | 3226 | return EINVAL; |
0a7de745 A |
3227 | } |
3228 | ||
6601e61a | 3229 | /* Make sure the variable-length name fits (+1 is for NUL terminator) */ |
0a7de745 | 3230 | if (&ae->name[ae->namelen + 1] > buf_end) { |
6601e61a | 3231 | return EINVAL; |
0a7de745 A |
3232 | } |
3233 | ||
f427ee49 A |
3234 | /* Make sure that namelen is matching name's real length, namelen included NUL */ |
3235 | if (strnlen((const char *)ae->name, ae->namelen) != ae->namelen - 1) { | |
3236 | return EINVAL; | |
3237 | } | |
3238 | ||
3239 | ||
6601e61a | 3240 | /* Swap the attribute entry fields */ |
0a7de745 A |
3241 | ae->offset = SWAP32(ae->offset); |
3242 | ae->length = SWAP32(ae->length); | |
3243 | ae->flags = SWAP16(ae->flags); | |
3244 | ||
f427ee49 | 3245 | /* Make sure the attribute content fits and points to the data part */ |
6601e61a | 3246 | end = ae->offset + ae->length; |
0a7de745 | 3247 | if (end < ae->offset || end > ah->total_size) { |
6601e61a | 3248 | return EINVAL; |
0a7de745 A |
3249 | } |
3250 | ||
f427ee49 A |
3251 | /* Make sure entry points to data section and not header */ |
3252 | if (ae->offset < ah->data_start) { | |
3253 | return EINVAL; | |
3254 | } | |
3255 | ||
6601e61a | 3256 | ae = ATTR_NEXT(ae); |
91447636 | 3257 | } |
0a7de745 | 3258 | |
6601e61a | 3259 | return 0; |
91447636 A |
3260 | } |
3261 | ||
3262 | // | |
3263 | // "start" & "end" are byte offsets in the file. | |
3264 | // "to" is the byte offset we want to move the | |
3265 | // data to. "to" should be > "start". | |
3266 | // | |
3267 | // we do the copy backwards to avoid problems if | |
3268 | // there's an overlap. | |
3269 | // | |
3270 | static int | |
3271 | shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context) | |
3272 | { | |
3273 | int ret, iolen; | |
3274 | size_t chunk, orig_chunk; | |
3275 | char *buff; | |
3276 | off_t pos; | |
b0d623f7 | 3277 | kauth_cred_t ucred = vfs_context_ucred(context); |
91447636 | 3278 | proc_t p = vfs_context_proc(context); |
0a7de745 | 3279 | |
91447636 A |
3280 | if (delta == 0 || len == 0) { |
3281 | return 0; | |
3282 | } | |
0a7de745 | 3283 | |
91447636 A |
3284 | chunk = 4096; |
3285 | if (len < chunk) { | |
3286 | chunk = len; | |
3287 | } | |
3288 | orig_chunk = chunk; | |
3289 | ||
3e170ce0 | 3290 | if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk, VM_KERN_MEMORY_FILE)) { |
91447636 A |
3291 | return ENOMEM; |
3292 | } | |
3293 | ||
0a7de745 | 3294 | for (pos = start + len - chunk; pos >= start; pos -= chunk) { |
f427ee49 | 3295 | ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); |
91447636 | 3296 | if (iolen != 0) { |
2d21ac55 | 3297 | printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n", |
0a7de745 | 3298 | pos, ret, chunk, ret); |
91447636 A |
3299 | break; |
3300 | } | |
0a7de745 | 3301 | |
f427ee49 | 3302 | ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); |
91447636 | 3303 | if (iolen != 0) { |
2d21ac55 | 3304 | printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n", |
0a7de745 | 3305 | pos + delta, ret, chunk, ret); |
91447636 A |
3306 | break; |
3307 | } | |
0a7de745 | 3308 | |
b0d623f7 | 3309 | if ((pos - (off_t)chunk) < start) { |
91447636 | 3310 | chunk = pos - start; |
0a7de745 | 3311 | |
91447636 A |
3312 | if (chunk == 0) { // we're all done |
3313 | break; | |
3314 | } | |
3315 | } | |
3316 | } | |
3317 | kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk); | |
3318 | ||
3319 | return 0; | |
3320 | } | |
3321 | ||
3322 | ||
3323 | static int | |
3324 | shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context) | |
3325 | { | |
3326 | int ret, iolen; | |
3327 | size_t chunk, orig_chunk; | |
3328 | char *buff; | |
3329 | off_t pos; | |
3330 | off_t end; | |
b0d623f7 | 3331 | kauth_cred_t ucred = vfs_context_ucred(context); |
91447636 | 3332 | proc_t p = vfs_context_proc(context); |
0a7de745 | 3333 | |
91447636 A |
3334 | if (delta == 0 || len == 0) { |
3335 | return 0; | |
3336 | } | |
0a7de745 | 3337 | |
91447636 A |
3338 | chunk = 4096; |
3339 | if (len < chunk) { | |
3340 | chunk = len; | |
3341 | } | |
3342 | orig_chunk = chunk; | |
3343 | end = start + len; | |
3344 | ||
3e170ce0 | 3345 | if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk, VM_KERN_MEMORY_FILE)) { |
91447636 A |
3346 | return ENOMEM; |
3347 | } | |
3348 | ||
0a7de745 | 3349 | for (pos = start; pos < end; pos += chunk) { |
f427ee49 | 3350 | ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); |
91447636 | 3351 | if (iolen != 0) { |
2d21ac55 | 3352 | printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n", |
0a7de745 | 3353 | pos, ret, chunk, ret); |
91447636 A |
3354 | break; |
3355 | } | |
0a7de745 | 3356 | |
f427ee49 | 3357 | ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); |
91447636 | 3358 | if (iolen != 0) { |
2d21ac55 | 3359 | printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n", |
0a7de745 | 3360 | pos + delta, ret, chunk, ret); |
91447636 A |
3361 | break; |
3362 | } | |
0a7de745 | 3363 | |
b0d623f7 | 3364 | if ((pos + (off_t)chunk) > end) { |
91447636 | 3365 | chunk = end - pos; |
0a7de745 | 3366 | |
91447636 A |
3367 | if (chunk == 0) { // we're all done |
3368 | break; | |
3369 | } | |
3370 | } | |
3371 | } | |
3372 | kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk); | |
3373 | ||
3374 | return 0; | |
3375 | } | |
3376 | ||
3377 | static int | |
3378 | lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context) | |
3379 | { | |
3380 | struct flock lf; | |
2d21ac55 | 3381 | int error; |
91447636 A |
3382 | |
3383 | lf.l_whence = SEEK_SET; | |
3384 | lf.l_start = 0; | |
3385 | lf.l_len = 0; | |
3386 | lf.l_type = locktype; /* F_WRLCK or F_RDLCK */ | |
3387 | /* Note: id is just a kernel address that's not a proc */ | |
0a7de745 A |
3388 | error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK | F_WAIT, context, NULL); |
3389 | return error == ENOTSUP ? 0 : error; | |
91447636 A |
3390 | } |
3391 | ||
0a7de745 | 3392 | int |
91447636 A |
3393 | unlock_xattrfile(vnode_t xvp, vfs_context_t context) |
3394 | { | |
3395 | struct flock lf; | |
2d21ac55 | 3396 | int error; |
91447636 A |
3397 | |
3398 | lf.l_whence = SEEK_SET; | |
3399 | lf.l_start = 0; | |
3400 | lf.l_len = 0; | |
3401 | lf.l_type = F_UNLCK; | |
3402 | /* Note: id is just a kernel address that's not a proc */ | |
39236c6e | 3403 | error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context, NULL); |
0a7de745 | 3404 | return error == ENOTSUP ? 0 : error; |
91447636 A |
3405 | } |
3406 | ||
39236c6e A |
3407 | #else /* CONFIG_APPLEDOUBLE */ |
3408 | ||
39236c6e A |
3409 | |
3410 | static int | |
fe8ab488 | 3411 | default_getxattr(__unused vnode_t vp, __unused const char *name, |
39236c6e A |
3412 | __unused uio_t uio, __unused size_t *size, __unused int options, |
3413 | __unused vfs_context_t context) | |
3414 | { | |
0a7de745 | 3415 | return ENOTSUP; |
39236c6e A |
3416 | } |
3417 | ||
3418 | static int | |
fe8ab488 | 3419 | default_setxattr(__unused vnode_t vp, __unused const char *name, |
39236c6e A |
3420 | __unused uio_t uio, __unused int options, __unused vfs_context_t context) |
3421 | { | |
0a7de745 | 3422 | return ENOTSUP; |
39236c6e A |
3423 | } |
3424 | ||
3425 | static int | |
fe8ab488 | 3426 | default_listxattr(__unused vnode_t vp, |
39236c6e A |
3427 | __unused uio_t uio, __unused size_t *size, __unused int options, |
3428 | __unused vfs_context_t context) | |
3429 | { | |
0a7de745 | 3430 | return ENOTSUP; |
39236c6e A |
3431 | } |
3432 | ||
3433 | static int | |
fe8ab488 | 3434 | default_removexattr(__unused vnode_t vp, __unused const char *name, |
0a7de745 | 3435 | __unused int options, __unused vfs_context_t context) |
39236c6e | 3436 | { |
0a7de745 | 3437 | return ENOTSUP; |
39236c6e A |
3438 | } |
3439 | ||
3440 | #endif /* CONFIG_APPLEDOUBLE */ |