1// Copyright 2015 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "file.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#ifdef __linux__
10#include <linux/fs.h>
11#endif  // __linux__
12#include <string.h>
13#include <sys/ioctl.h>
14#include <sys/stat.h>
15#include <sys/types.h>
16#include <unistd.h>
17
18// TEMP_FAILURE_RETRY is defined by some versions of <unistd.h>.
19#ifndef TEMP_FAILURE_RETRY
20#include <utils/Compat.h>
21#endif
22
23#include <algorithm>
24
25namespace bsdiff {
26
27std::unique_ptr<File> File::FOpen(const char* pathname, int flags) {
28  int fd = TEMP_FAILURE_RETRY(open(pathname, flags, 0644));
29  if (fd < 0)
30    return std::unique_ptr<File>();
31  return std::unique_ptr<File>(new File(fd));
32}
33
34File::~File() {
35  Close();
36}
37
38bool File::Read(void* buf, size_t count, size_t* bytes_read) {
39  if (fd_ < 0) {
40    errno = EBADF;
41    return false;
42  }
43  ssize_t rc = TEMP_FAILURE_RETRY(read(fd_, buf, count));
44  if (rc == -1)
45    return false;
46  *bytes_read = static_cast<size_t>(rc);
47  return true;
48}
49
50bool File::Write(const void* buf, size_t count, size_t* bytes_written) {
51  if (fd_ < 0) {
52    errno = EBADF;
53    return false;
54  }
55  ssize_t rc = TEMP_FAILURE_RETRY(write(fd_, buf, count));
56  if (rc == -1)
57    return false;
58  *bytes_written = static_cast<size_t>(rc);
59  return true;
60}
61
62bool File::Seek(off_t pos) {
63  if (fd_ < 0) {
64    errno = EBADF;
65    return false;
66  }
67  // fseek() uses a long value for the offset which could be smaller than off_t.
68  if (pos > std::numeric_limits<long>::max()) {
69    errno = EOVERFLOW;
70    return false;
71  }
72  off_t newpos = lseek(fd_, pos, SEEK_SET);
73  if (newpos < 0)
74    return false;
75  if (newpos != pos) {
76    errno = EINVAL;
77    return false;
78  }
79  return true;
80}
81
82bool File::Close() {
83  if (fd_ < 0) {
84    errno = EBADF;
85    return false;
86  }
87  bool success = close(fd_) == 0;
88  if (!success && errno == EINTR)
89    success = true;
90  fd_ = -1;
91  return success;
92}
93
94bool File::GetSize(uint64_t* size) {
95  struct stat stbuf;
96  if (fstat(fd_, &stbuf) == -1)
97    return false;
98  if (S_ISREG(stbuf.st_mode)) {
99    *size = stbuf.st_size;
100    return true;
101  }
102  if (S_ISBLK(stbuf.st_mode)) {
103#if defined(BLKGETSIZE64)
104    return ioctl(fd_, BLKGETSIZE64, size);
105#elif defined(DKIOCGETBLOCKCOUNT)
106    uint64_t sectors = 0;
107    if (ioctl(fd_, DKIOCGETBLOCKCOUNT, &sectors) == 0) {
108      *size = sectors << 9;
109      return true;
110    }
111    return false;
112#else
113    // Fall back to doing seeks to know the EOF.
114    off_t pos = lseek(fd_, 0, SEEK_CUR);
115    if (pos == -1)
116      return false;
117    off_t end_pos = lseek(fd_, 0, SEEK_END);
118    if (end_pos == -1)
119      return false;
120    *size = end_pos;
121    lseek(fd_, 0, SEEK_END);
122    return true;
123#endif
124  }
125  return false;
126}
127
128File::File(int fd)
129    : fd_(fd) {}
130
131}  // namespace bsdiff
132