xref: /aosp_15_r20/external/libbrillo/brillo/streams/file_stream.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
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