summaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-09-16 18:50:56 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-09-16 18:50:56 +0000
commite755e827f7c8ecb21787a4369d7afdeda54d112b (patch)
tree007b1506e69ffcc25bc6ba3b0e6686d806a34264 /libbb
parented6ac53104d811ee88c71aff45c7cad666aaee46 (diff)
downloadbusybox-w32-1_7_1.tar.gz
busybox-w32-1_7_1.tar.bz2
busybox-w32-1_7_1.zip
apply post 1.7.0 patches, set version to 1.7.11_7_1
Diffstat (limited to 'libbb')
-rw-r--r--libbb/copy_file.c191
-rw-r--r--libbb/xreadlink.c22
2 files changed, 125 insertions, 88 deletions
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index b68a257b5..3da8a3531 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -19,28 +19,31 @@
19// (or fail, if it points to dir/nonexistent location/etc). 19// (or fail, if it points to dir/nonexistent location/etc).
20// This is strange, but POSIX-correct. 20// This is strange, but POSIX-correct.
21// coreutils cp has --remove-destination to override this... 21// coreutils cp has --remove-destination to override this...
22//
23// NB: we have special code which still allows for "cp file /dev/node"
24// to work POSIX-ly (the only realistic case where it makes sense)
22 25
23#define DO_POSIX_CP 0 /* 1 - POSIX behavior, 0 - safe behavior */ 26#define DO_POSIX_CP 0 /* 1 - POSIX behavior, 0 - safe behavior */
24 27
25 28// errno must be set to relevant value ("why we cannot create dest?")
29// for POSIX mode to give reasonable error message
26static int ask_and_unlink(const char *dest, int flags) 30static int ask_and_unlink(const char *dest, int flags)
27{ 31{
28 // If !DO_POSIX_CP, act as if -f is always in effect - we don't want
29 // "'file' exists" msg, we want unlink to be done (silently unless -i
30 // is also in effect).
31 // This prevents safe way from asking more questions than POSIX does.
32#if DO_POSIX_CP 32#if DO_POSIX_CP
33 if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { 33 if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) {
34 fprintf(stderr, "'%s' exists\n", dest); 34 // Either it exists, or the *path* doesnt exist
35 bb_perror_msg("cannot create '%s'", dest);
35 return -1; 36 return -1;
36 } 37 }
37#endif 38#endif
39 // If !DO_POSIX_CP, act as if -f is always in effect - we don't want
40 // "cannot create" msg, we want unlink to be done (silently unless -i).
38 41
39 // TODO: maybe we should do it only if ctty is present? 42 // TODO: maybe we should do it only if ctty is present?
40 if (flags & FILEUTILS_INTERACTIVE) { 43 if (flags & FILEUTILS_INTERACTIVE) {
41 // We would not do POSIX insanity. -i asks, 44 // We would not do POSIX insanity. -i asks,
42 // then _unlinks_ the offender. Presto. 45 // then _unlinks_ the offender. Presto.
43 // (No opening without O_EXCL, no unlinks only if -f) 46 // (No "opening without O_EXCL", no "unlink only if -f")
44 // Or else we will end up having 3 open()s! 47 // Or else we will end up having 3 open()s!
45 fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); 48 fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest);
46 if (!bb_ask_confirmation()) 49 if (!bb_ask_confirmation())
@@ -60,9 +63,11 @@ static int ask_and_unlink(const char *dest, int flags)
60 */ 63 */
61int copy_file(const char *source, const char *dest, int flags) 64int copy_file(const char *source, const char *dest, int flags)
62{ 65{
66 /* This is a recursive function, try to minimize stack usage */
67 /* NB: each struct stat is ~100 bytes */
63 struct stat source_stat; 68 struct stat source_stat;
64 struct stat dest_stat; 69 struct stat dest_stat;
65 int status = 0; 70 signed char retval = 0;
66 signed char dest_exists = 0; 71 signed char dest_exists = 0;
67 signed char ovr; 72 signed char ovr;
68 73
@@ -112,6 +117,7 @@ int copy_file(const char *source, const char *dest, int flags)
112 117
113 if (S_ISDIR(source_stat.st_mode)) { 118 if (S_ISDIR(source_stat.st_mode)) {
114 DIR *dp; 119 DIR *dp;
120 const char *tp;
115 struct dirent *d; 121 struct dirent *d;
116 mode_t saved_umask = 0; 122 mode_t saved_umask = 0;
117 123
@@ -120,12 +126,23 @@ int copy_file(const char *source, const char *dest, int flags)
120 return -1; 126 return -1;
121 } 127 }
122 128
123 /* Create DEST. */ 129 /* Did we ever create source ourself before? */
130 tp = is_in_ino_dev_hashtable(&source_stat);
131 if (tp) {
132 /* We did! it's a recursion! man the lifeboats... */
133 bb_error_msg("recursion detected, omitting directory '%s'",
134 source);
135 return -1;
136 }
137
138 /* Create DEST */
124 if (dest_exists) { 139 if (dest_exists) {
125 if (!S_ISDIR(dest_stat.st_mode)) { 140 if (!S_ISDIR(dest_stat.st_mode)) {
126 bb_error_msg("target '%s' is not a directory", dest); 141 bb_error_msg("target '%s' is not a directory", dest);
127 return -1; 142 return -1;
128 } 143 }
144 /* race here: user can substitute a symlink between
145 * this check and actual creation of files inside dest */
129 } else { 146 } else {
130 mode_t mode; 147 mode_t mode;
131 saved_umask = umask(0); 148 saved_umask = umask(0);
@@ -133,22 +150,29 @@ int copy_file(const char *source, const char *dest, int flags)
133 mode = source_stat.st_mode; 150 mode = source_stat.st_mode;
134 if (!(flags & FILEUTILS_PRESERVE_STATUS)) 151 if (!(flags & FILEUTILS_PRESERVE_STATUS))
135 mode = source_stat.st_mode & ~saved_umask; 152 mode = source_stat.st_mode & ~saved_umask;
153 /* Allow owner to access new dir (at least for now) */
136 mode |= S_IRWXU; 154 mode |= S_IRWXU;
137
138 if (mkdir(dest, mode) < 0) { 155 if (mkdir(dest, mode) < 0) {
139 umask(saved_umask); 156 umask(saved_umask);
140 bb_perror_msg("cannot create directory '%s'", dest); 157 bb_perror_msg("cannot create directory '%s'", dest);
141 return -1; 158 return -1;
142 } 159 }
143
144 umask(saved_umask); 160 umask(saved_umask);
161 /* need stat info for add_to_ino_dev_hashtable */
162 if (lstat(dest, &dest_stat) < 0) {
163 bb_perror_msg("cannot stat '%s'", dest);
164 return -1;
165 }
145 } 166 }
167 /* remember (dev,inode) of each created dir.
168 * NULL: name is not remembered */
169 add_to_ino_dev_hashtable(&dest_stat, NULL);
146 170
147 /* Recursively copy files in SOURCE. */ 171 /* Recursively copy files in SOURCE */
148 dp = opendir(source); 172 dp = opendir(source);
149 if (dp == NULL) { 173 if (dp == NULL) {
150 status = -1; 174 retval = -1;
151 goto preserve_status; 175 goto preserve_mode_ugid_time;
152 } 176 }
153 177
154 while ((d = readdir(dp)) != NULL) { 178 while ((d = readdir(dp)) != NULL) {
@@ -159,7 +183,7 @@ int copy_file(const char *source, const char *dest, int flags)
159 continue; 183 continue;
160 new_dest = concat_path_file(dest, d->d_name); 184 new_dest = concat_path_file(dest, d->d_name);
161 if (copy_file(new_source, new_dest, flags) < 0) 185 if (copy_file(new_source, new_dest, flags) < 0)
162 status = -1; 186 retval = -1;
163 free(new_source); 187 free(new_source);
164 free(new_dest); 188 free(new_dest);
165 } 189 }
@@ -168,11 +192,13 @@ int copy_file(const char *source, const char *dest, int flags)
168 if (!dest_exists 192 if (!dest_exists
169 && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 193 && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
170 ) { 194 ) {
171 bb_perror_msg("cannot change permissions of '%s'", dest); 195 bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest);
172 status = -1; 196 /* retval = -1; - WRONG! copy *WAS* made */
173 } 197 }
198 goto preserve_mode_ugid_time;
199 }
174 200
175 } else if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { 201 if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
176 int (*lf)(const char *oldpath, const char *newpath); 202 int (*lf)(const char *oldpath, const char *newpath);
177 make_links: 203 make_links:
178 // Hmm... maybe 204 // Hmm... maybe
@@ -188,47 +214,52 @@ int copy_file(const char *source, const char *dest, int flags)
188 return -1; 214 return -1;
189 } 215 }
190 } 216 }
217 /* _Not_ jumping to preserve_mode_ugid_time:
218 * hard/softlinks don't have those */
191 return 0; 219 return 0;
220 }
192 221
193 } else if (S_ISREG(source_stat.st_mode) 222 if (S_ISREG(source_stat.st_mode)
194 /* Huh? DEREF uses stat, which never returns links! */ 223 /* DEREF uses stat, which never returns S_ISLNK() == true. */
195 /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ 224 /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
196 ) { 225 ) {
197 int src_fd; 226 int src_fd;
198 int dst_fd; 227 int dst_fd;
199 if (ENABLE_FEATURE_PRESERVE_HARDLINKS) {
200 char *link_target;
201 228
202 if (!FLAGS_DEREF) { 229 if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) {
203 link_target = is_in_ino_dev_hashtable(&source_stat); 230 const char *link_target;
204 if (link_target) { 231 link_target = is_in_ino_dev_hashtable(&source_stat);
232 if (link_target) {
233 if (link(link_target, dest) < 0) {
234 ovr = ask_and_unlink(dest, flags);
235 if (ovr <= 0)
236 return ovr;
205 if (link(link_target, dest) < 0) { 237 if (link(link_target, dest) < 0) {
206 ovr = ask_and_unlink(dest, flags); 238 bb_perror_msg("cannot create link '%s'", dest);
207 if (ovr <= 0) 239 return -1;
208 return ovr;
209 if (link(link_target, dest) < 0) {
210 bb_perror_msg("cannot create link '%s'", dest);
211 return -1;
212 }
213 } 240 }
214 return 0;
215 } 241 }
242 return 0;
216 } 243 }
217 add_to_ino_dev_hashtable(&source_stat, dest); 244 add_to_ino_dev_hashtable(&source_stat, dest);
218 } 245 }
219 246
220 src_fd = open_or_warn(source, O_RDONLY); 247 src_fd = open_or_warn(source, O_RDONLY);
221 if (src_fd < 0) { 248 if (src_fd < 0)
222 return -1; 249 return -1;
223 }
224 250
225#if DO_POSIX_CP /* POSIX way (a security problem versus symlink attacks!): */ 251 /* POSIX way is a security problem versus symlink attacks,
226 dst_fd = open(dest, (flags & FILEUTILS_INTERACTIVE) 252 * we do it only for non-symlinks, and only for non-recursive,
227 ? O_WRONLY|O_CREAT|O_EXCL 253 * non-interactive cp. NB: it is still racy
228 : O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); 254 * for "cp file /home/bad_user/file" case
229#else /* safe way: */ 255 * (user can rm file and create a link to /etc/passwd) */
230 dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode); 256 if (DO_POSIX_CP
231#endif 257 || (dest_exists && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE))
258 && !S_ISLNK(dest_stat.st_mode))
259 ) {
260 dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode);
261 } else /* safe way: */
262 dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
232 if (dst_fd == -1) { 263 if (dst_fd == -1) {
233 ovr = ask_and_unlink(dest, flags); 264 ovr = ask_and_unlink(dest, flags);
234 if (ovr <= 0) { 265 if (ovr <= 0) {
@@ -264,60 +295,55 @@ int copy_file(const char *source, const char *dest, int flags)
264 } 295 }
265#endif 296#endif
266 if (bb_copyfd_eof(src_fd, dst_fd) == -1) 297 if (bb_copyfd_eof(src_fd, dst_fd) == -1)
267 status = -1; 298 retval = -1;
299 /* Ok, writing side I can understand... */
268 if (close(dst_fd) < 0) { 300 if (close(dst_fd) < 0) {
269 bb_perror_msg("cannot close '%s'", dest); 301 bb_perror_msg("cannot close '%s'", dest);
270 status = -1; 302 retval = -1;
271 }
272 if (close(src_fd) < 0) {
273 bb_perror_msg("cannot close '%s'", source);
274 status = -1;
275 } 303 }
304 /* ...but read size is already checked by bb_copyfd_eof */
305 close(src_fd);
306 goto preserve_mode_ugid_time;
307 }
276 308
277 } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) 309 /* Source is a symlink or a special file */
278 || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) 310 /* We are lazy here, a bit lax with races... */
279 || S_ISLNK(source_stat.st_mode) 311 if (dest_exists) {
280 ) { 312 errno = EEXIST;
281 // We are lazy here, a bit lax with races... 313 ovr = ask_and_unlink(dest, flags);
282 if (dest_exists) { 314 if (ovr <= 0)
283 ovr = ask_and_unlink(dest, flags); 315 return ovr;
284 if (ovr <= 0) 316 }
285 return ovr; 317 if (S_ISLNK(source_stat.st_mode)) {
286 } 318 char *lpath = xmalloc_readlink_or_warn(source);
287 if (S_ISFIFO(source_stat.st_mode)) { 319 if (lpath) {
288 if (mkfifo(dest, source_stat.st_mode) < 0) { 320 int r = symlink(lpath, dest);
289 bb_perror_msg("cannot create fifo '%s'", dest); 321 free(lpath);
290 return -1; 322 if (r < 0) {
291 }
292 } else if (S_ISLNK(source_stat.st_mode)) {
293 char *lpath;
294
295 lpath = xmalloc_readlink_or_warn(source);
296 if (lpath && symlink(lpath, dest) < 0) {
297 bb_perror_msg("cannot create symlink '%s'", dest); 323 bb_perror_msg("cannot create symlink '%s'", dest);
298 free(lpath);
299 return -1; 324 return -1;
300 } 325 }
301 free(lpath);
302
303 if (flags & FILEUTILS_PRESERVE_STATUS) 326 if (flags & FILEUTILS_PRESERVE_STATUS)
304 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) 327 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
305 bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); 328 bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest);
306 329 }
307 return 0; 330 /* _Not_ jumping to preserve_mode_ugid_time:
308 331 * symlinks don't have those */
309 } else { 332 return 0;
310 if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { 333 }
311 bb_perror_msg("cannot create '%s'", dest); 334 if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
312 return -1; 335 || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
313 } 336 ) {
337 if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
338 bb_perror_msg("cannot create '%s'", dest);
339 return -1;
314 } 340 }
315 } else { 341 } else {
316 bb_error_msg("internal error: unrecognized file type"); 342 bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode);
317 return -1; 343 return -1;
318 } 344 }
319 345
320 preserve_status: 346 preserve_mode_ugid_time:
321 347
322 if (flags & FILEUTILS_PRESERVE_STATUS 348 if (flags & FILEUTILS_PRESERVE_STATUS
323 /* Cannot happen: */ 349 /* Cannot happen: */
@@ -327,6 +353,7 @@ int copy_file(const char *source, const char *dest, int flags)
327 353
328 times.actime = source_stat.st_atime; 354 times.actime = source_stat.st_atime;
329 times.modtime = source_stat.st_mtime; 355 times.modtime = source_stat.st_mtime;
356 /* BTW, utimes sets usec-precision time - just FYI */
330 if (utime(dest, &times) < 0) 357 if (utime(dest, &times) < 0)
331 bb_perror_msg("cannot preserve %s of '%s'", "times", dest); 358 bb_perror_msg("cannot preserve %s of '%s'", "times", dest);
332 if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { 359 if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
@@ -337,5 +364,5 @@ int copy_file(const char *source, const char *dest, int flags)
337 bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest); 364 bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest);
338 } 365 }
339 366
340 return status; 367 return retval;
341} 368}
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index 18a8b9467..4d87b944d 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -10,8 +10,7 @@
10 * NOTE: This function returns a malloced char* that you will have to free 10 * NOTE: This function returns a malloced char* that you will have to free
11 * yourself. You have been warned. 11 * yourself. You have been warned.
12 */ 12 */
13 13char *xmalloc_readlink(const char *path)
14char *xmalloc_readlink_or_warn(const char *path)
15{ 14{
16 enum { GROWBY = 80 }; /* how large we will grow strings by */ 15 enum { GROWBY = 80 }; /* how large we will grow strings by */
17 16
@@ -20,20 +19,30 @@ char *xmalloc_readlink_or_warn(const char *path)
20 19
21 do { 20 do {
22 buf = xrealloc(buf, bufsize += GROWBY); 21 buf = xrealloc(buf, bufsize += GROWBY);
23 readsize = readlink(path, buf, bufsize); /* 1st try */ 22 readsize = readlink(path, buf, bufsize);
24 if (readsize == -1) { 23 if (readsize == -1) {
25 bb_perror_msg("%s", path);
26 free(buf); 24 free(buf);
27 return NULL; 25 return NULL;
28 } 26 }
29 } 27 } while (bufsize < readsize + 1);
30 while (bufsize < readsize + 1);
31 28
32 buf[readsize] = '\0'; 29 buf[readsize] = '\0';
33 30
34 return buf; 31 return buf;
35} 32}
36 33
34char *xmalloc_readlink_or_warn(const char *path)
35{
36 char *buf = xmalloc_readlink(path);
37 if (!buf) {
38 /* EINVAL => "file: Invalid argument" => puzzled user */
39 bb_error_msg("%s: cannot read link (not a symlink?)", path);
40 }
41 return buf;
42}
43
44/* UNUSED */
45#if 0
37char *xmalloc_realpath(const char *path) 46char *xmalloc_realpath(const char *path)
38{ 47{
39#if defined(__GLIBC__) && !defined(__UCLIBC__) 48#if defined(__GLIBC__) && !defined(__UCLIBC__)
@@ -46,3 +55,4 @@ char *xmalloc_realpath(const char *path)
46 return xstrdup(realpath(path, buf)); 55 return xstrdup(realpath(path, buf));
47#endif 56#endif
48} 57}
58#endif