3

It seems mv -T is a GNU extension to mv.

Is there a robust way (race-free, portable, and otherwise without "gotchas") to do the equivalent of mv -T dir1 dir2?

To be clear:

  • I DO NOT want this to ever result in dir2/dir1. If dir2 exists, I want the command to fail. If dir1 is moved at all, it must become dir2.

  • I DO NOT want to move out every child one-by-one. I want to move the directory itself.

  • I DO want to avoid race conditions. It's trivial to test if dir2 exists first, but then it might be created after the check but before the move.

user541686
  • 3,083
  • I don't think it's possible to satisfy "If dir2 exists, I want the command to fail" portably; POSIX requires rename to delete an empty dir2 and succeed. – Michael Homer Feb 26 '19 at 03:37
  • The spec text: "If the old argument points to the pathname of a directory, the new argument shall not point to the pathname of a file that is not a directory. If the directory named by the new argument exists, it shall be removed and old renamed to new. [...] If new names an existing directory, it shall be required to be an empty directory.". That also applies to renameat. I don't believe there can be any portable method to rename a file that does not go through that path. Given that, which of your requirements can you relax? What sorts of race condition are you concerned with? – Michael Homer Feb 26 '19 at 03:41
  • @MichaelHomer: Interesting, thanks. The race condition I'm trying to avoid is dir1 landing inside dir2, which can happen if multiple processes try to run this command at once. However, I guess it would be fine if an empty dir2 were deleted. Would that help? Also, if there's still no single POSIX solution, I'd be interested in knowing if there are even platform-specific solution that I can call. E.g. is there a solution that works on Mac, let alone other platforms? I haven't found any. – user541686 Feb 26 '19 at 03:54
  • The renameat2 system call takes a flag argument and one such flag is RENAME_NOREPLACE which causes it to: "Don't overwrite newpath of the rename. Return an error if newpath already exists." ... But that doesn't really satisfy "portable", so not sure it solves your problem... – filbranden Feb 26 '19 at 04:47
  • @filbranden: It's Linux-only, right? In which case I already have mv -T... – user541686 Feb 26 '19 at 04:58
  • Yes, Linux only, available on kernel 3.15+ and not exposed by glibc... – filbranden Feb 26 '19 at 05:05
  • @filbranden: Wait so mv -T is actually prone to a race too? Or does it use another technique? – user541686 Feb 26 '19 at 05:06
  • mv -T uses renameatu, which has a race condition (by design in order not to fail eagerly), yes. I don't think it's possible not to have one at all, but you can minimise the risk (for some systems at least). If the issue is when "multiple processes try to run this command at once", can't standard locking in "this command" do the job? – Michael Homer Feb 26 '19 at 06:22
  • @MichaelHomer: I see. Lockfiles in... a Bash script? What if the process dies before the lockfile is removed? Nobody is going to be watching over the system 24/7. – user541686 Feb 26 '19 at 06:38
  • Your dir2 is in effect a lock file: if mkdir dir2 succeeds, that means you uniquely, atomically created it, and then rename can do its job of replacing it. The problem becomes accessing rename, which I don't think you can do from Bash, but luckily this isn't a [tag:bash] question. This question does list some ways of running library functions from the shell, though. There are still other race conditions: someone could make a file inside the directory in between, or rename a parent directory, or change a symlink... – Michael Homer Feb 26 '19 at 07:17
  • Ultimately, returning to my second comment, I think you need to specify very clearly exactly what your requirements are: what sorts of race conditions, which programs or processes might be involved, what your implementation languages or constraints are, what you consider a success or a failure, etc. At the moment there are lots of not-quite-there possibilities and no way of winnowing them down. – Michael Homer Feb 26 '19 at 07:19

0 Answers0