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