Serenity Operating System
at master 185 lines 5.4 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibCore/File.h> 9#include <LibCore/System.h> 10#include <fcntl.h> 11#include <unistd.h> 12 13namespace Core { 14 15ErrorOr<NonnullOwnPtr<File>> File::open(StringView filename, OpenMode mode, mode_t permissions) 16{ 17 auto file = TRY(adopt_nonnull_own_or_enomem(new (nothrow) File(mode))); 18 TRY(file->open_path(filename, permissions)); 19 return file; 20} 21 22ErrorOr<NonnullOwnPtr<File>> File::adopt_fd(int fd, OpenMode mode, ShouldCloseFileDescriptor should_close_file_descriptor) 23{ 24 if (fd < 0) { 25 return Error::from_errno(EBADF); 26 } 27 28 if (!has_any_flag(mode, OpenMode::ReadWrite)) { 29 dbgln("Core::File::adopt_fd: Attempting to adopt a file with neither Read nor Write specified in mode"); 30 return Error::from_errno(EINVAL); 31 } 32 33 auto file = TRY(adopt_nonnull_own_or_enomem(new (nothrow) File(mode, should_close_file_descriptor))); 34 file->m_fd = fd; 35 return file; 36} 37 38ErrorOr<NonnullOwnPtr<File>> File::standard_input() 39{ 40 return File::adopt_fd(STDIN_FILENO, OpenMode::Read, ShouldCloseFileDescriptor::No); 41} 42ErrorOr<NonnullOwnPtr<File>> File::standard_output() 43{ 44 return File::adopt_fd(STDOUT_FILENO, OpenMode::Write, ShouldCloseFileDescriptor::No); 45} 46ErrorOr<NonnullOwnPtr<File>> File::standard_error() 47{ 48 return File::adopt_fd(STDERR_FILENO, OpenMode::Write, ShouldCloseFileDescriptor::No); 49} 50 51ErrorOr<NonnullOwnPtr<File>> File::open_file_or_standard_stream(StringView filename, OpenMode mode) 52{ 53 if (!filename.is_empty() && filename != "-"sv) 54 return File::open(filename, mode); 55 56 switch (mode) { 57 case OpenMode::Read: 58 return standard_input(); 59 case OpenMode::Write: 60 return standard_output(); 61 default: 62 VERIFY_NOT_REACHED(); 63 } 64} 65 66int File::open_mode_to_options(OpenMode mode) 67{ 68 int flags = 0; 69 if (has_flag(mode, OpenMode::ReadWrite)) { 70 flags |= O_RDWR | O_CREAT; 71 } else if (has_flag(mode, OpenMode::Read)) { 72 flags |= O_RDONLY; 73 } else if (has_flag(mode, OpenMode::Write)) { 74 flags |= O_WRONLY | O_CREAT; 75 bool should_truncate = !has_any_flag(mode, OpenMode::Append | OpenMode::MustBeNew); 76 if (should_truncate) 77 flags |= O_TRUNC; 78 } 79 80 if (has_flag(mode, OpenMode::Append)) 81 flags |= O_APPEND; 82 if (has_flag(mode, OpenMode::Truncate)) 83 flags |= O_TRUNC; 84 if (has_flag(mode, OpenMode::MustBeNew)) 85 flags |= O_EXCL; 86 if (!has_flag(mode, OpenMode::KeepOnExec)) 87 flags |= O_CLOEXEC; 88 if (!has_flag(mode, OpenMode::Nonblocking)) 89 flags |= O_NONBLOCK; 90 return flags; 91} 92 93ErrorOr<void> File::open_path(StringView filename, mode_t permissions) 94{ 95 VERIFY(m_fd == -1); 96 auto flags = open_mode_to_options(m_mode); 97 98 m_fd = TRY(System::open(filename, flags, permissions)); 99 return {}; 100} 101 102ErrorOr<Bytes> File::read_some(Bytes buffer) 103{ 104 if (!has_flag(m_mode, OpenMode::Read)) { 105 // NOTE: POSIX says that if the fd is not open for reading, the call 106 // will return EBADF. Since we already know whether we can or 107 // can't read the file, let's avoid a syscall. 108 return Error::from_errno(EBADF); 109 } 110 111 ssize_t nread = TRY(System::read(m_fd, buffer)); 112 m_last_read_was_eof = nread == 0; 113 return buffer.trim(nread); 114} 115 116ErrorOr<ByteBuffer> File::read_until_eof(size_t block_size) 117{ 118 // Note: This is used as a heuristic, it's not valid for devices or virtual files. 119 auto const potential_file_size = TRY(System::fstat(m_fd)).st_size; 120 121 return read_until_eof_impl(block_size, potential_file_size); 122} 123 124ErrorOr<size_t> File::write_some(ReadonlyBytes buffer) 125{ 126 if (!has_flag(m_mode, OpenMode::Write)) { 127 // NOTE: Same deal as Read. 128 return Error::from_errno(EBADF); 129 } 130 131 return TRY(System::write(m_fd, buffer)); 132} 133 134bool File::is_eof() const { return m_last_read_was_eof; } 135bool File::is_open() const { return m_fd >= 0; } 136 137void File::close() 138{ 139 if (!is_open()) { 140 return; 141 } 142 143 // NOTE: The closing of the file can be interrupted by a signal, in which 144 // case EINTR will be returned by the close syscall. So let's try closing 145 // the file until we aren't interrupted by rude signals. :^) 146 ErrorOr<void> result; 147 do { 148 result = System::close(m_fd); 149 } while (result.is_error() && result.error().code() == EINTR); 150 151 VERIFY(!result.is_error()); 152 m_fd = -1; 153} 154 155ErrorOr<size_t> File::seek(i64 offset, SeekMode mode) 156{ 157 int syscall_mode; 158 switch (mode) { 159 case SeekMode::SetPosition: 160 syscall_mode = SEEK_SET; 161 break; 162 case SeekMode::FromCurrentPosition: 163 syscall_mode = SEEK_CUR; 164 break; 165 case SeekMode::FromEndPosition: 166 syscall_mode = SEEK_END; 167 break; 168 default: 169 VERIFY_NOT_REACHED(); 170 } 171 172 size_t seek_result = TRY(System::lseek(m_fd, offset, syscall_mode)); 173 m_last_read_was_eof = false; 174 return seek_result; 175} 176 177ErrorOr<void> File::truncate(size_t length) 178{ 179 if (length > static_cast<size_t>(NumericLimits<off_t>::max())) 180 return Error::from_string_literal("Length is larger than the maximum supported length"); 181 182 return System::ftruncate(m_fd, length); 183} 184 185}