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