1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16
17 #include "tink/util/file_input_stream.h"
18
19 #include <unistd.h>
20 #include <algorithm>
21
22 #include "absl/status/status.h"
23 #include "tink/util/errors.h"
24 #include "tink/util/status.h"
25 #include "tink/util/statusor.h"
26
27 namespace crypto {
28 namespace tink {
29 namespace util {
30 namespace {
31
32 constexpr int kDefaultBufferSize = 128 * 1024;
33
34 // Attempts to close file descriptor fd, while ignoring EINTR.
35 // (code borrowed from ZeroCopy-streams)
close_ignoring_eintr(int fd)36 int close_ignoring_eintr(int fd) {
37 int result;
38 do {
39 result = close(fd);
40 } while (result < 0 && errno == EINTR);
41 return result;
42 }
43
44 // Attempts to read 'count' bytes of data data from file descriptor fd
45 // to 'buf' while ignoring EINTR.
read_ignoring_eintr(int fd,void * buf,size_t count)46 int read_ignoring_eintr(int fd, void *buf, size_t count) {
47 int result;
48 do {
49 result = read(fd, buf, count);
50 } while (result < 0 && errno == EINTR);
51 return result;
52 }
53
54 } // anonymous namespace
55
FileInputStream(int file_descriptor,int buffer_size)56 FileInputStream::FileInputStream(int file_descriptor, int buffer_size)
57 : status_(util::OkStatus()),
58 fd_(file_descriptor),
59 buffer_(buffer_size > 0 ? buffer_size : kDefaultBufferSize) {}
60
Next(const void ** data)61 util::StatusOr<int> FileInputStream::Next(const void** data) {
62 if (data == nullptr) {
63 return util::Status(absl::StatusCode::kInvalidArgument,
64 "Data pointer must not be nullptr");
65 }
66 if (!status_.ok()) return status_;
67 if (count_backedup_ > 0) { // Return the backed-up bytes.
68 buffer_offset_ = buffer_offset_ + (count_in_buffer_ - count_backedup_);
69 count_in_buffer_ = count_backedup_;
70 count_backedup_ = 0;
71 *data = buffer_.data() + buffer_offset_;
72 position_ = position_ + count_in_buffer_;
73 return count_in_buffer_;
74 }
75 // Read new bytes to buffer_.
76 int read_result = read_ignoring_eintr(fd_, buffer_.data(), buffer_.size());
77 if (read_result <= 0) { // EOF or an I/O error.
78 if (read_result == 0) {
79 status_ = Status(absl::StatusCode::kOutOfRange, "EOF");
80 } else {
81 status_ =
82 ToStatusF(absl::StatusCode::kInternal, "I/O error: %d", read_result);
83 }
84 return status_;
85 }
86 buffer_offset_ = 0;
87 count_backedup_ = 0;
88 count_in_buffer_ = read_result;
89 position_ = position_ + count_in_buffer_;
90 *data = buffer_.data();
91 return count_in_buffer_;
92 }
93
BackUp(int count)94 void FileInputStream::BackUp(int count) {
95 if (!status_.ok() || count < 1 || count_backedup_ == count_in_buffer_) return;
96 int actual_count = std::min(count, count_in_buffer_ - count_backedup_);
97 count_backedup_ = count_backedup_ + actual_count;
98 position_ = position_ - actual_count;
99 }
100
~FileInputStream()101 FileInputStream::~FileInputStream() { close_ignoring_eintr(fd_); }
102
Position() const103 int64_t FileInputStream::Position() const { return position_; }
104
105 } // namespace util
106 } // namespace tink
107 } // namespace crypto
108