xz: Allow directories in io_open_src() in recursive mode.
If the directory is a symlink, it is skipped to prevent a loop in the directory structure that would cause infinite recursion.
This commit is contained in:
parent
e08d65acaf
commit
fe1af552d3
|
@ -613,6 +613,48 @@ io_copy_attrs(const file_pair *pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || (defined(_WIN32) && defined(HAVE_DIRENT_H))
|
||||||
|
/// \brief Tells whether the path is a directory and should be parsed.
|
||||||
|
///
|
||||||
|
/// On Windows, open() will return EACCES if the path is a directory. This
|
||||||
|
/// function will print determine if the directory should be processed or
|
||||||
|
/// print a better error message.
|
||||||
|
static bool
|
||||||
|
should_parse_dir_windows(const char *path)
|
||||||
|
{
|
||||||
|
DWORD file_attr = GetFileAttributes(path);
|
||||||
|
|
||||||
|
// If there is an error, it won't change errno. If we wanted to
|
||||||
|
// know more information about the error we coud use
|
||||||
|
// message_windows_error() to show detailed error description.
|
||||||
|
// Instead we can let the code fall through since the errno from
|
||||||
|
// the original _open() call is likely descriptive enough.
|
||||||
|
if (file_attr != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
if (file_attr & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
// The FILE_ATTRIBUTE_REPARSE_POINT means the
|
||||||
|
// directory is either a symlink or a reparse point.
|
||||||
|
// We do not want to recurse into either of these,
|
||||||
|
// especially a symlink to a directory since this
|
||||||
|
// could lead to an infinite directory processing loop.
|
||||||
|
if (opt_recursive && (file_attr
|
||||||
|
& FILE_ATTRIBUTE_REPARSE_POINT))
|
||||||
|
message_warning(_("%s: Is a symlink to a "
|
||||||
|
"directory, skipping"), path);
|
||||||
|
else if (opt_recursive)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
message_warning(_("%s: Is a directory, skipping"),
|
||||||
|
path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message_error("%s: %s", path, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// Opens the source file. Returns false on success, true on error.
|
/// Opens the source file. Returns false on success, true on error.
|
||||||
static bool
|
static bool
|
||||||
io_open_src_real(file_pair *pair)
|
io_open_src_real(file_pair *pair)
|
||||||
|
@ -751,14 +793,28 @@ io_open_src_real(file_pair *pair)
|
||||||
|
|
||||||
if (was_symlink)
|
if (was_symlink)
|
||||||
message_warning(_("%s: Is a symbolic link, "
|
message_warning(_("%s: Is a symbolic link, "
|
||||||
"skipping"), pair->src_name);
|
"skipping"), pair->src_name);
|
||||||
else
|
else {
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
// The _open() function with MSVC will fail with
|
||||||
|
// EACCES if the path is a directory. We can give a
|
||||||
|
// more accurate error message in this case or, if
|
||||||
|
// in recursive mode, we can process the directory.
|
||||||
|
if (errno == EACCES) {
|
||||||
|
pair->is_directory = should_parse_dir_windows(
|
||||||
|
pair->src_name);
|
||||||
|
return pair->is_directory;
|
||||||
|
}
|
||||||
|
#else
|
||||||
// Something else than O_NOFOLLOW failing
|
// Something else than O_NOFOLLOW failing
|
||||||
// (assuming that the race conditions didn't
|
// (assuming that the race conditions didn't
|
||||||
// confuse us).
|
// confuse us).
|
||||||
message_error(_("%s: %s"), pair->src_name,
|
message_error(_("%s: %s"), pair->src_name,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -778,11 +834,37 @@ io_open_src_real(file_pair *pair)
|
||||||
goto error_msg;
|
goto error_msg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_DIRENT_H
|
||||||
|
// MSVC cannot open() directories, so this check is
|
||||||
|
// skipped in that case.
|
||||||
if (S_ISDIR(pair->src_st.st_mode)) {
|
if (S_ISDIR(pair->src_st.st_mode)) {
|
||||||
message_warning(_("%s: Is a directory, skipping"),
|
if (!opt_recursive) {
|
||||||
pair->src_name);
|
message_warning(_("%s: Is a directory, skipping"),
|
||||||
goto error;
|
pair->src_name);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not allow symlinks with recursive mode because this
|
||||||
|
// could lead to a loop in the file system and thus infinite
|
||||||
|
// recursion. If a symlink is detected, skip it.
|
||||||
|
// S_ISLNK and lstat() are not available with MSVC so these need
|
||||||
|
// to be in an #ifdef
|
||||||
|
if (follow_symlinks) {
|
||||||
|
if (lstat(pair->src_name, &pair->src_st) != 0)
|
||||||
|
goto error_msg;
|
||||||
|
|
||||||
|
if (S_ISLNK(pair->src_st.st_mode)) {
|
||||||
|
message_warning(_("%s: Is a symlink to a "
|
||||||
|
"directory, skipping"), pair->src_name);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)close(pair->src_fd);
|
||||||
|
pair->is_directory = true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (reg_files_only && !S_ISREG(pair->src_st.st_mode)) {
|
if (reg_files_only && !S_ISREG(pair->src_st.st_mode)) {
|
||||||
message_warning(_("%s: Not a regular file, skipping"),
|
message_warning(_("%s: Not a regular file, skipping"),
|
||||||
|
@ -880,6 +962,9 @@ io_open_src(const char *src_name)
|
||||||
.flush_needed = false,
|
.flush_needed = false,
|
||||||
.dest_try_sparse = false,
|
.dest_try_sparse = false,
|
||||||
.dest_pending_sparse = 0,
|
.dest_pending_sparse = 0,
|
||||||
|
#if defined(_MSC_VER) || defined(HAVE_DIRENT_H)
|
||||||
|
.is_directory = false,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Block the signals, for which we have a custom signal handler, so
|
// Block the signals, for which we have a custom signal handler, so
|
||||||
|
|
|
@ -70,6 +70,12 @@ typedef struct {
|
||||||
/// a sparse file.
|
/// a sparse file.
|
||||||
bool dest_try_sparse;
|
bool dest_try_sparse;
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(HAVE_DIRENT_H)
|
||||||
|
/// If true, this entry is a directory, not a file. This can only
|
||||||
|
/// be set if the --recursive option is used.
|
||||||
|
bool is_directory;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// This is used only if dest_try_sparse is true. This holds the
|
/// This is used only if dest_try_sparse is true. This holds the
|
||||||
/// number of zero bytes we haven't written out, because we plan
|
/// number of zero bytes we haven't written out, because we plan
|
||||||
/// to make that byte range a sparse chunk.
|
/// to make that byte range a sparse chunk.
|
||||||
|
|
Loading…
Reference in New Issue