xref: /illumos-gate/usr/src/uts/common/os/inst_sync.c (revision 94c894bb)
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 int
_init(void)104 _init(void)
105 {
106 	return (mod_install(&modlinkage));
107 }
108 
109 int
_info(struct modinfo * modinfop)110 _info(struct modinfo *modinfop)
111 {
112 	return (mod_info(&modlinkage, modinfop));
113 }
114 
115 int
_fini(void)116 _fini(void)
117 {
118 	return (mod_remove(&modlinkage));
119 }
120 
121 static int in_write_instance(struct vnode *vp);
122 
123 static int inst_sync_disable = 0;
124 
125 static int
in_sync_sys(char * pathname,uint_t flags)126 in_sync_sys(char *pathname, uint_t flags)
127 {
128 	struct vnode *vp;
129 	int error;
130 
131 	/* For debugging/testing */
132 	if (inst_sync_disable)
133 		return (0);
134 
135 	/*
136 	 * We must have sufficient privilege to do this, since we lock critical
137 	 * data structures whilst we're doing it ..
138 	 */
139 	if ((error = secpolicy_sys_devices(CRED())) != 0)
140 		return (set_errno(error));
141 
142 	if (flags != INST_SYNC_ALWAYS && flags != INST_SYNC_IF_REQUIRED)
143 		return (set_errno(EINVAL));
144 
145 	/*
146 	 * Only one process is allowed to get the state of the instance
147 	 * number assignments on the system at any given time.
148 	 */
149 	e_ddi_enter_instance();
150 
151 	/*
152 	 * Recreate the instance file only if the device tree has changed
153 	 * or if the caller explicitly requests so.
154 	 */
155 	if (e_ddi_instance_is_clean() && flags != INST_SYNC_ALWAYS) {
156 		error = EALREADY;
157 		goto end;
158 	}
159 
160 	/*
161 	 * Create an instance file for writing, giving it a mode that
162 	 * will only permit reading.  Note that we refuse to overwrite
163 	 * an existing file.
164 	 */
165 	if ((error = vn_open(pathname, UIO_USERSPACE,
166 	    FCREAT, 0444, &vp, CRCREAT, 0)) != 0) {
167 		if (error == EISDIR)
168 			error = EACCES;	/* SVID compliance? */
169 		goto end;
170 	}
171 
172 	/*
173 	 * So far so good.  We're singly threaded, the vnode is beckoning
174 	 * so let's get on with it.  Any error, and we just give up and
175 	 * hand the first error we get back to userland.
176 	 */
177 	error = in_write_instance(vp);
178 
179 	/*
180 	 * If there was any sort of error, we deliberately go and
181 	 * remove the file we just created so that any attempts to
182 	 * use it will quickly fail.
183 	 */
184 	if (error)
185 		(void) vn_remove(pathname, UIO_USERSPACE, RMFILE);
186 	else
187 		e_ddi_instance_set_clean();
188 end:
189 	e_ddi_exit_instance();
190 	return (error ? set_errno(error) : 0);
191 }
192 
193 /*
194  * At the risk of reinventing stdio ..
195  */
196 #define	FBUFSIZE	512
197 
198 typedef struct _File {
199 	char	*ptr;
200 	int	count;
201 	char	buf[FBUFSIZE];
202 	vnode_t	*vp;
203 	offset_t voffset;
204 } File;
205 
206 static int
in_write(struct vnode * vp,offset_t * vo,caddr_t buf,int count)207 in_write(struct vnode *vp, offset_t *vo, caddr_t buf, int count)
208 {
209 	int error;
210 	ssize_t resid;
211 	rlim64_t rlimit = *vo + count + 1;
212 
213 	error = vn_rdwr(UIO_WRITE, vp, buf, count, *vo,
214 	    UIO_SYSSPACE, 0, rlimit, CRED(), &resid);
215 
216 	*vo += (offset_t)(count - resid);
217 
218 	return (error);
219 }
220 
221 static File *
in_fvpopen(struct vnode * vp)222 in_fvpopen(struct vnode *vp)
223 {
224 	File *fp;
225 
226 	fp = kmem_zalloc(sizeof (File), KM_SLEEP);
227 	fp->vp = vp;
228 	fp->ptr = fp->buf;
229 
230 	return (fp);
231 }
232 
233 static int
in_fclose(File * fp)234 in_fclose(File *fp)
235 {
236 	int error;
237 
238 	error = VOP_CLOSE(fp->vp, FCREAT, 1, (offset_t)0, CRED(), NULL);
239 	VN_RELE(fp->vp);
240 	kmem_free(fp, sizeof (File));
241 	return (error);
242 }
243 
244 static int
in_fflush(File * fp)245 in_fflush(File *fp)
246 {
247 	int error = 0;
248 
249 	if (fp->count)
250 		error = in_write(fp->vp, &fp->voffset, fp->buf, fp->count);
251 	if (error == 0)
252 		error = VOP_FSYNC(fp->vp, FSYNC, CRED(), NULL);
253 	return (error);
254 }
255 
256 static int
in_fputs(File * fp,char * buf)257 in_fputs(File *fp, char *buf)
258 {
259 	int error = 0;
260 
261 	while (*buf) {
262 		*fp->ptr++ = *buf++;
263 		if (++fp->count == FBUFSIZE) {
264 			error = in_write(fp->vp, &fp->voffset, fp->buf,
265 			    fp->count);
266 			if (error)
267 				break;
268 			fp->count = 0;
269 			fp->ptr = fp->buf;
270 		}
271 	}
272 
273 	return (error);
274 }
275 
276 /*
277  * External linkage
278  */
279 static File *in_fp;
280 
281 /*
282  * XXX what is the maximum length of the name of a driver?  Must be maximum
283  * XXX file name length (find the correct constant and substitute for this one
284  */
285 #define	DRVNAMELEN (1 + 256)
286 static char linebuffer[MAXPATHLEN + 1 + 1 + 1 + 1 + 10 + 1 + DRVNAMELEN];
287 
288 /*
289  * XXX	Maybe we should just write 'in_fprintf' instead ..
290  */
291 static int
in_walktree(in_node_t * np,char * this)292 in_walktree(in_node_t *np, char *this)
293 {
294 	char *next;
295 	int error = 0;
296 	in_drv_t *dp;
297 
298 	for (error = 0; np; np = np->in_sibling) {
299 
300 		if (np->in_drivers == NULL)
301 			continue;
302 
303 		if (np->in_unit_addr[0] == '\0')
304 			(void) sprintf(this, "/%s", np->in_node_name);
305 		else
306 			(void) sprintf(this, "/%s@%s", np->in_node_name,
307 			    np->in_unit_addr);
308 		next = this + strlen(this);
309 
310 		ASSERT(np->in_drivers);
311 
312 		for (dp = np->in_drivers; dp; dp = dp->ind_next_drv) {
313 			uint_t inst_val = dp->ind_instance;
314 
315 			/*
316 			 * Flushing IN_PROVISIONAL could result in duplicate
317 			 * instances
318 			 * Flushing IN_UNKNOWN results in instance -1
319 			 */
320 			if (dp->ind_state != IN_PERMANENT)
321 				continue;
322 
323 			(void) sprintf(next, "\" %d \"%s\"\n", inst_val,
324 			    dp->ind_driver_name);
325 			if (error = in_fputs(in_fp, linebuffer))
326 				return (error);
327 		}
328 
329 		if (np->in_child)
330 			if (error = in_walktree(np->in_child, next))
331 				break;
332 	}
333 	return (error);
334 }
335 
336 
337 /*
338  * Walk the instance tree, writing out what we find.
339  *
340  * There's some fairly nasty sharing of buffers in this
341  * bit of code, so be careful out there when you're
342  * rewriting it ..
343  */
344 static int
in_write_instance(struct vnode * vp)345 in_write_instance(struct vnode *vp)
346 {
347 	int error;
348 	char *cp;
349 
350 	in_fp = in_fvpopen(vp);
351 
352 	/*
353 	 * Place a bossy comment at the beginning of the file.
354 	 */
355 	error = in_fputs(in_fp,
356 	    "#\n#\tCaution! This file contains critical kernel state\n#\n");
357 
358 	if (error == 0) {
359 		in_node_t *root = e_ddi_instance_root();
360 		cp = linebuffer;
361 		*cp++ = '\"';
362 		error = in_walktree(root->in_child, cp);
363 	}
364 
365 	if (error == 0) {
366 		if ((error = in_fflush(in_fp)) == 0)
367 			error = in_fclose(in_fp);
368 	} else
369 		(void) in_fclose(in_fp);
370 
371 	return (error);
372 }
373