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 | |
parent | 512499c8cab30684785c6b116abbb7c868ac5be9 (diff) | |
download | busybox-w32-8a5fab6333eee874e0675a89937d31b9ae3a7345.tar.gz busybox-w32-8a5fab6333eee874e0675a89937d31b9ae3a7345.tar.bz2 busybox-w32-8a5fab6333eee874e0675a89937d31b9ae3a7345.zip |
cp: detect and prevent infinite recursion
-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) { |