1/* $NetBSD: vfs.c,v 1.4 2011/04/02 04:57:35 rmind Exp $ */ 2 3/*- 4 * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30/* __FBSDID("$FreeBSD: src/sys/compat/opensolaris/kern/opensolaris_vfs.c,v 1.7 2007/11/01 08:58:29 pjd Exp $"); */ 31 32#include <sys/param.h> 33#include <sys/kernel.h> 34#include <sys/systm.h> 35#include <sys/mount.h> 36#include <sys/cred.h> 37#include <sys/vfs.h> 38#include <sys/pathname.h> 39#include <sys/priv.h> 40#include <lib/libkern/libkern.h> 41 42int 43lookupname(char *dirname, enum uio_seg seg, vnode_t **dirvpp, vnode_t **compvpp) 44{ 45 struct nameidata nd; 46 int error; 47 48 error = 0; 49 50 KASSERT(dirvpp == NULL); 51 52 error = namei_simple_kernel(dirname, NSM_FOLLOW_NOEMULROOT, compvpp); 53 54 return error; 55} 56 57 58int 59lookupnameat(char *dirname, enum uio_seg seg, vnode_t **dirvpp, 60 vnode_t **compvpp, vnode_t *startvp) 61{ 62 63 struct nameidata nd; 64 int error; 65 66 error = EOPNOTSUPP; 67 68/* XXX Disable until I upgrade testing kernel. 69 KASSERT(dirvpp == NULL); 70 71 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, dirname); 72 73 if ((error = nameiat(&nd, startvp)) != 0) 74 return error; 75 76 *compvpp = nd.ni_vp;*/ 77 78 return (error); 79} 80 81 82void 83vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg, 84 int flags) 85{ 86 87 if (strcmp("ro", name) == 0) 88 vfsp->mnt_flag |= MNT_RDONLY; 89 90 if (strcmp("rw", name) == 0) 91 vfsp->mnt_flag &= ~MNT_RDONLY; 92 93 if (strcmp("nodevices", name) == 0) 94 vfsp->mnt_flag |= MNT_NODEV; 95 96 if (strcmp("noatime", name) == 0) 97 vfsp->mnt_flag |= MNT_NOATIME; 98 99 if (strcmp("atime", name) == 0) 100 vfsp->mnt_flag &= ~MNT_NOATIME; 101 102 if (strcmp("nosuid", name) == 0) 103 vfsp->mnt_flag |= MNT_NOSUID; 104 105 if (strcmp("suid", name) == 0) 106 vfsp->mnt_flag &= ~MNT_NOSUID; 107 108 if (strcmp("noexec", name) == 0) 109 vfsp->mnt_flag |= MNT_NOEXEC; 110 111 if (strcmp("exec", name) == 0) 112 vfsp->mnt_flag &= ~MNT_NOEXEC; 113 114 vfsp->mnt_flag |= MNT_LOCAL; 115} 116 117void 118vfs_clearmntopt(vfs_t *vfsp, const char *name) 119{ 120 121 if (strcmp("ro", name) == 0) 122 vfsp->mnt_flag |= MNT_RDONLY; 123 124 if (strcmp("rw", name) == 0) 125 vfsp->mnt_flag &= ~MNT_RDONLY; 126 127 if (strcmp("nodevices", name) == 0) 128 vfsp->mnt_flag &= ~MNT_NODEV; 129 130 if (strcmp("noatime", name) == 0) 131 vfsp->mnt_flag &= ~MNT_NOATIME; 132 133 if (strcmp("atime", name) == 0) 134 vfsp->mnt_flag |= MNT_NOATIME; 135 136 if (strcmp("nosuid", name) == 0) 137 vfsp->mnt_flag &= ~MNT_NOSUID; 138 139 if (strcmp("suid", name) == 0) 140 vfsp->mnt_flag |= MNT_NOSUID; 141 142 if (strcmp("noexec", name) == 0) 143 vfsp->mnt_flag &= ~MNT_NOEXEC; 144 145 if (strcmp("exec", name) == 0) 146 vfsp->mnt_flag |= MNT_NOEXEC; 147} 148 149int 150vfs_optionisset(const vfs_t *vfsp, const char *name, char **argp) 151{ 152 153 if (strcmp("ro", name) == 0) 154 return (vfsp->mnt_flag & MNT_RDONLY) != 0; 155 156 if (strcmp("rw", name) == 0) 157 return (vfsp->mnt_flag & MNT_RDONLY) == 0; 158 159 if (strcmp("nodevices", name) == 0) 160 return (vfsp->mnt_flag & MNT_NODEV) != 0; 161 162 if (strcmp("noatime", name) == 0) 163 return (vfsp->mnt_flag & MNT_NOATIME) != 0; 164 165 if (strcmp("atime", name) == 0) 166 return (vfsp->mnt_flag & MNT_NOATIME) == 0; 167 168 if (strcmp("nosuid", name) == 0) 169 return (vfsp->mnt_flag & MNT_NOSUID) != 0; 170 171 if (strcmp("suid", name) == 0) 172 return (vfsp->mnt_flag & MNT_NOSUID) == 0; 173 174 if (strcmp("noexec", name) == 0) 175 return (vfsp->mnt_flag & MNT_NOEXEC) != 0; 176 177 if (strcmp("exec", name) == 0) 178 return (vfsp->mnt_flag & MNT_NOEXEC) == 0; 179 180 return 0; 181} 182 183#ifdef PORT_FREEBSD 184int 185traverse(vnode_t **cvpp, int lktype) 186{ 187 kthread_t *td = curthread; 188 vnode_t *cvp; 189 vnode_t *tvp; 190 vfs_t *vfsp; 191 int error; 192 193 cvp = *cvpp; 194 tvp = NULL; 195 196 /* 197 * If this vnode is mounted on, then we transparently indirect 198 * to the vnode which is the root of the mounted file system. 199 * Before we do this we must check that an unmount is not in 200 * progress on this vnode. 201 */ 202 203 for (;;) { 204 /* 205 * Reached the end of the mount chain? 206 */ 207 vfsp = vn_mountedvfs(cvp); 208 if (vfsp == NULL) 209 break; 210 /* 211 * tvp is NULL for *cvpp vnode, which we can't unlock. 212 */ 213 if (tvp != NULL) 214 vput(cvp); 215 else 216 vrele(cvp); 217 218 /* 219 * The read lock must be held across the call to VFS_ROOT() to 220 * prevent a concurrent unmount from destroying the vfs. 221 */ 222 error = VFS_ROOT(vfsp, &tvp); 223 if (error != 0) 224 return (error); 225 cvp = tvp; 226 } 227 228 *cvpp = cvp; 229 return (0); 230} 231 232int 233domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath, 234 char *fspec, int fsflags) 235{ 236 struct mount *mp; 237 struct vfsconf *vfsp; 238 struct ucred *newcr, *oldcr; 239 int error; 240 241 /* 242 * Be ultra-paranoid about making sure the type and fspath 243 * variables will fit in our mp buffers, including the 244 * terminating NUL. 245 */ 246 if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) 247 return (ENAMETOOLONG); 248 249 vfsp = vfs_byname_kld(fstype, td, &error); 250 if (vfsp == NULL) 251 return (ENODEV); 252 253 if (vp->v_type != VDIR) 254 return (ENOTDIR); 255 simple_lock(&vp->v_interlock); 256 if ((vp->v_iflag & VI_MOUNT) != 0 || 257 vp->v_mountedhere != NULL) { 258 simple_unlock(&vp->v_interlock); 259 return (EBUSY); 260 } 261 vp->v_iflag |= VI_MOUNT; 262 simple_unlock(&vp->v_interlock); 263 264 /* 265 * Allocate and initialize the filesystem. 266 */ 267 vn_lock(vp, LK_SHARED | LK_RETRY); 268 mp = vfs_mount_alloc(vp, vfsp, fspath, td); 269 VOP_UNLOCK(vp); 270 271 mp->mnt_optnew = NULL; 272 vfs_setmntopt(mp, "from", fspec, 0); 273 mp->mnt_optnew = mp->mnt_opt; 274 mp->mnt_opt = NULL; 275 276 /* 277 * Set the mount level flags. 278 * crdup() can sleep, so do it before acquiring a mutex. 279 */ 280 newcr = crdup(kcred); 281 MNT_ILOCK(mp); 282 if (fsflags & MNT_RDONLY) 283 mp->mnt_flag |= MNT_RDONLY; 284 mp->mnt_flag &=~ MNT_UPDATEMASK; 285 mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS); 286 /* 287 * Unprivileged user can trigger mounting a snapshot, but we don't want 288 * him to unmount it, so we switch to privileged credentials. 289 */ 290 oldcr = mp->mnt_cred; 291 mp->mnt_cred = newcr; 292 mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid; 293 MNT_IUNLOCK(mp); 294 crfree(oldcr); 295 /* 296 * Mount the filesystem. 297 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they 298 * get. No freeing of cn_pnbuf. 299 */ 300 error = VFS_MOUNT(mp, td); 301 302 if (!error) { 303 if (mp->mnt_opt != NULL) 304 vfs_freeopts(mp->mnt_opt); 305 mp->mnt_opt = mp->mnt_optnew; 306 (void)VFS_STATFS(mp, &mp->mnt_stat, td); 307 } 308 /* 309 * Prevent external consumers of mount options from reading 310 * mnt_optnew. 311 */ 312 mp->mnt_optnew = NULL; 313 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 314 /* 315 * Put the new filesystem on the mount list after root. 316 */ 317#ifdef FREEBSD_NAMECACHE 318 cache_purge(vp); 319#endif 320 if (!error) { 321 vnode_t *mvp; 322 323 simple_lock(&vp->v_interlock); 324 vp->v_iflag &= ~VI_MOUNT; 325 simple_unlock(&vp->v_interlock); 326 vp->v_mountedhere = mp; 327 mutex_enter(&mountlist_lock); 328 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 329 mutex_exit(&mountlist_lock); 330 vfs_event_signal(NULL, VQ_MOUNT, 0); 331 if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td)) 332 panic("mount: lost mount"); 333 mountcheckdirs(vp, mvp); 334 vput(mvp); 335 VOP_UNLOCK(vp); 336 if ((mp->mnt_flag & MNT_RDONLY) == 0) 337 error = vfs_allocate_syncvnode(mp); 338 vfs_unbusy(mp, td); 339 if (error) 340 vrele(vp); 341 else 342 vfs_mountedfrom(mp, fspec); 343 } else { 344 simple_lock(&vp->v_interlock); 345 vp->v_iflag &= ~VI_MOUNT; 346 simple_unlock(&vp->v_interlock); 347 VOP_UNLOCK(vp); 348 vfs_unbusy(mp, td); 349 vfs_mount_destroy(mp); 350 } 351 return (error); 352} 353#endif 354