1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <brillo/streams/file_stream.h>
6
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 #include <algorithm>
12 #include <utility>
13
14 #include <base/bind.h>
15 #include <base/files/file_descriptor_watcher_posix.h>
16 #include <base/files/file_util.h>
17 #include <base/posix/eintr_wrapper.h>
18 #include <brillo/errors/error_codes.h>
19 #include <brillo/message_loops/message_loop.h>
20 #include <brillo/streams/stream_errors.h>
21 #include <brillo/streams/stream_utils.h>
22
23 namespace brillo {
24
25 // FileDescriptor is a helper class that serves two purposes:
26 // 1. It wraps low-level system APIs (as FileDescriptorInterface) to allow
27 // mocking calls to them in tests.
28 // 2. It provides file descriptor watching services using FileDescriptorWatcher
29 // and MessageLoopForIO::Watcher interface.
30 // The real FileStream uses this class to perform actual file I/O on the
31 // contained file descriptor.
32 class FileDescriptor : public FileStream::FileDescriptorInterface {
33 public:
FileDescriptor(int fd,bool own)34 FileDescriptor(int fd, bool own) : fd_{fd}, own_{own} {}
~FileDescriptor()35 ~FileDescriptor() override {
36 if (IsOpen()) {
37 Close();
38 }
39 }
40
41 // Overrides for FileStream::FileDescriptorInterface methods.
IsOpen() const42 bool IsOpen() const override { return fd_ >= 0; }
43
Read(void * buf,size_t nbyte)44 ssize_t Read(void* buf, size_t nbyte) override {
45 return HANDLE_EINTR(read(fd_, buf, nbyte));
46 }
47
Write(const void * buf,size_t nbyte)48 ssize_t Write(const void* buf, size_t nbyte) override {
49 return HANDLE_EINTR(write(fd_, buf, nbyte));
50 }
51
Seek(off64_t offset,int whence)52 off64_t Seek(off64_t offset, int whence) override {
53 return lseek64(fd_, offset, whence);
54 }
55
GetFileMode() const56 mode_t GetFileMode() const override {
57 struct stat file_stat;
58 if (fstat(fd_, &file_stat) < 0)
59 return 0;
60 return file_stat.st_mode;
61 }
62
GetSize() const63 uint64_t GetSize() const override {
64 struct stat file_stat;
65 if (fstat(fd_, &file_stat) < 0)
66 return 0;
67 return file_stat.st_size;
68 }
69
Truncate(off64_t length) const70 int Truncate(off64_t length) const override {
71 return HANDLE_EINTR(ftruncate(fd_, length));
72 }
73
Close()74 int Close() override {
75 int fd = -1;
76 // The stream may or may not own the file descriptor stored in |fd_|.
77 // Despite that, we will need to set |fd_| to -1 when Close() finished.
78 // So, here we set it to -1 first and if we own the old descriptor, close
79 // it before exiting.
80 std::swap(fd, fd_);
81 CancelPendingAsyncOperations();
82 return own_ ? IGNORE_EINTR(close(fd)) : 0;
83 }
84
WaitForData(Stream::AccessMode mode,const DataCallback & data_callback,ErrorPtr * error)85 bool WaitForData(Stream::AccessMode mode,
86 const DataCallback& data_callback,
87 ErrorPtr* error) override {
88 if (stream_utils::IsReadAccessMode(mode)) {
89 CHECK(read_data_callback_.is_null());
90 read_watcher_ = base::FileDescriptorWatcher::WatchReadable(
91 fd_,
92 base::BindRepeating(&FileDescriptor::OnReadable,
93 base::Unretained(this)));
94 if (!read_watcher_) {
95 Error::AddTo(error, FROM_HERE, errors::stream::kDomain,
96 errors::stream::kInvalidParameter,
97 "File descriptor doesn't support watching for reading.");
98 return false;
99 }
100 read_data_callback_ = data_callback;
101 }
102 if (stream_utils::IsWriteAccessMode(mode)) {
103 CHECK(write_data_callback_.is_null());
104 write_watcher_ = base::FileDescriptorWatcher::WatchWritable(
105 fd_,
106 base::BindRepeating(&FileDescriptor::OnWritable,
107 base::Unretained(this)));
108 if (!write_watcher_) {
109 Error::AddTo(error, FROM_HERE, errors::stream::kDomain,
110 errors::stream::kInvalidParameter,
111 "File descriptor doesn't support watching for writing.");
112 return false;
113 }
114 write_data_callback_ = data_callback;
115 }
116 return true;
117 }
118
WaitForDataBlocking(Stream::AccessMode in_mode,base::TimeDelta timeout,Stream::AccessMode * out_mode)119 int WaitForDataBlocking(Stream::AccessMode in_mode,
120 base::TimeDelta timeout,
121 Stream::AccessMode* out_mode) override {
122 fd_set read_fds;
123 fd_set write_fds;
124 fd_set error_fds;
125
126 FD_ZERO(&read_fds);
127 FD_ZERO(&write_fds);
128 FD_ZERO(&error_fds);
129
130 if (stream_utils::IsReadAccessMode(in_mode))
131 FD_SET(fd_, &read_fds);
132
133 if (stream_utils::IsWriteAccessMode(in_mode))
134 FD_SET(fd_, &write_fds);
135
136 FD_SET(fd_, &error_fds);
137 timeval timeout_val = {};
138 if (!timeout.is_max()) {
139 const timespec ts = timeout.ToTimeSpec();
140 TIMESPEC_TO_TIMEVAL(&timeout_val, &ts);
141 }
142 int res = HANDLE_EINTR(select(fd_ + 1, &read_fds, &write_fds, &error_fds,
143 timeout.is_max() ? nullptr : &timeout_val));
144 if (res > 0 && out_mode) {
145 *out_mode = stream_utils::MakeAccessMode(FD_ISSET(fd_, &read_fds),
146 FD_ISSET(fd_, &write_fds));
147 }
148 return res;
149 }
150
CancelPendingAsyncOperations()151 void CancelPendingAsyncOperations() override {
152 read_data_callback_.Reset();
153 read_watcher_ = nullptr;
154 write_data_callback_.Reset();
155 write_watcher_ = nullptr;
156 }
157
158 // Called from the brillo::MessageLoop when the file descriptor is available
159 // for reading.
OnReadable()160 void OnReadable() {
161 CHECK(!read_data_callback_.is_null());
162
163 read_watcher_ = nullptr;
164 DataCallback cb = std::move(read_data_callback_);
165 cb.Run(Stream::AccessMode::READ);
166 }
167
OnWritable()168 void OnWritable() {
169 CHECK(!write_data_callback_.is_null());
170
171 write_watcher_ = nullptr;
172 DataCallback cb = std::move(write_data_callback_);
173 cb.Run(Stream::AccessMode::WRITE);
174 }
175
176 private:
177 // The actual file descriptor we are working with. Will contain -1 if the
178 // file stream has been closed.
179 int fd_;
180
181 // |own_| is set to true if the file stream owns the file descriptor |fd_| and
182 // must close it when the stream is closed. This will be false for file
183 // descriptors that shouldn't be closed (e.g. stdin, stdout, stderr).
184 bool own_;
185
186 // Stream callbacks to be called when read and/or write operations can be
187 // performed on the file descriptor without blocking.
188 DataCallback read_data_callback_;
189 DataCallback write_data_callback_;
190
191 // Monitoring read/write operations on the file descriptor.
192 std::unique_ptr<base::FileDescriptorWatcher::Controller> read_watcher_;
193 std::unique_ptr<base::FileDescriptorWatcher::Controller> write_watcher_;
194
195 DISALLOW_COPY_AND_ASSIGN(FileDescriptor);
196 };
197
Open(const base::FilePath & path,AccessMode mode,Disposition disposition,ErrorPtr * error)198 StreamPtr FileStream::Open(const base::FilePath& path,
199 AccessMode mode,
200 Disposition disposition,
201 ErrorPtr* error) {
202 StreamPtr stream;
203 int open_flags = O_CLOEXEC;
204 switch (mode) {
205 case AccessMode::READ:
206 open_flags |= O_RDONLY;
207 break;
208 case AccessMode::WRITE:
209 open_flags |= O_WRONLY;
210 break;
211 case AccessMode::READ_WRITE:
212 open_flags |= O_RDWR;
213 break;
214 }
215
216 switch (disposition) {
217 case Disposition::OPEN_EXISTING:
218 // Nothing else to do.
219 break;
220 case Disposition::CREATE_ALWAYS:
221 open_flags |= O_CREAT | O_TRUNC;
222 break;
223 case Disposition::CREATE_NEW_ONLY:
224 open_flags |= O_CREAT | O_EXCL;
225 break;
226 case Disposition::TRUNCATE_EXISTING:
227 open_flags |= O_TRUNC;
228 break;
229 }
230
231 mode_t creation_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
232 int fd = HANDLE_EINTR(open(path.value().c_str(), open_flags, creation_mode));
233 if (fd < 0) {
234 brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
235 return stream;
236 }
237 if (HANDLE_EINTR(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)) < 0) {
238 brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
239 IGNORE_EINTR(close(fd));
240 return stream;
241 }
242
243 std::unique_ptr<FileDescriptorInterface> fd_interface{
244 new FileDescriptor{fd, true}};
245
246 stream.reset(new FileStream{std::move(fd_interface), mode});
247 return stream;
248 }
249
CreateTemporary(ErrorPtr * error)250 StreamPtr FileStream::CreateTemporary(ErrorPtr* error) {
251 StreamPtr stream;
252 base::FilePath path;
253 // The "proper" solution would be here to add O_TMPFILE flag to |open_flags|
254 // below and pass just the temp directory path to open(), so the actual file
255 // name isn't even needed. However this is supported only as of Linux kernel
256 // 3.11 and not all our configurations have that. So, for now just create
257 // a temp file first and then open it.
258 if (!base::CreateTemporaryFile(&path)) {
259 brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
260 return stream;
261 }
262 int open_flags = O_CLOEXEC | O_RDWR | O_CREAT | O_TRUNC;
263 mode_t creation_mode = S_IRUSR | S_IWUSR;
264 int fd = HANDLE_EINTR(open(path.value().c_str(), open_flags, creation_mode));
265 if (fd < 0) {
266 brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
267 return stream;
268 }
269 unlink(path.value().c_str());
270
271 stream = FromFileDescriptor(fd, true, error);
272 if (!stream)
273 IGNORE_EINTR(close(fd));
274 return stream;
275 }
276
FromFileDescriptor(int file_descriptor,bool own_descriptor,ErrorPtr * error)277 StreamPtr FileStream::FromFileDescriptor(int file_descriptor,
278 bool own_descriptor,
279 ErrorPtr* error) {
280 StreamPtr stream;
281 if (file_descriptor < 0 || file_descriptor >= FD_SETSIZE) {
282 Error::AddTo(error, FROM_HERE, errors::stream::kDomain,
283 errors::stream::kInvalidParameter,
284 "Invalid file descriptor value");
285 return stream;
286 }
287
288 int fd_flags = HANDLE_EINTR(fcntl(file_descriptor, F_GETFL));
289 if (fd_flags < 0) {
290 brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
291 return stream;
292 }
293 int file_access_mode = (fd_flags & O_ACCMODE);
294 AccessMode access_mode = AccessMode::READ_WRITE;
295 if (file_access_mode == O_RDONLY)
296 access_mode = AccessMode::READ;
297 else if (file_access_mode == O_WRONLY)
298 access_mode = AccessMode::WRITE;
299
300 // Make sure the file descriptor is set to perform non-blocking operations
301 // if not enabled already.
302 if ((fd_flags & O_NONBLOCK) == 0) {
303 fd_flags |= O_NONBLOCK;
304 if (HANDLE_EINTR(fcntl(file_descriptor, F_SETFL, fd_flags)) < 0) {
305 brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
306 return stream;
307 }
308 }
309
310 std::unique_ptr<FileDescriptorInterface> fd_interface{
311 new FileDescriptor{file_descriptor, own_descriptor}};
312
313 stream.reset(new FileStream{std::move(fd_interface), access_mode});
314 return stream;
315 }
316
FileStream(std::unique_ptr<FileDescriptorInterface> fd_interface,AccessMode mode)317 FileStream::FileStream(std::unique_ptr<FileDescriptorInterface> fd_interface,
318 AccessMode mode)
319 : fd_interface_(std::move(fd_interface)),
320 access_mode_(mode) {
321 switch (fd_interface_->GetFileMode() & S_IFMT) {
322 case S_IFCHR: // Character device
323 case S_IFSOCK: // Socket
324 case S_IFIFO: // FIFO/pipe
325 // We know that these devices are not seekable and stream size is unknown.
326 seekable_ = false;
327 can_get_size_ = false;
328 break;
329
330 case S_IFBLK: // Block device
331 case S_IFDIR: // Directory
332 case S_IFREG: // Normal file
333 case S_IFLNK: // Symbolic link
334 default:
335 // The above devices support seek. Also, if not sure/in doubt, err on the
336 // side of "allowable".
337 seekable_ = true;
338 can_get_size_ = true;
339 break;
340 }
341 }
342
IsOpen() const343 bool FileStream::IsOpen() const {
344 return fd_interface_->IsOpen();
345 }
346
CanRead() const347 bool FileStream::CanRead() const {
348 return IsOpen() && stream_utils::IsReadAccessMode(access_mode_);
349 }
350
CanWrite() const351 bool FileStream::CanWrite() const {
352 return IsOpen() && stream_utils::IsWriteAccessMode(access_mode_);
353 }
354
CanSeek() const355 bool FileStream::CanSeek() const {
356 return IsOpen() && seekable_;
357 }
358
CanGetSize() const359 bool FileStream::CanGetSize() const {
360 return IsOpen() && can_get_size_;
361 }
362
GetSize() const363 uint64_t FileStream::GetSize() const {
364 return IsOpen() ? fd_interface_->GetSize() : 0;
365 }
366
SetSizeBlocking(uint64_t size,ErrorPtr * error)367 bool FileStream::SetSizeBlocking(uint64_t size, ErrorPtr* error) {
368 if (!IsOpen())
369 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
370
371 if (!stream_utils::CheckInt64Overflow(FROM_HERE, size, 0, error))
372 return false;
373
374 if (fd_interface_->Truncate(size) >= 0)
375 return true;
376
377 errors::system::AddSystemError(error, FROM_HERE, errno);
378 return false;
379 }
380
GetRemainingSize() const381 uint64_t FileStream::GetRemainingSize() const {
382 if (!CanGetSize())
383 return 0;
384 uint64_t pos = GetPosition();
385 uint64_t size = GetSize();
386 return (pos < size) ? (size - pos) : 0;
387 }
388
GetPosition() const389 uint64_t FileStream::GetPosition() const {
390 if (!CanSeek())
391 return 0;
392
393 off64_t pos = fd_interface_->Seek(0, SEEK_CUR);
394 const off64_t min_pos = 0;
395 return std::max(min_pos, pos);
396 }
397
Seek(int64_t offset,Whence whence,uint64_t * new_position,ErrorPtr * error)398 bool FileStream::Seek(int64_t offset,
399 Whence whence,
400 uint64_t* new_position,
401 ErrorPtr* error) {
402 if (!IsOpen())
403 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
404
405 int raw_whence = 0;
406 switch (whence) {
407 case Whence::FROM_BEGIN:
408 raw_whence = SEEK_SET;
409 break;
410 case Whence::FROM_CURRENT:
411 raw_whence = SEEK_CUR;
412 break;
413 case Whence::FROM_END:
414 raw_whence = SEEK_END;
415 break;
416 default:
417 Error::AddTo(error, FROM_HERE, errors::stream::kDomain,
418 errors::stream::kInvalidParameter, "Invalid whence");
419 return false;
420 }
421 off64_t pos = fd_interface_->Seek(offset, raw_whence);
422 if (pos < 0) {
423 errors::system::AddSystemError(error, FROM_HERE, errno);
424 return false;
425 }
426
427 if (new_position)
428 *new_position = static_cast<uint64_t>(pos);
429 return true;
430 }
431
ReadNonBlocking(void * buffer,size_t size_to_read,size_t * size_read,bool * end_of_stream,ErrorPtr * error)432 bool FileStream::ReadNonBlocking(void* buffer,
433 size_t size_to_read,
434 size_t* size_read,
435 bool* end_of_stream,
436 ErrorPtr* error) {
437 if (!IsOpen())
438 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
439
440 ssize_t read = fd_interface_->Read(buffer, size_to_read);
441 if (read < 0) {
442 // If read() fails, check if this is due to no data being currently
443 // available and we do non-blocking I/O.
444 if (errno == EWOULDBLOCK || errno == EAGAIN) {
445 if (end_of_stream)
446 *end_of_stream = false;
447 *size_read = 0;
448 return true;
449 }
450 // Otherwise a real problem occurred.
451 errors::system::AddSystemError(error, FROM_HERE, errno);
452 return false;
453 }
454 if (end_of_stream)
455 *end_of_stream = (read == 0 && size_to_read != 0);
456 *size_read = read;
457 return true;
458 }
459
WriteNonBlocking(const void * buffer,size_t size_to_write,size_t * size_written,ErrorPtr * error)460 bool FileStream::WriteNonBlocking(const void* buffer,
461 size_t size_to_write,
462 size_t* size_written,
463 ErrorPtr* error) {
464 if (!IsOpen())
465 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
466
467 ssize_t written = fd_interface_->Write(buffer, size_to_write);
468 if (written < 0) {
469 // If write() fails, check if this is due to the fact that no data
470 // can be presently written and we do non-blocking I/O.
471 if (errno == EWOULDBLOCK || errno == EAGAIN) {
472 *size_written = 0;
473 return true;
474 }
475 // Otherwise a real problem occurred.
476 errors::system::AddSystemError(error, FROM_HERE, errno);
477 return false;
478 }
479 *size_written = written;
480 return true;
481 }
482
FlushBlocking(ErrorPtr * error)483 bool FileStream::FlushBlocking(ErrorPtr* error) {
484 if (!IsOpen())
485 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
486
487 // File descriptors don't have an internal buffer to flush.
488 return true;
489 }
490
CloseBlocking(ErrorPtr * error)491 bool FileStream::CloseBlocking(ErrorPtr* error) {
492 if (!IsOpen())
493 return true;
494
495 if (fd_interface_->Close() < 0) {
496 errors::system::AddSystemError(error, FROM_HERE, errno);
497 return false;
498 }
499
500 return true;
501 }
502
WaitForData(AccessMode mode,const base::Callback<void (AccessMode)> & callback,ErrorPtr * error)503 bool FileStream::WaitForData(
504 AccessMode mode,
505 const base::Callback<void(AccessMode)>& callback,
506 ErrorPtr* error) {
507 if (!IsOpen())
508 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
509
510 return fd_interface_->WaitForData(mode, callback, error);
511 }
512
WaitForDataBlocking(AccessMode in_mode,base::TimeDelta timeout,AccessMode * out_mode,ErrorPtr * error)513 bool FileStream::WaitForDataBlocking(AccessMode in_mode,
514 base::TimeDelta timeout,
515 AccessMode* out_mode,
516 ErrorPtr* error) {
517 if (!IsOpen())
518 return stream_utils::ErrorStreamClosed(FROM_HERE, error);
519
520 int ret = fd_interface_->WaitForDataBlocking(in_mode, timeout, out_mode);
521 if (ret < 0) {
522 errors::system::AddSystemError(error, FROM_HERE, errno);
523 return false;
524 }
525 if (ret == 0)
526 return stream_utils::ErrorOperationTimeout(FROM_HERE, error);
527
528 return true;
529 }
530
CancelPendingAsyncOperations()531 void FileStream::CancelPendingAsyncOperations() {
532 if (IsOpen()) {
533 fd_interface_->CancelPendingAsyncOperations();
534 }
535 Stream::CancelPendingAsyncOperations();
536 }
537
538 } // namespace brillo
539