Java 1.7中的符号链接lastModifiedTime

Java 1.7中的符号链接lastModifiedTime,java,time,symlink,Java,Time,Symlink,我正在尝试更改没有目标的符号链接的lastModifiedTime值 例如:foo-->nothing 我可以使用 String fooPath=“/Users/me/test/foo”; 路径路径=新文件(fooPath).toPath(); FileTime t=Files.getLastModifiedTime(路径,LinkOption.NOFOLLOW\u LINKS); 但是,我无法使用下面的代码段设置相同的符号链接;这为目标提供了一个java.nio.file.NoSuchFil

我正在尝试更改没有目标的符号链接的lastModifiedTime值

例如:foo-->nothing

我可以使用

String fooPath=“/Users/me/test/foo”;
路径路径=新文件(fooPath).toPath();
FileTime t=Files.getLastModifiedTime(路径,LinkOption.NOFOLLOW\u LINKS);
但是,我无法使用下面的代码段设置相同的符号链接;这为目标提供了一个
java.nio.file.NoSuchFileException

String fooPath=“/Users/me/test/foo”;
路径路径=新文件(fooPath).toPath();
FileTime t=FileTime.fromMillis(date.getTime());
setLastModifiedTime(路径,t);
我甚至尝试手动设置属性,但这给了我一个
java.nio.file.FileSystemException
“符号链接级别太多或无法访问符号链接的属性”错误:

Files.setAttribute(路径,“lastModifiedTime”,t,LinkOption.NOFOLLOW\u LINKS);

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

这可以说是JDK中的一个缺陷或限制,至少在Linux和Solaris上是如此(我还没有尝试Windows)。假设您创建的
BasicFileAttributeView
没有
link选项。没有follow\u LINKS
。问题是
sun.nio.fs.UnixFileAttributeViews$Basic.setTimes()
调用
sun.nio.fs.UnixPath.openForAttributeAccess()
,这反过来又调用符号链接上的
open
/
open64
。现在,如果符号链接有一个目标,这将成功并返回指向目标的fd
setTimes()
然后在fd上调用
futimesat
,以更新访问和修改时间。但是,这将更新链接目标的修改时间,而不是链接本身,这不是您想要的,并且如果链接断开,将不起作用

因此,当您请求
BasicFileAttributeView
时,您会认为答案是通过
LinkOption.NOFOLLOW\u LINKS
。但是,在该场景中,
openForAttributeAccess()
O_NOFOLLOW
传递到
open
,指定在出现符号链接时返回
ELOOP
错误,这将导致您提到的错误消息。无论如何,由于无法获得符号链接的fd,JDK使用的策略将不起作用。它需要放弃fd,而是使用say
utimensat
lutimes


不幸的是,看起来使用系统调用(比如使用JNA)是这里唯一的选择
utimensat
是POSIX 2008的标准方式,但它非常新,许多类Unix操作系统还没有,或者只在最新版本中才有
lutimes
存在于Linux和BSD上,但它是非标准的。

以下是使用FileTime作为输入的utimensat的JNA代码

POM相关性:

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

net.java.dev.jna
jna
4.2.2
爪哇

import java.nio.file.Path;
导入java.nio.file.attribute.FileTime;
导入java.util.array;
导入java.util.List;
导入java.util.concurrent.TimeUnit;
导入com.sun.jna.Library;
导入com.sun.jna.Native;
导入com.sun.jna.NativeLong;
导入com.sun.jna.Structure;
公共类FileUtil{
符号链接处的公共静态最终整数=0x100;
公共接口CLibrary扩展库{
CLibrary实例=(CLibrary)Native.loadLibrary(“c”,CLibrary.class);
int-utimensat(int-dirfd、字符串文件名、timespec-times、int标志);
}
公共静态类timespec扩展结构{
公共静态类ByReference扩展timespec实现结构。ByReference{}
公共国家电视节目_sec;//秒
公共本地电视\u nsec;//纳秒
公共时间段(){
}
@凌驾
受保护列表getFieldOrder(){
返回Arrays.asList(新字符串[]{“tv_-sec”,“tv_-nsec”});
}
}
公共静态布尔值changeFileTime(路径文件、文件时间atime、文件时间mtime){
timespec times=新timespec.ByReference();
timespec[]VAL=(timespec[]次)toArray(2);
设置时间(VAL[0],atime);
设置时间(VAL[1],mtime);
int rtn=CLibrary.INSTANCE.utimensat(0,file.toString(),times,AT_SYMLINK_NOFOLLOW);
返回(rtn==0);
}
私有静态void设置时间(timespec val,FileTime){
val.tv_sec=新的NativeLong(time.to(TimeUnit.SECONDS));
val.tv_nsec=new NativeLong(time.toInstant().getNano());
}
}

即使链接没有目标,也可以使用
touch-mht 200805191919/Users/me/test/foo在命令行上更改modtime。不幸的是,~unix的所有版本上都不存在touch-h。
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());
    }
}