Print this page
OS-4043 tmpfs should support gigabyte sizes
OS-4044 tmpfs should support "mode" option
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>


   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) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/param.h>
  28 #include <sys/sysmacros.h>
  29 #include <sys/kmem.h>
  30 #include <sys/time.h>
  31 #include <sys/pathname.h>
  32 #include <sys/vfs.h>
  33 #include <sys/vfs_opreg.h>
  34 #include <sys/vnode.h>
  35 #include <sys/stat.h>
  36 #include <sys/uio.h>
  37 #include <sys/stat.h>
  38 #include <sys/errno.h>
  39 #include <sys/cmn_err.h>
  40 #include <sys/cred.h>
  41 #include <sys/statvfs.h>
  42 #include <sys/mount.h>
  43 #include <sys/debug.h>


  74 static mntopts_t tmpfs_proto_opttbl;
  75 
  76 static vfsdef_t vfw = {
  77         VFSDEF_VERSION,
  78         "tmpfs",
  79         tmpfsinit,
  80         VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_ZMOUNT,
  81         &tmpfs_proto_opttbl
  82 };
  83 
  84 /*
  85  * in-kernel mnttab options
  86  */
  87 static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL };
  88 static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL };
  89 
  90 static mntopt_t tmpfs_options[] = {
  91         /* Option name          Cancel Opt      Arg     Flags           Data */
  92         { MNTOPT_XATTR,         xattr_cancel,   NULL,   MO_DEFAULT,     NULL},
  93         { MNTOPT_NOXATTR,       noxattr_cancel, NULL,   NULL,           NULL},
  94         { "size",               NULL,           "0",    MO_HASVALUE,    NULL}

  95 };
  96 
  97 
  98 static mntopts_t tmpfs_proto_opttbl = {
  99         sizeof (tmpfs_options) / sizeof (mntopt_t),
 100         tmpfs_options
 101 };
 102 
 103 /*
 104  * Module linkage information
 105  */
 106 static struct modlfs modlfs = {
 107         &mod_fsops, "filesystem for tmpfs", &vfw
 108 };
 109 
 110 static struct modlinkage modlinkage = {
 111         MODREV_1, &modlfs, NULL
 112 };
 113 
 114 int


 210                  */
 211                 tmpfs_minfree = btopr(TMPMINFREE);
 212         }
 213 
 214         /*
 215          * The maximum amount of space tmpfs can allocate is
 216          * TMPMAXPROCKMEM percent of kernel memory
 217          */
 218         if (tmpfs_maxkmem == 0)
 219                 tmpfs_maxkmem = MAX(PAGESIZE, kmem_maxavail() / TMPMAXFRACKMEM);
 220 
 221         if ((tmpfs_major = getudev()) == (major_t)-1) {
 222                 cmn_err(CE_WARN, "tmpfsinit: Can't get unique device number.");
 223                 tmpfs_major = 0;
 224         }
 225         mutex_init(&tmpfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
 226         return (0);
 227 }
 228 
 229 static int
 230 tmp_mount(
 231         struct vfs *vfsp,
 232         struct vnode *mvp,
 233         struct mounta *uap,
 234         struct cred *cr)
 235 {
 236         struct tmount *tm = NULL;
 237         struct tmpnode *tp;
 238         struct pathname dpn;
 239         int error;
 240         pgcnt_t anonmax;
 241         struct vattr rattr;
 242         int got_attrs;



 243 
 244         char *sizestr;
 245 
 246         if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
 247                 return (error);
 248 
 249         if (mvp->v_type != VDIR)
 250                 return (ENOTDIR);
 251 
 252         mutex_enter(&mvp->v_lock);
 253         if ((uap->flags & MS_REMOUNT) == 0 && (uap->flags & MS_OVERLAY) == 0 &&
 254             (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
 255                 mutex_exit(&mvp->v_lock);
 256                 return (EBUSY);
 257         }
 258         mutex_exit(&mvp->v_lock);
 259 
 260         /*
 261          * Having the resource be anything but "swap" doesn't make sense.
 262          */
 263         vfs_setresource(vfsp, "swap", 0);
 264 
 265         /*
 266          * now look for options we understand...
 267          */
 268 
 269         /* tmpfs doesn't support read-only mounts */
 270         if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
 271                 error = EINVAL;
 272                 goto out;
 273         }
 274 
 275         /*
 276          * tm_anonmax is set according to the mount arguments
 277          * if any.  Otherwise, it is set to a maximum value.
 278          */
 279         if (vfs_optionisset(vfsp, "size", &sizestr)) {
 280                 if ((error = tmp_convnum(sizestr, &anonmax)) != 0)
 281                         goto out;
 282         } else {
 283                 anonmax = ULONG_MAX;
 284         }
 285 











 286         if (error = pn_get(uap->dir,
 287             (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn))
 288                 goto out;
 289 
 290         if (uap->flags & MS_REMOUNT) {
 291                 tm = (struct tmount *)VFSTOTM(vfsp);
 292 
 293                 /*
 294                  * If we change the size so its less than what is currently
 295                  * being used, we allow that. The file system will simply be
 296                  * full until enough files have been removed to get below the
 297                  * new max.
 298                  */
 299                 mutex_enter(&tm->tm_contents);
 300                 tm->tm_anonmax = anonmax;
 301                 mutex_exit(&tm->tm_contents);
 302                 goto out;
 303         }
 304 
 305         if ((tm = tmp_memalloc(sizeof (struct tmount), 0)) == NULL) {


 324          */
 325         mutex_init(&tm->tm_contents, NULL, MUTEX_DEFAULT, NULL);
 326         mutex_init(&tm->tm_renamelck, NULL, MUTEX_DEFAULT, NULL);
 327 
 328         tm->tm_vfsp = vfsp;
 329         tm->tm_anonmax = anonmax;
 330 
 331         vfsp->vfs_data = (caddr_t)tm;
 332         vfsp->vfs_fstype = tmpfsfstype;
 333         vfsp->vfs_dev = tm->tm_dev;
 334         vfsp->vfs_bsize = PAGESIZE;
 335         vfsp->vfs_flag |= VFS_NOTRUNC;
 336         vfs_make_fsid(&vfsp->vfs_fsid, tm->tm_dev, tmpfsfstype);
 337         tm->tm_mntpath = tmp_memalloc(dpn.pn_pathlen + 1, TMP_MUSTHAVE);
 338         (void) strcpy(tm->tm_mntpath, dpn.pn_path);
 339 
 340         /*
 341          * allocate and initialize root tmpnode structure
 342          */
 343         bzero(&rattr, sizeof (struct vattr));
 344         rattr.va_mode = (mode_t)(S_IFDIR | 0777);       /* XXX modes */
 345         rattr.va_type = VDIR;
 346         rattr.va_rdev = 0;
 347         tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
 348         tmpnode_init(tm, tp, &rattr, cr);
 349 
 350         /*
 351          * Get the mode, uid, and gid from the underlying mount point.
 352          */
 353         rattr.va_mask = AT_MODE|AT_UID|AT_GID;  /* Hint to getattr */
 354         got_attrs = VOP_GETATTR(mvp, &rattr, 0, cr, NULL);
 355 
 356         rw_enter(&tp->tn_rwlock, RW_WRITER);
 357         TNTOV(tp)->v_flag |= VROOT;
 358 
 359         /*
 360          * If the getattr succeeded, use its results.  Otherwise allow
 361          * the previously set hardwired defaults to prevail.
 362          */
 363         if (got_attrs == 0) {






 364                 tp->tn_mode = rattr.va_mode;

 365                 tp->tn_uid = rattr.va_uid;
 366                 tp->tn_gid = rattr.va_gid;
 367         }
 368 
 369         /*
 370          * initialize linked list of tmpnodes so that the back pointer of
 371          * the root tmpnode always points to the last one on the list
 372          * and the forward pointer of the last node is null
 373          */
 374         tp->tn_back = tp;
 375         tp->tn_forw = NULL;
 376         tp->tn_nlink = 0;
 377         tm->tm_rootnode = tp;
 378 
 379         tdirinit(tp, tp);
 380 
 381         rw_exit(&tp->tn_rwlock);
 382 
 383         pn_free(&dpn);
 384         error = 0;




   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) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2015 Joyent, Inc.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/param.h>
  28 #include <sys/sysmacros.h>
  29 #include <sys/kmem.h>
  30 #include <sys/time.h>
  31 #include <sys/pathname.h>
  32 #include <sys/vfs.h>
  33 #include <sys/vfs_opreg.h>
  34 #include <sys/vnode.h>
  35 #include <sys/stat.h>
  36 #include <sys/uio.h>
  37 #include <sys/stat.h>
  38 #include <sys/errno.h>
  39 #include <sys/cmn_err.h>
  40 #include <sys/cred.h>
  41 #include <sys/statvfs.h>
  42 #include <sys/mount.h>
  43 #include <sys/debug.h>


  74 static mntopts_t tmpfs_proto_opttbl;
  75 
  76 static vfsdef_t vfw = {
  77         VFSDEF_VERSION,
  78         "tmpfs",
  79         tmpfsinit,
  80         VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_ZMOUNT,
  81         &tmpfs_proto_opttbl
  82 };
  83 
  84 /*
  85  * in-kernel mnttab options
  86  */
  87 static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL };
  88 static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL };
  89 
  90 static mntopt_t tmpfs_options[] = {
  91         /* Option name          Cancel Opt      Arg     Flags           Data */
  92         { MNTOPT_XATTR,         xattr_cancel,   NULL,   MO_DEFAULT,     NULL},
  93         { MNTOPT_NOXATTR,       noxattr_cancel, NULL,   NULL,           NULL},
  94         { "size",               NULL,           "0",    MO_HASVALUE,    NULL},
  95         { "mode",               NULL,           NULL,   MO_HASVALUE,    NULL}
  96 };
  97 
  98 
  99 static mntopts_t tmpfs_proto_opttbl = {
 100         sizeof (tmpfs_options) / sizeof (mntopt_t),
 101         tmpfs_options
 102 };
 103 
 104 /*
 105  * Module linkage information
 106  */
 107 static struct modlfs modlfs = {
 108         &mod_fsops, "filesystem for tmpfs", &vfw
 109 };
 110 
 111 static struct modlinkage modlinkage = {
 112         MODREV_1, &modlfs, NULL
 113 };
 114 
 115 int


 211                  */
 212                 tmpfs_minfree = btopr(TMPMINFREE);
 213         }
 214 
 215         /*
 216          * The maximum amount of space tmpfs can allocate is
 217          * TMPMAXPROCKMEM percent of kernel memory
 218          */
 219         if (tmpfs_maxkmem == 0)
 220                 tmpfs_maxkmem = MAX(PAGESIZE, kmem_maxavail() / TMPMAXFRACKMEM);
 221 
 222         if ((tmpfs_major = getudev()) == (major_t)-1) {
 223                 cmn_err(CE_WARN, "tmpfsinit: Can't get unique device number.");
 224                 tmpfs_major = 0;
 225         }
 226         mutex_init(&tmpfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
 227         return (0);
 228 }
 229 
 230 static int
 231 tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)




 232 {
 233         struct tmount *tm = NULL;
 234         struct tmpnode *tp;
 235         struct pathname dpn;
 236         int error;
 237         pgcnt_t anonmax;
 238         struct vattr rattr;
 239         int got_attrs;
 240         boolean_t mode_arg = B_FALSE;
 241         mode_t root_mode = 0777;
 242         char *argstr;
 243 


 244         if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
 245                 return (error);
 246 
 247         if (mvp->v_type != VDIR)
 248                 return (ENOTDIR);
 249 
 250         mutex_enter(&mvp->v_lock);
 251         if ((uap->flags & MS_REMOUNT) == 0 && (uap->flags & MS_OVERLAY) == 0 &&
 252             (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
 253                 mutex_exit(&mvp->v_lock);
 254                 return (EBUSY);
 255         }
 256         mutex_exit(&mvp->v_lock);
 257 
 258         /*
 259          * Having the resource be anything but "swap" doesn't make sense.
 260          */
 261         vfs_setresource(vfsp, "swap", 0);
 262 
 263         /*
 264          * now look for options we understand...
 265          */
 266 
 267         /* tmpfs doesn't support read-only mounts */
 268         if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
 269                 error = EINVAL;
 270                 goto out;
 271         }
 272 
 273         /*
 274          * tm_anonmax is set according to the mount arguments
 275          * if any.  Otherwise, it is set to a maximum value.
 276          */
 277         if (vfs_optionisset(vfsp, "size", &argstr)) {
 278                 if ((error = tmp_convnum(argstr, &anonmax)) != 0)
 279                         goto out;
 280         } else {
 281                 anonmax = ULONG_MAX;
 282         }
 283 
 284         /*
 285          * The "mode" mount argument allows the operator to override the
 286          * permissions of the root of the tmpfs mount.
 287          */
 288         if (vfs_optionisset(vfsp, "mode", &argstr)) {
 289                 if ((error = tmp_convmode(argstr, &root_mode)) != 0) {
 290                         goto out;
 291                 }
 292                 mode_arg = B_TRUE;
 293         }
 294 
 295         if (error = pn_get(uap->dir,
 296             (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn))
 297                 goto out;
 298 
 299         if (uap->flags & MS_REMOUNT) {
 300                 tm = (struct tmount *)VFSTOTM(vfsp);
 301 
 302                 /*
 303                  * If we change the size so its less than what is currently
 304                  * being used, we allow that. The file system will simply be
 305                  * full until enough files have been removed to get below the
 306                  * new max.
 307                  */
 308                 mutex_enter(&tm->tm_contents);
 309                 tm->tm_anonmax = anonmax;
 310                 mutex_exit(&tm->tm_contents);
 311                 goto out;
 312         }
 313 
 314         if ((tm = tmp_memalloc(sizeof (struct tmount), 0)) == NULL) {


 333          */
 334         mutex_init(&tm->tm_contents, NULL, MUTEX_DEFAULT, NULL);
 335         mutex_init(&tm->tm_renamelck, NULL, MUTEX_DEFAULT, NULL);
 336 
 337         tm->tm_vfsp = vfsp;
 338         tm->tm_anonmax = anonmax;
 339 
 340         vfsp->vfs_data = (caddr_t)tm;
 341         vfsp->vfs_fstype = tmpfsfstype;
 342         vfsp->vfs_dev = tm->tm_dev;
 343         vfsp->vfs_bsize = PAGESIZE;
 344         vfsp->vfs_flag |= VFS_NOTRUNC;
 345         vfs_make_fsid(&vfsp->vfs_fsid, tm->tm_dev, tmpfsfstype);
 346         tm->tm_mntpath = tmp_memalloc(dpn.pn_pathlen + 1, TMP_MUSTHAVE);
 347         (void) strcpy(tm->tm_mntpath, dpn.pn_path);
 348 
 349         /*
 350          * allocate and initialize root tmpnode structure
 351          */
 352         bzero(&rattr, sizeof (struct vattr));
 353         rattr.va_mode = (mode_t)(S_IFDIR | root_mode);
 354         rattr.va_type = VDIR;
 355         rattr.va_rdev = 0;
 356         tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
 357         tmpnode_init(tm, tp, &rattr, cr);
 358 
 359         /*
 360          * Get the mode, uid, and gid from the underlying mount point.
 361          */
 362         rattr.va_mask = AT_MODE|AT_UID|AT_GID;  /* Hint to getattr */
 363         got_attrs = VOP_GETATTR(mvp, &rattr, 0, cr, NULL);
 364 
 365         rw_enter(&tp->tn_rwlock, RW_WRITER);
 366         TNTOV(tp)->v_flag |= VROOT;
 367 
 368         /*
 369          * If the getattr succeeded, use its results.  Otherwise allow
 370          * the previously set hardwired defaults to prevail.
 371          */
 372         if (got_attrs == 0) {
 373                 if (!mode_arg) {
 374                         /*
 375                          * Only use the underlying mount point for the
 376                          * mode if the "mode" mount argument was not
 377                          * provided.
 378                          */
 379                         tp->tn_mode = rattr.va_mode;
 380                 }
 381                 tp->tn_uid = rattr.va_uid;
 382                 tp->tn_gid = rattr.va_gid;
 383         }
 384 
 385         /*
 386          * initialize linked list of tmpnodes so that the back pointer of
 387          * the root tmpnode always points to the last one on the list
 388          * and the forward pointer of the last node is null
 389          */
 390         tp->tn_back = tp;
 391         tp->tn_forw = NULL;
 392         tp->tn_nlink = 0;
 393         tm->tm_rootnode = tp;
 394 
 395         tdirinit(tp, tp);
 396 
 397         rw_exit(&tp->tn_rwlock);
 398 
 399         pn_free(&dpn);
 400         error = 0;