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 104 _init(void) 105 { 106 return (mod_install(&modlinkage)); 107 } 108 109 int 110 _info(struct modinfo *modinfop) 111 { 112 return (mod_info(&modlinkage, modinfop)); 113 } 114 115 int 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 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 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 * 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 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 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 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 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 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