Home | History | Annotate | Download | only in bsdiff
      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 "bsdiff/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 
     25 namespace bsdiff {
     26 
     27 std::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 
     34 File::~File() {
     35   Close();
     36 }
     37 
     38 bool 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 
     50 bool 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 
     62 bool File::Seek(off_t pos) {
     63   if (fd_ < 0) {
     64     errno = EBADF;
     65     return false;
     66   }
     67 
     68   off_t newpos = lseek(fd_, pos, SEEK_SET);
     69   if (newpos < 0)
     70     return false;
     71   if (newpos != pos) {
     72     errno = EINVAL;
     73     return false;
     74   }
     75   return true;
     76 }
     77 
     78 bool File::Close() {
     79   if (fd_ < 0) {
     80     errno = EBADF;
     81     return false;
     82   }
     83   bool success = close(fd_) == 0;
     84   if (!success && errno == EINTR)
     85     success = true;
     86   fd_ = -1;
     87   return success;
     88 }
     89 
     90 bool File::GetSize(uint64_t* size) {
     91   struct stat stbuf;
     92   if (fstat(fd_, &stbuf) == -1)
     93     return false;
     94   if (S_ISREG(stbuf.st_mode)) {
     95     *size = stbuf.st_size;
     96     return true;
     97   }
     98   if (S_ISBLK(stbuf.st_mode)) {
     99 #if defined(BLKGETSIZE64)
    100     return ioctl(fd_, BLKGETSIZE64, size);
    101 #elif defined(DKIOCGETBLOCKCOUNT)
    102     uint64_t sectors = 0;
    103     if (ioctl(fd_, DKIOCGETBLOCKCOUNT, &sectors) == 0) {
    104       *size = sectors << 9;
    105       return true;
    106     }
    107     return false;
    108 #else
    109     // Fall back to doing seeks to know the EOF.
    110     off_t pos = lseek(fd_, 0, SEEK_CUR);
    111     if (pos == -1)
    112       return false;
    113     off_t end_pos = lseek(fd_, 0, SEEK_END);
    114     if (end_pos == -1)
    115       return false;
    116     *size = end_pos;
    117     lseek(fd_, 0, SEEK_END);
    118     return true;
    119 #endif
    120   }
    121   return false;
    122 }
    123 
    124 File::File(int fd)
    125     : fd_(fd) {}
    126 
    127 }  // namespace bsdiff
    128