完成了文件系统的加载,自然就免不了对其中的文件目录进行一番大刀阔斧的修修剪剪,然后再温柔的过河拆桥,卸载文件系统。
5、卸载文件系统
5.1 sys_oldumount
为了和旧版的兼容,给出了sys_oldumount的接口,在实际的实现里它是不带flag参数得sys_umount。src/fs/namespace.c中是这样实现的:
asmlinkage long sys_oldumount(char __user * name)
{
return sys_umount(name, 0);
}
5.2 sys_mount
sys_umount可以处理挂载点,也可以处理快设备。这在非命名块设备的文件系统种尤为重要。其源码如下:
/*
* Now umount can handle mount points as well as block devices.
* This is important for filesystems which use unnamed block devices.
*
* We now support a flag for forced unmount like the other 'big iron'
* unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
*/
asmlinkage long sys_umount(char __user * name, int flags)
{
struct nameidata nd;
int retval;
-------ietf add start-------
根据name即,挂载点的路径参数,获得该挂在点的inode和dentry信息,将结果存入nd中。如果name参数是绝对路径,将按路径从根目录开始查找,否则从当前目录查找。具体请参见src/fs/namei.c的do_path_lookup函数(line 1103)。
-------ietf add end -------
retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
if (retval)
goto out;
retval = -EINVAL;
-------ietf add start-------
如果给定的路径不是一个挂载点,则返回
-------ietf add end -------
if (nd.dentry != nd.mnt->mnt_root)
goto dput_and_out;
if (!check_mnt(nd.mnt))
goto dput_and_out;
retval = -EPERM;
if (!capable(CAP_SYS_ADMIN))
goto dput_and_out;
-------ietf add start-------
根据flags,对挂载点执行卸载操作,详见5.3
-------ietf add end -------
retval = do_umount(nd.mnt, flags);
dput_and_out:
-------ietf add start-------
将原先挂载点在dentry中的数据全部清除,给dentry瘦身;释放设备superblock,文件系统模块引用释放。
-------ietf add end -------
path_release_on_umount(&nd);
out:
return retval;
}
5.3 do_umount
static int do_umount(struct vfsmount *mnt, int flags)
{
struct super_block *sb = mnt->mnt_sb;
int retval;
LIST_HEAD(umount_list);
-------ietf add start-------
安全框架的卸载操作
-------ietf add end -------
retval = security_sb_umount(mnt, flags);
if (retval)
return retval;
/*
* Allow userspace to request a mountpoint be expired rather than
* unmounting unconditionally. Unmount only happens if:
* (1) the mark is already set (the mark is cleared by mntput())
* (2) the usage count == 1 [parent vfsmount] + 1 [sys_umount]
*/
if (flags & MNT_EXPIRE) {
if (mnt == current->fs->rootmnt ||
flags & (MNT_FORCE | MNT_DETACH))
return -EINVAL;
if (atomic_read(&mnt->mnt_count) != 2)
return -EBUSY;
if (!xchg(&mnt->mnt_expiry_mark, 1))
return -EAGAIN;
}
/*
* If we may have to abort operations to get out of this
* mount, and they will themselves hold resources we must
* allow the fs to do things. In the Unix tradition of
* 'Gee thats tricky lets do it in userspace' the umount_begin
* might fail to complete on the first run through as other tasks
* must return, and the like. Thats for the mount program to worry
* about for the moment.
*/
-------ietf add start-------
调用对应文件系统superblock的卸载程序
-------ietf add end -------
lock_kernel();
if (sb->s_op->umount_begin)
sb->s_op->umount_begin(mnt, flags);
unlock_kernel();
/*
* No sense to grab the lock for this test, but test itself looks
* somewhat bogus. Suggestions for better replacement?
* Ho-hum... In principle, we might treat that as umount + switch
* to rootfs. GC would eventually take care of the old vfsmount.
* Actually it makes sense, especially if rootfs would contain a
* /reboot - static binary that would close all descriptors and
* call reboot(9). Then init(8) could umount root and exec /reboot.
*/
-------ietf add start-------
对于系统根文件系统的保护操作
-------ietf add end -------
if (mnt == current->fs->rootmnt && !(flags & MNT_DETACH)) {
/*
* Special case for "unmounting" root ...
* we just try to remount it readonly.
*/
down_write(&sb->s_umount);
if (!(sb->s_flags & MS_RDONLY)) {
lock_kernel();
DQUOT_OFF(sb);
retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
unlock_kernel();
}
up_write(&sb->s_umount);
return retval;
}
down_write(&namespace_sem);
spin_lock(&vfsmount_lock);
event++;
-------ietf add start-------
遍历umout链表,卸载其中的项。在umount_tree中,对于当前挂载点有bind,slave关系的挂载关系进行重新处理。
-------ietf add end -------
retval = -EBUSY;
if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
if (!list_empty(&mnt->mnt_list))
umount_tree(mnt, 1, &umount_list);
retval = 0;
}
spin_unlock(&vfsmount_lock);
if (retval)
security_sb_umount_busy(mnt);
up_write(&namespace_sem);
release_mounts(&umount_list);
return retval;
}