]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
cf7d32b8 | 2 | * Copyright (c) 2000-2008 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 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. | |
8f6c56a5 | 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. | |
17 | * | |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ | |
29 | /* | |
30 | * Copyright (c) 1982, 1986, 1989, 1993 | |
31 | * The Regents of the University of California. All rights reserved. | |
32 | * (c) UNIX System Laboratories, Inc. | |
33 | * All or some portions of this file are derived from material licensed | |
34 | * to the University of California by American Telephone and Telegraph | |
35 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
36 | * the permission of UNIX System Laboratories, Inc. | |
37 | * | |
38 | * Redistribution and use in source and binary forms, with or without | |
39 | * modification, are permitted provided that the following conditions | |
40 | * are met: | |
41 | * 1. Redistributions of source code must retain the above copyright | |
42 | * notice, this list of conditions and the following disclaimer. | |
43 | * 2. Redistributions in binary form must reproduce the above copyright | |
44 | * notice, this list of conditions and the following disclaimer in the | |
45 | * documentation and/or other materials provided with the distribution. | |
46 | * 3. All advertising materials mentioning features or use of this software | |
47 | * must display the following acknowledgement: | |
48 | * This product includes software developed by the University of | |
49 | * California, Berkeley and its contributors. | |
50 | * 4. Neither the name of the University nor the names of its contributors | |
51 | * may be used to endorse or promote products derived from this software | |
52 | * without specific prior written permission. | |
53 | * | |
54 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
55 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
56 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
57 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
58 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
59 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
60 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
61 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
62 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
63 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
64 | * SUCH DAMAGE. | |
65 | * | |
66 | * @(#)vfs_vnops.c 8.14 (Berkeley) 6/15/95 | |
67 | * | |
1c79356b | 68 | */ |
2d21ac55 A |
69 | /* |
70 | * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce | |
71 | * support for mandatory and extensible security protections. This notice | |
72 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
73 | * Version 2.0. | |
74 | */ | |
1c79356b A |
75 | |
76 | #include <sys/param.h> | |
91447636 | 77 | #include <sys/types.h> |
1c79356b A |
78 | #include <sys/systm.h> |
79 | #include <sys/kernel.h> | |
91447636 | 80 | #include <sys/file_internal.h> |
1c79356b | 81 | #include <sys/stat.h> |
91447636 A |
82 | #include <sys/proc_internal.h> |
83 | #include <sys/kauth.h> | |
84 | #include <sys/mount_internal.h> | |
1c79356b | 85 | #include <sys/namei.h> |
91447636 | 86 | #include <sys/vnode_internal.h> |
1c79356b A |
87 | #include <sys/ioctl.h> |
88 | #include <sys/tty.h> | |
2d21ac55 A |
89 | /* Temporary workaround for ubc.h until <rdar://4714366 is resolved */ |
90 | #define ubc_setcred ubc_setcred_deprecated | |
1c79356b | 91 | #include <sys/ubc.h> |
2d21ac55 A |
92 | #undef ubc_setcred |
93 | int ubc_setcred(struct vnode *, struct proc *); | |
9bccf70c A |
94 | #include <sys/conf.h> |
95 | #include <sys/disk.h> | |
91447636 A |
96 | #include <sys/fsevents.h> |
97 | #include <sys/kdebug.h> | |
98 | #include <sys/xattr.h> | |
99 | #include <sys/ubc_internal.h> | |
100 | #include <sys/uio_internal.h> | |
0c530ab8 | 101 | #include <sys/resourcevar.h> |
2d21ac55 | 102 | #include <sys/signalvar.h> |
9bccf70c A |
103 | |
104 | #include <vm/vm_kern.h> | |
91447636 | 105 | #include <vm/vm_map.h> |
9bccf70c A |
106 | |
107 | #include <miscfs/specfs/specdev.h> | |
108 | ||
2d21ac55 A |
109 | #if CONFIG_MACF |
110 | #include <security/mac_framework.h> | |
111 | #endif | |
91447636 A |
112 | |
113 | ||
2d21ac55 A |
114 | static int vn_closefile(struct fileglob *fp, vfs_context_t ctx); |
115 | static int vn_ioctl(struct fileproc *fp, u_long com, caddr_t data, | |
116 | vfs_context_t ctx); | |
117 | static int vn_read(struct fileproc *fp, struct uio *uio, int flags, | |
118 | vfs_context_t ctx); | |
119 | static int vn_write(struct fileproc *fp, struct uio *uio, int flags, | |
120 | vfs_context_t ctx); | |
121 | static int vn_select( struct fileproc *fp, int which, void * wql, | |
122 | vfs_context_t ctx); | |
123 | static int vn_kqfilt_add(struct fileproc *fp, struct knote *kn, | |
124 | vfs_context_t ctx); | |
91447636 | 125 | #if 0 |
2d21ac55 A |
126 | static int vn_kqfilt_remove(struct vnode *vp, uintptr_t ident, |
127 | vfs_context_t ctx); | |
91447636 | 128 | #endif |
1c79356b A |
129 | |
130 | struct fileops vnops = | |
2d21ac55 | 131 | { vn_read, vn_write, vn_ioctl, vn_select, vn_closefile, vn_kqfilt_add, NULL }; |
1c79356b A |
132 | |
133 | /* | |
134 | * Common code for vnode open operations. | |
91447636 A |
135 | * Check permissions, and call the VNOP_OPEN or VNOP_CREATE routine. |
136 | * | |
137 | * XXX the profusion of interfaces here is probably a bad thing. | |
1c79356b | 138 | */ |
9bccf70c | 139 | int |
91447636 | 140 | vn_open(struct nameidata *ndp, int fmode, int cmode) |
55e303ae | 141 | { |
91447636 | 142 | return(vn_open_modflags(ndp, &fmode, cmode)); |
55e303ae A |
143 | } |
144 | ||
91447636 A |
145 | int |
146 | vn_open_modflags(struct nameidata *ndp, int *fmodep, int cmode) | |
1c79356b | 147 | { |
91447636 A |
148 | struct vnode_attr va; |
149 | ||
150 | VATTR_INIT(&va); | |
151 | VATTR_SET(&va, va_mode, cmode); | |
152 | ||
153 | return(vn_open_auth(ndp, fmodep, &va)); | |
154 | } | |
155 | ||
0c530ab8 A |
156 | /* |
157 | * Open a file with authorization, updating the contents of the structures | |
158 | * pointed to by ndp, fmodep, and vap as necessary to perform the requested | |
159 | * operation. This function is used for both opens of existing files, and | |
160 | * creation of new files. | |
161 | * | |
162 | * Parameters: ndp The nami data pointer describing the | |
163 | * file | |
164 | * fmodep A pointer to an int containg the mode | |
165 | * information to be used for the open | |
166 | * vap A pointer to the vnode attribute | |
167 | * descriptor to be used for the open | |
168 | * | |
169 | * Indirect: * Contents of the data structures pointed | |
170 | * to by the parameters are modified as | |
171 | * necessary to the requested operation. | |
172 | * | |
173 | * Returns: 0 Success | |
174 | * !0 errno value | |
175 | * | |
176 | * Notes: The kauth_filesec_t in 'vap', if any, is in host byte order. | |
177 | * | |
178 | * The contents of '*ndp' will be modified, based on the other | |
179 | * arguments to this function, and to return file and directory | |
180 | * data necessary to satisfy the requested operation. | |
181 | * | |
182 | * If the file does not exist and we are creating it, then the | |
183 | * O_TRUNC flag will be cleared in '*fmodep' to indicate to the | |
184 | * caller that the file was not truncated. | |
185 | * | |
186 | * If the file exists and the O_EXCL flag was not specified, then | |
187 | * the O_CREAT flag will be cleared in '*fmodep' to indicate to | |
188 | * the caller that the existing file was merely opened rather | |
189 | * than created. | |
190 | * | |
191 | * The contents of '*vap' will be modified as necessary to | |
192 | * complete the operation, including setting of supported | |
193 | * attribute, clearing of fields containing unsupported attributes | |
194 | * in the request, if the request proceeds without them, etc.. | |
195 | * | |
196 | * XXX: This function is too complicated in actings on its arguments | |
197 | * | |
198 | * XXX: We should enummerate the possible errno values here, and where | |
199 | * in the code they originated. | |
200 | */ | |
91447636 A |
201 | int |
202 | vn_open_auth(struct nameidata *ndp, int *fmodep, struct vnode_attr *vap) | |
203 | { | |
204 | struct vnode *vp; | |
205 | struct vnode *dvp; | |
206 | vfs_context_t ctx = ndp->ni_cnd.cn_context; | |
1c79356b | 207 | int error; |
91447636 A |
208 | int fmode; |
209 | kauth_action_t action; | |
1c79356b | 210 | |
91447636 A |
211 | again: |
212 | vp = NULL; | |
213 | dvp = NULL; | |
214 | fmode = *fmodep; | |
1c79356b | 215 | if (fmode & O_CREAT) { |
0c530ab8 A |
216 | if ( (fmode & O_DIRECTORY) ) { |
217 | error = EINVAL; | |
218 | goto out; | |
219 | } | |
1c79356b | 220 | ndp->ni_cnd.cn_nameiop = CREATE; |
2d21ac55 A |
221 | /* Inherit USEDVP flag only */ |
222 | ndp->ni_cnd.cn_flags &= USEDVP; | |
223 | ndp->ni_cnd.cn_flags |= LOCKPARENT | LOCKLEAF | AUDITVNPATH1; | |
224 | #if NAMEDRSRCFORK | |
225 | /* open calls are allowed for resource forks. */ | |
226 | ndp->ni_cnd.cn_flags |= CN_ALLOWRSRCFORK; | |
227 | #endif | |
228 | if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) | |
1c79356b | 229 | ndp->ni_cnd.cn_flags |= FOLLOW; |
91447636 A |
230 | if ( (error = namei(ndp)) ) |
231 | goto out; | |
232 | dvp = ndp->ni_dvp; | |
233 | vp = ndp->ni_vp; | |
234 | ||
235 | /* not found, create */ | |
236 | if (vp == NULL) { | |
237 | /* must have attributes for a new file */ | |
238 | if (vap == NULL) { | |
239 | error = EINVAL; | |
240 | goto badcreate; | |
241 | } | |
242 | ||
2d21ac55 A |
243 | VATTR_SET(vap, va_type, VREG); |
244 | #if CONFIG_MACF | |
245 | error = mac_vnode_check_create(ctx, | |
246 | dvp, &ndp->ni_cnd, vap); | |
247 | if (error) | |
248 | goto badcreate; | |
249 | #endif /* MAC */ | |
250 | ||
91447636 A |
251 | /* authorize before creating */ |
252 | if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx)) != 0) | |
253 | goto badcreate; | |
254 | ||
1c79356b A |
255 | if (fmode & O_EXCL) |
256 | vap->va_vaflags |= VA_EXCLUSIVE; | |
2d21ac55 A |
257 | #if NAMEDRSRCFORK |
258 | if (ndp->ni_cnd.cn_flags & CN_WANTSRSRCFORK) { | |
259 | if ((error = vnode_makenamedstream(dvp, &ndp->ni_vp, XATTR_RESOURCEFORK_NAME, 0, ctx)) != 0) | |
260 | goto badcreate; | |
261 | } else | |
262 | #endif | |
91447636 A |
263 | if ((error = vn_create(dvp, &ndp->ni_vp, &ndp->ni_cnd, vap, 0, ctx)) != 0) |
264 | goto badcreate; | |
55e303ae | 265 | |
91447636 A |
266 | vp = ndp->ni_vp; |
267 | ||
268 | if (vp) { | |
269 | int update_flags = 0; | |
270 | ||
271 | // Make sure the name & parent pointers are hooked up | |
272 | if (vp->v_name == NULL) | |
273 | update_flags |= VNODE_UPDATE_NAME; | |
274 | if (vp->v_parent == NULLVP) | |
275 | update_flags |= VNODE_UPDATE_PARENT; | |
276 | ||
277 | if (update_flags) | |
278 | vnode_update_identity(vp, dvp, ndp->ni_cnd.cn_nameptr, ndp->ni_cnd.cn_namelen, ndp->ni_cnd.cn_hash, update_flags); | |
279 | ||
2d21ac55 | 280 | #if CONFIG_FSE |
91447636 A |
281 | if (need_fsevent(FSE_CREATE_FILE, vp)) { |
282 | add_fsevent(FSE_CREATE_FILE, ctx, | |
283 | FSE_ARG_VNODE, vp, | |
284 | FSE_ARG_DONE); | |
285 | } | |
2d21ac55 A |
286 | #endif |
287 | ||
91447636 A |
288 | } |
289 | /* | |
290 | * nameidone has to happen before we vnode_put(dvp) | |
291 | * and clear the ni_dvp field, since it may need | |
292 | * to release the fs_nodelock on the dvp | |
293 | */ | |
294 | badcreate: | |
295 | nameidone(ndp); | |
296 | ndp->ni_dvp = NULL; | |
297 | vnode_put(dvp); | |
298 | ||
299 | if (error) { | |
300 | /* | |
301 | * Check for a creation race. | |
302 | */ | |
303 | if ((error == EEXIST) && !(fmode & O_EXCL)) { | |
304 | goto again; | |
305 | } | |
306 | goto bad; | |
55e303ae | 307 | } |
91447636 | 308 | fmode &= ~O_TRUNC; |
1c79356b | 309 | } else { |
91447636 | 310 | nameidone(ndp); |
1c79356b | 311 | ndp->ni_dvp = NULL; |
91447636 A |
312 | vnode_put(dvp); |
313 | ||
1c79356b A |
314 | if (fmode & O_EXCL) { |
315 | error = EEXIST; | |
316 | goto bad; | |
317 | } | |
318 | fmode &= ~O_CREAT; | |
319 | } | |
320 | } else { | |
321 | ndp->ni_cnd.cn_nameiop = LOOKUP; | |
2d21ac55 A |
322 | /* Inherit USEDVP flag only */ |
323 | ndp->ni_cnd.cn_flags &= USEDVP; | |
324 | ndp->ni_cnd.cn_flags |= FOLLOW | LOCKLEAF | AUDITVNPATH1; | |
325 | #if NAMEDRSRCFORK | |
326 | /* open calls are allowed for resource forks. */ | |
327 | ndp->ni_cnd.cn_flags |= CN_ALLOWRSRCFORK; | |
328 | #endif | |
329 | if (fmode & O_NOFOLLOW || fmode & O_SYMLINK) { | |
330 | ndp->ni_cnd.cn_flags &= ~FOLLOW; | |
331 | } | |
332 | ||
91447636 A |
333 | if ( (error = namei(ndp)) ) |
334 | goto out; | |
1c79356b | 335 | vp = ndp->ni_vp; |
91447636 A |
336 | nameidone(ndp); |
337 | ndp->ni_dvp = NULL; | |
0c530ab8 A |
338 | |
339 | if ( (fmode & O_DIRECTORY) && vp->v_type != VDIR ) { | |
340 | error = ENOTDIR; | |
341 | goto bad; | |
342 | } | |
1c79356b | 343 | } |
2d21ac55 | 344 | |
91447636 A |
345 | if (vp->v_type == VSOCK && vp->v_tag != VT_FDESC) { |
346 | error = EOPNOTSUPP; /* Operation not supported on socket */ | |
1c79356b A |
347 | goto bad; |
348 | } | |
9bccf70c | 349 | |
2d21ac55 A |
350 | if (vp->v_type == VLNK && (fmode & O_NOFOLLOW) != 0) { |
351 | error = ELOOP; /* O_NOFOLLOW was specified and the target is a symbolic link */ | |
352 | goto bad; | |
353 | } | |
9bccf70c | 354 | |
91447636 | 355 | /* authorize open of an existing file */ |
1c79356b | 356 | if ((fmode & O_CREAT) == 0) { |
91447636 A |
357 | |
358 | /* disallow write operations on directories */ | |
359 | if (vnode_isdir(vp) && (fmode & (FWRITE | O_TRUNC))) { | |
360 | error = EISDIR; | |
361 | goto bad; | |
1c79356b | 362 | } |
91447636 | 363 | |
2d21ac55 A |
364 | #if CONFIG_MACF |
365 | error = mac_vnode_check_open(ctx, vp, fmode); | |
366 | if (error) | |
367 | goto bad; | |
368 | #endif | |
369 | ||
91447636 A |
370 | /* compute action to be authorized */ |
371 | action = 0; | |
2d21ac55 | 372 | if (fmode & FREAD) { |
91447636 | 373 | action |= KAUTH_VNODE_READ_DATA; |
2d21ac55 A |
374 | } |
375 | if (fmode & (FWRITE | O_TRUNC)) { | |
376 | /* | |
377 | * If we are writing, appending, and not truncating, | |
378 | * indicate that we are appending so that if the | |
379 | * UF_APPEND or SF_APPEND bits are set, we do not deny | |
380 | * the open. | |
381 | */ | |
382 | if ((fmode & O_APPEND) && !(fmode & O_TRUNC)) { | |
383 | action |= KAUTH_VNODE_APPEND_DATA; | |
384 | } else { | |
91447636 | 385 | action |= KAUTH_VNODE_WRITE_DATA; |
2d21ac55 A |
386 | } |
387 | } | |
91447636 A |
388 | if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) |
389 | goto bad; | |
390 | ||
2d21ac55 A |
391 | |
392 | // | |
393 | // if the vnode is tagged VOPENEVT and the current process | |
394 | // has the P_CHECKOPENEVT flag set, then we or in the O_EVTONLY | |
395 | // flag to the open mode so that this open won't count against | |
396 | // the vnode when carbon delete() does a vnode_isinuse() to see | |
397 | // if a file is currently in use. this allows spotlight | |
398 | // importers to not interfere with carbon apps that depend on | |
399 | // the no-delete-if-busy semantics of carbon delete(). | |
400 | // | |
401 | if ((vp->v_flag & VOPENEVT) && (current_proc()->p_flag & P_CHECKOPENEVT)) { | |
402 | fmode |= O_EVTONLY; | |
403 | } | |
404 | ||
1c79356b | 405 | } |
0b4e3aa0 | 406 | |
91447636 | 407 | if ( (error = VNOP_OPEN(vp, fmode, ctx)) ) { |
0b4e3aa0 A |
408 | goto bad; |
409 | } | |
2d21ac55 | 410 | if ( (error = vnode_ref_ext(vp, fmode)) ) { |
593a1d5f | 411 | goto bad2; |
2d21ac55 | 412 | } |
91447636 A |
413 | |
414 | /* call out to allow 3rd party notification of open. | |
415 | * Ignore result of kauth_authorize_fileop call. | |
416 | */ | |
417 | kauth_authorize_fileop(vfs_context_ucred(ctx), KAUTH_FILEOP_OPEN, | |
418 | (uintptr_t)vp, 0); | |
0b4e3aa0 | 419 | |
55e303ae | 420 | *fmodep = fmode; |
1c79356b | 421 | return (0); |
593a1d5f A |
422 | bad2: |
423 | VNOP_CLOSE(vp, fmode, ctx); | |
1c79356b | 424 | bad: |
55e303ae | 425 | ndp->ni_vp = NULL; |
91447636 | 426 | if (vp) { |
c910b4d9 A |
427 | #if NAMEDRSRCFORK |
428 | if ((vnode_isnamedstream(vp)) && (vp->v_parent != NULLVP) && | |
429 | (vnode_isshadow (vp))) { | |
430 | vnode_recycle(vp); | |
431 | } | |
432 | #endif | |
433 | vnode_put(vp); | |
91447636 A |
434 | /* |
435 | * Check for a race against unlink. We had a vnode | |
436 | * but according to vnode_authorize or VNOP_OPEN it | |
437 | * no longer exists. | |
935ed37a A |
438 | * |
439 | * EREDRIVEOPEN: means that we were hit by the tty allocation race. | |
91447636 | 440 | */ |
935ed37a | 441 | if (((error == ENOENT) && (*fmodep & O_CREAT)) || (error == EREDRIVEOPEN)) { |
91447636 A |
442 | goto again; |
443 | } | |
444 | } | |
445 | out: | |
1c79356b A |
446 | return (error); |
447 | } | |
448 | ||
2d21ac55 | 449 | #if vn_access_DEPRECATED |
1c79356b | 450 | /* |
91447636 A |
451 | * Authorize an action against a vnode. This has been the canonical way to |
452 | * ensure that the credential/process/etc. referenced by a vfs_context | |
453 | * is granted the rights called out in 'mode' against the vnode 'vp'. | |
454 | * | |
455 | * Unfortunately, the use of VREAD/VWRITE/VEXEC makes it very difficult | |
456 | * to add support for more rights. As such, this interface will be deprecated | |
457 | * and callers will use vnode_authorize instead. | |
1c79356b | 458 | */ |
9bccf70c | 459 | int |
91447636 | 460 | vn_access(vnode_t vp, int mode, vfs_context_t context) |
1c79356b | 461 | { |
91447636 A |
462 | kauth_action_t action; |
463 | ||
464 | action = 0; | |
465 | if (mode & VREAD) | |
466 | action |= KAUTH_VNODE_READ_DATA; | |
467 | if (mode & VWRITE) | |
468 | action |= KAUTH_VNODE_WRITE_DATA; | |
469 | if (mode & VEXEC) | |
470 | action |= KAUTH_VNODE_EXECUTE; | |
471 | ||
472 | return(vnode_authorize(vp, NULL, action, context)); | |
1c79356b | 473 | } |
2d21ac55 | 474 | #endif /* vn_access_DEPRECATED */ |
1c79356b A |
475 | |
476 | /* | |
477 | * Vnode close call | |
478 | */ | |
9bccf70c | 479 | int |
2d21ac55 | 480 | vn_close(struct vnode *vp, int flags, vfs_context_t ctx) |
1c79356b A |
481 | { |
482 | int error; | |
1c79356b | 483 | |
2d21ac55 | 484 | #if CONFIG_FSE |
91447636 A |
485 | if (flags & FWASWRITTEN) { |
486 | if (need_fsevent(FSE_CONTENT_MODIFIED, vp)) { | |
2d21ac55 | 487 | add_fsevent(FSE_CONTENT_MODIFIED, ctx, |
91447636 A |
488 | FSE_ARG_VNODE, vp, |
489 | FSE_ARG_DONE); | |
55e303ae A |
490 | } |
491 | } | |
2d21ac55 | 492 | #endif |
55e303ae | 493 | |
2d21ac55 | 494 | #if NAMEDRSRCFORK |
cf7d32b8 | 495 | /* Sync data from resource fork shadow file if needed. */ |
2d21ac55 A |
496 | if ((vp->v_flag & VISNAMEDSTREAM) && |
497 | (vp->v_parent != NULLVP) && | |
c910b4d9 | 498 | (vnode_isshadow(vp))) { |
2d21ac55 A |
499 | if (flags & FWASWRITTEN) { |
500 | (void) vnode_flushnamedstream(vp->v_parent, vp, ctx); | |
501 | } | |
2d21ac55 A |
502 | } |
503 | #endif | |
593a1d5f A |
504 | |
505 | /* work around for foxhound */ | |
506 | if (vp->v_type == VBLK) | |
507 | (void)vnode_rele_ext(vp, flags, 0); | |
508 | ||
2d21ac55 | 509 | error = VNOP_CLOSE(vp, flags, ctx); |
91447636 | 510 | |
593a1d5f A |
511 | if (vp->v_type != VBLK) |
512 | (void)vnode_rele_ext(vp, flags, 0); | |
513 | ||
1c79356b A |
514 | return (error); |
515 | } | |
516 | ||
91447636 A |
517 | static int |
518 | vn_read_swapfile( | |
519 | struct vnode *vp, | |
520 | uio_t uio) | |
521 | { | |
522 | static char *swap_read_zero_page = NULL; | |
523 | int error; | |
524 | off_t swap_count, this_count; | |
525 | off_t file_end, read_end; | |
526 | off_t prev_resid; | |
527 | ||
528 | /* | |
529 | * Reading from a swap file will get you all zeroes. | |
530 | */ | |
531 | error = 0; | |
532 | swap_count = uio_resid(uio); | |
533 | ||
534 | file_end = ubc_getsize(vp); | |
535 | read_end = uio->uio_offset + uio_resid(uio); | |
536 | if (uio->uio_offset >= file_end) { | |
537 | /* uio starts after end of file: nothing to read */ | |
538 | swap_count = 0; | |
539 | } else if (read_end > file_end) { | |
540 | /* uio extends beyond end of file: stop before that */ | |
541 | swap_count -= (read_end - file_end); | |
542 | } | |
543 | ||
544 | while (swap_count > 0) { | |
545 | if (swap_read_zero_page == NULL) { | |
546 | char *my_zero_page; | |
547 | int funnel_state; | |
548 | ||
549 | /* | |
550 | * Take kernel funnel so that only one thread | |
551 | * sets up "swap_read_zero_page". | |
552 | */ | |
553 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
554 | ||
555 | if (swap_read_zero_page == NULL) { | |
556 | MALLOC(my_zero_page, char *, PAGE_SIZE, | |
557 | M_TEMP, M_WAITOK); | |
558 | memset(my_zero_page, '?', PAGE_SIZE); | |
559 | /* | |
560 | * Adding a newline character here | |
561 | * and there prevents "less(1)", for | |
562 | * example, from getting too confused | |
563 | * about a file with one really really | |
564 | * long line. | |
565 | */ | |
566 | my_zero_page[PAGE_SIZE-1] = '\n'; | |
567 | if (swap_read_zero_page == NULL) { | |
568 | swap_read_zero_page = my_zero_page; | |
569 | } else { | |
570 | FREE(my_zero_page, M_TEMP); | |
571 | } | |
572 | } else { | |
573 | /* | |
574 | * Someone else raced us here and won; | |
575 | * just use their page. | |
576 | */ | |
577 | } | |
578 | thread_funnel_set(kernel_flock, funnel_state); | |
579 | } | |
580 | ||
581 | this_count = swap_count; | |
582 | if (this_count > PAGE_SIZE) { | |
583 | this_count = PAGE_SIZE; | |
584 | } | |
585 | ||
586 | prev_resid = uio_resid(uio); | |
587 | error = uiomove((caddr_t) swap_read_zero_page, | |
588 | this_count, | |
589 | uio); | |
590 | if (error) { | |
591 | break; | |
592 | } | |
593 | swap_count -= (prev_resid - uio_resid(uio)); | |
594 | } | |
595 | ||
596 | return error; | |
597 | } | |
1c79356b A |
598 | /* |
599 | * Package up an I/O request on a vnode into a uio and do it. | |
600 | */ | |
9bccf70c | 601 | int |
91447636 A |
602 | vn_rdwr( |
603 | enum uio_rw rw, | |
604 | struct vnode *vp, | |
605 | caddr_t base, | |
606 | int len, | |
607 | off_t offset, | |
608 | enum uio_seg segflg, | |
609 | int ioflg, | |
610 | kauth_cred_t cred, | |
611 | int *aresid, | |
2d21ac55 | 612 | proc_t p) |
1c79356b | 613 | { |
91447636 A |
614 | return vn_rdwr_64(rw, |
615 | vp, | |
616 | (uint64_t)(uintptr_t)base, | |
617 | (int64_t)len, | |
618 | offset, | |
619 | segflg, | |
620 | ioflg, | |
621 | cred, | |
622 | aresid, | |
623 | p); | |
624 | } | |
625 | ||
626 | ||
627 | int | |
628 | vn_rdwr_64( | |
629 | enum uio_rw rw, | |
630 | struct vnode *vp, | |
631 | uint64_t base, | |
632 | int64_t len, | |
633 | off_t offset, | |
634 | enum uio_seg segflg, | |
635 | int ioflg, | |
636 | kauth_cred_t cred, | |
637 | int *aresid, | |
2d21ac55 | 638 | proc_t p) |
91447636 A |
639 | { |
640 | uio_t auio; | |
641 | int spacetype; | |
642 | struct vfs_context context; | |
1c79356b | 643 | int error=0; |
91447636 A |
644 | char uio_buf[ UIO_SIZEOF(1) ]; |
645 | ||
2d21ac55 | 646 | context.vc_thread = current_thread(); |
91447636 | 647 | context.vc_ucred = cred; |
1c79356b | 648 | |
91447636 A |
649 | if (UIO_SEG_IS_USER_SPACE(segflg)) { |
650 | spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; | |
651 | } | |
652 | else { | |
653 | spacetype = UIO_SYSSPACE; | |
654 | } | |
655 | auio = uio_createwithbuffer(1, offset, spacetype, rw, | |
656 | &uio_buf[0], sizeof(uio_buf)); | |
657 | uio_addiov(auio, base, len); | |
658 | ||
2d21ac55 A |
659 | #if CONFIG_MACF |
660 | /* XXXMAC | |
661 | * IO_NOAUTH should be re-examined. | |
662 | * Likely that mediation should be performed in caller. | |
663 | */ | |
664 | if ((ioflg & IO_NOAUTH) == 0) { | |
665 | /* passed cred is fp->f_cred */ | |
666 | if (rw == UIO_READ) | |
667 | error = mac_vnode_check_read(&context, cred, vp); | |
668 | else | |
669 | error = mac_vnode_check_write(&context, cred, vp); | |
670 | } | |
671 | #endif | |
672 | ||
673 | if (error == 0) { | |
674 | if (rw == UIO_READ) { | |
675 | if (vp->v_flag & VSWAP) { | |
676 | error = vn_read_swapfile(vp, auio); | |
677 | } else { | |
678 | error = VNOP_READ(vp, auio, ioflg, &context); | |
679 | } | |
91447636 | 680 | } else { |
2d21ac55 | 681 | error = VNOP_WRITE(vp, auio, ioflg, &context); |
91447636 | 682 | } |
91447636 | 683 | } |
1c79356b A |
684 | |
685 | if (aresid) | |
91447636 A |
686 | // LP64todo - fix this |
687 | *aresid = uio_resid(auio); | |
1c79356b | 688 | else |
91447636 | 689 | if (uio_resid(auio) && error == 0) |
1c79356b | 690 | error = EIO; |
1c79356b A |
691 | return (error); |
692 | } | |
693 | ||
694 | /* | |
695 | * File table vnode read routine. | |
696 | */ | |
9bccf70c | 697 | static int |
2d21ac55 | 698 | vn_read(struct fileproc *fp, struct uio *uio, int flags, vfs_context_t ctx) |
1c79356b | 699 | { |
9bccf70c A |
700 | struct vnode *vp; |
701 | int error, ioflag; | |
1c79356b | 702 | off_t count; |
9bccf70c | 703 | |
91447636 A |
704 | vp = (struct vnode *)fp->f_fglob->fg_data; |
705 | if ( (error = vnode_getwithref(vp)) ) { | |
706 | return(error); | |
707 | } | |
2d21ac55 A |
708 | |
709 | #if CONFIG_MACF | |
710 | error = mac_vnode_check_read(ctx, vfs_context_ucred(ctx), vp); | |
711 | if (error) { | |
712 | (void)vnode_put(vp); | |
713 | return (error); | |
714 | } | |
715 | #endif | |
716 | ||
9bccf70c | 717 | ioflag = 0; |
91447636 | 718 | if (fp->f_fglob->fg_flag & FNONBLOCK) |
9bccf70c | 719 | ioflag |= IO_NDELAY; |
2d21ac55 A |
720 | if ((fp->f_fglob->fg_flag & FNOCACHE) || vnode_isnocache(vp)) |
721 | ioflag |= IO_NOCACHE; | |
722 | if (fp->f_fglob->fg_flag & FNORDAHEAD) | |
723 | ioflag |= IO_RAOFF; | |
91447636 | 724 | |
9bccf70c | 725 | if ((flags & FOF_OFFSET) == 0) |
91447636 A |
726 | uio->uio_offset = fp->f_fglob->fg_offset; |
727 | count = uio_resid(uio); | |
728 | ||
729 | if (vp->v_flag & VSWAP) { | |
730 | /* special case for swap files */ | |
731 | error = vn_read_swapfile(vp, uio); | |
732 | } else { | |
2d21ac55 | 733 | error = VNOP_READ(vp, uio, ioflag, ctx); |
9bccf70c | 734 | } |
9bccf70c | 735 | if ((flags & FOF_OFFSET) == 0) |
91447636 A |
736 | fp->f_fglob->fg_offset += count - uio_resid(uio); |
737 | ||
738 | (void)vnode_put(vp); | |
1c79356b A |
739 | return (error); |
740 | } | |
741 | ||
742 | ||
743 | /* | |
744 | * File table vnode write routine. | |
745 | */ | |
9bccf70c | 746 | static int |
2d21ac55 | 747 | vn_write(struct fileproc *fp, struct uio *uio, int flags, vfs_context_t ctx) |
1c79356b | 748 | { |
9bccf70c A |
749 | struct vnode *vp; |
750 | int error, ioflag; | |
1c79356b | 751 | off_t count; |
2d21ac55 A |
752 | int clippedsize = 0; |
753 | int partialwrite=0; | |
754 | int residcount, oldcount; | |
755 | proc_t p = vfs_context_proc(ctx); | |
91447636 | 756 | |
91447636 A |
757 | count = 0; |
758 | vp = (struct vnode *)fp->f_fglob->fg_data; | |
759 | if ( (error = vnode_getwithref(vp)) ) { | |
760 | return(error); | |
761 | } | |
2d21ac55 A |
762 | |
763 | #if CONFIG_MACF | |
764 | error = mac_vnode_check_write(ctx, vfs_context_ucred(ctx), vp); | |
765 | if (error) { | |
766 | (void)vnode_put(vp); | |
767 | return (error); | |
768 | } | |
769 | #endif | |
770 | ||
9bccf70c | 771 | ioflag = IO_UNIT; |
91447636 | 772 | if (vp->v_type == VREG && (fp->f_fglob->fg_flag & O_APPEND)) |
1c79356b | 773 | ioflag |= IO_APPEND; |
91447636 | 774 | if (fp->f_fglob->fg_flag & FNONBLOCK) |
1c79356b | 775 | ioflag |= IO_NDELAY; |
2d21ac55 A |
776 | if ((fp->f_fglob->fg_flag & FNOCACHE) || vnode_isnocache(vp)) |
777 | ioflag |= IO_NOCACHE; | |
91447636 | 778 | if ((fp->f_fglob->fg_flag & O_FSYNC) || |
1c79356b A |
779 | (vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS))) |
780 | ioflag |= IO_SYNC; | |
91447636 | 781 | |
9bccf70c | 782 | if ((flags & FOF_OFFSET) == 0) { |
91447636 A |
783 | uio->uio_offset = fp->f_fglob->fg_offset; |
784 | count = uio_resid(uio); | |
9bccf70c | 785 | } |
2d21ac55 A |
786 | if (((flags & FOF_OFFSET) == 0) && |
787 | vfs_context_proc(ctx) && (vp->v_type == VREG) && | |
788 | (((rlim_t)(uio->uio_offset + uio_uio_resid(uio)) > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) || | |
789 | ((rlim_t)uio_uio_resid(uio) > (p->p_rlimit[RLIMIT_FSIZE].rlim_cur - uio->uio_offset)))) { | |
790 | /* | |
791 | * If the requested residual would cause us to go past the | |
792 | * administrative limit, then we need to adjust the residual | |
793 | * down to cause fewer bytes than requested to be written. If | |
794 | * we can't do that (e.g. the residual is already 1 byte), | |
795 | * then we fail the write with EFBIG. | |
796 | */ | |
797 | residcount = uio_uio_resid(uio); | |
798 | if ((rlim_t)(uio->uio_offset + uio_uio_resid(uio)) > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { | |
799 | clippedsize = (uio->uio_offset + uio_uio_resid(uio)) - p->p_rlimit[RLIMIT_FSIZE].rlim_cur; | |
800 | } else if ((rlim_t)uio_uio_resid(uio) > (p->p_rlimit[RLIMIT_FSIZE].rlim_cur - uio->uio_offset)) { | |
801 | clippedsize = (p->p_rlimit[RLIMIT_FSIZE].rlim_cur - uio->uio_offset); | |
802 | } | |
803 | if (clippedsize >= residcount) { | |
804 | psignal(p, SIGXFSZ); | |
805 | vnode_put(vp); | |
806 | return (EFBIG); | |
807 | } | |
808 | partialwrite = 1; | |
809 | uio_setresid(uio, residcount-clippedsize); | |
810 | } | |
811 | if ((flags & FOF_OFFSET) != 0) { | |
812 | /* for pwrite, append should be ignored */ | |
813 | ioflag &= ~IO_APPEND; | |
814 | if (p && (vp->v_type == VREG) && | |
815 | ((rlim_t)uio->uio_offset >= p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { | |
91447636 A |
816 | psignal(p, SIGXFSZ); |
817 | vnode_put(vp); | |
818 | return (EFBIG); | |
9bccf70c | 819 | } |
2d21ac55 A |
820 | if (p && (vp->v_type == VREG) && |
821 | ((rlim_t)(uio->uio_offset + uio_uio_resid(uio)) > p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { | |
822 | //Debugger("vn_bwrite:overstepping the bounds"); | |
823 | residcount = uio_uio_resid(uio); | |
824 | clippedsize = (uio->uio_offset + uio_uio_resid(uio)) - p->p_rlimit[RLIMIT_FSIZE].rlim_cur; | |
825 | partialwrite = 1; | |
826 | uio_setresid(uio, residcount-clippedsize); | |
827 | } | |
828 | } | |
829 | ||
830 | error = VNOP_WRITE(vp, uio, ioflag, ctx); | |
91447636 | 831 | |
2d21ac55 A |
832 | if (partialwrite) { |
833 | oldcount = uio_resid(uio); | |
834 | uio_setresid(uio, oldcount + clippedsize); | |
835 | } | |
1c79356b | 836 | |
9bccf70c A |
837 | if ((flags & FOF_OFFSET) == 0) { |
838 | if (ioflag & IO_APPEND) | |
91447636 | 839 | fp->f_fglob->fg_offset = uio->uio_offset; |
9bccf70c | 840 | else |
91447636 | 841 | fp->f_fglob->fg_offset += count - uio_resid(uio); |
9bccf70c A |
842 | } |
843 | ||
1c79356b A |
844 | /* |
845 | * Set the credentials on successful writes | |
846 | */ | |
847 | if ((error == 0) && (vp->v_tag == VT_NFS) && (UBCINFOEXISTS(vp))) { | |
13fec989 A |
848 | /* |
849 | * When called from aio subsystem, we only have the proc from | |
850 | * which to get the credential, at this point, so use that | |
851 | * instead. This means aio functions are incompatible with | |
852 | * per-thread credentials (aio operations are proxied). We | |
853 | * can't easily correct the aio vs. settid race in this case | |
854 | * anyway, so we disallow it. | |
855 | */ | |
856 | if ((flags & FOF_PCRED) == 0) { | |
857 | ubc_setthreadcred(vp, p, current_thread()); | |
858 | } else { | |
859 | ubc_setcred(vp, p); | |
860 | } | |
1c79356b | 861 | } |
91447636 | 862 | (void)vnode_put(vp); |
1c79356b A |
863 | return (error); |
864 | } | |
865 | ||
866 | /* | |
867 | * File table vnode stat routine. | |
2d21ac55 A |
868 | * |
869 | * Returns: 0 Success | |
870 | * EBADF | |
871 | * ENOMEM | |
872 | * vnode_getattr:??? | |
1c79356b | 873 | */ |
9bccf70c | 874 | int |
2d21ac55 | 875 | vn_stat_noauth(struct vnode *vp, void *sbptr, kauth_filesec_t *xsec, int isstat64, vfs_context_t ctx) |
1c79356b | 876 | { |
91447636 | 877 | struct vnode_attr va; |
1c79356b A |
878 | int error; |
879 | u_short mode; | |
91447636 | 880 | kauth_filesec_t fsec; |
2d21ac55 A |
881 | struct stat *sb = (struct stat *)0; /* warning avoidance ; protected by isstat64 */ |
882 | struct stat64 * sb64 = (struct stat64 *)0; /* warning avoidance ; protected by isstat64 */ | |
883 | ||
884 | if (isstat64 != 0) | |
885 | sb64 = (struct stat64 *)sbptr; | |
886 | else | |
887 | sb = (struct stat *)sbptr; | |
91447636 A |
888 | |
889 | VATTR_INIT(&va); | |
890 | VATTR_WANTED(&va, va_fsid); | |
891 | VATTR_WANTED(&va, va_fileid); | |
892 | VATTR_WANTED(&va, va_mode); | |
893 | VATTR_WANTED(&va, va_type); | |
894 | VATTR_WANTED(&va, va_nlink); | |
895 | VATTR_WANTED(&va, va_uid); | |
896 | VATTR_WANTED(&va, va_gid); | |
897 | VATTR_WANTED(&va, va_rdev); | |
898 | VATTR_WANTED(&va, va_data_size); | |
899 | VATTR_WANTED(&va, va_access_time); | |
900 | VATTR_WANTED(&va, va_modify_time); | |
901 | VATTR_WANTED(&va, va_change_time); | |
2d21ac55 | 902 | VATTR_WANTED(&va, va_create_time); |
91447636 A |
903 | VATTR_WANTED(&va, va_flags); |
904 | VATTR_WANTED(&va, va_gen); | |
905 | VATTR_WANTED(&va, va_iosize); | |
906 | /* lower layers will synthesise va_total_alloc from va_data_size if required */ | |
907 | VATTR_WANTED(&va, va_total_alloc); | |
908 | if (xsec != NULL) { | |
909 | VATTR_WANTED(&va, va_uuuid); | |
910 | VATTR_WANTED(&va, va_guuid); | |
911 | VATTR_WANTED(&va, va_acl); | |
912 | } | |
913 | error = vnode_getattr(vp, &va, ctx); | |
1c79356b | 914 | if (error) |
91447636 | 915 | goto out; |
1c79356b A |
916 | /* |
917 | * Copy from vattr table | |
918 | */ | |
2d21ac55 A |
919 | if (isstat64 != 0) { |
920 | sb64->st_dev = va.va_fsid; | |
921 | sb64->st_ino = (ino64_t)va.va_fileid; | |
922 | ||
923 | } else { | |
924 | sb->st_dev = va.va_fsid; | |
925 | sb->st_ino = (ino_t)va.va_fileid; | |
926 | } | |
91447636 | 927 | mode = va.va_mode; |
1c79356b A |
928 | switch (vp->v_type) { |
929 | case VREG: | |
930 | mode |= S_IFREG; | |
931 | break; | |
932 | case VDIR: | |
933 | mode |= S_IFDIR; | |
934 | break; | |
935 | case VBLK: | |
936 | mode |= S_IFBLK; | |
937 | break; | |
938 | case VCHR: | |
939 | mode |= S_IFCHR; | |
940 | break; | |
941 | case VLNK: | |
942 | mode |= S_IFLNK; | |
943 | break; | |
944 | case VSOCK: | |
945 | mode |= S_IFSOCK; | |
946 | break; | |
947 | case VFIFO: | |
948 | mode |= S_IFIFO; | |
949 | break; | |
950 | default: | |
91447636 A |
951 | error = EBADF; |
952 | goto out; | |
1c79356b | 953 | }; |
2d21ac55 A |
954 | if (isstat64 != 0) { |
955 | sb64->st_mode = mode; | |
956 | sb64->st_nlink = VATTR_IS_SUPPORTED(&va, va_nlink) ? (u_int16_t)va.va_nlink : 1; | |
957 | sb64->st_uid = va.va_uid; | |
958 | sb64->st_gid = va.va_gid; | |
959 | sb64->st_rdev = va.va_rdev; | |
960 | sb64->st_size = va.va_data_size; | |
961 | sb64->st_atimespec = va.va_access_time; | |
962 | sb64->st_mtimespec = va.va_modify_time; | |
963 | sb64->st_ctimespec = va.va_change_time; | |
964 | sb64->st_birthtimespec = | |
965 | VATTR_IS_SUPPORTED(&va, va_create_time) ? va.va_create_time : va.va_change_time; | |
966 | sb64->st_blksize = va.va_iosize; | |
967 | sb64->st_flags = va.va_flags; | |
968 | sb64->st_blocks = roundup(va.va_total_alloc, 512) / 512; | |
969 | } else { | |
970 | sb->st_mode = mode; | |
971 | sb->st_nlink = VATTR_IS_SUPPORTED(&va, va_nlink) ? (u_int16_t)va.va_nlink : 1; | |
972 | sb->st_uid = va.va_uid; | |
973 | sb->st_gid = va.va_gid; | |
974 | sb->st_rdev = va.va_rdev; | |
975 | sb->st_size = va.va_data_size; | |
976 | sb->st_atimespec = va.va_access_time; | |
977 | sb->st_mtimespec = va.va_modify_time; | |
978 | sb->st_ctimespec = va.va_change_time; | |
979 | sb->st_blksize = va.va_iosize; | |
980 | sb->st_flags = va.va_flags; | |
981 | sb->st_blocks = roundup(va.va_total_alloc, 512) / 512; | |
982 | } | |
91447636 A |
983 | |
984 | /* if we're interested in exended security data and we got an ACL */ | |
985 | if (xsec != NULL) { | |
986 | if (!VATTR_IS_SUPPORTED(&va, va_acl) && | |
987 | !VATTR_IS_SUPPORTED(&va, va_uuuid) && | |
988 | !VATTR_IS_SUPPORTED(&va, va_guuid)) { | |
989 | *xsec = KAUTH_FILESEC_NONE; | |
990 | } else { | |
991 | ||
992 | if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) { | |
993 | fsec = kauth_filesec_alloc(va.va_acl->acl_entrycount); | |
994 | } else { | |
995 | fsec = kauth_filesec_alloc(0); | |
996 | } | |
997 | if (fsec == NULL) { | |
998 | error = ENOMEM; | |
999 | goto out; | |
1000 | } | |
1001 | fsec->fsec_magic = KAUTH_FILESEC_MAGIC; | |
1002 | if (VATTR_IS_SUPPORTED(&va, va_uuuid)) { | |
1003 | fsec->fsec_owner = va.va_uuuid; | |
1004 | } else { | |
1005 | fsec->fsec_owner = kauth_null_guid; | |
1006 | } | |
1007 | if (VATTR_IS_SUPPORTED(&va, va_guuid)) { | |
1008 | fsec->fsec_group = va.va_guuid; | |
1009 | } else { | |
1010 | fsec->fsec_group = kauth_null_guid; | |
1011 | } | |
1012 | if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) { | |
1013 | bcopy(va.va_acl, &(fsec->fsec_acl), KAUTH_ACL_COPYSIZE(va.va_acl)); | |
1014 | } else { | |
1015 | fsec->fsec_acl.acl_entrycount = KAUTH_FILESEC_NOACL; | |
1016 | } | |
1017 | *xsec = fsec; | |
1018 | } | |
1019 | } | |
1020 | ||
1c79356b | 1021 | /* Do not give the generation number out to unpriviledged users */ |
2d21ac55 A |
1022 | if (va.va_gen && !vfs_context_issuser(ctx)) { |
1023 | if (isstat64 != 0) | |
1024 | sb64->st_gen = 0; | |
1025 | else | |
1026 | sb->st_gen = 0; | |
1027 | } else { | |
1028 | if (isstat64 != 0) | |
1029 | sb64->st_gen = va.va_gen; | |
1030 | else | |
1031 | sb->st_gen = va.va_gen; | |
1032 | } | |
91447636 A |
1033 | |
1034 | error = 0; | |
1035 | out: | |
1036 | if (VATTR_IS_SUPPORTED(&va, va_acl) && va.va_acl != NULL) | |
1037 | kauth_acl_free(va.va_acl); | |
1038 | return (error); | |
1039 | } | |
1040 | ||
1041 | int | |
2d21ac55 | 1042 | vn_stat(struct vnode *vp, void *sb, kauth_filesec_t *xsec, int isstat64, vfs_context_t ctx) |
91447636 A |
1043 | { |
1044 | int error; | |
1045 | ||
2d21ac55 A |
1046 | #if CONFIG_MACF |
1047 | error = mac_vnode_check_stat(ctx, NOCRED, vp); | |
1048 | if (error) | |
1049 | return (error); | |
1050 | #endif | |
1051 | ||
91447636 A |
1052 | /* authorize */ |
1053 | if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_ATTRIBUTES | KAUTH_VNODE_READ_SECURITY, ctx)) != 0) | |
1054 | return(error); | |
1055 | ||
1056 | /* actual stat */ | |
2d21ac55 | 1057 | return(vn_stat_noauth(vp, sb, xsec, isstat64, ctx)); |
1c79356b A |
1058 | } |
1059 | ||
91447636 | 1060 | |
1c79356b A |
1061 | /* |
1062 | * File table vnode ioctl routine. | |
1063 | */ | |
9bccf70c | 1064 | static int |
2d21ac55 | 1065 | vn_ioctl(struct fileproc *fp, u_long com, caddr_t data, vfs_context_t ctx) |
1c79356b | 1066 | { |
2d21ac55 | 1067 | struct vnode *vp = ((struct vnode *)fp->f_fglob->fg_data); |
91447636 | 1068 | off_t file_size; |
1c79356b | 1069 | int error; |
fa4905b1 | 1070 | struct vnode *ttyvp; |
91447636 | 1071 | int funnel_state; |
2d21ac55 | 1072 | struct session * sessp; |
9bccf70c | 1073 | |
91447636 A |
1074 | if ( (error = vnode_getwithref(vp)) ) { |
1075 | return(error); | |
1076 | } | |
91447636 | 1077 | |
2d21ac55 A |
1078 | #if CONFIG_MACF |
1079 | error = mac_vnode_check_ioctl(ctx, vp, com); | |
1080 | if (error) | |
1081 | goto out; | |
1082 | #endif | |
1c79356b | 1083 | |
2d21ac55 | 1084 | switch (vp->v_type) { |
1c79356b A |
1085 | case VREG: |
1086 | case VDIR: | |
1087 | if (com == FIONREAD) { | |
2d21ac55 | 1088 | if ((error = vnode_size(vp, &file_size, ctx)) != 0) |
91447636 A |
1089 | goto out; |
1090 | *(int *)data = file_size - fp->f_fglob->fg_offset; | |
1091 | goto out; | |
1092 | } | |
1093 | if (com == FIONBIO || com == FIOASYNC) { /* XXX */ | |
1094 | goto out; | |
1c79356b | 1095 | } |
1c79356b A |
1096 | /* fall into ... */ |
1097 | ||
1098 | default: | |
91447636 A |
1099 | error = ENOTTY; |
1100 | goto out; | |
1c79356b A |
1101 | |
1102 | case VFIFO: | |
1103 | case VCHR: | |
1104 | case VBLK: | |
9bccf70c | 1105 | |
91447636 A |
1106 | /* Should not be able to set block size from user space */ |
1107 | if (com == DKIOCSETBLOCKSIZE) { | |
1108 | error = EPERM; | |
1109 | goto out; | |
1110 | } | |
1111 | ||
1112 | if (com == FIODTYPE) { | |
1113 | if (vp->v_type == VBLK) { | |
1114 | if (major(vp->v_rdev) >= nblkdev) { | |
1115 | error = ENXIO; | |
1116 | goto out; | |
1117 | } | |
1118 | *(int *)data = bdevsw[major(vp->v_rdev)].d_type; | |
1119 | ||
1120 | } else if (vp->v_type == VCHR) { | |
1121 | if (major(vp->v_rdev) >= nchrdev) { | |
1122 | error = ENXIO; | |
1123 | goto out; | |
1124 | } | |
1125 | *(int *)data = cdevsw[major(vp->v_rdev)].d_type; | |
1126 | } else { | |
1127 | error = ENOTTY; | |
1128 | goto out; | |
1129 | } | |
1130 | goto out; | |
1131 | } | |
2d21ac55 | 1132 | error = VNOP_IOCTL(vp, com, data, fp->f_fglob->fg_flag, ctx); |
91447636 A |
1133 | |
1134 | if (error == 0 && com == TIOCSCTTY) { | |
1135 | vnode_ref(vp); | |
1136 | ||
1137 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
2d21ac55 A |
1138 | sessp = proc_session(vfs_context_proc(ctx)); |
1139 | ||
1140 | session_lock(sessp); | |
1141 | ttyvp = sessp->s_ttyvp; | |
1142 | sessp->s_ttyvp = vp; | |
1143 | sessp->s_ttyvid = vnode_vid(vp); | |
1144 | session_unlock(sessp); | |
1145 | session_rele(sessp); | |
91447636 A |
1146 | thread_funnel_set(kernel_flock, funnel_state); |
1147 | ||
1148 | if (ttyvp) | |
1149 | vnode_rele(ttyvp); | |
1150 | } | |
1c79356b | 1151 | } |
91447636 A |
1152 | out: |
1153 | (void)vnode_put(vp); | |
1154 | return(error); | |
1c79356b A |
1155 | } |
1156 | ||
1157 | /* | |
1158 | * File table vnode select routine. | |
1159 | */ | |
9bccf70c | 1160 | static int |
2d21ac55 | 1161 | vn_select(struct fileproc *fp, int which, void *wql, __unused vfs_context_t ctx) |
1c79356b | 1162 | { |
91447636 A |
1163 | int error; |
1164 | struct vnode * vp = (struct vnode *)fp->f_fglob->fg_data; | |
1165 | struct vfs_context context; | |
1166 | ||
1167 | if ( (error = vnode_getwithref(vp)) == 0 ) { | |
2d21ac55 | 1168 | context.vc_thread = current_thread(); |
91447636 A |
1169 | context.vc_ucred = fp->f_fglob->fg_cred; |
1170 | ||
2d21ac55 A |
1171 | #if CONFIG_MACF |
1172 | /* | |
1173 | * XXX We should use a per thread credential here; minimally, | |
1174 | * XXX the process credential should have a persistent | |
1175 | * XXX reference on it before being passed in here. | |
1176 | */ | |
1177 | error = mac_vnode_check_select(ctx, vp, which); | |
1178 | if (error == 0) | |
1179 | #endif | |
1180 | error = VNOP_SELECT(vp, which, fp->f_fglob->fg_flag, wql, ctx); | |
1c79356b | 1181 | |
91447636 A |
1182 | (void)vnode_put(vp); |
1183 | } | |
1184 | return(error); | |
1185 | ||
1c79356b A |
1186 | } |
1187 | ||
1188 | /* | |
1189 | * Check that the vnode is still valid, and if so | |
1190 | * acquire requested lock. | |
1191 | */ | |
1192 | int | |
91447636 | 1193 | vn_lock(__unused vnode_t vp, __unused int flags, __unused proc_t p) |
1c79356b | 1194 | { |
91447636 | 1195 | return (0); |
1c79356b A |
1196 | } |
1197 | ||
1198 | /* | |
1199 | * File table vnode close routine. | |
1200 | */ | |
9bccf70c | 1201 | static int |
2d21ac55 | 1202 | vn_closefile(struct fileglob *fg, vfs_context_t ctx) |
1c79356b | 1203 | { |
91447636 A |
1204 | struct vnode *vp = (struct vnode *)fg->fg_data; |
1205 | int error; | |
2d21ac55 | 1206 | struct flock lf; |
91447636 A |
1207 | |
1208 | if ( (error = vnode_getwithref(vp)) == 0 ) { | |
2d21ac55 A |
1209 | |
1210 | if ((fg->fg_flag & FHASLOCK) && fg->fg_type == DTYPE_VNODE) { | |
1211 | lf.l_whence = SEEK_SET; | |
1212 | lf.l_start = 0; | |
1213 | lf.l_len = 0; | |
1214 | lf.l_type = F_UNLCK; | |
1215 | ||
1216 | (void)VNOP_ADVLOCK(vp, (caddr_t)fg, F_UNLCK, &lf, F_FLOCK, ctx); | |
1217 | } | |
1218 | error = vn_close(vp, fg->fg_flag, ctx); | |
91447636 A |
1219 | |
1220 | (void)vnode_put(vp); | |
1221 | } | |
1222 | return(error); | |
1223 | } | |
1224 | ||
2d21ac55 A |
1225 | /* |
1226 | * Returns: 0 Success | |
1227 | * VNOP_PATHCONF:??? | |
1228 | */ | |
91447636 A |
1229 | int |
1230 | vn_pathconf(vnode_t vp, int name, register_t *retval, vfs_context_t ctx) | |
1231 | { | |
1232 | int error = 0; | |
1233 | ||
1234 | switch(name) { | |
1235 | case _PC_EXTENDED_SECURITY_NP: | |
2d21ac55 | 1236 | *retval = vfs_extendedsecurity(vnode_mount(vp)) ? 1 : 0; |
91447636 A |
1237 | break; |
1238 | case _PC_AUTH_OPAQUE_NP: | |
1239 | *retval = vfs_authopaque(vnode_mount(vp)); | |
1240 | break; | |
2d21ac55 A |
1241 | case _PC_2_SYMLINKS: |
1242 | *retval = 1; /* XXX NOTSUP on MSDOS, etc. */ | |
1243 | break; | |
1244 | case _PC_ALLOC_SIZE_MIN: | |
1245 | *retval = 1; /* XXX lie: 1 byte */ | |
1246 | break; | |
1247 | case _PC_ASYNC_IO: /* unistd.h: _POSIX_ASYNCHRONUS_IO */ | |
1248 | *retval = 1; /* [AIO] option is supported */ | |
1249 | break; | |
1250 | case _PC_PRIO_IO: /* unistd.h: _POSIX_PRIORITIZED_IO */ | |
1251 | *retval = 0; /* [PIO] option is not supported */ | |
1252 | break; | |
1253 | case _PC_REC_INCR_XFER_SIZE: | |
1254 | *retval = 4096; /* XXX go from MIN to MAX 4K at a time */ | |
1255 | break; | |
1256 | case _PC_REC_MIN_XFER_SIZE: | |
1257 | *retval = 4096; /* XXX recommend 4K minimum reads/writes */ | |
1258 | break; | |
1259 | case _PC_REC_MAX_XFER_SIZE: | |
1260 | *retval = 65536; /* XXX recommend 64K maximum reads/writes */ | |
1261 | break; | |
1262 | case _PC_REC_XFER_ALIGN: | |
1263 | *retval = 4096; /* XXX recommend page aligned buffers */ | |
1264 | break; | |
1265 | case _PC_SYMLINK_MAX: | |
1266 | *retval = 255; /* Minimum acceptable POSIX value */ | |
1267 | break; | |
1268 | case _PC_SYNC_IO: /* unistd.h: _POSIX_SYNCHRONIZED_IO */ | |
1269 | *retval = 0; /* [SIO] option is not supported */ | |
1270 | break; | |
91447636 A |
1271 | default: |
1272 | error = VNOP_PATHCONF(vp, name, retval, ctx); | |
1273 | break; | |
1274 | } | |
1c79356b | 1275 | |
91447636 | 1276 | return (error); |
1c79356b | 1277 | } |
55e303ae A |
1278 | |
1279 | static int | |
2d21ac55 | 1280 | vn_kqfilt_add(struct fileproc *fp, struct knote *kn, vfs_context_t ctx) |
55e303ae | 1281 | { |
91447636 | 1282 | struct vnode *vp = (struct vnode *)fp->f_fglob->fg_data; |
55e303ae | 1283 | int error; |
91447636 | 1284 | int funnel_state; |
55e303ae | 1285 | |
91447636 | 1286 | if ( (error = vnode_getwithref(vp)) == 0 ) { |
2d21ac55 A |
1287 | |
1288 | #if CONFIG_MACF | |
1289 | error = mac_vnode_check_kqfilter(ctx, fp->f_fglob->fg_cred, kn, vp); | |
1290 | if (error) { | |
1291 | (void)vnode_put(vp); | |
1292 | return (error); | |
1293 | } | |
1294 | #endif | |
91447636 A |
1295 | |
1296 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
2d21ac55 | 1297 | error = VNOP_KQFILT_ADD(vp, kn, ctx); |
91447636 A |
1298 | thread_funnel_set(kernel_flock, funnel_state); |
1299 | ||
1300 | (void)vnode_put(vp); | |
1301 | } | |
55e303ae A |
1302 | return (error); |
1303 | } | |
1304 | ||
91447636 A |
1305 | #if 0 |
1306 | /* No one calls this yet. */ | |
55e303ae | 1307 | static int |
2d21ac55 | 1308 | vn_kqfilt_remove(vp, ident, ctx) |
55e303ae A |
1309 | struct vnode *vp; |
1310 | uintptr_t ident; | |
2d21ac55 | 1311 | vfs_context_t ctx; |
55e303ae A |
1312 | { |
1313 | int error; | |
91447636 | 1314 | int funnel_state; |
55e303ae | 1315 | |
91447636 | 1316 | if ( (error = vnode_getwithref(vp)) == 0 ) { |
91447636 A |
1317 | |
1318 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
2d21ac55 | 1319 | error = VNOP_KQFILT_REMOVE(vp, ident, ctx); |
91447636 A |
1320 | thread_funnel_set(kernel_flock, funnel_state); |
1321 | ||
1322 | (void)vnode_put(vp); | |
1323 | } | |
55e303ae A |
1324 | return (error); |
1325 | } | |
91447636 | 1326 | #endif |