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