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.
|
||||
static bool
|
||||
io_open_src_real(file_pair *pair)
|
||||
|
@ -751,14 +793,28 @@ io_open_src_real(file_pair *pair)
|
|||
|
||||
if (was_symlink)
|
||||
message_warning(_("%s: Is a symbolic link, "
|
||||
"skipping"), pair->src_name);
|
||||
else
|
||||
"skipping"), pair->src_name);
|
||||
else {
|
||||
#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
|
||||
// (assuming that the race conditions didn't
|
||||
// confuse us).
|
||||
message_error(_("%s: %s"), pair->src_name,
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -778,11 +834,37 @@ io_open_src_real(file_pair *pair)
|
|||
goto error_msg;
|
||||
#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)) {
|
||||
message_warning(_("%s: Is a directory, skipping"),
|
||||
pair->src_name);
|
||||
goto error;
|
||||
if (!opt_recursive) {
|
||||
message_warning(_("%s: Is a directory, skipping"),
|
||||
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)) {
|
||||
message_warning(_("%s: Not a regular file, skipping"),
|
||||
|
@ -880,6 +962,9 @@ io_open_src(const char *src_name)
|
|||
.flush_needed = false,
|
||||
.dest_try_sparse = false,
|
||||
.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
|
||||
|
|
|
@ -70,6 +70,12 @@ typedef struct {
|
|||
/// a sparse file.
|
||||
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
|
||||
/// number of zero bytes we haven't written out, because we plan
|
||||
/// to make that byte range a sparse chunk.
|
||||
|
|
Loading…
Reference in New Issue