free(name);
}
+#if ENABLE_FEATURE_UNZIP_CDF
static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn)
{
char *target;
bb_perror_msg_and_die("can't create symlink '%s'", dst_fn);
free(target);
}
+#endif
static void unzip_extract(zip_header_t *zip, int dst_fd)
{
}
}
+static int get_lstat_mode(const char *dst_fn)
+{
+ struct stat stat_buf;
+ if (lstat(dst_fn, &stat_buf) == -1) {
+ if (errno != ENOENT) {
+ bb_perror_msg_and_die("can't stat '%s'", dst_fn);
+ }
+ /* File does not exist */
+ return -1;
+ }
+ return stat_buf.st_mode;
+}
+
int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int unzip_main(int argc, char **argv)
{
char *base_dir = NULL;
int i, opt;
char key_buf[80]; /* must match size used by my_fgets80 */
- struct stat stat_buf;
/* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP:
*
goto do_extract;
}
if (last_char_is(dst_fn, '/')) {
+ int mode;
+
/* Extract directory */
- if (stat(dst_fn, &stat_buf) == -1) {
- if (errno != ENOENT) {
- bb_perror_msg_and_die("can't stat '%s'", dst_fn);
- }
+ mode = get_lstat_mode(dst_fn);
+ if (mode == -1) { /* ENOENT */
if (!quiet) {
printf(" creating: %s\n", dst_fn);
}
xfunc_die();
}
} else {
- if (!S_ISDIR(stat_buf.st_mode)) {
+ if (!S_ISDIR(mode)) {
bb_error_msg_and_die("'%s' exists but is not a %s",
dst_fn, "directory");
}
goto skip_cmpsize;
}
check_file:
- /* Extract file */
- if (lstat(dst_fn, &stat_buf) == -1) {
- /* File does not exist */
- if (errno != ENOENT) {
- bb_perror_msg_and_die("can't stat '%s'", dst_fn);
+ /* Does target file already exist? */
+ {
+ int mode = get_lstat_mode(dst_fn);
+ if (mode == -1) {
+ /* ENOENT: does not exist */
+ goto do_open_and_extract;
}
- goto do_open_and_extract;
- }
- /* File already exists */
- if (overwrite == O_NEVER) {
- goto skip_cmpsize;
- }
- if (!S_ISREG(stat_buf.st_mode)) {
- /* File is not regular file */
- bb_error_msg_and_die("'%s' exists but is not a %s",
- dst_fn, "regular file");
+ if (overwrite == O_NEVER) {
+ goto skip_cmpsize;
+ }
+ if (!S_ISREG(mode)) {
+ fishy:
+ bb_error_msg_and_die("'%s' exists but is not a %s",
+ dst_fn, "regular file");
+ }
+ if (overwrite == O_ALWAYS) {
+ goto do_open_and_extract;
+ }
+ printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
+ my_fgets80(key_buf);
+ /* User input could take a long time. Is it still a regular file? */
+ mode = get_lstat_mode(dst_fn);
+ if (!S_ISREG(mode))
+ goto fishy;
}
- /* File is regular file */
- if (overwrite == O_ALWAYS)
- goto do_open_and_extract;
- printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
- my_fgets80(key_buf);
-//TODO: redo lstat + ISREG check! user input could have taken a long time!
+ /* Extract (or skip) it */
switch (key_buf[0]) {
case 'A':
overwrite = O_ALWAYS;
do_open_and_extract:
unzip_create_leading_dirs(dst_fn);
#if ENABLE_FEATURE_UNZIP_CDF
- if (!S_ISLNK(file_mode))
- dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
+ dst_fd = -1;
+ if (!S_ISLNK(file_mode)) {
+ dst_fd = xopen3(dst_fn,
+ O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW,
+ file_mode);
+ }
#else
- dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC);
+ /* O_NOFOLLOW defends against symlink attacks */
+ dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
#endif
do_extract:
if (!quiet) {
}
#if ENABLE_FEATURE_UNZIP_CDF
if (S_ISLNK(file_mode)) {
- if (dst_fd != STDOUT_FILENO) /* no -p */
+ if (dst_fd != STDOUT_FILENO) /* not -p? */
unzip_extract_symlink(&zip, dst_fn);
} else
#endif