在 Linux 上使用 ACL 的"触摸"文件会导致"Operation not permitted"



在Java代码中,我想"触摸"一个文件。我想将时间戳更新为当前时间。该文件使用 ACL。这似乎是问题所在。

该文件:

$ ll file.xml 
-rw-rwxrw-+ 1 root root 8611 Oct  4 17:28 file.xml
$ getfacl file.xml 
# file: file.xml
# owner: root
# group: root
user::rw-
user:tomcat8:rwx
group::r-x
mask::rwx
other::rw-

我的Java应用程序从Tomcat 8运行,用户为tomcat8。sudo -u tomcat8 touch file.xml有效。如果我完全删除 ACL 并将 tomcat8 设置为所有者,它也可以工作。但这在生产环境中是不可能的。

所以起初我尝试了Apache common-io:

FileUtils.touch(path);

这会导致 IOException。我对其进行了更多调试,发现该库调用FileSystem.setLastModifiedTime调用Linux函数utimes

我调试了Linuxtouch命令,看到它调用了另一个更现代的功能:utimensat(0, NULL, NULL, 0)。它还调用dup2并复制文件描述符。

所以我用Java构建了自己的触摸方法:

long time = System.currentTimeMillis();
FileTime fileTimeNow = FileTime.fromMillis(time);
BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(derivative.toPath(), BasicFileAttributeView.class);
fileAttributeView.setTimes(fileTimeNow, fileTimeNow, fileTimeNow);

这也会引发异常(不允许操作(。

在内部,它调用utimensat(69, NULL, [{1538666780, 483000000}, {1538666780, 483000000}], 0)

我无法在.setTimes(...)上设置null.此调用将被忽略。并且没有Java方法来复制文件描述符(dup2(。所以我不能测试进一步的步骤,让它更像Linux的触摸。

当文件使用 ACL 时如何使其工作?我不想运行外部程序(触摸(。

如果文件不是同时写入的,则可以打开它,读取其第一个字节,然后在偏移量为零处再次写回文件。 这将更新文件的修改时间,而无需所有权权限。

(顺便说一下,ACL看起来真的很好奇,尤其是other:: rw-部分。

这是man utimensat

权限要求

要将两个文件时间戳都设置为当前时间(即,时间为 NULL,或者两个tv_nsec字段都指定UTIME_NOW(,请执行以下任一操作:

  1. 调用方必须具有对文件的写入权限;

  2. 调用方的有效用户 ID 必须与文件的所有者匹配;或

  3. 调用方必须具有适当的权限。

要进行除将时间戳都设置为当前时间以外的任何更改(即,时间不为 NULL,并且tv_nsec字段均未UTIME_NOW且tv_nsec字段均未UTIME_OMIT(,上述条件 2 或 3 必须 应用。

你有#1,但没有#2或#3。如果要求touch将时间显式设置为当前时间戳,则也会失败:

$ getfacl test | sed -e "s/$USER/myuser/"
# file: test
# owner: root
# group: root
user::rw-
user:myuser:rwx
group::r--
mask::rwx
other::r--
$ touch -d "@$(date +%s)" test
touch: setting times of ‘test’: Operation not permitted

不过,对于该怎么做,我没有任何好的建议。您可以对文件进行无操作更改,也可以调用touch作为外部命令:

String path="some path";
// See https://stackoverflow.com/a/52651585 for why we're not doing this via Java
int result = Runtime.getRuntime().exec(new String[] { "touch", "--", path }).waitFor();
if(result != 0) throw new IOException("Can't update timestamp");

相关内容

最新更新