/* * Copyright (c) 2005-2007, Kohsuke Ohtani * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * mount.c - mount operations */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vfs.h" /* * List for VFS mount points. */ static struct list mount_list = LIST_INIT(mount_list); /* * Global lock to access mount point. */ #if CONFIG_FS_THREADS > 1 static mutex_t mount_lock = MUTEX_INITIALIZER; #define MOUNT_LOCK() mutex_lock(&mount_lock) #define MOUNT_UNLOCK() mutex_unlock(&mount_lock) #else #define MOUNT_LOCK() #define MOUNT_UNLOCK() #endif /* * Lookup file system. */ static const struct vfssw * fs_lookup(char *name) { const struct vfssw *fs; for (fs = vfssw_table; fs->vs_name; fs++) { if (!strncmp(name, fs->vs_name, FSMAXNAMES)) break; } if (!fs->vs_name) return NULL; return fs; } int sys_mount(char *dev, char *dir, char *fsname, int flags, void *data) { const struct vfssw *fs; mount_t mp; list_t head, n; device_t device; vnode_t vp, vp_covered; int err; dprintf("VFS: Mounting %s dev=%s dir=%s\n", fsname, dev, dir); if (!dir || *dir == '\0') return ENOENT; /* Find a file system. */ if (!(fs = fs_lookup(fsname))) return ENODEV; /* No such file system */ /* Open device. NULL can be specified as a device. */ device = 0; if (*dev != '\0') { if (strncmp(dev, "/dev/", 5)) return ENOTBLK; if ((err = device_open(dev + 5, DO_RDWR, &device)) != 0) return err; } MOUNT_LOCK(); /* Check if device or directory has already been mounted. */ head = &mount_list; for (n = list_first(head); n != head; n = list_next(n)) { mp = list_entry(n, struct mount, m_link); if (!strcmp(mp->m_path, dir) || (device && mp->m_dev == (dev_t)device)) { err = EBUSY; /* Already mounted */ goto err1; } } /* * Create VFS mount entry */ if (!(mp = malloc(sizeof(struct mount)))) { err = ENOMEM; goto err1; } mp->m_count = 0; mp->m_op = fs->vs_op; mp->m_flags = flags; mp->m_dev = (dev_t)device; strlcpy(mp->m_path, dir, PATH_MAX); mp->m_path[PATH_MAX - 1] = '\0'; /* * Get vnode to be covered in the upper file system. */ if (*dir == '/' && *(dir + 1) == '\0') { /* Ignore if it mounts to global root directory. */ vp_covered = NULL; } else { if ((err = namei(dir, &vp_covered)) != 0) { err = ENOENT; goto err2; } if (vp_covered->v_type != VDIR) { err = ENOTDIR; goto err3; } } mp->m_covered = vp_covered; /* * Create a root vnode for this file system. */ if ((vp = vget(mp, "/")) == NULL) { err = ENOMEM; goto err3; } vp->v_type = VDIR; vp->v_flags = VROOT; vp->v_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; mp->m_root = vp; /* * Call a file system specific routine to mount. */ if ((err = VFS_MOUNT(mp, dev, flags, data)) != 0) goto err4; /* * Keep reference count for root/covered vnode. */ vn_unlock(vp); if (vp_covered) vn_unlock(vp_covered); /* * Insert to mount list */ list_insert(&mount_list, &mp->m_link); MOUNT_UNLOCK(); return 0; err4: vput(vp); err3: if (vp_covered) vput(vp_covered); err2: free(mp); err1: device_close(device); MOUNT_UNLOCK(); return err; } int sys_umount(char *path) { mount_t mp; list_t head, n; int err; DPRINTF(VFSDB_SYSCALL, ("sys_umount: path=%s\n", path)); MOUNT_LOCK(); /* Get mount entry */ head = &mount_list; for (n = list_first(head); n != head; n = list_next(n)) { mp = list_entry(n, struct mount, m_link); if (!strcmp(path, mp->m_path)) break; } if (n == head) { err = EINVAL; goto out; } /* * Root fs can not be unmounted. */ if (mp->m_covered == NULL) { err = EINVAL; goto out; } if ((err = VFS_UNMOUNT(mp)) != 0) goto out; list_remove(&mp->m_link); /* Decrement referece count of root vnode */ vrele(mp->m_covered); /* Release all vnodes */ vflush(mp); /* Flush all buffers */ binval(mp->m_dev); if (mp->m_dev) device_close((device_t)mp->m_dev); free(mp); out: MOUNT_UNLOCK(); return err; } int sys_sync(void) { mount_t mp; list_t head, n; /* Call each mounted file system. */ MOUNT_LOCK(); head = &mount_list; for (n = list_first(head); n != head; n = list_next(n)) { mp = list_entry(n, struct mount, m_link); VFS_SYNC(mp); } MOUNT_UNLOCK(); bio_sync(); return 0; } /* * Compare two path strings. Return matched length. * @path: target path. * @root: vfs root path as mount point. */ static size_t count_match(char *path, char *mount_root) { size_t len = 0; while (*path && *mount_root) { if (*path++ != *mount_root++) break; len++; } if (*mount_root != '\0') return 0; if (len == 1 && *(path - 1) == '/') return 1; if (*path == '\0' || *path == '/') return len; return 0; } /* * Get the root directory and mount point for specified path. * @path: full path. * @mp: mount point to return. * @root: pointer to root directory in path. */ int vfs_findroot(char *path, mount_t *mp, char **root) { mount_t m, tmp; list_t head, n; size_t len, max_len = 0; if (!path) return -1; /* Find mount point from nearest path */ MOUNT_LOCK(); m = NULL; head = &mount_list; for (n = list_first(head); n != head; n = list_next(n)) { tmp = list_entry(n, struct mount, m_link); len = count_match(path, tmp->m_path); if (len > max_len) { max_len = len; m = tmp; } } MOUNT_UNLOCK(); if (m == NULL) return -1; *root = (char *)(path + max_len); if (**root == '/') (*root)++; *mp = m; return 0; } /* * Mark a mount point as busy. */ void vfs_busy(mount_t mp) { MOUNT_LOCK(); mp->m_count++; MOUNT_UNLOCK(); } /* * Mark a mount point as busy. */ void vfs_unbusy(mount_t mp) { MOUNT_LOCK(); mp->m_count--; MOUNT_UNLOCK(); } int vfs_nullop(void) { return 0; } int vfs_einval(void) { return EINVAL; } #ifdef DEBUG void mount_dump(void) { list_t head, n; mount_t mp; MOUNT_LOCK(); dprintf("mount_dump\n"); dprintf("dev count root\n"); dprintf("-------- ----- --------\n"); head = &mount_list; for (n = list_first(head); n != head; n = list_next(n)) { mp = list_entry(n, struct mount, m_link); dprintf("%8x %5d %s\n", mp->m_dev, mp->m_count, mp->m_path); } MOUNT_UNLOCK(); } #endif