ループバックマウント
Rev.4を表示中。最新版はこちら。
loopbackとは自分自身に戻る。と言うことで、ループバックマウントは、以下のように、マウント済みファイルシステム下にファイルを作成し、それを擬似的なブロックデバイスとしてマウントすると言う事です。(ある意味、バーチャルなパーティションって感じでしょうか。)[root@localhost tmp]# dd if=/dev/zero of=disk bs=1M count=1 1+0 レコード入力 1+0 レコード出力 1048576 バイト (1.0 MB) コピーされました、 0.0105052 秒、 99.8 MB/秒 [root@localhost tmp]# mkfs.ext2 disk mke2fs 1.41.14 (22-Dec-2010) disk is not a block special device. Proceed anyway? (y,n) y : Writing inode tables: done Writing superblocks and filesystem accounting information: done [root@localhost tmp]# mount ./disk /mnt1 [root@localhost tmp]# ls /mnt1 lost+foundとなります。この操作は、一般的にループバックデバイスによるマウントという事ですが、カーネルサイドからは、通常のマウントとして扱っています。
上記での処理の概要でstraceの内容の抜粋です。./diskが通常ファイルなら、/dev/loopデバイスファイルをオープンし、/dev/loopデバイスファイルのコールバック関数となるioctl()で、./diskをバンドリングしています。mountコマンドでは、この処理をmountコマンド内で行っており、システムコールのsys_mountへは、デバイスファイルとして/dev/loopとして、コールしているだけで、通常のmount処理と同じなのです。
実装的には、ループバックもそうでないマウントも、両者ともvfsmountを取得するのですが、/dev/loopの場合、すでにスーパブロックは./diskのファイルシステム下で取得済みであり、従ってスーパブロックの参照カウントをインクリメントすることにあります。
[root@localhost tmp]# strace mount ./disk /mnt1 execve("/bin/mount", ["mount", "./disk", "/mnt1"], [/* 23 vars */]) = 0 : open("/tmp/disk", O_RDWR|O_LARGEFILE) = 3 open("/dev/loop0", O_RDWR|O_LARGEFILE) = 4 readlink("/tmp", 0xbf8f570b, 4096) = -1 EINVAL (Invalid argument) readlink("/tmp/disk", 0xbf8f570b, 4096) = -1 EINVAL (Invalid argument) : ioctl(4, LOOP_SET_FD, 0x3) = 0 close(3) = 0 ioctl(4, LOOP_SET_STATUS64, {offset=0, number=0, flags=LO_FLAGS_AUTOCLEAR, file_name="/tmp/disk", ...}) = 0 : mount("/dev/loop0", "/mnt1", "ext2", MS_MGC_VAL, NULL) = 0 :ループバックデバイスのファイルオペレーションコールバック
static struct block_device_operations lo_fops = { .owner = THIS_MODULE, .open = lo_open, .release = lo_release, .ioctl = lo_ioctl, }; static int lo_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { struct loop_device *lo = inode->i_bdev->bd_disk->private_data; int err; down(&lo->lo_ctl_mutex); switch (cmd) { case LOOP_SET_FD: err = loop_set_fd(lo, file, inode->i_bdev, arg); break; case LOOP_CLR_FD: err = loop_clr_fd(lo, inode->i_bdev); break; case LOOP_SET_STATUS: err = loop_set_status_old(lo, (struct loop_info *) arg); break; case LOOP_GET_STATUS: err = loop_get_status_old(lo, (struct loop_info *) arg); break; case LOOP_SET_STATUS64: err = loop_set_status64(lo, (struct loop_info64 *) arg); break; case LOOP_GET_STATUS64: err = loop_get_status64(lo, (struct loop_info64 *) arg); break; default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; } up(&lo->lo_ctl_mutex); return err; }
補足
long do_mount(char * dev_name, char * dir_name, char *type_page, unsigned long flags, void *data_page) { : if (flags & MS_REMOUNT) retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); else retval = do_add_mount(&nd, type_page, flags, mnt_flags, dev_name, data_page); : }/dev/loopでコールされても、do_add_mount()がコールされ、カーネルとしてループバックマウントは、bindオプションのmountとなるわけです。