1// Copyright 2017 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 "puffin/src/file_stream.h"
6
7#include <fcntl.h>
8#include <unistd.h>
9
10#include <algorithm>
11#include <utility>
12
13#include "puffin/src/include/puffin/common.h"
14#include "puffin/src/set_errors.h"
15
16namespace puffin {
17
18using std::string;
19
20UniqueStreamPtr FileStream::Open(const string& path, bool read, bool write) {
21  TEST_AND_RETURN_VALUE(read || write, nullptr);
22  int flags = O_CLOEXEC;
23  if (read && write) {
24    flags |= O_RDWR | O_CREAT;
25  } else if (read) {
26    flags |= O_RDONLY;
27  } else {
28    flags |= O_WRONLY | O_CREAT;
29  }
30
31  mode_t mode = 0644;  // -rw-r--r--
32  int fd = open(path.c_str(), flags, mode);
33  TEST_AND_RETURN_VALUE(fd >= 0, nullptr);
34  return UniqueStreamPtr(new FileStream(fd));
35}
36
37bool FileStream::GetSize(uint64_t* size) const {
38  auto cur_off = lseek(fd_, 0, SEEK_CUR);
39  TEST_AND_RETURN_FALSE(cur_off >= 0);
40  auto fsize = lseek(fd_, 0, SEEK_END);
41  TEST_AND_RETURN_FALSE(fsize >= 0);
42  cur_off = lseek(fd_, cur_off, SEEK_SET);
43  TEST_AND_RETURN_FALSE(cur_off >= 0);
44  *size = fsize;
45  return true;
46}
47
48bool FileStream::GetOffset(uint64_t* offset) const {
49  auto off = lseek(fd_, 0, SEEK_CUR);
50  TEST_AND_RETURN_FALSE(off >= 0);
51  *offset = off;
52  return true;
53}
54
55bool FileStream::Seek(uint64_t offset) {
56  auto off = lseek(fd_, offset, SEEK_SET);
57  TEST_AND_RETURN_FALSE(off == static_cast<off_t>(offset));
58  return true;
59}
60
61bool FileStream::Read(void* buffer, size_t length) {
62  auto c_bytes = static_cast<uint8_t*>(buffer);
63  size_t total_bytes_read = 0;
64  while (total_bytes_read < length) {
65    auto bytes_read =
66        read(fd_, c_bytes + total_bytes_read, length - total_bytes_read);
67    // if bytes_read is zero then EOF is reached and we should not be here.
68    TEST_AND_RETURN_FALSE(bytes_read > 0);
69    total_bytes_read += bytes_read;
70  }
71  return true;
72}
73
74bool FileStream::Write(const void* buffer, size_t length) {
75  auto c_bytes = static_cast<const uint8_t*>(buffer);
76  size_t total_bytes_wrote = 0;
77  while (total_bytes_wrote < length) {
78    auto bytes_wrote =
79        write(fd_, c_bytes + total_bytes_wrote, length - total_bytes_wrote);
80    TEST_AND_RETURN_FALSE(bytes_wrote >= 0);
81    total_bytes_wrote += bytes_wrote;
82  }
83  return true;
84}
85
86bool FileStream::Close() {
87  return close(fd_) == 0;
88}
89
90}  // namespace puffin
91