On Fri, Jan 17, 2020 at 04:59:04PM +0000, Al Viro wrote: > On Fri, Jan 17, 2020 at 08:36:16AM -0800, Omar Sandoval wrote: > > > The semantics I implemented in my series were basically "linkat with > > AT_REPLACE replaces the target iff rename would replace the target". > > Therefore, symlinks are replaced, not followed, and mountpoints get > > EXDEV. In my opinion that's both sane and unsurprising. > > Umm... EXDEV in rename() comes when _parents_ are on different mounts. > rename() over a mountpoint is EBUSY if it has mounts in caller's > namespace, but it succeeds (and detaches all mounts on the victim > in any namespaces) otherwise. > > When are you returning EXDEV? EXDEV was a thinko, the patch does what rename does: + if (is_local_mountpoint(new_dentry)) { + error = -EBUSY; + goto out; + } ... + if (target) { + dont_mount(new_dentry); + detach_mounts(new_dentry); + } Anyways, my point is that the rename semantics cover 90% of AT_REPLACE. Before I resend the patches, I'll write up the documentation and we can see what other corner cases I missed. > Incidentally, mounts _are_ traversed on > the link source, so what should that variant do when /tmp/foo is > a mountpoint and you feed it "/tmp/foo" both for source and target? EBUSY and noop both seem reasonable in this case, so we pick one and document it.