[Toybox] [PATCH] Implement mv -n / cp -n (no clobber).

Rob Landley rob at landley.net
Fri Mar 25 21:57:22 PDT 2016


On 03/21/2016 02:03 AM, Andy Chu wrote:
> On Sun, Mar 20, 2016 at 8:57 PM, Rob Landley <rob at landley.net> wrote:
> > That's why this is still on my todo list, I wanted to have
> > scripts/install.sh --force do --remove-destination, but can't assume the
> > host's cp has it. I should just make it rm -f then cp -n in two commands...
>
> Maybe I'm not understanding --remove-destination correctly, but does
> this patch make it any harder to implmene it?

No, that's just the rathole of analysis I go down when I try to think
through what the correct behavior is here, and then I get distracted
by interrupt du jour. :P

Sorry, properly reading through your patch now.

> Even after reading the coreutils manual, I see understand a situation
> where you would want to use  --remove-destination instead of --force.
> I guess this is not the situation is applies in but I can't think of
> another one.

In the linux from scratch build the bunzip2 package would cp bunzip2 /usr/bin
(as root) and if there was already a symlink there to busybox, it would
follow the symlink and overwrite the busybox binary, and brick the chroot.

(Yeah stock LFS installs itself in /tools to avoid that, but I just made a
chroot I could selectively build and install arbitrary packages in. Yes,
this is a chroot under qemu but the root filesystem aboriginal linux boots
into is combination of tmpfs (limited space) and squashfs (read-only),
so it just copies everything into a clean directory under /home (ext3
partition with 2 gigs of free space) and chroots into it to run the build
in a context where you _can_ install packages and it should just upgrade
the system you're running.)

If bunzip2 does "cp -f bunzip2 /usr/bin" this doesn't help, because -f only
changes the behavior if it COULDN'T overwrite the file. (It tries to overwrite,
does an rm, and tries to overwrite a second time.)

If bunzip2 does "cp -n bunzip2 /usr/bin" it doesn't replace the host busybox,
so the install (silently) fails leaving the busybox version behind.

This is why I removed the writeable bit from the toybox binary, even for
root. That way, "cp -f command /usr/bin/symlink" _does_ work. It's a bit
of a hack, but multiplexer binaries are a bit special.

However, that doesn't fix bunzip2 installing over _busybox_, and I also have
the question of "how should TOYBOX install itself, given that it might be
stomping over busybox or maybe the gzip package that busybox copied the
multiplexer idea from back in the 90's?" (gunzip was a synonym for gzip -d
in the gzip 1.2.4 package the oldest versions of busybox contain, see
'Changes in applet structure' from https://busybox.net/~landley/forensics.txt
and yes that file _is_ what happens when I get angry enough).

So that's why I was thinking of adding cp --big-long-optinon-name to
the toybox install.sh, but I just went with "rm -f oldname && cp new oldname"
in the recent cleanup instead, because I can't trust the host's cp to have
that option _before_ toybox is installed, and we can't use the toybox
cp to install itself when cross compiling.

> The result seems the same either way ?  The destination is copied over.

Yes, but sometimes you want it to be _replaced_, not copied over.

  $ echo hello > one
  $ ln -s one two
  $ echo test > test
  $ cp test two
  $ cat one two
  test
  test
  $ echo hello > one
  $ cp -f test two
  $ cat one two
  test
  test
  $ echo hello > one
  $ cp -n test two
  $ cat one two
  hello
  hello
  $ cp --remove-destination test two
  $ cat one two
  hello
  test

Replacing one of a constellation of symlinks that didn't think to chmod -r
the central binary turns out to be fiddly otherwise. Unless you want the
rm and cp to be separate commands (which admittedly isn't any _more_ racy...
unless the "cp" command is what you're replacing and NOW I remember
why I didn't do that earlier. I broke upgrading toybox by installing over it
on a system that's using it. Although it's actually the toybox binary that
has this problem because ln -f works fine for replacing the individual
command names, but "rm /usr/bin/toybox; cp toybox /usr/bin/toybox" fails
with cp not found if it's a symlink to toybox on the host...)

Sigh...

Rob

 1458968242.0


More information about the Toybox mailing list