umountのMNT_DETACH
MNT_DETACHはumountするファイルシステムのファイルがopenされていてもumountします。(通常はできません。)ただし、そのvfsmntは解放されないため、openしているファイルのinodeは有効で、read/write可能です。
サンプル
サンプル
#include <sys/syscall.h> #include <stdio.h> #include <sys/mount.h> #include <fcntl.h> void mount_fread(int flag); void main(int argc, char* argv[]) { int flag; flag = (argc == 1)? 0: MNT_DETACH; mount_fread(flag); } void mount_fread(int flag) { int fd, ret; char buf[11]; fd = open("/mnt3/babakaka", O_RDONLY); ret = syscall(SYS_umount2, "/mnt3", flag); if(!ret) { printf("umount ok\n"); } else { printf("%d:can not umount\n", ret); } if (fd > 0) { buf[10] = 0; ret = read(fd, buf, 10); printf("%s\n", buf); } else { printf("can not open\n"); } close(fd); fd = open("/mnt3/babakaka", O_RDONLY); if (fd > 0) { printf("can open /mnt3\n"); } else { printf("can not open /mnt3\n"); } }検証
[root@localhost north]# mount -o loop disk /mnt3 [root@localhost north]# echo "0123456789" > /mnt3/babakaka [root@localhost north]# ./a.out -1:can not umount 0123456789 can open /mnt3 [root@localhost north]# ./a.out MNT_DETACH umount ok 0123456789 can not open /mnt3マウント時alloc_vfsmnt()取得されるmnt->mnt_countの初期値は1で、 sys_umount()でアンマウントするパス参照dで、mnt->mnt_countがインクリメントされ、従ってmnt->mnt_count!=2なら、そのファイルシステム下のファイルを他で参照しているという事で、-EBUSYでアンマウントできませんが、MNT_DETACHならそれを考慮されません。
static int do_umount(struct vfsmount *mnt, int flags) { struct super_block * sb = mnt->mnt_sb; int retval; retval = security_sb_umount(mnt, flags); if (retval) return retval; lock_kernel(); if( (flags&MNT_FORCE) && sb->s_op->umount_begin) sb->s_op->umount_begin(sb); unlock_kernel(); if (mnt == current->fs->rootmnt && !(flags & MNT_DETACH)) { down_write(&sb->s_umount); if (!(sb->s_flags & MS_RDONLY)) { lock_kernel(); retval = do_remount_sb(sb, MS_RDONLY, 0, 0); unlock_kernel(); } up_write(&sb->s_umount); return retval; } down_write(¤t->namespace->sem); spin_lock(&vfsmount_lock); if (atomic_read(&sb->s_active) == 1) { spin_unlock(&vfsmount_lock); lock_kernel(); DQUOT_OFF(sb); acct_auto_close(sb); unlock_kernel(); security_sb_umount_close(mnt); spin_lock(&vfsmount_lock); } retval = -EBUSY; if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { if (!list_empty(&mnt->mnt_list)) umount_tree(mnt); retval = 0; } spin_unlock(&vfsmount_lock); if (retval) security_sb_umount_busy(mnt); up_write(¤t->namespace->sem); return retval; } struct vfsmount *alloc_vfsmnt(const char *name) { struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL); if (mnt) { memset(mnt, 0, sizeof(struct vfsmount)); atomic_set(&mnt->mnt_count,1); INIT_LIST_HEAD(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); INIT_LIST_HEAD(&mnt->mnt_mounts); INIT_LIST_HEAD(&mnt->mnt_list); if (name) { int size = strlen(name)+1; char *newname = kmalloc(size, GFP_KERNEL); if (newname) { memcpy(newname, name, size); mnt->mnt_devname = newname; } } } return mnt; }mnt->mnt_mountpointがマウント先のdentryで、dentry->d_mounted--で(dentry->d_mounted=0)、このdentryのディレクトリはマウントが解除されますが、mntput()で、mnt->mnt_count==0の時、そのvfsmntは解放されます。
void umount_tree(struct vfsmount *mnt) { struct vfsmount *p; LIST_HEAD(kill); for (p = mnt; p; p = next_mnt(p, mnt)) { list_del(&p->mnt_list); list_add(&p->mnt_list, &kill); } while (!list_empty(&kill)) { mnt = list_entry(kill.next, struct vfsmount, mnt_list); list_del_init(&mnt->mnt_list); if (mnt->mnt_parent == mnt) { spin_unlock(&vfsmount_lock); } else { struct nameidata old_nd; detach_mnt(mnt, &old_nd); spin_unlock(&vfsmount_lock); path_release(&old_nd); } mntput(mnt); spin_lock(&vfsmount_lock); } } static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) { old_nd->dentry = mnt->mnt_mountpoint; old_nd->mnt = mnt->mnt_parent; mnt->mnt_parent = mnt; mnt->mnt_mountpoint = mnt->mnt_root; list_del_init(&mnt->mnt_child); list_del_init(&mnt->mnt_hash); old_nd->dentry->d_mounted--; } static inline void mntput(struct vfsmount *mnt) { if (mnt) { if (atomic_dec_and_test(&mnt->mnt_count)) __mntput(mnt); } } void __mntput(struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; dput(mnt->mnt_root); free_vfsmnt(mnt); deactivate_super(sb); }