• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
FileDescriptor(int fd,bool own)31   FileDescriptor(int fd, bool own) : fd_{fd}, own_{own} {}
~FileDescriptor()32   ~FileDescriptor() override {
33     if (IsOpen()) {
34       Close();
35     }
36   }
37 
38   // Overrides for FileStream::FileDescriptorInterface methods.
IsOpen() const39   bool IsOpen() const override { return fd_ >= 0; }
40 
Read(void * buf,size_t nbyte)41   ssize_t Read(void* buf, size_t nbyte) override {
42     return HANDLE_EINTR(read(fd_, buf, nbyte));
43   }
44 
Write(const void * buf,size_t nbyte)45   ssize_t Write(const void* buf, size_t nbyte) override {
46     return HANDLE_EINTR(write(fd_, buf, nbyte));
47   }
48 
Seek(off64_t offset,int whence)49   off64_t Seek(off64_t offset, int whence) override {
50     return lseek64(fd_, offset, whence);
51   }
52 
GetFileMode() const53   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 
GetSize() const60   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 
Truncate(off64_t length) const67   int Truncate(off64_t length) const override {
68     return HANDLE_EINTR(ftruncate(fd_, length));
69   }
70 
Close()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 
WaitForData(Stream::AccessMode mode,const DataCallback & data_callback,ErrorPtr * error)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 
WaitForDataBlocking(Stream::AccessMode in_mode,base::TimeDelta timeout,Stream::AccessMode * out_mode)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 
CancelPendingAsyncOperations()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.
OnFileCanReadWithoutBlocking()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 
OnFileCanWriteWithoutBlocking()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 
Open(const base::FilePath & path,AccessMode mode,Disposition disposition,ErrorPtr * error)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 
CreateTemporary(ErrorPtr * error)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 
FromFileDescriptor(int file_descriptor,bool own_descriptor,ErrorPtr * error)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 
FileStream(std::unique_ptr<FileDescriptorInterface> fd_interface,AccessMode mode)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 
IsOpen() const353 bool FileStream::IsOpen() const {
354   return fd_interface_->IsOpen();
355 }
356 
CanRead() const357 bool FileStream::CanRead() const {
358   return IsOpen() && stream_utils::IsReadAccessMode(access_mode_);
359 }
360 
CanWrite() const361 bool FileStream::CanWrite() const {
362   return IsOpen() && stream_utils::IsWriteAccessMode(access_mode_);
363 }
364 
CanSeek() const365 bool FileStream::CanSeek() const {
366   return IsOpen() && seekable_;
367 }
368 
CanGetSize() const369 bool FileStream::CanGetSize() const {
370   return IsOpen() && can_get_size_;
371 }
372 
GetSize() const373 uint64_t FileStream::GetSize() const {
374   return IsOpen() ? fd_interface_->GetSize() : 0;
375 }
376 
SetSizeBlocking(uint64_t size,ErrorPtr * error)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 
GetRemainingSize() const391 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 
GetPosition() const399 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 
Seek(int64_t offset,Whence whence,uint64_t * new_position,ErrorPtr * error)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 
ReadNonBlocking(void * buffer,size_t size_to_read,size_t * size_read,bool * end_of_stream,ErrorPtr * error)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 
WriteNonBlocking(const void * buffer,size_t size_to_write,size_t * size_written,ErrorPtr * error)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 
FlushBlocking(ErrorPtr * error)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 
CloseBlocking(ErrorPtr * error)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 
WaitForData(AccessMode mode,const base::Callback<void (AccessMode)> & callback,ErrorPtr * error)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 
WaitForDataBlocking(AccessMode in_mode,base::TimeDelta timeout,AccessMode * out_mode,ErrorPtr * error)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 
CancelPendingAsyncOperations()541 void FileStream::CancelPendingAsyncOperations() {
542   if (IsOpen()) {
543     fd_interface_->CancelPendingAsyncOperations();
544   }
545   Stream::CancelPendingAsyncOperations();
546 }
547 
548 }  // namespace brillo
549