1 /*
2  * Copyright (C) 2009 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 "base/unix_file/fd_file.h"
18 
19 #include <errno.h>
20 #include <limits>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include "base/logging.h"
26 
27 // Includes needed for FdFile::Copy().
28 #ifdef __linux__
29 #include <sys/sendfile.h>
30 #else
31 #include <algorithm>
32 #include "base/stl_util.h"
33 #include "globals.h"
34 #endif
35 
36 namespace unix_file {
37 
FdFile()38 FdFile::FdFile()
39     : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) {
40 }
41 
FdFile(int fd,bool check_usage)42 FdFile::FdFile(int fd, bool check_usage)
43     : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
44       fd_(fd), auto_close_(true), read_only_mode_(false) {
45 }
46 
FdFile(int fd,const std::string & path,bool check_usage)47 FdFile::FdFile(int fd, const std::string& path, bool check_usage)
48     : FdFile(fd, path, check_usage, false) {
49 }
50 
FdFile(int fd,const std::string & path,bool check_usage,bool read_only_mode)51 FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode)
52     : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
53       fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) {
54 }
55 
~FdFile()56 FdFile::~FdFile() {
57   if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
58     if (guard_state_ < GuardState::kFlushed) {
59       LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
60     }
61     if (guard_state_ < GuardState::kClosed) {
62       LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
63     }
64     CHECK_GE(guard_state_, GuardState::kClosed);
65   }
66   if (auto_close_ && fd_ != -1) {
67     if (Close() != 0) {
68       PLOG(::art::WARNING) << "Failed to close file " << file_path_;
69     }
70   }
71 }
72 
moveTo(GuardState target,GuardState warn_threshold,const char * warning)73 void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
74   if (kCheckSafeUsage) {
75     if (guard_state_ < GuardState::kNoCheck) {
76       if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
77         LOG(::art::ERROR) << warning;
78       }
79       guard_state_ = target;
80     }
81   }
82 }
83 
moveUp(GuardState target,const char * warning)84 void FdFile::moveUp(GuardState target, const char* warning) {
85   if (kCheckSafeUsage) {
86     if (guard_state_ < GuardState::kNoCheck) {
87       if (guard_state_ < target) {
88         guard_state_ = target;
89       } else if (target < guard_state_) {
90         LOG(::art::ERROR) << warning;
91       }
92     }
93   }
94 }
95 
DisableAutoClose()96 void FdFile::DisableAutoClose() {
97   auto_close_ = false;
98 }
99 
Open(const std::string & path,int flags)100 bool FdFile::Open(const std::string& path, int flags) {
101   return Open(path, flags, 0640);
102 }
103 
Open(const std::string & path,int flags,mode_t mode)104 bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
105   CHECK_EQ(fd_, -1) << path;
106   read_only_mode_ = (flags & O_RDONLY) != 0;
107   fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
108   if (fd_ == -1) {
109     return false;
110   }
111   file_path_ = path;
112   static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
113   if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
114     // Start in the base state (not flushed, not closed).
115     guard_state_ = GuardState::kBase;
116   } else {
117     // We are not concerned with read-only files. In that case, proper flushing and closing is
118     // not important.
119     guard_state_ = GuardState::kNoCheck;
120   }
121   return true;
122 }
123 
Close()124 int FdFile::Close() {
125   int result = close(fd_);
126 
127   // Test here, so the file is closed and not leaked.
128   if (kCheckSafeUsage) {
129     CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
130         << " has not been flushed before closing.";
131     moveUp(GuardState::kClosed, nullptr);
132   }
133 
134   if (result == -1) {
135     return -errno;
136   } else {
137     fd_ = -1;
138     file_path_ = "";
139     return 0;
140   }
141 }
142 
Flush()143 int FdFile::Flush() {
144   DCHECK(!read_only_mode_);
145 #ifdef __linux__
146   int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
147 #else
148   int rc = TEMP_FAILURE_RETRY(fsync(fd_));
149 #endif
150   moveUp(GuardState::kFlushed, "Flushing closed file.");
151   return (rc == -1) ? -errno : rc;
152 }
153 
Read(char * buf,int64_t byte_count,int64_t offset) const154 int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
155 #ifdef __linux__
156   int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset));
157 #else
158   int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset));
159 #endif
160   return (rc == -1) ? -errno : rc;
161 }
162 
SetLength(int64_t new_length)163 int FdFile::SetLength(int64_t new_length) {
164   DCHECK(!read_only_mode_);
165 #ifdef __linux__
166   int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
167 #else
168   int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
169 #endif
170   moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file.");
171   return (rc == -1) ? -errno : rc;
172 }
173 
GetLength() const174 int64_t FdFile::GetLength() const {
175   struct stat s;
176   int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s));
177   return (rc == -1) ? -errno : s.st_size;
178 }
179 
Write(const char * buf,int64_t byte_count,int64_t offset)180 int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
181   DCHECK(!read_only_mode_);
182 #ifdef __linux__
183   int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
184 #else
185   int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
186 #endif
187   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
188   return (rc == -1) ? -errno : rc;
189 }
190 
Fd() const191 int FdFile::Fd() const {
192   return fd_;
193 }
194 
ReadOnlyMode() const195 bool FdFile::ReadOnlyMode() const {
196   return read_only_mode_;
197 }
198 
CheckUsage() const199 bool FdFile::CheckUsage() const {
200   return guard_state_ != GuardState::kNoCheck;
201 }
202 
IsOpened() const203 bool FdFile::IsOpened() const {
204   return fd_ >= 0;
205 }
206 
ReadIgnoreOffset(int fd,void * buf,size_t count,off_t offset)207 static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) {
208   DCHECK_EQ(offset, 0);
209   return read(fd, buf, count);
210 }
211 
212 template <ssize_t (*read_func)(int, void*, size_t, off_t)>
ReadFullyGeneric(int fd,void * buffer,size_t byte_count,size_t offset)213 static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) {
214   char* ptr = static_cast<char*>(buffer);
215   while (byte_count > 0) {
216     ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset));
217     if (bytes_read <= 0) {
218       // 0: end of file
219       // -1: error
220       return false;
221     }
222     byte_count -= bytes_read;  // Reduce the number of remaining bytes.
223     ptr += bytes_read;  // Move the buffer forward.
224     offset += static_cast<size_t>(bytes_read);  // Move the offset forward.
225   }
226   return true;
227 }
228 
ReadFully(void * buffer,size_t byte_count)229 bool FdFile::ReadFully(void* buffer, size_t byte_count) {
230   return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
231 }
232 
PreadFully(void * buffer,size_t byte_count,size_t offset)233 bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
234   return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
235 }
236 
237 template <bool kUseOffset>
WriteFullyGeneric(const void * buffer,size_t byte_count,size_t offset)238 bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
239   DCHECK(!read_only_mode_);
240   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
241   DCHECK(kUseOffset || offset == 0u);
242   const char* ptr = static_cast<const char*>(buffer);
243   while (byte_count > 0) {
244     ssize_t bytes_written = kUseOffset
245         ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
246         : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
247     if (bytes_written == -1) {
248       return false;
249     }
250     byte_count -= bytes_written;  // Reduce the number of remaining bytes.
251     ptr += bytes_written;  // Move the buffer forward.
252     offset += static_cast<size_t>(bytes_written);
253   }
254   return true;
255 }
256 
PwriteFully(const void * buffer,size_t byte_count,size_t offset)257 bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
258   return WriteFullyGeneric<true>(buffer, byte_count, offset);
259 }
260 
WriteFully(const void * buffer,size_t byte_count)261 bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
262   return WriteFullyGeneric<false>(buffer, byte_count, 0u);
263 }
264 
Copy(FdFile * input_file,int64_t offset,int64_t size)265 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
266   DCHECK(!read_only_mode_);
267   off_t off = static_cast<off_t>(offset);
268   off_t sz = static_cast<off_t>(size);
269   if (offset < 0 || static_cast<int64_t>(off) != offset ||
270       size < 0 || static_cast<int64_t>(sz) != size ||
271       sz > std::numeric_limits<off_t>::max() - off) {
272     errno = EINVAL;
273     return false;
274   }
275   if (size == 0) {
276     return true;
277   }
278 #ifdef __linux__
279   // Use sendfile(), available for files since linux kernel 2.6.33.
280   off_t end = off + sz;
281   while (off != end) {
282     int result = TEMP_FAILURE_RETRY(
283         sendfile(Fd(), input_file->Fd(), &off, end - off));
284     if (result == -1) {
285       return false;
286     }
287     // Ignore the number of bytes in `result`, sendfile() already updated `off`.
288   }
289 #else
290   if (lseek(input_file->Fd(), off, SEEK_SET) != off) {
291     return false;
292   }
293   constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize;
294   const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize);
295   art::UniqueCPtr<void> buffer(malloc(buffer_size));
296   if (buffer == nullptr) {
297     errno = ENOMEM;
298     return false;
299   }
300   while (size != 0) {
301     size_t chunk_size = std::min<uint64_t>(buffer_size, size);
302     if (!input_file->ReadFully(buffer.get(), chunk_size) ||
303         !WriteFully(buffer.get(), chunk_size)) {
304       return false;
305     }
306     size -= chunk_size;
307   }
308 #endif
309   return true;
310 }
311 
Erase()312 void FdFile::Erase() {
313   DCHECK(!read_only_mode_);
314   TEMP_FAILURE_RETRY(SetLength(0));
315   TEMP_FAILURE_RETRY(Flush());
316   TEMP_FAILURE_RETRY(Close());
317 }
318 
FlushCloseOrErase()319 int FdFile::FlushCloseOrErase() {
320   DCHECK(!read_only_mode_);
321   int flush_result = TEMP_FAILURE_RETRY(Flush());
322   if (flush_result != 0) {
323     LOG(::art::ERROR) << "CloseOrErase failed while flushing a file.";
324     Erase();
325     return flush_result;
326   }
327   int close_result = TEMP_FAILURE_RETRY(Close());
328   if (close_result != 0) {
329     LOG(::art::ERROR) << "CloseOrErase failed while closing a file.";
330     Erase();
331     return close_result;
332   }
333   return 0;
334 }
335 
FlushClose()336 int FdFile::FlushClose() {
337   DCHECK(!read_only_mode_);
338   int flush_result = TEMP_FAILURE_RETRY(Flush());
339   if (flush_result != 0) {
340     LOG(::art::ERROR) << "FlushClose failed while flushing a file.";
341   }
342   int close_result = TEMP_FAILURE_RETRY(Close());
343   if (close_result != 0) {
344     LOG(::art::ERROR) << "FlushClose failed while closing a file.";
345   }
346   return (flush_result != 0) ? flush_result : close_result;
347 }
348 
MarkUnchecked()349 void FdFile::MarkUnchecked() {
350   guard_state_ = GuardState::kNoCheck;
351 }
352 
ClearContent()353 bool FdFile::ClearContent() {
354   DCHECK(!read_only_mode_);
355   if (SetLength(0) < 0) {
356     PLOG(art::ERROR) << "Failed to reset the length";
357     return false;
358   }
359   return ResetOffset();
360 }
361 
ResetOffset()362 bool FdFile::ResetOffset() {
363   DCHECK(!read_only_mode_);
364   off_t rc =  TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET));
365   if (rc == static_cast<off_t>(-1)) {
366     PLOG(art::ERROR) << "Failed to reset the offset";
367     return false;
368   }
369   return true;
370 }
371 
372 }  // namespace unix_file
373