/* * unmount system call */ int ffs_unmount(struct mount *mp, int mntflags, struct proc *p) { struct ufsmount *ump; struct fs *fs; int error, flags; flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; ump = VFSTOUFS(mp); fs = ump->um_fs; if (mp->mnt_flag & MNT_SOFTDEP) error = softdep_flushfiles(mp, flags, p); else error = ffs_flushfiles(mp, flags, p); if (error != 0) return (error); if (fs->fs_ronly == 0) { fs->fs_clean = (fs->fs_flags & FS_UNCLEAN) ? 0 : 1; error = ffs_sbupdate(ump, MNT_WAIT); /* ignore write errors if mounted RW on read-only device */ if (error && error != EROFS) { fs->fs_clean = 0; return (error); } free(fs->fs_contigdirs, M_UFSMNT); } ump->um_devvp->v_specmountpoint = NULL; vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY, p); vinvalbuf(ump->um_devvp, V_SAVE, NOCRED, p, 0, 0); error = VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE, NOCRED, p); VOP_UNLOCK(ump->um_devvp, 0, p); vrele(ump->um_devvp); free(fs->fs_csp, M_UFSMNT); free(fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); }
/* * ffs_mount * * Called when mounting local physical media * * PARAMETERS: * mountroot * mp mount point structure * path NULL (flag for root mount!!!) * data <unused> * p process (user credentials check [statfs]) * * mount * mp mount point structure * path path to mount point * data pointer to argument struct in user space * p process (user credentials check) * * RETURNS: 0 Success * !0 error number (errno.h) * * LOCK STATE: * * ENTRY * mount point is locked * EXIT * mount point is locked * * NOTES: * A NULL path can be used for a flag since the mount * system call will fail with EFAULT in copyinstr in * nlookup() if it is a genuine NULL from the user. */ static int ffs_mount(struct mount *mp, /* mount struct pointer */ char *path, /* path to mount point */ caddr_t data, /* arguments to FS specific mount */ struct ucred *cred) /* process requesting mount */ { size_t size; int error; struct vnode *devvp; struct ufs_args args; struct ufsmount *ump = NULL; struct fs *fs; int flags, ronly = 0; mode_t accessmode; struct nlookupdata nd; struct vnode *rootvp; devvp = NULL; error = 0; /* * Use NULL path to flag a root mount */ if (path == NULL) { /* *** * Mounting root filesystem *** */ if ((error = bdevvp(rootdev, &rootvp))) { kprintf("ffs_mountroot: can't find rootvp\n"); return (error); } if( ( error = ffs_mountfs(rootvp, mp, M_FFSNODE)) != 0) { /* fs specific cleanup (if any)*/ goto error_1; } devvp = rootvp; goto dostatfs; /* success*/ } /* *** * Mounting non-root filesystem or updating a filesystem *** */ /* copy in user arguments*/ error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)); if (error) goto error_1; /* can't get arguments*/ /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); fs = ump->um_fs; devvp = ump->um_devvp; error = 0; ronly = fs->fs_ronly; /* MNT_RELOAD might change this */ if (ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { /* * Flush any dirty data. */ VFS_SYNC(mp, MNT_WAIT); /* * Check for and optionally get rid of files open * for writing. */ flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (mp->mnt_flag & MNT_SOFTDEP) { error = softdep_flushfiles(mp, flags); } else { error = ffs_flushfiles(mp, flags); } ronly = 1; } if (!error && (mp->mnt_flag & MNT_RELOAD)) { error = ffs_reload(mp, NULL); } if (error) { goto error_1; } if (ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ if (cred->cr_uid != 0) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); if ((error = VOP_EACCESS(devvp, VREAD | VWRITE, cred)) != 0) { vn_unlock(devvp); return (error); } vn_unlock(devvp); } fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; if (mp->mnt_flag & MNT_FORCE) { kprintf( "WARNING: %s was not properly dismounted\n", fs->fs_fsmnt); } else { kprintf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->fs_fsmnt); error = EPERM; goto error_1; } } /* check to see if we need to start softdep */ if (fs->fs_flags & FS_DOSOFTDEP) { error = softdep_mount(devvp, mp, fs); if (error) goto error_1; } ronly = 0; } /* * Soft updates is incompatible with "async", * so if we are doing softupdates stop the user * from setting the async flag in an update. * Softdep_mount() clears it in an initial mount * or ro->rw remount. */ if (mp->mnt_flag & MNT_SOFTDEP) { mp->mnt_flag &= ~MNT_ASYNC; } /* if not updating name...*/ if (args.fspec == 0) { /* * Process export requests. Jumping to "success" * will return the vfs_export() error code. */ error = vfs_export(mp, &ump->um_export, &args.export); goto success; }
/* * VFS Operations. * * mount system call */ int ffs_mount(struct mount *mp, const char *path, void *data, struct nameidata *ndp, struct proc *p) { struct vnode *devvp; struct ufs_args args; struct ufsmount *ump = NULL; struct fs *fs; int error = 0, flags; int ronly; mode_t accessmode; size_t size; error = copyin(data, &args, sizeof (struct ufs_args)); if (error) return (error); #ifndef FFS_SOFTUPDATES if (mp->mnt_flag & MNT_SOFTDEP) { printf("WARNING: soft updates isn't compiled in\n"); mp->mnt_flag &= ~MNT_SOFTDEP; } #endif /* * Soft updates is incompatible with "async", * so if we are doing softupdates stop the user * from setting the async flag. */ if ((mp->mnt_flag & (MNT_SOFTDEP | MNT_ASYNC)) == (MNT_SOFTDEP | MNT_ASYNC)) { return (EINVAL); } /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); fs = ump->um_fs; devvp = ump->um_devvp; error = 0; ronly = fs->fs_ronly; if (ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { /* Flush any dirty data */ mp->mnt_flag &= ~MNT_RDONLY; VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p); mp->mnt_flag |= MNT_RDONLY; /* * Get rid of files open for writing. */ flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (fs->fs_flags & FS_DOSOFTDEP) { error = softdep_flushfiles(mp, flags, p); mp->mnt_flag &= ~MNT_SOFTDEP; } else error = ffs_flushfiles(mp, flags, p); ronly = 1; } /* * Flush soft dependencies if disabling it via an update * mount. This may leave some items to be processed, * so don't do this yet XXX. */ if ((fs->fs_flags & FS_DOSOFTDEP) && !(mp->mnt_flag & MNT_SOFTDEP) && !(mp->mnt_flag & MNT_RDONLY) && fs->fs_ronly == 0) { #if 0 flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = softdep_flushfiles(mp, flags, p); #elif FFS_SOFTUPDATES mp->mnt_flag |= MNT_SOFTDEP; #endif } /* * When upgrading to a softdep mount, we must first flush * all vnodes. (not done yet -- see above) */ if (!(fs->fs_flags & FS_DOSOFTDEP) && (mp->mnt_flag & MNT_SOFTDEP) && fs->fs_ronly == 0) { #if 0 flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = ffs_flushfiles(mp, flags, p); #else mp->mnt_flag &= ~MNT_SOFTDEP; #endif } if (!error && (mp->mnt_flag & MNT_RELOAD)) error = ffs_reload(mp, ndp->ni_cnd.cn_cred, p); if (error) goto error_1; if (ronly && (mp->mnt_flag & MNT_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ if (suser(p, 0)) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_ACCESS(devvp, VREAD | VWRITE, p->p_ucred, p); VOP_UNLOCK(devvp, 0, p); if (error) goto error_1; } if (fs->fs_clean == 0) { #if 0 /* * It is safe mount unclean file system * if it was previously mounted with softdep * but we may loss space and must * sometimes run fsck manually. */ if (fs->fs_flags & FS_DOSOFTDEP) printf( "WARNING: %s was not properly unmounted\n", fs->fs_fsmnt); else #endif if (mp->mnt_flag & MNT_FORCE) { printf( "WARNING: %s was not properly unmounted\n", fs->fs_fsmnt); } else { printf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->fs_fsmnt); error = EROFS; goto error_1; } } if ((fs->fs_flags & FS_DOSOFTDEP)) { error = softdep_mount(devvp, mp, fs, p->p_ucred); if (error) goto error_1; } fs->fs_contigdirs=(u_int8_t*)malloc((u_long)fs->fs_ncg, M_UFSMNT, M_WAITOK); bzero(fs->fs_contigdirs, fs->fs_ncg); ronly = 0; } if (args.fspec == 0) { /* * Process export requests. */ error = vfs_export(mp, &ump->um_export, &args.export_info); if (error) goto error_1; else goto success; } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if ((error = namei(ndp)) != 0) goto error_1; devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { error = ENOTBLK; goto error_2; } if (major(devvp->v_rdev) >= nblkdev) { error = ENXIO; goto error_2; } /* * If mount by non-root, then verify that user has necessary * permissions on the device. */ if (suser(p, 0)) { accessmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accessmode |= VWRITE; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p); VOP_UNLOCK(devvp, 0, p); if (error) goto error_2; } if (mp->mnt_flag & MNT_UPDATE) { /* * UPDATE * If it's not the same vnode, or at least the same device * then it's not correct. */ if (devvp != ump->um_devvp) { if (devvp->v_rdev == ump->um_devvp->v_rdev) { vrele(devvp); } else { error = EINVAL; /* needs translation */ } } else vrele(devvp); /* * Update device name only on success */ if (!error) { /* * Save "mounted from" info for mount point (NULL pad) */ copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); } } else { /* * Since this is a new mount, we want the names for * the device and the mount point copied in. If an * error occurs, the mountpoint is discarded by the * upper level code. */ /* Save "last mounted on" info for mount point (NULL pad)*/ copyinstr(path, /* mount point*/ mp->mnt_stat.f_mntonname, /* save area*/ MNAMELEN - 1, /* max size*/ &size); /* real size*/ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); /* Save "mounted from" info for mount point (NULL pad)*/ copyinstr(args.fspec, /* device name*/ mp->mnt_stat.f_mntfromname, /* save area*/ MNAMELEN - 1, /* max size*/ &size); /* real size*/ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); error = ffs_mountfs(devvp, mp, p); } if (error) goto error_2; /* * Initialize FS stat information in mount struct; uses both * mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname * * This code is common to root and non-root mounts */ bcopy(&args, &mp->mnt_stat.mount_info.ufs_args, sizeof(args)); (void)VFS_STATFS(mp, &mp->mnt_stat, p); success: if (path && (mp->mnt_flag & MNT_UPDATE)) { /* Update clean flag after changing read-onlyness. */ fs = ump->um_fs; if (ronly != fs->fs_ronly) { fs->fs_ronly = ronly; fs->fs_clean = ronly && (fs->fs_flags & FS_UNCLEAN) == 0 ? 1 : 0; if (ronly) free(fs->fs_contigdirs, M_UFSMNT); } if (!ronly) { if (mp->mnt_flag & MNT_SOFTDEP) fs->fs_flags |= FS_DOSOFTDEP; else fs->fs_flags &= ~FS_DOSOFTDEP; } ffs_sbupdate(ump, MNT_WAIT); } return (0); error_2: /* error with devvp held */ vrele (devvp); error_1: /* no state to back out */ return (error); }