[Toybox] [PATCH] Fix 'cp -R' handling of symlinks

Stephen Mcgruer smcgruer at google.com
Thu Jan 8 07:03:02 PST 2015


Currently 'cp -R' incorrectly handles symlinks in the source directory, as
it reuses the same global buffer (toybuf) for successive pairs of
'readlinkat', 'symlinkat'. The issue is that readlinkat does not append a
null byte <http://linux.die.net/man/2/readlink> to the buffer, so longer
symlink paths 'stick around' in toybuf and corrupt later, shorter paths.

Example reproduction (before this patch):

$ mkdir targets_dir
$ touch
targets_dir/10-a-very-long-file-name.conf targets_dir/20-shorter.conf
$ mkdir from_dir && cd from_dir
$ ln -s ../targets_dir/10-a-very-long-file-name.conf
10-a-very-long-file-name.conf
$ ln -s ../targets_dir/20-shorter.conf 20-shorter.conf
$ cd ..
$ ls -l from_dir/
lrwxrwxrwx 1 smcgruer eng 44 Jan  8 09:37 10-a-very-long-file-name.conf ->
../targets_dir/10-a-very-long-file-name.conf
lrwxrwxrwx 1 smcgruer eng 30 Jan  8 09:37 20-shorter.conf ->
../targets_dir/20-shorter.conf
$ toybox cp -r from_dir/ to_dir/
$ ls -l to_dir/
total 0
lrwxrwxrwx 1 smcgruer eng 44 Jan  8 09:38 10-a-very-long-file-name.conf ->
../targets_dir/10-a-very-long-file-name.conf
lrwxrwxrwx 1 smcgruer eng 44 Jan  8 09:38 20-shorter.conf ->
../targets_dir/20-shorter.conffile-name.conf

To fix this, I just inserted a null-byte using the return value of
readlinkat (which returns the number of bytes written). Note that this is
safe because the existing code first checks that sizeof(toybuf) > i. It's a
little messy, but I was trying to avoid unrolling the ternary into multiple
layers of conditionals. Perhaps someone on the list can think of a better
solution!

Thanks,
Stephen

Patch:

diff --git a/toys/posix/cp.c b/toys/posix/cp.c
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -231,7 +231,8 @@
         // make symlink, or make block/char/fifo/socket
         if (S_ISLNK(try->st.st_mode)
             ? (0 < (i = readlinkat(tfd, try->name, toybuf,
sizeof(toybuf))) &&
-               sizeof(toybuf) > i && !symlinkat(toybuf, cfd, catch))
+               sizeof(toybuf) > i && ((toybuf[i] = 0) == 0) &&
+               !symlinkat(toybuf, cfd, catch))
             : !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev))
         {
           err = 0;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.landley.net/pipermail/toybox-landley.net/attachments/20150108/46cb533d/attachment-0002.htm>


More information about the Toybox mailing list