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 <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <limits>
25 
26 #include <android-base/logging.h>
27 
28 // Includes needed for FdFile::Copy().
29 #ifdef __linux__
30 #include <sys/sendfile.h>
31 #else
32 #include <algorithm>
33 #include "base/stl_util.h"
34 #include "base/globals.h"
35 #endif
36 
37 namespace unix_file {
38 
FdFile()39 FdFile::FdFile()
40     : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) {
41 }
42 
FdFile(int fd,bool check_usage)43 FdFile::FdFile(int fd, bool check_usage)
44     : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
45       fd_(fd), auto_close_(true), read_only_mode_(false) {
46 }
47 
FdFile(int fd,const std::string & path,bool check_usage)48 FdFile::FdFile(int fd, const std::string& path, bool check_usage)
49     : FdFile(fd, path, check_usage, false) {
50 }
51 
FdFile(int fd,const std::string & path,bool check_usage,bool read_only_mode)52 FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode)
53     : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
54       fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) {
55 }
56 
FdFile(const std::string & path,int flags,mode_t mode,bool check_usage)57 FdFile::FdFile(const std::string& path, int flags, mode_t mode, bool check_usage)
58     : fd_(-1), auto_close_(true) {
59   Open(path, flags, mode);
60   if (!check_usage || !IsOpened()) {
61     guard_state_ = GuardState::kNoCheck;
62   }
63 }
64 
Destroy()65 void FdFile::Destroy() {
66   if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
67     if (guard_state_ < GuardState::kFlushed) {
68       LOG(ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
69     }
70     if (guard_state_ < GuardState::kClosed) {
71       LOG(ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
72     }
73     DCHECK_GE(guard_state_, GuardState::kClosed);
74   }
75   if (auto_close_ && fd_ != -1) {
76     if (Close() != 0) {
77       PLOG(WARNING) << "Failed to close file with fd=" << fd_ << " path=" << file_path_;
78     }
79   }
80 }
81 
operator =(FdFile && other)82 FdFile& FdFile::operator=(FdFile&& other) {
83   if (this == &other) {
84     return *this;
85   }
86 
87   if (this->fd_ != other.fd_) {
88     Destroy();  // Free old state.
89   }
90 
91   guard_state_ = other.guard_state_;
92   fd_ = other.fd_;
93   file_path_ = std::move(other.file_path_);
94   auto_close_ = other.auto_close_;
95   read_only_mode_ = other.read_only_mode_;
96   other.Release();  // Release other.
97 
98   return *this;
99 }
100 
~FdFile()101 FdFile::~FdFile() {
102   Destroy();
103 }
104 
moveTo(GuardState target,GuardState warn_threshold,const char * warning)105 void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
106   if (kCheckSafeUsage) {
107     if (guard_state_ < GuardState::kNoCheck) {
108       if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
109         LOG(ERROR) << warning;
110       }
111       guard_state_ = target;
112     }
113   }
114 }
115 
moveUp(GuardState target,const char * warning)116 void FdFile::moveUp(GuardState target, const char* warning) {
117   if (kCheckSafeUsage) {
118     if (guard_state_ < GuardState::kNoCheck) {
119       if (guard_state_ < target) {
120         guard_state_ = target;
121       } else if (target < guard_state_) {
122         LOG(ERROR) << warning;
123       }
124     }
125   }
126 }
127 
DisableAutoClose()128 void FdFile::DisableAutoClose() {
129   auto_close_ = false;
130 }
131 
Open(const std::string & path,int flags)132 bool FdFile::Open(const std::string& path, int flags) {
133   return Open(path, flags, 0640);
134 }
135 
Open(const std::string & path,int flags,mode_t mode)136 bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
137   static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
138   DCHECK_EQ(fd_, -1) << path;
139   read_only_mode_ = ((flags & O_ACCMODE) == O_RDONLY);
140   fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
141   if (fd_ == -1) {
142     return false;
143   }
144   file_path_ = path;
145   if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
146     // Start in the base state (not flushed, not closed).
147     guard_state_ = GuardState::kBase;
148   } else {
149     // We are not concerned with read-only files. In that case, proper flushing and closing is
150     // not important.
151     guard_state_ = GuardState::kNoCheck;
152   }
153   return true;
154 }
155 
Close()156 int FdFile::Close() {
157   int result = close(fd_);
158 
159   // Test here, so the file is closed and not leaked.
160   if (kCheckSafeUsage) {
161     DCHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
162         << " has not been flushed before closing.";
163     moveUp(GuardState::kClosed, nullptr);
164   }
165 
166 #if defined(__linux__)
167   // close always succeeds on linux, even if failure is reported.
168   UNUSED(result);
169 #else
170   if (result == -1) {
171     return -errno;
172   }
173 #endif
174 
175   fd_ = -1;
176   file_path_ = "";
177   return 0;
178 }
179 
Flush()180 int FdFile::Flush() {
181   DCHECK(!read_only_mode_);
182 
183 #ifdef __linux__
184   int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
185 #else
186   int rc = TEMP_FAILURE_RETRY(fsync(fd_));
187 #endif
188 
189   moveUp(GuardState::kFlushed, "Flushing closed file.");
190   if (rc == 0) {
191     return 0;
192   }
193 
194   // Don't report failure if we just tried to flush a pipe or socket.
195   return errno == EINVAL ? 0 : -errno;
196 }
197 
Read(char * buf,int64_t byte_count,int64_t offset) const198 int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
199 #ifdef __linux__
200   int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset));
201 #else
202   int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset));
203 #endif
204   return (rc == -1) ? -errno : rc;
205 }
206 
SetLength(int64_t new_length)207 int FdFile::SetLength(int64_t new_length) {
208   DCHECK(!read_only_mode_);
209 #ifdef __linux__
210   int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
211 #else
212   int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
213 #endif
214   moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file.");
215   return (rc == -1) ? -errno : rc;
216 }
217 
GetLength() const218 int64_t FdFile::GetLength() const {
219   struct stat s;
220   int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s));
221   return (rc == -1) ? -errno : s.st_size;
222 }
223 
Write(const char * buf,int64_t byte_count,int64_t offset)224 int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
225   DCHECK(!read_only_mode_);
226 #ifdef __linux__
227   int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
228 #else
229   int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
230 #endif
231   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
232   return (rc == -1) ? -errno : rc;
233 }
234 
Fd() const235 int FdFile::Fd() const {
236   return fd_;
237 }
238 
ReadOnlyMode() const239 bool FdFile::ReadOnlyMode() const {
240   return read_only_mode_;
241 }
242 
CheckUsage() const243 bool FdFile::CheckUsage() const {
244   return guard_state_ != GuardState::kNoCheck;
245 }
246 
IsOpened() const247 bool FdFile::IsOpened() const {
248   return fd_ >= 0;
249 }
250 
ReadIgnoreOffset(int fd,void * buf,size_t count,off_t offset)251 static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) {
252   DCHECK_EQ(offset, 0);
253   return read(fd, buf, count);
254 }
255 
256 template <ssize_t (*read_func)(int, void*, size_t, off_t)>
ReadFullyGeneric(int fd,void * buffer,size_t byte_count,size_t offset)257 static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) {
258   char* ptr = static_cast<char*>(buffer);
259   while (byte_count > 0) {
260     ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset));
261     if (bytes_read <= 0) {
262       // 0: end of file
263       // -1: error
264       return false;
265     }
266     byte_count -= bytes_read;  // Reduce the number of remaining bytes.
267     ptr += bytes_read;  // Move the buffer forward.
268     offset += static_cast<size_t>(bytes_read);  // Move the offset forward.
269   }
270   return true;
271 }
272 
ReadFully(void * buffer,size_t byte_count)273 bool FdFile::ReadFully(void* buffer, size_t byte_count) {
274   return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
275 }
276 
PreadFully(void * buffer,size_t byte_count,size_t offset)277 bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
278   return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
279 }
280 
281 template <bool kUseOffset>
WriteFullyGeneric(const void * buffer,size_t byte_count,size_t offset)282 bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
283   DCHECK(!read_only_mode_);
284   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
285   DCHECK(kUseOffset || offset == 0u);
286   const char* ptr = static_cast<const char*>(buffer);
287   while (byte_count > 0) {
288     ssize_t bytes_written = kUseOffset
289         ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
290         : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
291     if (bytes_written == -1) {
292       return false;
293     }
294     byte_count -= bytes_written;  // Reduce the number of remaining bytes.
295     ptr += bytes_written;  // Move the buffer forward.
296     offset += static_cast<size_t>(bytes_written);
297   }
298   return true;
299 }
300 
PwriteFully(const void * buffer,size_t byte_count,size_t offset)301 bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
302   return WriteFullyGeneric<true>(buffer, byte_count, offset);
303 }
304 
WriteFully(const void * buffer,size_t byte_count)305 bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
306   return WriteFullyGeneric<false>(buffer, byte_count, 0u);
307 }
308 
Copy(FdFile * input_file,int64_t offset,int64_t size)309 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
310   DCHECK(!read_only_mode_);
311   off_t off = static_cast<off_t>(offset);
312   off_t sz = static_cast<off_t>(size);
313   if (offset < 0 || static_cast<int64_t>(off) != offset ||
314       size < 0 || static_cast<int64_t>(sz) != size ||
315       sz > std::numeric_limits<off_t>::max() - off) {
316     errno = EINVAL;
317     return false;
318   }
319   if (size == 0) {
320     return true;
321   }
322 #ifdef __linux__
323   // Use sendfile(), available for files since linux kernel 2.6.33.
324   off_t end = off + sz;
325   while (off != end) {
326     int result = TEMP_FAILURE_RETRY(
327         sendfile(Fd(), input_file->Fd(), &off, end - off));
328     if (result == -1) {
329       return false;
330     }
331     // Ignore the number of bytes in `result`, sendfile() already updated `off`.
332   }
333 #else
334   if (lseek(input_file->Fd(), off, SEEK_SET) != off) {
335     return false;
336   }
337   constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize;
338   const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize);
339   art::UniqueCPtr<void> buffer(malloc(buffer_size));
340   if (buffer == nullptr) {
341     errno = ENOMEM;
342     return false;
343   }
344   while (size != 0) {
345     size_t chunk_size = std::min<uint64_t>(buffer_size, size);
346     if (!input_file->ReadFully(buffer.get(), chunk_size) ||
347         !WriteFully(buffer.get(), chunk_size)) {
348       return false;
349     }
350     size -= chunk_size;
351   }
352 #endif
353   return true;
354 }
355 
Unlink()356 bool FdFile::Unlink() {
357   if (file_path_.empty()) {
358     return false;
359   }
360 
361   // Try to figure out whether this file is still referring to the one on disk.
362   bool is_current = false;
363   {
364     struct stat this_stat, current_stat;
365     int cur_fd = TEMP_FAILURE_RETRY(open(file_path_.c_str(), O_RDONLY));
366     if (cur_fd > 0) {
367       // File still exists.
368       if (fstat(fd_, &this_stat) == 0 && fstat(cur_fd, &current_stat) == 0) {
369         is_current = (this_stat.st_dev == current_stat.st_dev) &&
370                      (this_stat.st_ino == current_stat.st_ino);
371       }
372       close(cur_fd);
373     }
374   }
375 
376   if (is_current) {
377     unlink(file_path_.c_str());
378   }
379 
380   return is_current;
381 }
382 
Erase(bool unlink)383 bool FdFile::Erase(bool unlink) {
384   DCHECK(!read_only_mode_);
385 
386   bool ret_result = true;
387   if (unlink) {
388     ret_result = Unlink();
389   }
390 
391   int result;
392   result = SetLength(0);
393   result = Flush();
394   result = Close();
395   // Ignore the errors.
396 
397   return ret_result;
398 }
399 
FlushCloseOrErase()400 int FdFile::FlushCloseOrErase() {
401   DCHECK(!read_only_mode_);
402   int flush_result = Flush();
403   if (flush_result != 0) {
404     LOG(ERROR) << "CloseOrErase failed while flushing a file.";
405     Erase();
406     return flush_result;
407   }
408   int close_result = Close();
409   if (close_result != 0) {
410     LOG(ERROR) << "CloseOrErase failed while closing a file.";
411     Erase();
412     return close_result;
413   }
414   return 0;
415 }
416 
FlushClose()417 int FdFile::FlushClose() {
418   DCHECK(!read_only_mode_);
419   int flush_result = Flush();
420   if (flush_result != 0) {
421     LOG(ERROR) << "FlushClose failed while flushing a file.";
422   }
423   int close_result = Close();
424   if (close_result != 0) {
425     LOG(ERROR) << "FlushClose failed while closing a file.";
426   }
427   return (flush_result != 0) ? flush_result : close_result;
428 }
429 
MarkUnchecked()430 void FdFile::MarkUnchecked() {
431   guard_state_ = GuardState::kNoCheck;
432 }
433 
ClearContent()434 bool FdFile::ClearContent() {
435   DCHECK(!read_only_mode_);
436   if (SetLength(0) < 0) {
437     PLOG(ERROR) << "Failed to reset the length";
438     return false;
439   }
440   return ResetOffset();
441 }
442 
ResetOffset()443 bool FdFile::ResetOffset() {
444   DCHECK(!read_only_mode_);
445   off_t rc =  TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET));
446   if (rc == static_cast<off_t>(-1)) {
447     PLOG(ERROR) << "Failed to reset the offset";
448     return false;
449   }
450   return true;
451 }
452 
Compare(FdFile * other)453 int FdFile::Compare(FdFile* other) {
454   int64_t length = GetLength();
455   int64_t length2 = other->GetLength();
456   if (length != length2) {
457     return length < length2 ? -1 : 1;
458   }
459   static const size_t kBufferSize = 4096;
460   std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
461   std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
462   size_t offset = 0;
463   while (length > 0) {
464     size_t len = std::min(kBufferSize, static_cast<size_t>(length));
465     if (!PreadFully(&buffer1[0], len, offset)) {
466       return -1;
467     }
468     if (!other->PreadFully(&buffer2[0], len, offset)) {
469       return 1;
470     }
471     int result = memcmp(&buffer1[0], &buffer2[0], len);
472     if (result != 0) {
473       return result;
474     }
475     length -= len;
476     offset += len;
477   }
478   return 0;
479 }
480 
481 }  // namespace unix_file
482