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 }