renameシステムコールとmvコマンド
Rev.3を表示中。最新版はこちら。
システムコールにrenameというのがあり、それにもとづくrenameコマンドがありますが、通常ファイル名を変更する場合、mvコマンドを使つかいます。renameコマンドが直感的なコマンド体系でないからです。[root@localhost a1]# man rename 書式 rename from to file... 説明 rename は、指定したファイル名において from に最初にマッチする部分を to で 置き換える。たとえばaaaのファイルをbbbに置き換える場合、以下のように使うことになります。
[root@localhost a1]# ls aaa [root@localhost a1]# rename aaa bbb aaa [root@localhost a1]# ls bbbmvコマンドだと
[root@localhost a1]# ls bbb [root@localhost a1]# mv bbb aaa [root@localhost a1]# ls aaarenameコマンドは、ディレクトリの名前を変更することのみで実現しています。従って変更前のファイルと変更後のファイル名が同じファイルシステム下のファイルでないといけない制約があるからです。もし異なるファイルシステム下でも可能とするなら、ディレクトリの変更だけでなく、データブロックの移動の処理が発生することが考えられます。そうなるとvfs下でのrename処理が必要となります。vfsではrenameは定義されていません。
renameシステムコールが呼ばれると、sys_renameat(renameat)がコールされ、最初にファイルシステムが同じがどうかチェックしています。そしてファイルが書き込み可能とかの他のチェックをして、vfs_renameをコールします。
SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { ・・・・ error = user_path_parent(olddfd, oldname, &oldnd, &from); if (error) goto exit; error = user_path_parent(newdfd, newname, &newnd, &to); if (error) goto exit1; error = -EXDEV; if (oldnd.path.mnt != newnd.path.mnt) <- ファイルシステムが違うとエラー goto exit2; ・・・・ error = vfs_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); ・・・・
vfs_renameではファイル名の変更か、ディレクトリ名の変更かで処理がわかれます。前者は vfs_rename_otherを後者はvfs_rename_dirがコールされ、そこからファイルシステムに依存したrenameコールバック関数へとわたります。そこから ディレクトリへの変更はext3_add_entryを経由して、 add_dirent_to_bufで書き込み内容を編集しそれをディレクトリにアペンドしています。その時、変更前のファイルのiノードをそのまま使用しています。
static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { ・・・・ if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) error = -EBUSY; else error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); if (!error) { if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) d_move(old_dentry, new_dentry); } ・・・・
static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, struct inode *inode, struct ext3_dir_entry_2 *de, struct buffer_head * bh) { ・・・・ de->file_type = EXT3_FT_UNKNOWN; if (inode) { de->inode = cpu_to_le32(inode->i_ino); <- 変更前のiノード番号で上書き ext3_set_de_type(dir->i_sb, de, inode->i_mode); } else de->inode = 0; de->name_len = namelen; memcpy (de->name, name, namelen); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; ext3_update_dx_flag(dir); dir->i_version++; ・・・・mvというシステムコールは無いようです。変更前のファイルを変更後のファイルとして複写し、変更前のファイルを削除すればよさそうです。そして、このコマンドはファイルシステムを超えて可能です。結果としては想像できそうなものですが、同じファイルシステム下でおこなうmvと、異なるファイルシステム下でおこなうmvを調べてみると、(straceでもいいのでしょうが・・・)
ファイルシステムが同じ
[root@localhost a1]# ls -i 103382 aaa [root@localhost a1]# mv aaa ../a2/bbb [root@localhost a1]# ls -i ../a2 103382 bbbファイルシステムが違う
[root@localhost a1]# ls -i 103388 aaa [root@localhost a1]# mv aaa /mnt [root@localhost a1]# ls -i /mnt/aaa 15 /mnt/aaa同じファイルシステムではiノードが同じで、異なるファイルシステムでは、iノードが異なっています。mvコマンドで変更前と変更後のファイルが同じファイルシステムの場合、renameシステムコールを使っていることが推測できます。