fd_file.cc revision 8f4b056427a9d2321e3aa4f21ca8ffb18b3e5ae6
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 "globals.h"
35#endif
36
37namespace unix_file {
38
39FdFile::FdFile()
40    : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) {
41}
42
43FdFile::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
48FdFile::FdFile(int fd, const std::string& path, bool check_usage)
49    : FdFile(fd, path, check_usage, false) {
50}
51
52FdFile::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
57FdFile::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
65void 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
82FdFile& 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
101FdFile::~FdFile() {
102  Destroy();
103}
104
105void 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
116void 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
128void FdFile::DisableAutoClose() {
129  auto_close_ = false;
130}
131
132bool FdFile::Open(const std::string& path, int flags) {
133  return Open(path, flags, 0640);
134}
135
136bool 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
156int 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
180int 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
198int64_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
207int 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
218int64_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
224int64_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
235int FdFile::Fd() const {
236  return fd_;
237}
238
239bool FdFile::ReadOnlyMode() const {
240  return read_only_mode_;
241}
242
243bool FdFile::CheckUsage() const {
244  return guard_state_ != GuardState::kNoCheck;
245}
246
247bool FdFile::IsOpened() const {
248  return fd_ >= 0;
249}
250
251static 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
256template <ssize_t (*read_func)(int, void*, size_t, off_t)>
257static 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
273bool FdFile::ReadFully(void* buffer, size_t byte_count) {
274  return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
275}
276
277bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
278  return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
279}
280
281template <bool kUseOffset>
282bool 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
301bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
302  return WriteFullyGeneric<true>(buffer, byte_count, offset);
303}
304
305bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
306  return WriteFullyGeneric<false>(buffer, byte_count, 0u);
307}
308
309bool 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
356bool 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
383bool 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
400int 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
417int 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
430void FdFile::MarkUnchecked() {
431  guard_state_ = GuardState::kNoCheck;
432}
433
434bool 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
443bool 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
453int 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