为什么 `rename` 无法删除源文件,而 `unlink` 有效

Why does `rename` fail to delete the source file, when `unlink` works

我有一个 PHP 脚本使用函数 rename 移动文件,但部分失败了。调用 rename 会给出 "Permission denied" 警告。该文件似乎已被复制到目标目录(我在那里看到它很好),但它仍然存在于 rename.

之后的源目录中

file_exists 确认旧文件仍然存在。 unlink 然后可以成功删除文件 – 它 returns truefile_exists 确认文件现在已经消失了。

文件来自在 HTTP 请求中上传到 /tmp 目录(我使用 is_uploaded_file 来满足安全考虑——这不是这里的问题)。该文件确实具有 Web 服务用户 (www-data) 的 rw 权限。 move_uploaded_file 也可以正常工作。

目标目录在挂载的 CIFS 目录中。

Linux Ubuntu, PHP 版本 7.2.24.

(更新: OP 说目标目录是 CIFS 挂载。CIFS 挂载的权限可能更难以理解 - 请参阅 https://linux.die.net/man/8/mount.cifs 部分“文件和目录的所有权和权限")

rename 不是单个原子操作(在 PHP 中)。它实际上调用(对于普通文件按此顺序):

  • copy(创建文件的新副本)
  • chown(设置新副本的归属)
  • chmod(设置新副本的权限)
  • unlink(删去原来的)

(源代码为available on GitHub。)

如果这些步骤中的任何一个失败,它会中止并显示错误代码,并且不会回滚(这与您看到的症状一致)。

旁注:这实际上在源代码中记录为可能的行为:

 /*
  * Try to set user and permission info on the target.
  * If we're not root, then some of these may fail.
  * We try chown first, to set proper group info, relying
  * on the system environment to have proper umask to not allow                
  * access to the file in the meantime.
  */

我希望 目标 中父目录的权限(您正在将 复制到 的位置)阻止了 chmodchown 从工作。

(NB: 这不适用于 CIFS 安装...请参阅答案顶部的 link。)作为一个超级丑陋的 hack,你可以尝试将目标目录的权限设置为 777 (chmod 777 /my/target/directory/),这是不好的做法,但会证明理论。