2013-06-25 26 views
1

我試圖更改具有沒有目標的符號鏈接的lastModifiedTime值。Java 1.7中的符號鏈接lastModifiedTime

例如:FOO - >什麼

我可以使用訪問lastModifiedTime值...

String fooPath = "/Users/me/test/foo"; 
Path path = new File(fooPath).toPath(); 
FileTime t = Files.getLastModifiedTime(path, LinkOption.NOFOLLOW_LINKS); 

但是,我無法用下面的代碼片段設置相同的符號鏈接;這給了我一個java.nio.file.NoSuchFileException目標...

String fooPath = "/Users/me/test/foo"; 
Path path = new File(fooPath).toPath(); 
FileTime t = FileTime.fromMillis(date.getTime()); 
Files.setLastModifiedTime(path, t); 

我甚至試圖設置手工的屬性,但是這給了我一個java.nio.file.FileSystemException「符號鏈接或符號鏈接無法訪問屬性級別過多」錯誤:

Files.setAttribute(path, "lastModifiedTime", t, LinkOption.NOFOLLOW_LINKS); 

我不想去系統調用路由,因爲我需要跨平臺支持。

+0

您可以更改使用'觸摸-mht 200805191919 /用戶/我/測試/ foo'即使環節都有命令行modtime沒有目標。 –

回答

3

這可以說是JDK中的一個錯誤或限制,至少在Linux和Solaris上(我還沒有嘗試過Windows)。假設你在沒有LinkOption.NOFOLLOW_LINKS的情況下創建你的BasicFileAttributeView。問題在於sun.nio.fs.UnixFileAttributeViews$Basic.setTimes()調用sun.nio.fs.UnixPath.openForAttributeAccess(),而這又會在符號鏈接上調用open/open64。現在,如果符號鏈接有一個目標,這將成功並返回一個fd指向目標。 setTimes()然後在fd上調用futimesat來更新訪問和修改時間。但是,這將更新鏈接目標的修改時間,而不是鏈接本身,這不是您想要的,並且在鏈接中斷時不起作用。

所以你會認爲答案是當你要求BasicFileAttributeView時通過LinkOption.NOFOLLOW_LINKS。但是,在這種情況下,openForAttributeAccess()O_NOFOLLOW傳遞給open,指定在發生符號鏈接時返回ELOOP錯誤,這會導致您提到的錯誤消息。無論如何,由於無法爲符號鏈接獲取fd,因此JDK使用的策略將不起作用。它需要放棄fd,並用utimensatlutimes代替。

不幸的是,它看起來像使用系統調用(比如使用JNA)是唯一的選擇。 utimensat是POSIX 2008這樣做的標準方式,但它足夠新,許多類Unix操作系統還沒有它,或者只在最新版本中才有。在Linux和BSD上存在lutimes,但是不是標準的。

0

這裏是使用FileTime作爲輸入的utimensat的JNA代碼。

POM依賴關係:

<dependency> 
    <groupId>net.java.dev.jna</groupId> 
    <artifactId>jna</artifactId> 
    <version>4.2.2</version> 
</dependency> 

的Java

import java.nio.file.Path; 
import java.nio.file.attribute.FileTime; 
import java.util.Arrays; 
import java.util.List; 
import java.util.concurrent.TimeUnit; 

import com.sun.jna.Library; 
import com.sun.jna.Native; 
import com.sun.jna.NativeLong; 
import com.sun.jna.Structure; 

public class FileUtil { 

    public static final int AT_SYMLINK_NOFOLLOW = 0x100; 

    public interface CLibrary extends Library { 
     CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class); 

     int utimensat(int dirfd, String filename, timespec times, int flags); 
    } 

    public static class timespec extends Structure { 

     public static class ByReference extends timespec implements Structure.ByReference {} 

     public NativeLong tv_sec; // seconds 

     public NativeLong tv_nsec; // nanoseconds 

     public timespec() { 
     } 

     @Override 
     protected List<String> getFieldOrder() { 
      return Arrays.asList(new String[] { "tv_sec", "tv_nsec" }); 
     } 
    } 

    public static boolean changeFileTime(Path file, FileTime atime, FileTime mtime) { 
     timespec times = new timespec.ByReference(); 
     timespec[] vals = (timespec[])times.toArray(2); 
     setTime(vals[0], atime); 
     setTime(vals[1], mtime); 
     int rtn = CLibrary.INSTANCE.utimensat(0, file.toString(), times, AT_SYMLINK_NOFOLLOW); 
     return (rtn == 0); 
    } 

    private static void setTime(timespec val, FileTime time) { 
     val.tv_sec = new NativeLong(time.to(TimeUnit.SECONDS)); 
     val.tv_nsec = new NativeLong(time.toInstant().getNano()); 
    } 
}