]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. | |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
22 | * | |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | ||
26 | #include <sys/param.h> | |
27 | #include <sys/fcntl.h> | |
28 | #include <sys/malloc.h> | |
29 | #include <sys/namei.h> | |
30 | #include <sys/proc.h> | |
31 | #include <sys/stat.h> | |
32 | #include <sys/sysctl.h> | |
33 | #include <sys/vnode.h> | |
34 | #include <sys/vm.h> | |
35 | ||
36 | #include <mach/kern_return.h> | |
37 | ||
38 | /* prototypes not exported by osfmk. */ | |
39 | extern void kmem_free(vm_map_t, vm_offset_t, vm_size_t); | |
40 | extern kern_return_t kmem_alloc_wired(vm_map_t, vm_offset_t *, vm_size_t); | |
41 | ||
42 | ||
43 | /* Globals */ | |
44 | static off_t imagesizelimit = (4 * 4096); | |
45 | ||
46 | /* Information about the current panic image */ | |
47 | static int image_bits = 32; /* Bitdepth */ | |
48 | ||
49 | static char *image_pathname = NULL; /* path to it */ | |
50 | static size_t image_pathlen = 0; /* and the length of the pathname */ | |
51 | ||
52 | static vm_offset_t image_ptr = NULL; /* the image itself */ | |
53 | static off_t image_size = 0; /* and the imagesize */ | |
54 | ||
55 | ||
56 | __private_extern__ void | |
57 | get_panicimage(vm_offset_t *imageptr, vm_size_t *imagesize, int *imagebits) | |
58 | { | |
59 | *imageptr = image_ptr; | |
60 | *imagesize = image_size; | |
61 | *imagebits = image_bits; | |
62 | } | |
63 | ||
64 | static int | |
65 | panicimage_from_file( | |
66 | char *imname, | |
67 | off_t sizelimit, | |
68 | vm_offset_t *image, | |
69 | off_t *filesize, | |
70 | struct proc *p) | |
71 | { | |
72 | int error = 0; | |
73 | int error1 = 0; | |
74 | int aresid; | |
75 | struct nameidata nd; | |
76 | struct vattr vattr; | |
77 | struct vnode * vp; | |
78 | kern_return_t kret; | |
79 | struct pcred *pcred = p->p_cred; | |
80 | struct ucred *cred = pcred->pc_ucred; | |
81 | vm_offset_t iobuf; | |
82 | ||
83 | /* Open the file */ | |
84 | NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, imname, p); | |
85 | error = vn_open(&nd, FREAD, S_IRUSR); | |
86 | if (error) | |
87 | return (error); | |
88 | vp = nd.ni_vp; | |
89 | ||
90 | if (vp->v_type != VREG) { | |
91 | error = EFAULT; | |
92 | goto out; | |
93 | } | |
94 | ||
95 | /* get the file size */ | |
96 | error = VOP_GETATTR(vp, &vattr, cred, p); | |
97 | if (error) | |
98 | goto out; | |
99 | ||
100 | /* validate the file size */ | |
101 | if (vattr.va_size > sizelimit) { | |
102 | error = EFBIG; | |
103 | goto out; | |
104 | } | |
105 | ||
106 | /* allocate kernel wired memory */ | |
107 | kret = kmem_alloc_wired(kernel_map, &iobuf, | |
108 | (vm_size_t)vattr.va_size); | |
109 | if (kret != KERN_SUCCESS) { | |
110 | switch (kret) { | |
111 | default: | |
112 | error = EINVAL; | |
113 | break; | |
114 | case KERN_NO_SPACE: | |
115 | case KERN_RESOURCE_SHORTAGE: | |
116 | error = ENOMEM; | |
117 | break; | |
118 | case KERN_PROTECTION_FAILURE: | |
119 | error = EPERM; | |
120 | break; | |
121 | } | |
122 | goto out; | |
123 | } | |
124 | ||
125 | /* read the file in the kernel buffer */ | |
126 | error = vn_rdwr(UIO_READ, vp, (caddr_t)iobuf, (int)vattr.va_size, | |
127 | (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, | |
128 | cred, &aresid, p); | |
129 | if (error) { | |
130 | (void)kmem_free(kernel_map, iobuf, (vm_size_t)vattr.va_size); | |
131 | goto out; | |
132 | } | |
133 | ||
134 | /* | |
135 | * return the image to the caller | |
136 | * freeing this memory is callers responsibility | |
137 | */ | |
138 | *image = iobuf; | |
139 | *filesize = (off_t)vattr.va_size; | |
140 | ||
141 | out: | |
142 | VOP_UNLOCK(vp, 0, p); | |
143 | error1 = vn_close(vp, FREAD, cred, p); | |
144 | if (error == 0) | |
145 | error = error1; | |
146 | return (error); | |
147 | } | |
148 | ||
149 | __private_extern__ int | |
150 | sysctl_dopanicinfo(name, namelen, oldp, oldlenp, newp, newlen, p) | |
151 | int *name; | |
152 | u_int namelen; | |
153 | void *oldp; | |
154 | size_t *oldlenp; | |
155 | void *newp; | |
156 | size_t newlen; | |
157 | struct proc *p; | |
158 | { | |
159 | int error = 0; | |
160 | int bitdepth = 32; /* default is 32 bits */ | |
161 | char *imname; | |
162 | ||
163 | /* all sysctl names at this level are terminal */ | |
164 | if (namelen != 1) | |
165 | return (ENOTDIR); /* overloaded */ | |
166 | ||
167 | switch (name[0]) { | |
168 | default: | |
169 | return (EOPNOTSUPP); | |
170 | case KERN_PANICINFO_MAXSIZE: | |
171 | if (newp != NULL && (error = suser(p->p_ucred, &p->p_acflag))) | |
172 | return (error); | |
173 | error = sysctl_quad(oldp, oldlenp, newp, newlen, &imagesizelimit); | |
174 | return (error); | |
175 | ||
176 | case KERN_PANICINFO_IMAGE16: | |
177 | bitdepth = 16; | |
178 | /* and fall through */ | |
179 | case KERN_PANICINFO_IMAGE32: | |
180 | /* allocate a buffer for the image pathname */ | |
181 | MALLOC_ZONE(imname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); | |
182 | ||
183 | if (!newp) { | |
184 | bcopy(image_pathname, imname, image_pathlen); | |
185 | imname[image_pathlen] = '\0'; | |
186 | } else | |
187 | imname[0] = '\0'; | |
188 | error = sysctl_string(oldp, oldlenp, newp, newlen, | |
189 | imname, MAXPATHLEN); | |
190 | if (newp && !error) { | |
191 | char *tmpstr, *oldstr; | |
192 | off_t filesize = 0; | |
193 | size_t len; | |
194 | vm_offset_t image; | |
195 | vm_offset_t oimage = NULL; | |
196 | vm_size_t osize = 0; /* covariable: quiet compiler */ | |
197 | ||
198 | len = strlen(imname); | |
199 | oldstr = image_pathname; | |
200 | ||
201 | error = panicimage_from_file(imname, imagesizelimit, | |
202 | &image, &filesize, p); | |
203 | if (error) | |
204 | goto errout; | |
205 | ||
206 | /* release the old image */ | |
207 | if (image_ptr) { | |
208 | oimage = image_ptr; | |
209 | osize = image_size; | |
210 | } | |
211 | ||
212 | /* remember the new one */ | |
213 | image_ptr = image; | |
214 | image_bits = bitdepth; /* new bith depth */ | |
215 | image_size = filesize; /* new imagesize */ | |
216 | ||
217 | if (oimage) | |
218 | kmem_free(kernel_map, oimage, osize); | |
219 | ||
220 | /* save the new name */ | |
221 | MALLOC(tmpstr, char *, len+1, M_TEMP, M_WAITOK); | |
222 | bcopy(imname, tmpstr, len); | |
223 | tmpstr[len] = '\0'; | |
224 | ||
225 | image_pathname = tmpstr; /* new pathname */ | |
226 | image_pathlen = len; /* new pathname length */ | |
227 | ||
228 | /* free the old name */ | |
229 | FREE(oldstr, M_TEMP); | |
230 | } | |
231 | errout: | |
232 | FREE_ZONE(imname, MAXPATHLEN, M_NAMEI); | |
233 | return (error); | |
234 | } | |
235 | } |