Mercurial > hg > toybox
comparison toys/posix/cp.c @ 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 | 5bc258a4c750 |
children | f8f5ddb6b69a |
comparison
equal
deleted
inserted
replaced
789:9a6b08e7fe94 | 790:6aa6efdd5883 |
---|---|
95 } | 95 } |
96 | 96 |
97 // Loop for -f retry after unlink | 97 // Loop for -f retry after unlink |
98 do { | 98 do { |
99 | 99 |
100 // directory, hardlink, symlink, mknod (char, block, fifo, socket), file | |
101 | |
100 // Copy directory | 102 // Copy directory |
101 | 103 |
102 if (S_ISDIR(try->st.st_mode)) { | 104 if (S_ISDIR(try->st.st_mode)) { |
103 struct stat st2; | 105 struct stat st2; |
104 | 106 |
114 // possible that -p can be made to chown a directory other than the one | 116 // possible that -p can be made to chown a directory other than the one |
115 // we created. The closest we can do to closing this is make sure | 117 // we created. The closest we can do to closing this is make sure |
116 // that what we open _is_ a directory rather than something else. | 118 // that what we open _is_ a directory rather than something else. |
117 | 119 |
118 if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST) | 120 if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST) |
119 if (-1 != (try->extra = openat(cfd, catch, 0))) | 121 if (-1 != (try->extra = openat(cfd, catch, O_NOFOLLOW))) |
120 if (!fstat(try->extra, &st2)) | 122 if (!fstat(try->extra, &st2)) |
121 if (S_ISDIR(st2.st_mode)) return DIRTREE_COMEAGAIN; | 123 if (S_ISDIR(st2.st_mode)) return DIRTREE_COMEAGAIN; |
122 | 124 |
123 // Hardlink | 125 // Hardlink |
124 | 126 |
133 if (S_ISLNK(try->st.st_mode)) { | 135 if (S_ISLNK(try->st.st_mode)) { |
134 int i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf)); | 136 int i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf)); |
135 if (i < 1 || i >= sizeof(toybuf)) break; | 137 if (i < 1 || i >= sizeof(toybuf)) break; |
136 else if (!symlinkat(toybuf, cfd, catch)) err = 0; | 138 else if (!symlinkat(toybuf, cfd, catch)) err = 0; |
137 // block, char, fifo, socket | 139 // block, char, fifo, socket |
138 } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_dev)) | 140 } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev)) { |
139 if (!(flags & (FLAG_a|FLAG_p)) | 141 err = 0; |
140 || -1 != (fdout = openat(cfd, catch, O_RDONLY))) err = 0; | 142 if (flags & (FLAG_a|FLAG_p)) fdout = AT_FDCWD; |
143 } | |
141 | 144 |
142 // Copy contents of file. | 145 // Copy contents of file. |
143 } else { | 146 } else { |
144 int fdin; | 147 int fdin; |
145 | 148 |
163 if (flags & (FLAG_a|FLAG_p)) { | 166 if (flags & (FLAG_a|FLAG_p)) { |
164 struct timespec times[2]; | 167 struct timespec times[2]; |
165 | 168 |
166 // Inability to set these isn't fatal, some require root access. | 169 // Inability to set these isn't fatal, some require root access. |
167 | 170 |
168 fchown(fdout, try->st.st_uid, try->st.st_gid); | |
169 times[0] = try->st.st_atim; | 171 times[0] = try->st.st_atim; |
170 times[1] = try->st.st_mtim; | 172 times[1] = try->st.st_mtim; |
171 futimens(fdout, times); | 173 |
172 fchmod(fdout, try->st.st_mode); | 174 // If we can't get a filehandle to the actual object, use racy functions |
173 } | 175 if (fdout == AT_FDCWD) { |
174 | 176 if (fchownat(cfd, catch, try->st.st_uid, try->st.st_gid, |
175 xclose(fdout); | 177 AT_SYMLINK_NOFOLLOW) |
178 || utimensat(cfd, catch, times, AT_SYMLINK_NOFOLLOW) | |
179 || fchmodat(cfd, catch, try->st.st_mode&07777, 0)) | |
180 err = "%s"; | |
181 } else { | |
182 if (fchown(fdout, try->st.st_uid, try->st.st_gid) | |
183 || futimens(fdout, times) || fchmod(fdout, try->st.st_mode&07777)) | |
184 err = "%s"; | |
185 } | |
186 } | |
187 | |
188 if (fdout != AT_FDCWD) xclose(fdout); | |
176 } | 189 } |
177 | 190 |
178 if (err) perror_msg(err, catch); | 191 if (err) perror_msg(err, catch); |
179 return 0; | 192 return 0; |
180 } | 193 } |