Mercurial > hg > toybox
changeset 790:6aa6efdd5883
Make "sudo cp -rp /dev/null blah" work. Still not happy with it, fchmodat(AT_SYMLINK_NOFOLLOW) doesn't work (there's a glibc bug open for this. It's really a missing kernel syscall, but glibc fails without ever making any syscall if you feed it that flag, which isn't helpful).
author | Rob Landley <rob@landley.net> |
---|---|
date | Wed, 16 Jan 2013 06:57:44 -0600 |
parents | 9a6b08e7fe94 |
children | 126cd3b8015c |
files | toys/posix/cp.c |
diffstat | 1 files changed, 21 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/toys/posix/cp.c Tue Jan 15 12:33:46 2013 -0600 +++ b/toys/posix/cp.c Wed Jan 16 06:57:44 2013 -0600 @@ -97,6 +97,8 @@ // Loop for -f retry after unlink do { + // directory, hardlink, symlink, mknod (char, block, fifo, socket), file + // Copy directory if (S_ISDIR(try->st.st_mode)) { @@ -116,7 +118,7 @@ // that what we open _is_ a directory rather than something else. if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST) - if (-1 != (try->extra = openat(cfd, catch, 0))) + if (-1 != (try->extra = openat(cfd, catch, O_NOFOLLOW))) if (!fstat(try->extra, &st2)) if (S_ISDIR(st2.st_mode)) return DIRTREE_COMEAGAIN; @@ -135,9 +137,10 @@ if (i < 1 || i >= sizeof(toybuf)) break; else if (!symlinkat(toybuf, cfd, catch)) err = 0; // block, char, fifo, socket - } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_dev)) - if (!(flags & (FLAG_a|FLAG_p)) - || -1 != (fdout = openat(cfd, catch, O_RDONLY))) err = 0; + } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev)) { + err = 0; + if (flags & (FLAG_a|FLAG_p)) fdout = AT_FDCWD; + } // Copy contents of file. } else { @@ -165,14 +168,24 @@ // Inability to set these isn't fatal, some require root access. - fchown(fdout, try->st.st_uid, try->st.st_gid); times[0] = try->st.st_atim; times[1] = try->st.st_mtim; - futimens(fdout, times); - fchmod(fdout, try->st.st_mode); + + // If we can't get a filehandle to the actual object, use racy functions + if (fdout == AT_FDCWD) { + if (fchownat(cfd, catch, try->st.st_uid, try->st.st_gid, + AT_SYMLINK_NOFOLLOW) + || utimensat(cfd, catch, times, AT_SYMLINK_NOFOLLOW) + || fchmodat(cfd, catch, try->st.st_mode&07777, 0)) + err = "%s"; + } else { + if (fchown(fdout, try->st.st_uid, try->st.st_gid) + || futimens(fdout, times) || fchmod(fdout, try->st.st_mode&07777)) + err = "%s"; + } } - xclose(fdout); + if (fdout != AT_FDCWD) xclose(fdout); } if (err) perror_msg(err, catch);