aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archival/unzip.c84
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
337static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) 338static 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
369static void unzip_extract(zip_header_t *zip, int dst_fd) 371static 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
442static 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
440int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 455int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
441int unzip_main(int argc, char **argv) 456int 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