Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symlink lastModifiedTime in Java 1.7

Tags:

java

time

symlink

I'm trying to change the lastModifiedTime value for a symlink that has NO TARGET.

for example: foo --> nothing

I can access the lastModifiedTime value using...

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

However, I am unable to set the same symlink using the snippet below; this gives me a java.nio.file.NoSuchFileException for the target...

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

I even tried to set the attribute by hand, but that gives me a java.nio.file.FileSystemException 'Too many levels of symbolic links or unable to access attributes of symbolic link' error:

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

I don't want to go the system call route as I need cross platform support.

like image 604
paul allen Avatar asked May 14 '26 15:05

paul allen


1 Answers

This is arguably a bug or limitation in the JDK, at least on Linux and Solaris (I haven't tried Windows). Say you create your BasicFileAttributeView without LinkOption.NOFOLLOW_LINKS. The problem is that sun.nio.fs.UnixFileAttributeViews$Basic.setTimes() calls sun.nio.fs.UnixPath.openForAttributeAccess(), which in turn calls open/open64 on the symbolic link. Now, if the symbolic link has a target, this will succeed and return an fd pointing to the target. setTimes() then calls futimesat on the fd to update the access and modification times. However, this will update the modification time of the link target, not the link itself, which is not what you want, and will not work if the link is broken.

So you would think the answer would be to pass LinkOption.NOFOLLOW_LINKS when you request the BasicFileAttributeView. However, in that scenario, openForAttributeAccess() passes O_NOFOLLOW to open, which is specified to return an ELOOP error when a symlink occurs, which results in the error message you mentioned. In any event, since there is no way to get an fd for a symlink, the strategy the JDK uses won't work. It would need to forgo the fd, and use say utimensat or lutimes instead.

Unfortunately, it looks like using a system call (say using JNA) is the only option here. utimensat is the POSIX 2008 standard way of doing this, but it is new enough that many Unix-like operating systems don't have it yet or only do in their latest version. lutimes exists on Linux and BSD, but is non-standard.

like image 82
Simon Kissane Avatar answered May 17 '26 15:05

Simon Kissane



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!