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