1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 13:17:35 +00:00

FileOperation: Preserve mode bits and owner when copying files

Previously, all files and folders copied with FileManager would be
created with the default mode bits set and would have the same uid
and gid as the currently logged in user.
This commit is contained in:
Tim Ledbetter 2023-07-22 10:24:44 +01:00 committed by Andreas Kling
parent fa379b6e86
commit 21fdefd297

View file

@ -30,6 +30,9 @@ struct WorkItem {
DeprecatedString source;
DeprecatedString destination;
off_t size;
mode_t mode { 0 };
uid_t uid { 0 };
gid_t gid { 0 };
};
static void report_warning(StringView message);
@ -38,7 +41,7 @@ static ErrorOr<int> perform_copy(Vector<StringView> const& sources, DeprecatedSt
static ErrorOr<int> perform_move(Vector<StringView> const& sources, DeprecatedString const& destination);
static ErrorOr<int> perform_delete(Vector<StringView> const& sources);
static ErrorOr<int> execute_work_items(Vector<WorkItem> const& items);
static ErrorOr<NonnullOwnPtr<Core::File>> open_destination_file(DeprecatedString const& destination);
static ErrorOr<NonnullOwnPtr<Core::File>> open_destination_file(DeprecatedString const& destination, mode_t mode);
static DeprecatedString deduplicate_destination_file_name(DeprecatedString const& destination);
ErrorOr<int> serenity_main(Main::Arguments arguments)
@ -80,13 +83,17 @@ static void report_error(StringView message)
static ErrorOr<int> collect_copy_work_items(DeprecatedString const& source, DeprecatedString const& destination, Vector<WorkItem>& items)
{
if (auto const st = TRY(Core::System::lstat(source)); !S_ISDIR(st.st_mode)) {
auto const st = TRY(Core::System::lstat(source));
if (!S_ISDIR(st.st_mode)) {
// It's a file.
items.append(WorkItem {
.type = WorkItem::Type::CopyFile,
.source = source,
.destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(),
.size = st.st_size,
.mode = st.st_mode,
.uid = st.st_uid,
.gid = st.st_gid,
});
return 0;
}
@ -97,6 +104,9 @@ static ErrorOr<int> collect_copy_work_items(DeprecatedString const& source, Depr
.source = {},
.destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(),
.size = 0,
.mode = st.st_mode,
.uid = st.st_uid,
.gid = st.st_gid,
});
Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir);
@ -124,13 +134,17 @@ ErrorOr<int> perform_copy(Vector<StringView> const& sources, DeprecatedString co
static ErrorOr<int> collect_move_work_items(DeprecatedString const& source, DeprecatedString const& destination, Vector<WorkItem>& items)
{
if (auto const st = TRY(Core::System::lstat(source)); !S_ISDIR(st.st_mode)) {
auto const st = TRY(Core::System::lstat(source));
if (!S_ISDIR(st.st_mode)) {
// It's a file.
items.append(WorkItem {
.type = WorkItem::Type::MoveFile,
.source = source,
.destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(),
.size = st.st_size,
.mode = st.st_mode,
.uid = st.st_uid,
.gid = st.st_gid,
});
return 0;
}
@ -141,6 +155,9 @@ static ErrorOr<int> collect_move_work_items(DeprecatedString const& source, Depr
.source = {},
.destination = LexicalPath::join(destination, LexicalPath::basename(source)).string(),
.size = 0,
.mode = st.st_mode,
.uid = st.st_uid,
.gid = st.st_gid,
});
Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir);
@ -229,10 +246,11 @@ ErrorOr<int> execute_work_items(Vector<WorkItem> const& items)
outln("PROGRESS {} {} {} {} {} {} {}", i, items.size(), executed_work_bytes, total_work_bytes, item_done, item.size, item.source);
};
auto copy_file = [&](DeprecatedString const& source, DeprecatedString const& destination) -> ErrorOr<int> {
auto copy_file = [&](DeprecatedString const& source, DeprecatedString const& destination, mode_t mode, uid_t uid, gid_t gid) -> ErrorOr<int> {
auto source_file = TRY(Core::File::open(source, Core::File::OpenMode::Read));
// FIXME: When the file already exists, let the user choose the next action instead of renaming it by default.
auto destination_file = TRY(open_destination_file(destination));
auto destination_file = TRY(open_destination_file(destination, mode));
TRY(Core::System::fchown(destination_file->fd(), uid, gid));
auto buffer = TRY(ByteBuffer::create_zeroed(64 * KiB));
while (true) {
@ -262,8 +280,10 @@ ErrorOr<int> execute_work_items(Vector<WorkItem> const& items)
case WorkItem::Type::CreateDirectory: {
outln("MKDIR {}", item.destination);
// FIXME: Support deduplication like open_destination_file() when the directory already exists.
if (mkdir(item.destination.characters(), 0755) < 0 && errno != EEXIST)
if (auto maybe_error = Core::System::mkdir(item.destination, item.mode); maybe_error.is_error() && maybe_error.error().code() != EEXIST)
return Error::from_syscall("mkdir"sv, -errno);
TRY(Core::System::chown(item.destination, item.uid, item.gid));
break;
}
@ -273,7 +293,7 @@ ErrorOr<int> execute_work_items(Vector<WorkItem> const& items)
}
case WorkItem::Type::CopyFile: {
TRY(copy_file(item.source, item.destination));
TRY(copy_file(item.source, item.destination, item.mode, item.uid, item.gid));
break;
}
@ -300,7 +320,7 @@ ErrorOr<int> execute_work_items(Vector<WorkItem> const& items)
}
// EXDEV means we have to copy the file data and then remove the original
TRY(copy_file(item.source, item.destination));
TRY(copy_file(item.source, item.destination, item.mode, item.uid, item.gid));
TRY(Core::System::unlink(item.source));
break;
}
@ -327,11 +347,11 @@ ErrorOr<int> execute_work_items(Vector<WorkItem> const& items)
return 0;
}
ErrorOr<NonnullOwnPtr<Core::File>> open_destination_file(DeprecatedString const& destination)
ErrorOr<NonnullOwnPtr<Core::File>> open_destination_file(DeprecatedString const& destination, mode_t mode)
{
auto destination_file_or_error = Core::File::open(destination, (Core::File::OpenMode)(Core::File::OpenMode::Write | Core::File::OpenMode::Truncate | Core::File::OpenMode::MustBeNew));
auto destination_file_or_error = Core::File::open(destination, (Core::File::OpenMode::Write | Core::File::OpenMode::Truncate | Core::File::OpenMode::MustBeNew), mode);
if (destination_file_or_error.is_error() && destination_file_or_error.error().code() == EEXIST) {
return open_destination_file(deduplicate_destination_file_name(destination));
return open_destination_file(deduplicate_destination_file_name(destination), mode);
}
return destination_file_or_error;
}