我有一个Java进程,需要更新它不拥有的文件的修改时间。我尝试了下面看似正确的程序,但它失败了,并显示为Operation Not Permitted
。
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
public class Touch {
public static void main(String args[]) {
for (String filename : args) {
try {
Path path = new File(filename).toPath();
FileTime time = FileTime.fromMillis(System.currentTimeMillis());
Files.setLastModifiedTime(path, time);
} catch (IOException e) {
System.err.println("Failed to touch: " + filename);
e.printStackTrace();
}
}
}
}
在给定root拥有的现有文件asdfasdf
的情况下,这是程序的输出,但是我具有通过组(0660
权限)对其进行写访问的权限。
$ strace -f -o mtime.log java -cp . Touch asdfasdf
Failed to touch: asdfasdf
java.nio.file.FileSystemException: asdfasdf: Operation not permitted
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:91)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileAttributeViews$Basic.setTimes(UnixFileAttributeViews.java:109)
at java.nio.file.Files.setLastModifiedTime(Files.java:2306)
at Touch.main(Touch.java:15)
strace的相关部分如下:
13414 openat(AT_FDCWD, "asdfasdf", O_RDONLY) = 4
13414 fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
13414 utimensat(4, NULL, [{tv_sec=1545468214, tv_nsec=0} /* 2018-12-22T03:43:34-0500 */, {tv_sec=1545470858, tv_nsec=462000000} /* 2018-12-22T04:27:38.462000000-0500 */], 0) = -1 EPERM (Operation not permitted)
/usr/bin/touch
可以满足我的要求,所以我也做到了:
$ strace -f -o mtime.log touch asdfasdf
这是strace的相关部分:
13483 openat(AT_FDCWD, "asdfasdf", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = 3
13483 dup2(3, 0) = 0
13483 close(3) = 0
13483 utimensat(0, NULL, NULL, 0) = 0
查看utimensat手册页后发现,由于Java使用times
值!= NULL
进行调用,因此该进程必须是所有者。
Java API中是否存在另一种方法,它将使用正确的参数调用utimensat()
,所以我不会遇到EPERM
/ Operation Not Permitted
错误?>
进一步挖掘Java 8的Files.setLastModifiedTime
显示UnixFileAttributeViews使用futimes,然后使用先前找到的utimensat调用。