1 /* $NetBSD: vfs.c,v 1.3 2010/06/24 13:03:05 hannken 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 42 MALLOC_DECLARE(M_MOUNT); 43 44 int 45 lookupname(char *dirname, enum uio_seg seg, vnode_t **dirvpp, vnode_t **compvpp) 46 { 47 struct nameidata nd; 48 int error; 49 50 error = 0; 51 52 KASSERT(dirvpp == NULL); 53 54 error = namei_simple_kernel(dirname, NSM_FOLLOW_NOEMULROOT, compvpp); 55 56 return error; 57 } 58 59 60 int 61 lookupnameat(char *dirname, enum uio_seg seg, vnode_t **dirvpp, 62 vnode_t **compvpp, vnode_t *startvp) 63 { 64 65 struct nameidata nd; 66 int error; 67 68 error = EOPNOTSUPP; 69 70 /* XXX Disable until I upgrade testing kernel. 71 KASSERT(dirvpp == NULL); 72 73 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, dirname); 74 75 if ((error = nameiat(&nd, startvp)) != 0) 76 return error; 77 78 *compvpp = nd.ni_vp;*/ 79 80 return (error); 81 } 82 83 84 void 85 vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg, 86 int flags) 87 { 88 89 if (strcmp("ro", name) == 0) 90 vfsp->mnt_flag |= MNT_RDONLY; 91 92 if (strcmp("rw", name) == 0) 93 vfsp->mnt_flag &= ~MNT_RDONLY; 94 95 if (strcmp("nodevices", name) == 0) 96 vfsp->mnt_flag |= MNT_NODEV; 97 98 if (strcmp("noatime", name) == 0) 99 vfsp->mnt_flag |= MNT_NOATIME; 100 101 if (strcmp("atime", name) == 0) 102 vfsp->mnt_flag &= ~MNT_NOATIME; 103 104 if (strcmp("nosuid", name) == 0) 105 vfsp->mnt_flag |= MNT_NOSUID; 106 107 if (strcmp("suid", name) == 0) 108 vfsp->mnt_flag &= ~MNT_NOSUID; 109 110 if (strcmp("noexec", name) == 0) 111 vfsp->mnt_flag |= MNT_NOEXEC; 112 113 if (strcmp("exec", name) == 0) 114 vfsp->mnt_flag &= ~MNT_NOEXEC; 115 116 vfsp->mnt_flag |= MNT_LOCAL; 117 } 118 119 void 120 vfs_clearmntopt(vfs_t *vfsp, const char *name) 121 { 122 123 if (strcmp("ro", name) == 0) 124 vfsp->mnt_flag |= MNT_RDONLY; 125 126 if (strcmp("rw", name) == 0) 127 vfsp->mnt_flag &= ~MNT_RDONLY; 128 129 if (strcmp("nodevices", name) == 0) 130 vfsp->mnt_flag &= ~MNT_NODEV; 131 132 if (strcmp("noatime", name) == 0) 133 vfsp->mnt_flag &= ~MNT_NOATIME; 134 135 if (strcmp("atime", name) == 0) 136 vfsp->mnt_flag |= MNT_NOATIME; 137 138 if (strcmp("nosuid", name) == 0) 139 vfsp->mnt_flag &= ~MNT_NOSUID; 140 141 if (strcmp("suid", name) == 0) 142 vfsp->mnt_flag |= MNT_NOSUID; 143 144 if (strcmp("noexec", name) == 0) 145 vfsp->mnt_flag &= ~MNT_NOEXEC; 146 147 if (strcmp("exec", name) == 0) 148 vfsp->mnt_flag |= MNT_NOEXEC; 149 } 150 151 int 152 vfs_optionisset(const vfs_t *vfsp, const char *name, char **argp) 153 { 154 155 if (strcmp("ro", name) == 0) 156 return (vfsp->mnt_flag & MNT_RDONLY) != 0; 157 158 if (strcmp("rw", name) == 0) 159 return (vfsp->mnt_flag & MNT_RDONLY) == 0; 160 161 if (strcmp("nodevices", name) == 0) 162 return (vfsp->mnt_flag & MNT_NODEV) != 0; 163 164 if (strcmp("noatime", name) == 0) 165 return (vfsp->mnt_flag & MNT_NOATIME) != 0; 166 167 if (strcmp("atime", name) == 0) 168 return (vfsp->mnt_flag & MNT_NOATIME) == 0; 169 170 if (strcmp("nosuid", name) == 0) 171 return (vfsp->mnt_flag & MNT_NOSUID) != 0; 172 173 if (strcmp("suid", name) == 0) 174 return (vfsp->mnt_flag & MNT_NOSUID) == 0; 175 176 if (strcmp("noexec", name) == 0) 177 return (vfsp->mnt_flag & MNT_NOEXEC) != 0; 178 179 if (strcmp("exec", name) == 0) 180 return (vfsp->mnt_flag & MNT_NOEXEC) == 0; 181 182 return 0; 183 } 184 185 #ifdef PORT_FREEBSD 186 int 187 traverse(vnode_t **cvpp, int lktype) 188 { 189 kthread_t *td = curthread; 190 vnode_t *cvp; 191 vnode_t *tvp; 192 vfs_t *vfsp; 193 int error; 194 195 cvp = *cvpp; 196 tvp = NULL; 197 198 /* 199 * If this vnode is mounted on, then we transparently indirect 200 * to the vnode which is the root of the mounted file system. 201 * Before we do this we must check that an unmount is not in 202 * progress on this vnode. 203 */ 204 205 for (;;) { 206 /* 207 * Reached the end of the mount chain? 208 */ 209 vfsp = vn_mountedvfs(cvp); 210 if (vfsp == NULL) 211 break; 212 /* 213 * tvp is NULL for *cvpp vnode, which we can't unlock. 214 */ 215 if (tvp != NULL) 216 vput(cvp); 217 else 218 vrele(cvp); 219 220 /* 221 * The read lock must be held across the call to VFS_ROOT() to 222 * prevent a concurrent unmount from destroying the vfs. 223 */ 224 error = VFS_ROOT(vfsp, &tvp); 225 if (error != 0) 226 return (error); 227 cvp = tvp; 228 } 229 230 *cvpp = cvp; 231 return (0); 232 } 233 234 int 235 domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath, 236 char *fspec, int fsflags) 237 { 238 struct mount *mp; 239 struct vfsconf *vfsp; 240 struct ucred *newcr, *oldcr; 241 int error; 242 243 /* 244 * Be ultra-paranoid about making sure the type and fspath 245 * variables will fit in our mp buffers, including the 246 * terminating NUL. 247 */ 248 if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) 249 return (ENAMETOOLONG); 250 251 vfsp = vfs_byname_kld(fstype, td, &error); 252 if (vfsp == NULL) 253 return (ENODEV); 254 255 if (vp->v_type != VDIR) 256 return (ENOTDIR); 257 simple_lock(&vp->v_interlock); 258 if ((vp->v_iflag & VI_MOUNT) != 0 || 259 vp->v_mountedhere != NULL) { 260 simple_unlock(&vp->v_interlock); 261 return (EBUSY); 262 } 263 vp->v_iflag |= VI_MOUNT; 264 simple_unlock(&vp->v_interlock); 265 266 /* 267 * Allocate and initialize the filesystem. 268 */ 269 vn_lock(vp, LK_SHARED | LK_RETRY); 270 mp = vfs_mount_alloc(vp, vfsp, fspath, td); 271 VOP_UNLOCK(vp); 272 273 mp->mnt_optnew = NULL; 274 vfs_setmntopt(mp, "from", fspec, 0); 275 mp->mnt_optnew = mp->mnt_opt; 276 mp->mnt_opt = NULL; 277 278 /* 279 * Set the mount level flags. 280 * crdup() can sleep, so do it before acquiring a mutex. 281 */ 282 newcr = crdup(kcred); 283 MNT_ILOCK(mp); 284 if (fsflags & MNT_RDONLY) 285 mp->mnt_flag |= MNT_RDONLY; 286 mp->mnt_flag &=~ MNT_UPDATEMASK; 287 mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS); 288 /* 289 * Unprivileged user can trigger mounting a snapshot, but we don't want 290 * him to unmount it, so we switch to privileged credentials. 291 */ 292 oldcr = mp->mnt_cred; 293 mp->mnt_cred = newcr; 294 mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid; 295 MNT_IUNLOCK(mp); 296 crfree(oldcr); 297 /* 298 * Mount the filesystem. 299 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they 300 * get. No freeing of cn_pnbuf. 301 */ 302 error = VFS_MOUNT(mp, td); 303 304 if (!error) { 305 if (mp->mnt_opt != NULL) 306 vfs_freeopts(mp->mnt_opt); 307 mp->mnt_opt = mp->mnt_optnew; 308 (void)VFS_STATFS(mp, &mp->mnt_stat, td); 309 } 310 /* 311 * Prevent external consumers of mount options from reading 312 * mnt_optnew. 313 */ 314 mp->mnt_optnew = NULL; 315 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 316 /* 317 * Put the new filesystem on the mount list after root. 318 */ 319 #ifdef FREEBSD_NAMECACHE 320 cache_purge(vp); 321 #endif 322 if (!error) { 323 vnode_t *mvp; 324 325 simple_lock(&vp->v_interlock); 326 vp->v_iflag &= ~VI_MOUNT; 327 simple_unlock(&vp->v_interlock); 328 vp->v_mountedhere = mp; 329 mutex_enter(&mountlist_lock); 330 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 331 mutex_exit(&mountlist_lock); 332 vfs_event_signal(NULL, VQ_MOUNT, 0); 333 if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td)) 334 panic("mount: lost mount"); 335 mountcheckdirs(vp, mvp); 336 vput(mvp); 337 VOP_UNLOCK(vp); 338 if ((mp->mnt_flag & MNT_RDONLY) == 0) 339 error = vfs_allocate_syncvnode(mp); 340 vfs_unbusy(mp, td); 341 if (error) 342 vrele(vp); 343 else 344 vfs_mountedfrom(mp, fspec); 345 } else { 346 simple_lock(&vp->v_interlock); 347 vp->v_iflag &= ~VI_MOUNT; 348 simple_unlock(&vp->v_interlock); 349 VOP_UNLOCK(vp); 350 vfs_unbusy(mp, td); 351 vfs_mount_destroy(mp); 352 } 353 return (error); 354 } 355 #endif 356