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:
parent
fa379b6e86
commit
21fdefd297
1 changed files with 31 additions and 11 deletions
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue