Javaのファイル名の変更に失敗したことからmvの内部実装プロセスを見る


Javaのファイル名の変更方法を呼び出すウィジェットを次に示します.
 

  
  
  
  
  1. import java.io.*;  
  2. public class FileRenameTest  
  3. {  
  4.  
  5.   public static void main(String[] args)  
  6.   {  
  7.        
  8.    if(args.length<2)  
  9.    {  
  10.  
  11.       System.out.println("Usage:java FileRenameTest <srcFileName> <targetFileName>");  
  12.      return ;  
  13.    }    
  14.    System.out.println("prepare to renaming file["+args[0]+"] to file["+args[1]+"]");  
  15.    boolean status= new File(args[0]).renameTo(new File(args[1]));  
  16.    System.out.println("status:"+String.valueOf(status));  
  17.   }  
  18.  
  19.  

    aix 5.1またはrhel 6.1で、あるファイルの名前を別のファイルシステムに変更する準備をします.最終的に実行された結果は成功しませんでした.
    なぜシステムコマンドmvでファイルの名前を別のデバイスに変更することに成功したのでしょうか.
     このjavaプログラム実行プロセスシステム内部の処理プロセスをaixで以下のコマンドで観察します.
 

  
  
  
  
  1. truss -f -o rename.aix.truss java -cp . FileRenameTest origin.txt /tmp/origin.txt 

    出力ファイルに次の重要な情報が表示されます.
 

  
  
  
  
  1. 8486994: 5308421: kwrite(1, " p r e p a r e   t o   r".., 61) = 61  
  2. 8486994: 5308421: kwrite(1, "
    ", 1)            = 1  
  3. 8486994: 5308421: rename("origin.txt", "/tmp/origin.txt") Err#18 EXDEV  
  4. 8486994: 5308421: kwrite(1, " s t a t u s : f a l s e", 12) = 12  
  5. 8486994: 5308421: kwrite(1, "
    ", 1)            = 1 

    上記の内容から、システムがrenameを呼び出して実行した結果が失敗したことがわかります.Errnoは18、つまりEXDEVエラーです.
    rhel 6.1では、このjavaプログラム実行プロセスシステム内部の処理プロセスを以下のコマンドで観察する.
 

  
  
  
  
  1. strace -fxo rename.rh.strace java -cp . FileRenameTest origin.txt /tmp/origin.txt 

    出力ファイルには、次の重要な情報が表示されます.
 

  
  
  
  
  1. 19517 write(1, "prepare to renaming file[origin."..., 61) = 61  
  2. 19517 write(1, "
    "
    , 1)                 = 1  
  3. 19517 rename("origin.txt""/tmp/origin.txt") = -1 EXDEV (Invalid cross-device link)  
  4. 19517 write(1, "status:false", 12)      = 12  
  5. 19517 write(1, "
    "
    , 1)                 = 1 

      失敗の原因はaixで失敗した原因と一致し,いずれも不正なデバイス間操作である.
     aixおよびrhel上のmvコマンドの実行手順を観察します.
     次のコマンドでaix上のmvの実行手順を観察します.
 

  
  
  
  
  1. truss -f -o mv.aix.truss mv origin.txt /tmp/origin.txt 

      出力ファイルには、次の重要な情報が表示されます.
 

  
  
  
  
  1. 6283516: 10338403: statx("origin.txt", 0x2FF22570, 176, 021) = 0  
  2. 6283516: 10338403: statx("/tmp/origin.txt", 0x2FF22620, 176, 021) Err#2  ENOENT  
  3. 6283516: 10338403: access("/tmp/origin.txt", 0)     Err#2  ENOENT  
  4. 6283516: 10338403: rename("origin.txt""/tmp/origin.txt") Err#18 EXDEV  
  5. 6283516: 10338403: statx("origin.txt", 0x2FF22570, 176, 021) = 0  
  6. 6283516: 10338403: open("origin.txt", O_RDONLY|O_LARGEFILE) = 3  
  7. 6283516: 10338403: statx("/tmp/origin.txt", 0x2FF22198, 176, 020) Err#2  ENOENT  
  8. 6283516: 10338403: open("/tmp/origin.txt", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) = 4  
  9. 6283516: 10338403: fstatx(3, 0x2FF22248, 176, 020)  = 0  
  10. 6283516: 10338403: fstatx(4, 0x2FF222F8, 176, 020)  = 0  
  11. 6283516: 10338403: fstatfs(4, 0x2FF22430)       = 0  
  12. 6283516: 10338403: kread(3, " w w w w w w w
    \0\0\0\0"
    .., 4096) = 8  
  13. 6283516: 10338403: kwrite(4, " w w w w w w w
    "
    , 8) = 8  
  14. 6283516: 10338403: kread(3, " w w w w w w w
    \0\0\0\0"
    .., 4096) = 0  
  15. 6283516: 10338403: statx("origin.txt", 0x2FF22038, 176, 020) = 0  
  16. 6283516: 10338403: __statxacl("origin.txt", 0x0000000000000000, 0x00000000, 0x2FF22028, 0x30003D08, 0x2FF22010) = 0  
  17. 6283516: 10338403: chown("/tmp/origin.txt", 212, 1) = 0  
  18. 6283516: 10338403: chmod("/tmp/origin.txt", 0100644)    = 0  
  19. 6283516: 10338403: __chxacl("/tmp/origin.txt", 0x0000000000000000, 0x0000000000000002, 0x41495843, 0, 06000036410) = 0  
  20. 6283516: 10338403: listea("origin.txt""", 0)      Err#48 EFORMAT  
  21. 6283516: 10338403: close(3)             = 0  
  22. 6283516: 10338403: close(4)             = 0  
  23. 6283516: 10338403: utimes("/tmp/origin.txt", 0x2FF22188)    = 0  
  24. 6283516: 10338403: unlink("origin.txt")         = 0 

    mvコマンドの実行手順は、renameを呼び出し、失敗した場合は元のファイルの内容を読み込み、新しく作成したディレクトリファイルにコピーします.さらにunlinkで元のファイルのリンク数をキャンセルします.元のファイルのリンク数が0の場合、このファイルは削除されます.
    rhel上のmv内部呼び出しプロセスは、全体的にaix上と一致する.
    JavaクラスライブラリでのrenameToメソッドの内部実行がmvの実行と一致しないのは、どのような理由で考えられているのか分かりません.これはAPIの実装上の欠陥だと思います.