1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
#include "cru/common/platform/unix/UnixFileStream.h"
#include "cru/common/Exception.h"
#include "cru/common/Format.h"
#include "cru/common/io/Stream.h"
#include "cru/common/log/Logger.h"
#include <fcntl.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <cerrno>
namespace cru::platform::unix {
using namespace cru::io;
namespace {
bool OflagCanSeek([[maybe_unused]] int oflag) {
// Treat every file seekable.
return true;
}
bool OflagCanRead(int oflag) {
// There is a problem: O_RDONLY is 0. So we must test it specially.
// If it is not write-only. Then it can read.
return !(oflag & O_WRONLY);
}
bool OflagCanWrite(int oflag) { return oflag & O_WRONLY || oflag & O_RDWR; }
int MapSeekOrigin(Stream::SeekOrigin origin) {
switch (origin) {
case Stream::SeekOrigin::Begin:
return SEEK_SET;
case Stream::SeekOrigin::Current:
return SEEK_CUR;
case Stream::SeekOrigin::End:
return SEEK_END;
default:
throw Exception(u"Invalid seek origin.");
}
}
} // namespace
UnixFileStream::UnixFileStream(const char *path, int oflag, mode_t mode) {
file_descriptor_ = ::open(path, oflag, mode);
if (file_descriptor_ == -1) {
throw ErrnoException(
Format(u"Failed to open file {} with oflag {}, mode {}.",
String::FromUtf8(path), oflag, mode));
}
can_seek_ = OflagCanSeek(oflag);
can_read_ = OflagCanRead(oflag);
can_write_ = OflagCanWrite(oflag);
auto_close_ = true;
}
UnixFileStream::UnixFileStream(int fd, bool can_seek, bool can_read,
bool can_write, bool auto_close) {
file_descriptor_ = fd;
can_seek_ = can_seek;
can_read_ = can_read;
can_write_ = can_write;
auto_close_ = auto_close;
}
UnixFileStream::~UnixFileStream() {
if (auto_close_ && file_descriptor_ >= 0) {
if (::close(file_descriptor_) == -1) {
// We are in destructor, so we can not throw.
CRU_LOG_WARN(u"Failed to close file descriptor {}, errno {}.",
file_descriptor_, errno);
}
}
}
bool UnixFileStream::CanSeek() {
CheckClosed();
return can_seek_;
}
Index UnixFileStream::Seek(Index offset, SeekOrigin origin) {
CheckClosed();
StreamOperationNotSupportedException::CheckSeek(can_seek_);
off_t result = ::lseek(file_descriptor_, offset, MapSeekOrigin(origin));
if (result == -1) {
throw ErrnoException(u"Failed to seek file.");
}
return result;
}
bool UnixFileStream::CanRead() {
CheckClosed();
return can_read_;
}
Index UnixFileStream::Read(std::byte *buffer, Index offset, Index size) {
CheckClosed();
StreamOperationNotSupportedException::CheckRead(can_read_);
auto result = ::read(file_descriptor_, buffer + offset, size);
if (result == -1) {
throw ErrnoException(u"Failed to read file.");
}
return result;
}
bool UnixFileStream::CanWrite() {
CheckClosed();
return can_write_;
}
Index UnixFileStream::Write(const std::byte *buffer, Index offset, Index size) {
CheckClosed();
StreamOperationNotSupportedException::CheckWrite(can_write_);
auto result = ::write(file_descriptor_, buffer + offset, size);
if (result == -1) {
throw ErrnoException(u"Failed to write file.");
}
return result;
}
void UnixFileStream::Close() {
if (file_descriptor_ < 0) return;
if (::close(file_descriptor_) == -1) {
throw ErrnoException(u"Failed to close file.");
}
file_descriptor_ = -1;
}
void UnixFileStream::CheckClosed() {
StreamAlreadyClosedException::Check(file_descriptor_ < 0);
}
} // namespace cru::platform::unix
|