1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Syscall to write out the instance number data structures to
27 * stable storage.
28 */
29
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <sys/t_lock.h>
33 #include <sys/modctl.h>
34 #include <sys/systm.h>
35 #include <sys/syscall.h>
36 #include <sys/vfs.h>
37 #include <sys/vnode.h>
38 #include <sys/cred.h>
39 #include <sys/file.h>
40 #include <sys/cmn_err.h>
41 #include <sys/kmem.h>
42 #include <sys/cladm.h>
43 #include <sys/sunddi.h>
44 #include <sys/dditypes.h>
45 #include <sys/instance.h>
46 #include <sys/debug.h>
47 #include <sys/policy.h>
48
49 /*
50 * Userland sees:
51 *
52 * int inst_sync(pathname, flags);
53 *
54 * Returns zero if instance number information was successfully
55 * written to 'pathname', -1 plus error code in errno otherwise.
56 *
57 * POC notes:
58 *
59 * - This could be done as a case of the modctl(2) system call
60 * though the ability to have it load and unload would disappear.
61 *
62 * - 'flags' have either of two meanings:
63 * INST_SYNC_IF_REQUIRED 'pathname' will be written if there
64 * has been a change in the kernel's
65 * internal view of instance number
66 * information
67 * INST_SYNC_ALWAYS 'pathname' will be written even if
68 * the kernel's view hasn't changed.
69 *
70 * - Maybe we should pass through two filenames - one to create,
71 * and the other as the 'final' target i.e. do the rename of
72 * /etc/instance.new -> /etc/instance in the kernel.
73 */
74
75 static int in_sync_sys(char *pathname, uint_t flags);
76
77 static struct sysent in_sync_sysent = {
78 2, /* number of arguments */
79 SE_ARGC | SE_32RVAL1, /* c-style calling, 32-bit return value */
80 in_sync_sys, /* the handler */
81 (krwlock_t *)0 /* rw lock allocated/used by framework */
82 };
83
84 static struct modlsys modlsys = {
85 &mod_syscallops, "instance binding syscall", &in_sync_sysent
86 };
87
88 #ifdef _SYSCALL32_IMPL
89 static struct modlsys modlsys32 = {
90 &mod_syscallops32, "32-bit instance binding syscall", &in_sync_sysent
91 };
92 #endif
93
94 static struct modlinkage modlinkage = {
95 MODREV_1,
96 { &modlsys,
97 #ifdef _SYSCALL32_IMPL
98 &modlsys32,
99 #endif
100 NULL
101 }
102 };
103
104 int
105 _init(void)
106 {
107 return (mod_install(&modlinkage));
108 }
109
110 int
111 _info(struct modinfo *modinfop)
112 {
113 return (mod_info(&modlinkage, modinfop));
114 }
115
116 int
117 _fini(void)
118 {
119 return (mod_remove(&modlinkage));
120 }
121
122 static int in_write_instance(struct vnode *vp);
123
124 static int inst_sync_disable = 0;
125
126 static int
127 in_sync_sys(char *pathname, uint_t flags)
128 {
129 struct vnode *vp;
130 int error;
131
132 /* For debugging/testing */
133 if (inst_sync_disable)
134 return (0);
135
136 /*
137 * We must have sufficient privilege to do this, since we lock critical
138 * data structures whilst we're doing it ..
139 */
140 if ((error = secpolicy_sys_devices(CRED())) != 0)
141 return (set_errno(error));
142
143 if (flags != INST_SYNC_ALWAYS && flags != INST_SYNC_IF_REQUIRED)
144 return (set_errno(EINVAL));
145
146 /*
147 * Only one process is allowed to get the state of the instance
148 * number assignments on the system at any given time.
149 */
150 e_ddi_enter_instance();
151
152 /*
153 * Recreate the instance file only if the device tree has changed
154 * or if the caller explicitly requests so.
155 */
156 if (e_ddi_instance_is_clean() && flags != INST_SYNC_ALWAYS) {
157 error = EALREADY;
158 goto end;
159 }
160
161 /*
162 * Create an instance file for writing, giving it a mode that
163 * will only permit reading. Note that we refuse to overwrite
164 * an existing file.
165 */
166 if ((error = vn_open(pathname, UIO_USERSPACE,
167 FCREAT, 0444, &vp, CRCREAT, 0)) != 0) {
168 if (error == EISDIR)
169 error = EACCES; /* SVID compliance? */
170 goto end;
171 }
172
173 /*
174 * So far so good. We're singly threaded, the vnode is beckoning
175 * so let's get on with it. Any error, and we just give up and
176 * hand the first error we get back to userland.
177 */
178 error = in_write_instance(vp);
179
180 /*
181 * If there was any sort of error, we deliberately go and
182 * remove the file we just created so that any attempts to
183 * use it will quickly fail.
184 */
185 if (error)
186 (void) vn_remove(pathname, UIO_USERSPACE, RMFILE);
187 else
188 e_ddi_instance_set_clean();
189 end:
190 e_ddi_exit_instance();
191 return (error ? set_errno(error) : 0);
192 }
193
194 /*
195 * At the risk of reinventing stdio ..
196 */
197 #define FBUFSIZE 512
198
199 typedef struct _File {
200 char *ptr;
201 int count;
202 char buf[FBUFSIZE];
203 vnode_t *vp;
204 offset_t voffset;
205 } File;
206
207 static int
208 in_write(struct vnode *vp, offset_t *vo, caddr_t buf, int count)
209 {
210 int error;
211 ssize_t resid;
212 rlim64_t rlimit = *vo + count + 1;
213
214 error = vn_rdwr(UIO_WRITE, vp, buf, count, *vo,
215 UIO_SYSSPACE, 0, rlimit, CRED(), &resid);
216
217 *vo += (offset_t)(count - resid);
218
219 return (error);
220 }
221
222 static File *
223 in_fvpopen(struct vnode *vp)
224 {
225 File *fp;
226
227 fp = kmem_zalloc(sizeof (File), KM_SLEEP);
228 fp->vp = vp;
229 fp->ptr = fp->buf;
230
231 return (fp);
232 }
233
234 static int
235 in_fclose(File *fp)
236 {
237 int error;
238
239 error = VOP_CLOSE(fp->vp, FCREAT, 1, (offset_t)0, CRED(), NULL);
240 VN_RELE(fp->vp);
241 kmem_free(fp, sizeof (File));
242 return (error);
243 }
244
245 static int
246 in_fflush(File *fp)
247 {
248 int error = 0;
249
250 if (fp->count)
251 error = in_write(fp->vp, &fp->voffset, fp->buf, fp->count);
252 if (error == 0)
253 error = VOP_FSYNC(fp->vp, FSYNC, CRED(), NULL);
254 return (error);
255 }
256
257 static int
258 in_fputs(File *fp, char *buf)
259 {
260 int error = 0;
261
262 while (*buf) {
263 *fp->ptr++ = *buf++;
264 if (++fp->count == FBUFSIZE) {
265 error = in_write(fp->vp, &fp->voffset, fp->buf,
266 fp->count);
267 if (error)
268 break;
269 fp->count = 0;
270 fp->ptr = fp->buf;
271 }
272 }
273
274 return (error);
275 }
276
277 /*
278 * External linkage
279 */
280 static File *in_fp;
281
282 /*
283 * XXX what is the maximum length of the name of a driver? Must be maximum
284 * XXX file name length (find the correct constant and substitute for this one
285 */
286 #define DRVNAMELEN (1 + 256)
287 static char linebuffer[MAXPATHLEN + 1 + 1 + 1 + 1 + 10 + 1 + DRVNAMELEN];
288
289 /*
290 * XXX Maybe we should just write 'in_fprintf' instead ..
291 */
292 static int
293 in_walktree(in_node_t *np, char *this)
294 {
295 char *next;
296 int error = 0;
297 in_drv_t *dp;
298
299 for (error = 0; np; np = np->in_sibling) {
300
301 if (np->in_drivers == NULL)
302 continue;
303
304 if (np->in_unit_addr[0] == '\0')
305 (void) sprintf(this, "/%s", np->in_node_name);
306 else
307 (void) sprintf(this, "/%s@%s", np->in_node_name,
308 np->in_unit_addr);
309 next = this + strlen(this);
310
311 ASSERT(np->in_drivers);
312
313 for (dp = np->in_drivers; dp; dp = dp->ind_next_drv) {
314 uint_t inst_val = dp->ind_instance;
315
316 /*
317 * Flushing IN_PROVISIONAL could result in duplicate
318 * instances
319 * Flushing IN_UNKNOWN results in instance -1
320 */
321 if (dp->ind_state != IN_PERMANENT)
322 continue;
323
324 (void) sprintf(next, "\" %d \"%s\"\n", inst_val,
325 dp->ind_driver_name);
326 if (error = in_fputs(in_fp, linebuffer))
327 return (error);
328 }
329
330 if (np->in_child)
331 if (error = in_walktree(np->in_child, next))
332 break;
333 }
334 return (error);
335 }
336
337
338 /*
339 * Walk the instance tree, writing out what we find.
340 *
341 * There's some fairly nasty sharing of buffers in this
342 * bit of code, so be careful out there when you're
343 * rewriting it ..
344 */
345 static int
346 in_write_instance(struct vnode *vp)
347 {
348 int error;
349 char *cp;
350
351 in_fp = in_fvpopen(vp);
352
353 /*
354 * Place a bossy comment at the beginning of the file.
355 */
356 error = in_fputs(in_fp,
357 "#\n#\tCaution! This file contains critical kernel state\n#\n");
358
359 if (error == 0) {
360 in_node_t *root = e_ddi_instance_root();
361 cp = linebuffer;
362 *cp++ = '\"';
363 error = in_walktree(root->in_child, cp);
364 }
365
366 if (error == 0) {
367 if ((error = in_fflush(in_fp)) == 0)
368 error = in_fclose(in_fp);
369 } else
370 (void) in_fclose(in_fp);
371
372 return (error);
373 }