diff options
-rw-r--r-- | archival/unzip.c | 84 |
1 files changed, 53 insertions, 31 deletions
diff --git a/archival/unzip.c b/archival/unzip.c index 44c4a3125..21ba172b5 100644 --- a/archival/unzip.c +++ b/archival/unzip.c | |||
@@ -334,6 +334,7 @@ static void unzip_create_leading_dirs(const char *fn) | |||
334 | free(name); | 334 | free(name); |
335 | } | 335 | } |
336 | 336 | ||
337 | #if ENABLE_FEATURE_UNZIP_CDF | ||
337 | static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) | 338 | static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) |
338 | { | 339 | { |
339 | char *target; | 340 | char *target; |
@@ -365,6 +366,7 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) | |||
365 | bb_perror_msg_and_die("can't create symlink '%s'", dst_fn); | 366 | bb_perror_msg_and_die("can't create symlink '%s'", dst_fn); |
366 | free(target); | 367 | free(target); |
367 | } | 368 | } |
369 | #endif | ||
368 | 370 | ||
369 | static void unzip_extract(zip_header_t *zip, int dst_fd) | 371 | static void unzip_extract(zip_header_t *zip, int dst_fd) |
370 | { | 372 | { |
@@ -437,6 +439,19 @@ static void my_fgets80(char *buf80) | |||
437 | } | 439 | } |
438 | } | 440 | } |
439 | 441 | ||
442 | static int get_lstat_mode(const char *dst_fn) | ||
443 | { | ||
444 | struct stat stat_buf; | ||
445 | if (lstat(dst_fn, &stat_buf) == -1) { | ||
446 | if (errno != ENOENT) { | ||
447 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | ||
448 | } | ||
449 | /* File does not exist */ | ||
450 | return -1; | ||
451 | } | ||
452 | return stat_buf.st_mode; | ||
453 | } | ||
454 | |||
440 | int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 455 | int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
441 | int unzip_main(int argc, char **argv) | 456 | int unzip_main(int argc, char **argv) |
442 | { | 457 | { |
@@ -459,7 +474,6 @@ int unzip_main(int argc, char **argv) | |||
459 | char *base_dir = NULL; | 474 | char *base_dir = NULL; |
460 | int i, opt; | 475 | int i, opt; |
461 | char key_buf[80]; /* must match size used by my_fgets80 */ | 476 | char key_buf[80]; /* must match size used by my_fgets80 */ |
462 | struct stat stat_buf; | ||
463 | 477 | ||
464 | /* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP: | 478 | /* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP: |
465 | * | 479 | * |
@@ -836,11 +850,11 @@ int unzip_main(int argc, char **argv) | |||
836 | goto do_extract; | 850 | goto do_extract; |
837 | } | 851 | } |
838 | if (last_char_is(dst_fn, '/')) { | 852 | if (last_char_is(dst_fn, '/')) { |
853 | int mode; | ||
854 | |||
839 | /* Extract directory */ | 855 | /* Extract directory */ |
840 | if (stat(dst_fn, &stat_buf) == -1) { | 856 | mode = get_lstat_mode(dst_fn); |
841 | if (errno != ENOENT) { | 857 | if (mode == -1) { /* ENOENT */ |
842 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | ||
843 | } | ||
844 | if (!quiet) { | 858 | if (!quiet) { |
845 | printf(" creating: %s\n", dst_fn); | 859 | printf(" creating: %s\n", dst_fn); |
846 | } | 860 | } |
@@ -849,7 +863,7 @@ int unzip_main(int argc, char **argv) | |||
849 | xfunc_die(); | 863 | xfunc_die(); |
850 | } | 864 | } |
851 | } else { | 865 | } else { |
852 | if (!S_ISDIR(stat_buf.st_mode)) { | 866 | if (!S_ISDIR(mode)) { |
853 | bb_error_msg_and_die("'%s' exists but is not a %s", | 867 | bb_error_msg_and_die("'%s' exists but is not a %s", |
854 | dst_fn, "directory"); | 868 | dst_fn, "directory"); |
855 | } | 869 | } |
@@ -857,30 +871,33 @@ int unzip_main(int argc, char **argv) | |||
857 | goto skip_cmpsize; | 871 | goto skip_cmpsize; |
858 | } | 872 | } |
859 | check_file: | 873 | check_file: |
860 | /* Extract file */ | 874 | /* Does target file already exist? */ |
861 | if (lstat(dst_fn, &stat_buf) == -1) { | 875 | { |
862 | /* File does not exist */ | 876 | int mode = get_lstat_mode(dst_fn); |
863 | if (errno != ENOENT) { | 877 | if (mode == -1) { |
864 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | 878 | /* ENOENT: does not exist */ |
879 | goto do_open_and_extract; | ||
865 | } | 880 | } |
866 | goto do_open_and_extract; | 881 | if (overwrite == O_NEVER) { |
867 | } | 882 | goto skip_cmpsize; |
868 | /* File already exists */ | 883 | } |
869 | if (overwrite == O_NEVER) { | 884 | if (!S_ISREG(mode)) { |
870 | goto skip_cmpsize; | 885 | fishy: |
871 | } | 886 | bb_error_msg_and_die("'%s' exists but is not a %s", |
872 | if (!S_ISREG(stat_buf.st_mode)) { | 887 | dst_fn, "regular file"); |
873 | /* File is not regular file */ | 888 | } |
874 | bb_error_msg_and_die("'%s' exists but is not a %s", | 889 | if (overwrite == O_ALWAYS) { |
875 | dst_fn, "regular file"); | 890 | goto do_open_and_extract; |
891 | } | ||
892 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); | ||
893 | my_fgets80(key_buf); | ||
894 | /* User input could take a long time. Is it still a regular file? */ | ||
895 | mode = get_lstat_mode(dst_fn); | ||
896 | if (!S_ISREG(mode)) | ||
897 | goto fishy; | ||
876 | } | 898 | } |
877 | /* File is regular file */ | ||
878 | if (overwrite == O_ALWAYS) | ||
879 | goto do_open_and_extract; | ||
880 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); | ||
881 | my_fgets80(key_buf); | ||
882 | //TODO: redo lstat + ISREG check! user input could have taken a long time! | ||
883 | 899 | ||
900 | /* Extract (or skip) it */ | ||
884 | switch (key_buf[0]) { | 901 | switch (key_buf[0]) { |
885 | case 'A': | 902 | case 'A': |
886 | overwrite = O_ALWAYS; | 903 | overwrite = O_ALWAYS; |
@@ -888,10 +905,15 @@ int unzip_main(int argc, char **argv) | |||
888 | do_open_and_extract: | 905 | do_open_and_extract: |
889 | unzip_create_leading_dirs(dst_fn); | 906 | unzip_create_leading_dirs(dst_fn); |
890 | #if ENABLE_FEATURE_UNZIP_CDF | 907 | #if ENABLE_FEATURE_UNZIP_CDF |
891 | if (!S_ISLNK(file_mode)) | 908 | dst_fd = -1; |
892 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); | 909 | if (!S_ISLNK(file_mode)) { |
910 | dst_fd = xopen3(dst_fn, | ||
911 | O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, | ||
912 | file_mode); | ||
913 | } | ||
893 | #else | 914 | #else |
894 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); | 915 | /* O_NOFOLLOW defends against symlink attacks */ |
916 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW); | ||
895 | #endif | 917 | #endif |
896 | do_extract: | 918 | do_extract: |
897 | if (!quiet) { | 919 | if (!quiet) { |
@@ -901,7 +923,7 @@ int unzip_main(int argc, char **argv) | |||
901 | } | 923 | } |
902 | #if ENABLE_FEATURE_UNZIP_CDF | 924 | #if ENABLE_FEATURE_UNZIP_CDF |
903 | if (S_ISLNK(file_mode)) { | 925 | if (S_ISLNK(file_mode)) { |
904 | if (dst_fd != STDOUT_FILENO) /* no -p */ | 926 | if (dst_fd != STDOUT_FILENO) /* not -p? */ |
905 | unzip_extract_symlink(&zip, dst_fn); | 927 | unzip_extract_symlink(&zip, dst_fn); |
906 | } else | 928 | } else |
907 | #endif | 929 | #endif |