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(&current->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(&current->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);
}

追記

上記はLinux 2.6の実装で、Linux3.3では本実装に加えて、マウントされてるシステム下でマウントされているファイルシステム等を考慮した実装になっています。

最終更新 2016/04/05 16:22:30 - north
(2013/12/13 16:55:29 作成)


検索

アクセス数
3586298
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。