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