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