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