summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibCore/DeprecatedFile.cpp
diff options
context:
space:
mode:
authorTim Schumacher <timschumi@gmx.de>2023-02-08 21:08:01 +0100
committerLinus Groh <mail@linusgroh.de>2023-02-13 00:50:07 +0000
commitd43a7eae545cd699f301471bd0f82399174339c1 (patch)
treec0a55f768f845041c3aebed3f126d462155a799f /Userland/Libraries/LibCore/DeprecatedFile.cpp
parent14951b92ca6160664ccb68c5e1b2d40133763e5f (diff)
downloadserenity-d43a7eae545cd699f301471bd0f82399174339c1.zip
LibCore: Rename `File` to `DeprecatedFile`
As usual, this removes many unused includes and moves used includes further down the chain.
Diffstat (limited to 'Userland/Libraries/LibCore/DeprecatedFile.cpp')
-rw-r--r--Userland/Libraries/LibCore/DeprecatedFile.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/Userland/Libraries/LibCore/DeprecatedFile.cpp b/Userland/Libraries/LibCore/DeprecatedFile.cpp
new file mode 100644
index 0000000000..7bf17fa971
--- /dev/null
+++ b/Userland/Libraries/LibCore/DeprecatedFile.cpp
@@ -0,0 +1,632 @@
+/*
+ * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/LexicalPath.h>
+#include <AK/Platform.h>
+#include <AK/ScopeGuard.h>
+#include <LibCore/DeprecatedFile.h>
+#include <LibCore/DirIterator.h>
+#include <LibCore/System.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+
+#ifdef AK_OS_SERENITY
+# include <serenity.h>
+#endif
+
+// On Linux distros that use glibc `basename` is defined as a macro that expands to `__xpg_basename`, so we undefine it
+#if defined(AK_OS_LINUX) && defined(basename)
+# undef basename
+#endif
+
+namespace Core {
+
+ErrorOr<NonnullRefPtr<DeprecatedFile>> DeprecatedFile::open(DeprecatedString filename, OpenMode mode, mode_t permissions)
+{
+ auto file = DeprecatedFile::construct(move(filename));
+ if (!file->open_impl(mode, permissions))
+ return Error::from_errno(file->error());
+ return file;
+}
+
+DeprecatedFile::DeprecatedFile(DeprecatedString filename, Object* parent)
+ : IODevice(parent)
+ , m_filename(move(filename))
+{
+}
+
+DeprecatedFile::~DeprecatedFile()
+{
+ if (m_should_close_file_descriptor == ShouldCloseFileDescriptor::Yes && mode() != OpenMode::NotOpen)
+ close();
+}
+
+bool DeprecatedFile::open(int fd, OpenMode mode, ShouldCloseFileDescriptor should_close)
+{
+ set_fd(fd);
+ set_mode(mode);
+ m_should_close_file_descriptor = should_close;
+ return true;
+}
+
+bool DeprecatedFile::open(OpenMode mode)
+{
+ return open_impl(mode, 0666);
+}
+
+bool DeprecatedFile::open_impl(OpenMode mode, mode_t permissions)
+{
+ VERIFY(!m_filename.is_null());
+ int flags = 0;
+ if (has_flag(mode, OpenMode::ReadOnly) && has_flag(mode, OpenMode::WriteOnly)) {
+ flags |= O_RDWR | O_CREAT;
+ } else if (has_flag(mode, OpenMode::ReadOnly)) {
+ flags |= O_RDONLY;
+ } else if (has_flag(mode, OpenMode::WriteOnly)) {
+ flags |= O_WRONLY | O_CREAT;
+ bool should_truncate = !(has_flag(mode, OpenMode::Append) || has_flag(mode, OpenMode::MustBeNew));
+ if (should_truncate)
+ flags |= O_TRUNC;
+ }
+ if (has_flag(mode, OpenMode::Append))
+ flags |= O_APPEND;
+ if (has_flag(mode, OpenMode::Truncate))
+ flags |= O_TRUNC;
+ if (has_flag(mode, OpenMode::MustBeNew))
+ flags |= O_EXCL;
+ if (!has_flag(mode, OpenMode::KeepOnExec))
+ flags |= O_CLOEXEC;
+ int fd = ::open(m_filename.characters(), flags, permissions);
+ if (fd < 0) {
+ set_error(errno);
+ return false;
+ }
+
+ set_fd(fd);
+ set_mode(mode);
+ return true;
+}
+
+int DeprecatedFile::leak_fd()
+{
+ m_should_close_file_descriptor = ShouldCloseFileDescriptor::No;
+ return fd();
+}
+
+bool DeprecatedFile::is_device() const
+{
+ return is_device(fd());
+}
+
+bool DeprecatedFile::is_device(DeprecatedString const& filename)
+{
+ struct stat st;
+ if (stat(filename.characters(), &st) < 0)
+ return false;
+ return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
+}
+
+bool DeprecatedFile::is_device(int fd)
+{
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ return false;
+ return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
+}
+
+bool DeprecatedFile::is_block_device() const
+{
+ struct stat stat;
+ if (fstat(fd(), &stat) < 0)
+ return false;
+ return S_ISBLK(stat.st_mode);
+}
+
+bool DeprecatedFile::is_block_device(DeprecatedString const& filename)
+{
+ struct stat st;
+ if (stat(filename.characters(), &st) < 0)
+ return false;
+ return S_ISBLK(st.st_mode);
+}
+
+bool DeprecatedFile::is_char_device() const
+{
+ struct stat stat;
+ if (fstat(fd(), &stat) < 0)
+ return false;
+ return S_ISCHR(stat.st_mode);
+}
+
+bool DeprecatedFile::is_char_device(DeprecatedString const& filename)
+{
+ struct stat st;
+ if (stat(filename.characters(), &st) < 0)
+ return false;
+ return S_ISCHR(st.st_mode);
+}
+
+bool DeprecatedFile::is_directory() const
+{
+ return is_directory(fd());
+}
+
+bool DeprecatedFile::is_directory(DeprecatedString const& filename)
+{
+ struct stat st;
+ if (stat(filename.characters(), &st) < 0)
+ return false;
+ return S_ISDIR(st.st_mode);
+}
+
+bool DeprecatedFile::is_directory(int fd)
+{
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ return false;
+ return S_ISDIR(st.st_mode);
+}
+
+bool DeprecatedFile::is_link() const
+{
+ struct stat stat;
+ if (fstat(fd(), &stat) < 0)
+ return false;
+ return S_ISLNK(stat.st_mode);
+}
+
+bool DeprecatedFile::is_link(DeprecatedString const& filename)
+{
+ struct stat st;
+ if (lstat(filename.characters(), &st) < 0)
+ return false;
+ return S_ISLNK(st.st_mode);
+}
+
+bool DeprecatedFile::looks_like_shared_library() const
+{
+ return DeprecatedFile::looks_like_shared_library(m_filename);
+}
+
+bool DeprecatedFile::looks_like_shared_library(DeprecatedString const& filename)
+{
+ return filename.ends_with(".so"sv) || filename.contains(".so."sv);
+}
+
+bool DeprecatedFile::can_delete_or_move(StringView path)
+{
+ VERIFY(!path.is_empty());
+ auto directory = LexicalPath::dirname(path);
+ auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
+ if (!directory_has_write_access)
+ return false;
+
+ auto stat_or_empty = [](StringView path) {
+ auto stat_or_error = Core::System::stat(path);
+ if (stat_or_error.is_error()) {
+ struct stat stat { };
+ return stat;
+ }
+ return stat_or_error.release_value();
+ };
+
+ auto directory_stat = stat_or_empty(directory);
+ bool is_directory_sticky = directory_stat.st_mode & S_ISVTX;
+ if (!is_directory_sticky)
+ return true;
+
+ // Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
+ auto user_id = geteuid();
+ return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
+}
+
+bool DeprecatedFile::exists(StringView filename)
+{
+ return !Core::System::stat(filename).is_error();
+}
+
+ErrorOr<size_t> DeprecatedFile::size(DeprecatedString const& filename)
+{
+ struct stat st;
+ if (stat(filename.characters(), &st) < 0)
+ return Error::from_errno(errno);
+ return st.st_size;
+}
+
+DeprecatedString DeprecatedFile::real_path_for(DeprecatedString const& filename)
+{
+ if (filename.is_null())
+ return {};
+ auto* path = realpath(filename.characters(), nullptr);
+ DeprecatedString real_path(path);
+ free(path);
+ return real_path;
+}
+
+DeprecatedString DeprecatedFile::current_working_directory()
+{
+ char* cwd = getcwd(nullptr, 0);
+ if (!cwd) {
+ perror("getcwd");
+ return {};
+ }
+
+ auto cwd_as_string = DeprecatedString(cwd);
+ free(cwd);
+
+ return cwd_as_string;
+}
+
+DeprecatedString DeprecatedFile::absolute_path(DeprecatedString const& path)
+{
+ if (DeprecatedFile::exists(path))
+ return DeprecatedFile::real_path_for(path);
+
+ if (path.starts_with("/"sv))
+ return LexicalPath::canonicalized_path(path);
+
+ auto working_directory = DeprecatedFile::current_working_directory();
+ auto full_path = LexicalPath::join(working_directory, path);
+
+ return LexicalPath::canonicalized_path(full_path.string());
+}
+
+#ifdef AK_OS_SERENITY
+
+ErrorOr<DeprecatedString> DeprecatedFile::read_link(DeprecatedString const& link_path)
+{
+ // First, try using a 64-byte buffer, that ought to be enough for anybody.
+ char small_buffer[64];
+
+ int rc = serenity_readlink(link_path.characters(), link_path.length(), small_buffer, sizeof(small_buffer));
+ if (rc < 0)
+ return Error::from_errno(errno);
+
+ size_t size = rc;
+ // If the call was successful, the syscall (unlike the LibC wrapper)
+ // returns the full size of the link. Let's see if our small buffer
+ // was enough to read the whole link.
+ if (size <= sizeof(small_buffer))
+ return DeprecatedString { small_buffer, size };
+ // Nope, but at least now we know the right size.
+ char* large_buffer_ptr;
+ auto large_buffer = StringImpl::create_uninitialized(size, large_buffer_ptr);
+
+ rc = serenity_readlink(link_path.characters(), link_path.length(), large_buffer_ptr, size);
+ if (rc < 0)
+ return Error::from_errno(errno);
+
+ size_t new_size = rc;
+ if (new_size == size)
+ return { *large_buffer };
+
+ // If we're here, the symlink has changed while we were looking at it.
+ // If it became shorter, our buffer is valid, we just have to trim it a bit.
+ if (new_size < size)
+ return DeprecatedString { large_buffer_ptr, new_size };
+ // Otherwise, here's not much we can do, unless we want to loop endlessly
+ // in this case. Let's leave it up to the caller whether to loop.
+ errno = EAGAIN;
+ return Error::from_errno(errno);
+}
+
+#else
+
+// This is a sad version for other systems. It has to always make a copy of the
+// link path, and to always make two syscalls to get the right size first.
+ErrorOr<DeprecatedString> DeprecatedFile::read_link(DeprecatedString const& link_path)
+{
+ struct stat statbuf = {};
+ int rc = lstat(link_path.characters(), &statbuf);
+ if (rc < 0)
+ return Error::from_errno(errno);
+ char* buffer_ptr;
+ auto buffer = StringImpl::create_uninitialized(statbuf.st_size, buffer_ptr);
+ if (readlink(link_path.characters(), buffer_ptr, statbuf.st_size) < 0)
+ return Error::from_errno(errno);
+ // (See above.)
+ if (rc == statbuf.st_size)
+ return { *buffer };
+ return DeprecatedString { buffer_ptr, (size_t)rc };
+}
+
+#endif
+
+static RefPtr<DeprecatedFile> stdin_file;
+static RefPtr<DeprecatedFile> stdout_file;
+static RefPtr<DeprecatedFile> stderr_file;
+
+NonnullRefPtr<DeprecatedFile> DeprecatedFile::standard_input()
+{
+ if (!stdin_file) {
+ stdin_file = DeprecatedFile::construct();
+ stdin_file->open(STDIN_FILENO, OpenMode::ReadOnly, ShouldCloseFileDescriptor::No);
+ }
+ return *stdin_file;
+}
+
+NonnullRefPtr<DeprecatedFile> DeprecatedFile::standard_output()
+{
+ if (!stdout_file) {
+ stdout_file = DeprecatedFile::construct();
+ stdout_file->open(STDOUT_FILENO, OpenMode::WriteOnly, ShouldCloseFileDescriptor::No);
+ }
+ return *stdout_file;
+}
+
+NonnullRefPtr<DeprecatedFile> DeprecatedFile::standard_error()
+{
+ if (!stderr_file) {
+ stderr_file = DeprecatedFile::construct();
+ stderr_file->open(STDERR_FILENO, OpenMode::WriteOnly, ShouldCloseFileDescriptor::No);
+ }
+ return *stderr_file;
+}
+
+static DeprecatedString get_duplicate_name(DeprecatedString const& path, int duplicate_count)
+{
+ if (duplicate_count == 0) {
+ return path;
+ }
+ LexicalPath lexical_path(path);
+ StringBuilder duplicated_name;
+ duplicated_name.append('/');
+ auto& parts = lexical_path.parts_view();
+ for (size_t i = 0; i < parts.size() - 1; ++i) {
+ duplicated_name.appendff("{}/", parts[i]);
+ }
+ auto prev_duplicate_tag = DeprecatedString::formatted("({})", duplicate_count);
+ auto title = lexical_path.title();
+ if (title.ends_with(prev_duplicate_tag)) {
+ // remove the previous duplicate tag "(n)" so we can add a new tag.
+ title = title.substring_view(0, title.length() - prev_duplicate_tag.length());
+ }
+ duplicated_name.appendff("{} ({})", title, duplicate_count);
+ if (!lexical_path.extension().is_empty()) {
+ duplicated_name.appendff(".{}", lexical_path.extension());
+ }
+ return duplicated_name.to_deprecated_string();
+}
+
+ErrorOr<void, DeprecatedFile::CopyError> DeprecatedFile::copy_file_or_directory(DeprecatedString const& dst_path, DeprecatedString const& src_path, RecursionMode recursion_mode, LinkMode link_mode, AddDuplicateFileMarker add_duplicate_file_marker, PreserveMode preserve_mode)
+{
+ if (add_duplicate_file_marker == AddDuplicateFileMarker::Yes) {
+ int duplicate_count = 0;
+ while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) {
+ ++duplicate_count;
+ }
+ if (duplicate_count != 0) {
+ return copy_file_or_directory(get_duplicate_name(dst_path, duplicate_count), src_path, RecursionMode::Allowed, LinkMode::Disallowed, AddDuplicateFileMarker::Yes, preserve_mode);
+ }
+ }
+
+ auto source_or_error = DeprecatedFile::open(src_path, OpenMode::ReadOnly);
+ if (source_or_error.is_error())
+ return CopyError { errno, false };
+
+ auto& source = *source_or_error.value();
+
+ struct stat src_stat;
+ if (fstat(source.fd(), &src_stat) < 0)
+ return CopyError { errno, false };
+
+ if (source.is_directory()) {
+ if (recursion_mode == RecursionMode::Disallowed)
+ return CopyError { errno, true };
+ return copy_directory(dst_path, src_path, src_stat);
+ }
+
+ if (link_mode == LinkMode::Allowed) {
+ if (link(src_path.characters(), dst_path.characters()) < 0)
+ return CopyError { errno, false };
+
+ return {};
+ }
+
+ return copy_file(dst_path, src_stat, source, preserve_mode);
+}
+
+ErrorOr<void, DeprecatedFile::CopyError> DeprecatedFile::copy_file(DeprecatedString const& dst_path, struct stat const& src_stat, DeprecatedFile& source, PreserveMode preserve_mode)
+{
+ int dst_fd = creat(dst_path.characters(), 0666);
+ if (dst_fd < 0) {
+ if (errno != EISDIR)
+ return CopyError { errno, false };
+
+ auto dst_dir_path = DeprecatedString::formatted("{}/{}", dst_path, LexicalPath::basename(source.filename()));
+ dst_fd = creat(dst_dir_path.characters(), 0666);
+ if (dst_fd < 0)
+ return CopyError { errno, false };
+ }
+
+ ScopeGuard close_fd_guard([dst_fd]() { ::close(dst_fd); });
+
+ if (src_stat.st_size > 0) {
+ if (ftruncate(dst_fd, src_stat.st_size) < 0)
+ return CopyError { errno, false };
+ }
+
+ for (;;) {
+ char buffer[32768];
+ ssize_t nread = ::read(source.fd(), buffer, sizeof(buffer));
+ if (nread < 0) {
+ return CopyError { errno, false };
+ }
+ if (nread == 0)
+ break;
+ ssize_t remaining_to_write = nread;
+ char* bufptr = buffer;
+ while (remaining_to_write) {
+ ssize_t nwritten = ::write(dst_fd, bufptr, remaining_to_write);
+ if (nwritten < 0)
+ return CopyError { errno, false };
+
+ VERIFY(nwritten > 0);
+ remaining_to_write -= nwritten;
+ bufptr += nwritten;
+ }
+ }
+
+ auto my_umask = umask(0);
+ umask(my_umask);
+ // NOTE: We don't copy the set-uid and set-gid bits unless requested.
+ if (!has_flag(preserve_mode, PreserveMode::Permissions))
+ my_umask |= 06000;
+
+ if (fchmod(dst_fd, src_stat.st_mode & ~my_umask) < 0)
+ return CopyError { errno, false };
+
+ if (has_flag(preserve_mode, PreserveMode::Ownership)) {
+ if (fchown(dst_fd, src_stat.st_uid, src_stat.st_gid) < 0)
+ return CopyError { errno, false };
+ }
+
+ if (has_flag(preserve_mode, PreserveMode::Timestamps)) {
+ struct timespec times[2] = {
+#ifdef AK_OS_MACOS
+ src_stat.st_atimespec,
+ src_stat.st_mtimespec,
+#else
+ src_stat.st_atim,
+ src_stat.st_mtim,
+#endif
+ };
+ if (utimensat(AT_FDCWD, dst_path.characters(), times, 0) < 0)
+ return CopyError { errno, false };
+ }
+
+ return {};
+}
+
+ErrorOr<void, DeprecatedFile::CopyError> DeprecatedFile::copy_directory(DeprecatedString const& dst_path, DeprecatedString const& src_path, struct stat const& src_stat, LinkMode link, PreserveMode preserve_mode)
+{
+ if (mkdir(dst_path.characters(), 0755) < 0)
+ return CopyError { errno, false };
+
+ DeprecatedString src_rp = DeprecatedFile::real_path_for(src_path);
+ src_rp = DeprecatedString::formatted("{}/", src_rp);
+ DeprecatedString dst_rp = DeprecatedFile::real_path_for(dst_path);
+ dst_rp = DeprecatedString::formatted("{}/", dst_rp);
+
+ if (!dst_rp.is_empty() && dst_rp.starts_with(src_rp))
+ return CopyError { errno, false };
+
+ DirIterator di(src_path, DirIterator::SkipParentAndBaseDir);
+ if (di.has_error())
+ return CopyError { errno, false };
+
+ while (di.has_next()) {
+ DeprecatedString filename = di.next_path();
+ auto result = copy_file_or_directory(
+ DeprecatedString::formatted("{}/{}", dst_path, filename),
+ DeprecatedString::formatted("{}/{}", src_path, filename),
+ RecursionMode::Allowed, link, AddDuplicateFileMarker::Yes, preserve_mode);
+ if (result.is_error())
+ return result.release_error();
+ }
+
+ auto my_umask = umask(0);
+ umask(my_umask);
+
+ if (chmod(dst_path.characters(), src_stat.st_mode & ~my_umask) < 0)
+ return CopyError { errno, false };
+
+ if (has_flag(preserve_mode, PreserveMode::Ownership)) {
+ if (chown(dst_path.characters(), src_stat.st_uid, src_stat.st_gid) < 0)
+ return CopyError { errno, false };
+ }
+
+ if (has_flag(preserve_mode, PreserveMode::Timestamps)) {
+ struct timespec times[2] = {
+#ifdef AK_OS_MACOS
+ src_stat.st_atimespec,
+ src_stat.st_mtimespec,
+#else
+ src_stat.st_atim,
+ src_stat.st_mtim,
+#endif
+ };
+ if (utimensat(AT_FDCWD, dst_path.characters(), times, 0) < 0)
+ return CopyError { errno, false };
+ }
+
+ return {};
+}
+
+ErrorOr<void> DeprecatedFile::link_file(DeprecatedString const& dst_path, DeprecatedString const& src_path)
+{
+ int duplicate_count = 0;
+ while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) {
+ ++duplicate_count;
+ }
+ if (duplicate_count != 0) {
+ return link_file(get_duplicate_name(dst_path, duplicate_count), src_path);
+ }
+ if (symlink(src_path.characters(), dst_path.characters()) < 0)
+ return Error::from_errno(errno);
+ return {};
+}
+
+ErrorOr<void> DeprecatedFile::remove(StringView path, RecursionMode mode)
+{
+ auto path_stat = TRY(Core::System::lstat(path));
+
+ if (S_ISDIR(path_stat.st_mode) && mode == RecursionMode::Allowed) {
+ auto di = DirIterator(path, DirIterator::SkipParentAndBaseDir);
+ if (di.has_error())
+ return Error::from_errno(di.error());
+
+ while (di.has_next()) {
+ TRY(remove(di.next_full_path(), RecursionMode::Allowed));
+ }
+
+ TRY(Core::System::rmdir(path));
+ } else {
+ TRY(Core::System::unlink(path));
+ }
+
+ return {};
+}
+
+Optional<DeprecatedString> DeprecatedFile::resolve_executable_from_environment(StringView filename)
+{
+ if (filename.is_empty())
+ return {};
+
+ // Paths that aren't just a file name generally count as already resolved.
+ if (filename.contains('/')) {
+ if (access(DeprecatedString { filename }.characters(), X_OK) != 0)
+ return {};
+
+ return filename;
+ }
+
+ auto const* path_str = getenv("PATH");
+ StringView path;
+ if (path_str)
+ path = { path_str, strlen(path_str) };
+ if (path.is_empty())
+ path = DEFAULT_PATH_SV;
+
+ auto directories = path.split_view(':');
+
+ for (auto directory : directories) {
+ auto file = DeprecatedString::formatted("{}/{}", directory, filename);
+
+ if (access(file.characters(), X_OK) == 0)
+ return file;
+ }
+
+ return {};
+};
+
+}