umountのMNT_EXPIRE
MNT_EXPIREは、2回umountをコールする事で実現され、その間に、そのファイルシステムの参照があればumountできません。
サンプル
サンプル
#include <sys/syscall.h> #include <stdio.h> #include <sys/mount.h> void main(int argc, char* argv[]) { if(!syscall(SYS_umount2, "/mnt3", MNT_EXPIRE)) { printf("umounted\n"); } else { printf("can not umount\n"); } } [root@localhost north]# mount -o loop disk /mnt3 [root@localhost north]# ./a.out can not umount [root@localhost north]# ./a.out umounted [root@localhost north]# mount -o loop disk /mnt3 [root@localhost north]# ./a.out can not umount [root@localhost north]# touch /mnt3/babakaka [root@localhost north]# ./a.out can not umount [root@localhost north]# ./a.out umountedflags=MNT_EXPIREでmnt->mnt_expiry_markが0なら、mnt->mnt_expiry_mark=1としエラー(-EAGAIN)となり、umountできません。
static int do_umount(struct mount *mnt, int flags) { struct super_block *sb = mnt->mnt.mnt_sb; int retval; LIST_HEAD(umount_list); retval = security_sb_umount(&mnt->mnt, flags); if (retval) return retval; if (flags & MNT_EXPIRE) { if (&mnt->mnt == current->fs->root.mnt || flags & (MNT_FORCE | MNT_DETACH)) return -EINVAL; br_write_lock(vfsmount_lock); if (mnt_get_count(mnt) != 2) { br_write_unlock(vfsmount_lock); return -EBUSY; } br_write_unlock(vfsmount_lock); if (!xchg(&mnt->mnt_expiry_mark, 1)) return -EAGAIN; } if (flags & MNT_FORCE && sb->s_op->umount_begin) { sb->s_op->umount_begin(sb); } if (&mnt->mnt == current->fs->root.mnt && !(flags & MNT_DETACH)) { down_write(&sb->s_umount); if (!(sb->s_flags & MS_RDONLY)) retval = do_remount_sb(sb, MS_RDONLY, NULL, 0); up_write(&sb->s_umount); return retval; } down_write(&namespace_sem); br_write_lock(vfsmount_lock); event++; if (!(flags & MNT_DETACH)) shrink_submounts(mnt, &umount_list); 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; } br_write_unlock(vfsmount_lock); up_write(&namespace_sem); release_mounts(&umount_list); return retval; }ptr=xとし、元のptrを返り値とします。
#define xchg(ptr, x) \ ((__typeof__(*(ptr))) __xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { unsigned long ret, flags; switch (size) { case 1: #ifdef __xchg_u8 return __xchg_u8(x, ptr); #else local_irq_save(flags); ret = *(volatile u8 *)ptr; *(volatile u8 *)ptr = x; local_irq_restore(flags); return ret; #endif /* __xchg_u8 */ case 2: #ifdef __xchg_u16 return __xchg_u16(x, ptr); #else local_irq_save(flags); ret = *(volatile u16 *)ptr; *(volatile u16 *)ptr = x; local_irq_restore(flags); return ret; #endif /* __xchg_u16 */ case 4: #ifdef __xchg_u32 return __xchg_u32(x, ptr); #else local_irq_save(flags); ret = *(volatile u32 *)ptr; *(volatile u32 *)ptr = x; local_irq_restore(flags); return ret; #endif /* __xchg_u32 */ #ifdef CONFIG_64BIT case 8: #ifdef __xchg_u64 return __xchg_u64(x, ptr); #else local_irq_save(flags); ret = *(volatile u64 *)ptr; *(volatile u64 *)ptr = x; local_irq_restore(flags); return ret; #endif /* __xchg_u64 */ #endif /* CONFIG_64BIT */ default: __xchg_called_with_bad_pointer(); return x; } }パス検索でのdentry取得、及びファイルIDによるFILE取得時に、mntput()がコールされm->mnt_expiry_mark = 0とし、MNT_EXPIRでEのumountをできなくします。
void mntput(struct vfsmount *mnt) { if (mnt) { struct mount *m = real_mount(mnt); /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ if (unlikely(m->mnt_expiry_mark)) m->mnt_expiry_mark = 0; mntput_no_expire(m); } }
補足
私の環境のumountコマンドは、SYS_umountのoldumount()がコールされ、-Oオプションの設定ができませんでした。SYSCALL_DEFINE1(oldumount, char __user *, name) { return sys_umount(name, 0); }