ループバックマウント
Rev.3を表示中。最新版はこちら。
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となるわけです。