diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-08-27 16:51:30 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-08-27 16:51:30 +0000 |
| commit | 8a5fab6333eee874e0675a89937d31b9ae3a7345 (patch) | |
| tree | 67e8f21b25ccc1ab4d73f3dc5136854e6a432f91 /libbb | |
| parent | 512499c8cab30684785c6b116abbb7c868ac5be9 (diff) | |
| download | busybox-w32-8a5fab6333eee874e0675a89937d31b9ae3a7345.tar.gz busybox-w32-8a5fab6333eee874e0675a89937d31b9ae3a7345.tar.bz2 busybox-w32-8a5fab6333eee874e0675a89937d31b9ae3a7345.zip | |
cp: detect and prevent infinite recursion
Diffstat (limited to 'libbb')
| -rw-r--r-- | libbb/copy_file.c | 52 |
1 files changed, 35 insertions, 17 deletions
diff --git a/libbb/copy_file.c b/libbb/copy_file.c index a86e497f0..b70d7b5e0 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c | |||
| @@ -114,6 +114,7 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 114 | 114 | ||
| 115 | if (S_ISDIR(source_stat.st_mode)) { | 115 | if (S_ISDIR(source_stat.st_mode)) { |
| 116 | DIR *dp; | 116 | DIR *dp; |
| 117 | const char *existing_name; | ||
| 117 | struct dirent *d; | 118 | struct dirent *d; |
| 118 | mode_t saved_umask = 0; | 119 | mode_t saved_umask = 0; |
| 119 | 120 | ||
| @@ -122,6 +123,15 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 122 | return -1; | 123 | return -1; |
| 123 | } | 124 | } |
| 124 | 125 | ||
| 126 | /* Did we ever create source ourself before? */ | ||
| 127 | existing_name = is_in_ino_dev_hashtable(&source_stat); | ||
| 128 | if (existing_name) { | ||
| 129 | /* We did! it's a recursion! man the lifeboats... */ | ||
| 130 | bb_error_msg("recursion detected, omitting directory '%s'", | ||
| 131 | existing_name); | ||
| 132 | return -1; | ||
| 133 | } | ||
| 134 | |||
| 125 | /* Create DEST */ | 135 | /* Create DEST */ |
| 126 | if (dest_exists) { | 136 | if (dest_exists) { |
| 127 | if (!S_ISDIR(dest_stat.st_mode)) { | 137 | if (!S_ISDIR(dest_stat.st_mode)) { |
| @@ -143,7 +153,15 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 143 | return -1; | 153 | return -1; |
| 144 | } | 154 | } |
| 145 | umask(saved_umask); | 155 | umask(saved_umask); |
| 156 | /* need stat info for add_to_ino_dev_hashtable */ | ||
| 157 | if (lstat(dest, &dest_stat) < 0) { | ||
| 158 | bb_perror_msg("cannot stat '%s'", dest); | ||
| 159 | return -1; | ||
| 160 | } | ||
| 146 | } | 161 | } |
| 162 | /* remember (dev,inode) of each created dir. | ||
| 163 | * NULL: name is not remembered */ | ||
| 164 | add_to_ino_dev_hashtable(&dest_stat, NULL); | ||
| 147 | 165 | ||
| 148 | /* Recursively copy files in SOURCE */ | 166 | /* Recursively copy files in SOURCE */ |
| 149 | dp = opendir(source); | 167 | dp = opendir(source); |
| @@ -169,8 +187,8 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 169 | if (!dest_exists | 187 | if (!dest_exists |
| 170 | && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 | 188 | && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 |
| 171 | ) { | 189 | ) { |
| 172 | bb_perror_msg("cannot change permissions of '%s'", dest); | 190 | bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest); |
| 173 | retval = -1; | 191 | /* retval = -1; - WRONG! copy *WAS* made */ |
| 174 | } | 192 | } |
| 175 | goto preserve_mode_ugid_time; | 193 | goto preserve_mode_ugid_time; |
| 176 | } | 194 | } |
| @@ -197,7 +215,7 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 197 | } | 215 | } |
| 198 | 216 | ||
| 199 | if (S_ISREG(source_stat.st_mode) | 217 | if (S_ISREG(source_stat.st_mode) |
| 200 | /* Huh? DEREF uses stat, which never returns links! */ | 218 | /* DEREF uses stat, which never returns S_ISLNK() == true. */ |
| 201 | /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ | 219 | /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ |
| 202 | ) { | 220 | ) { |
| 203 | int src_fd; | 221 | int src_fd; |
| @@ -269,14 +287,13 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 269 | #endif | 287 | #endif |
| 270 | if (bb_copyfd_eof(src_fd, dst_fd) == -1) | 288 | if (bb_copyfd_eof(src_fd, dst_fd) == -1) |
| 271 | retval = -1; | 289 | retval = -1; |
| 290 | /* Ok, writing side I can understand... */ | ||
| 272 | if (close(dst_fd) < 0) { | 291 | if (close(dst_fd) < 0) { |
| 273 | bb_perror_msg("cannot close '%s'", dest); | 292 | bb_perror_msg("cannot close '%s'", dest); |
| 274 | retval = -1; | 293 | retval = -1; |
| 275 | } | 294 | } |
| 276 | if (close(src_fd) < 0) { | 295 | /* ...but read size is already checked by bb_copyfd_eof */ |
| 277 | bb_perror_msg("cannot close '%s'", source); | 296 | close(src_fd); |
| 278 | retval = -1; | ||
| 279 | } | ||
| 280 | goto preserve_mode_ugid_time; | 297 | goto preserve_mode_ugid_time; |
| 281 | } | 298 | } |
| 282 | 299 | ||
| @@ -289,18 +306,18 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 289 | return ovr; | 306 | return ovr; |
| 290 | } | 307 | } |
| 291 | if (S_ISLNK(source_stat.st_mode)) { | 308 | if (S_ISLNK(source_stat.st_mode)) { |
| 292 | char *lpath; | 309 | char *lpath = xmalloc_readlink_or_warn(source); |
| 293 | 310 | if (lpath) { | |
| 294 | lpath = xmalloc_readlink_or_warn(source); | 311 | int r = symlink(lpath, dest); |
| 295 | if (lpath && symlink(lpath, dest) < 0) { | ||
| 296 | bb_perror_msg("cannot create symlink '%s'", dest); | ||
| 297 | free(lpath); | 312 | free(lpath); |
| 298 | return -1; | 313 | if (r < 0) { |
| 314 | bb_perror_msg("cannot create symlink '%s'", dest); | ||
| 315 | return -1; | ||
| 316 | } | ||
| 317 | if (flags & FILEUTILS_PRESERVE_STATUS) | ||
| 318 | if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) | ||
| 319 | bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); | ||
| 299 | } | 320 | } |
| 300 | free(lpath); | ||
| 301 | if (flags & FILEUTILS_PRESERVE_STATUS) | ||
| 302 | if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) | ||
| 303 | bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); | ||
| 304 | /* _Not_ jumping to preserve_mode_ugid_time: | 321 | /* _Not_ jumping to preserve_mode_ugid_time: |
| 305 | * symlinks don't have those */ | 322 | * symlinks don't have those */ |
| 306 | return 0; | 323 | return 0; |
| @@ -327,6 +344,7 @@ int copy_file(const char *source, const char *dest, int flags) | |||
| 327 | 344 | ||
| 328 | times.actime = source_stat.st_atime; | 345 | times.actime = source_stat.st_atime; |
| 329 | times.modtime = source_stat.st_mtime; | 346 | times.modtime = source_stat.st_mtime; |
| 347 | /* BTW, utimes sets usec-precision time - just FYI */ | ||
| 330 | if (utime(dest, ×) < 0) | 348 | if (utime(dest, ×) < 0) |
| 331 | bb_perror_msg("cannot preserve %s of '%s'", "times", dest); | 349 | bb_perror_msg("cannot preserve %s of '%s'", "times", dest); |
| 332 | if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { | 350 | if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { |
