2

我正在尝试更改没有 TARGET的符号链接的 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);

我不想走系统调用路线,因为我需要跨平台支持。

4

2 回答 2

5

这可以说是 JDK 中的错误或限制,至少在 Linux 和 Solaris 上(我没有尝试过 Windows)。假设您创建了BasicFileAttributeView没有LinkOption.NOFOLLOW_LINKS. 问题是sun.nio.fs.UnixFileAttributeViews$Basic.setTimes()调用sun.nio.fs.UnixPath.openForAttributeAccess(),它又在符号链接上调用open/ 。open64现在,如果符号链接有一个目标,这将成功并返回一个指向目标的 fd。setTimes()然后调用futimesatfd 来更新访问和修改时间。但是,这将更新链接目标的修改时间,而不是链接本身,这不是您想要的,并且如果链接断开,将无法正常工作。

因此,LinkOption.NOFOLLOW_LINKS当您请求BasicFileAttributeView. 但是,在这种情况下,openForAttributeAccess()传递O_NOFOLLOWto open,它被指定为在发生符号链接时返回ELOOP错误,这会导致您提到的错误消息。无论如何,由于无法为符号链接获取 fd,JDK 使用的策略将不起作用。它需要放弃 fd,并使用 say utimensatorlutimes代替。

不幸的是,看起来使用系统调用(比如使用 JNA)是这里唯一的选择。utimensat是 POSIX 2008 执行此操作的标准方式,但它足够新,以至于许多类 Unix 操作系统还没有它或仅在其最新版本中具有它。lutimes存在于 Linux 和 BSD 上,但不是标准的。

于 2013-12-21T02:36:23.057 回答
0

这是使用 FileTime 作为输入的 utimensat 的 JNA 代码。

POM 依赖:

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

爪哇

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());
    }
}
于 2016-03-29T16:06:12.183 回答