<div dir="ltr"><div>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 <a href="http://linux.die.net/man/2/readlink">readlinkat does not append a null byte</a> to the buffer, so longer symlink paths 'stick around' in toybuf and corrupt later, shorter paths. </div><div><br></div><div>Example reproduction (before this patch):</div><div><br></div><div>$ mkdir targets_dir<br></div><div>$ touch targets_dir/10-a-very-long-file-name.conf targets_dir/20-shorter.conf</div><div>$ mkdir from_dir && cd from_dir</div><div>$ ln -s ../targets_dir/10-a-very-long-file-name.conf 10-a-very-long-file-name.conf</div><div>$ ln -s ../targets_dir/20-shorter.conf 20-shorter.conf</div><div>$ cd ..<br>$ ls -l from_dir/</div><div><div>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</div><div>lrwxrwxrwx 1 smcgruer eng 30 Jan  8 09:37 20-shorter.conf -> ../targets_dir/20-shorter.conf</div></div><div>$ toybox cp -r from_dir/ to_dir/</div><div>$ ls -l to_dir/</div><div>total 0</div><div>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</div><div>lrwxrwxrwx 1 smcgruer eng 44 Jan  8 09:38 20-shorter.conf -> ../targets_dir/20-shorter.conffile-name.conf</div><div><br></div><div>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!</div><div><br></div><div>Thanks,<br>Stephen</div><div><br></div><div>Patch:</div><div><br></div><div>diff --git a/toys/posix/cp.c b/toys/posix/cp.c</div><div>--- a/toys/posix/cp.c</div><div>+++ b/toys/posix/cp.c</div><div>@@ -231,7 +231,8 @@</div><div>         // make symlink, or make block/char/fifo/socket</div><div>         if (S_ISLNK(try->st.st_mode)</div><div>             ? (0 < (i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf))) &&</div><div>-               sizeof(toybuf) > i && !symlinkat(toybuf, cfd, catch))</div><div>+               sizeof(toybuf) > i && ((toybuf[i] = 0) == 0) &&</div><div>+               !symlinkat(toybuf, cfd, catch))</div><div>             : !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev))</div><div>         {</div><div>           err = 0;</div><div><br></div></div>