1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "io/FileStream.h"
18
19#include <errno.h>   // for errno
20#include <fcntl.h>   // for O_RDONLY
21#include <unistd.h>  // for read
22
23#include "android-base/errors.h"
24#include "android-base/file.h"  // for O_BINARY
25#include "android-base/macros.h"
26#include "android-base/utf8.h"
27
28#if defined(_WIN32)
29// This is only needed for O_CLOEXEC.
30#include <windows.h>
31#define O_CLOEXEC O_NOINHERIT
32#endif
33
34using ::android::base::SystemErrorCodeToString;
35using ::android::base::unique_fd;
36
37namespace aapt {
38namespace io {
39
40FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
41    : buffer_capacity_(buffer_capacity) {
42  int mode = O_RDONLY | O_CLOEXEC | O_BINARY;
43  fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode)));
44  if (fd_ == -1) {
45    error_ = SystemErrorCodeToString(errno);
46  } else {
47    buffer_.reset(new uint8_t[buffer_capacity_]);
48  }
49}
50
51FileInputStream::FileInputStream(int fd, size_t buffer_capacity)
52    : fd_(fd), buffer_capacity_(buffer_capacity) {
53  if (fd_ < 0) {
54    error_ = "Bad File Descriptor";
55  } else {
56    buffer_.reset(new uint8_t[buffer_capacity_]);
57  }
58}
59
60bool FileInputStream::Next(const void** data, size_t* size) {
61  if (HadError()) {
62    return false;
63  }
64
65  // Deal with any remaining bytes after BackUp was called.
66  if (buffer_offset_ != buffer_size_) {
67    *data = buffer_.get() + buffer_offset_;
68    *size = buffer_size_ - buffer_offset_;
69    total_byte_count_ += buffer_size_ - buffer_offset_;
70    buffer_offset_ = buffer_size_;
71    return true;
72  }
73
74  ssize_t n = TEMP_FAILURE_RETRY(read(fd_, buffer_.get(), buffer_capacity_));
75  if (n < 0) {
76    error_ = SystemErrorCodeToString(errno);
77    fd_.reset();
78    buffer_.reset();
79    return false;
80  }
81
82  buffer_size_ = static_cast<size_t>(n);
83  buffer_offset_ = buffer_size_;
84  total_byte_count_ += buffer_size_;
85
86  *data = buffer_.get();
87  *size = buffer_size_;
88  return buffer_size_ != 0u;
89}
90
91void FileInputStream::BackUp(size_t count) {
92  if (count > buffer_offset_) {
93    count = buffer_offset_;
94  }
95  buffer_offset_ -= count;
96  total_byte_count_ -= count;
97}
98
99size_t FileInputStream::ByteCount() const {
100  return total_byte_count_;
101}
102
103bool FileInputStream::HadError() const {
104  return fd_ == -1;
105}
106
107std::string FileInputStream::GetError() const {
108  return error_;
109}
110
111FileOutputStream::FileOutputStream(const std::string& path, size_t buffer_capacity)
112    : buffer_capacity_(buffer_capacity) {
113  int mode = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
114  owned_fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode, 0666)));
115  fd_ = owned_fd_.get();
116  if (fd_ < 0) {
117    error_ = SystemErrorCodeToString(errno);
118  } else {
119    buffer_.reset(new uint8_t[buffer_capacity_]);
120  }
121}
122
123FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
124    : FileOutputStream(fd.get(), buffer_capacity) {
125  owned_fd_ = std::move(fd);
126}
127
128FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
129    : fd_(fd), buffer_capacity_(buffer_capacity) {
130  if (fd_ < 0) {
131    error_ = "Bad File Descriptor";
132  } else {
133    buffer_.reset(new uint8_t[buffer_capacity_]);
134  }
135}
136
137FileOutputStream::~FileOutputStream() {
138  // Flush the buffer.
139  Flush();
140}
141
142bool FileOutputStream::Next(void** data, size_t* size) {
143  if (HadError()) {
144    return false;
145  }
146
147  if (buffer_offset_ == buffer_capacity_) {
148    if (!FlushImpl()) {
149      return false;
150    }
151  }
152
153  const size_t buffer_size = buffer_capacity_ - buffer_offset_;
154  *data = buffer_.get() + buffer_offset_;
155  *size = buffer_size;
156  total_byte_count_ += buffer_size;
157  buffer_offset_ = buffer_capacity_;
158  return true;
159}
160
161void FileOutputStream::BackUp(size_t count) {
162  if (count > buffer_offset_) {
163    count = buffer_offset_;
164  }
165  buffer_offset_ -= count;
166  total_byte_count_ -= count;
167}
168
169size_t FileOutputStream::ByteCount() const {
170  return total_byte_count_;
171}
172
173bool FileOutputStream::Flush() {
174  if (!HadError()) {
175    return FlushImpl();
176  }
177  return false;
178}
179
180bool FileOutputStream::FlushImpl() {
181  ssize_t n = TEMP_FAILURE_RETRY(write(fd_, buffer_.get(), buffer_offset_));
182  if (n < 0) {
183    error_ = SystemErrorCodeToString(errno);
184    owned_fd_.reset();
185    fd_ = -1;
186    buffer_.reset();
187    return false;
188  }
189
190  buffer_offset_ = 0u;
191  return true;
192}
193
194bool FileOutputStream::HadError() const {
195  return fd_ == -1;
196}
197
198std::string FileOutputStream::GetError() const {
199  return error_;
200}
201
202}  // namespace io
203}  // namespace aapt
204