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