Home | History | Annotate | Download | only in posix
      1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 
     16 #include <dirent.h>
     17 #include <errno.h>
     18 #include <fcntl.h>
     19 #include <stdio.h>
     20 #include <sys/mman.h>
     21 #if !defined(__APPLE__)
     22 #include <sys/sendfile.h>
     23 #endif
     24 #include <sys/stat.h>
     25 #include <sys/time.h>
     26 #include <sys/types.h>
     27 #include <time.h>
     28 #include <unistd.h>
     29 
     30 #include "tensorflow/core/lib/core/error_codes.pb.h"
     31 #include "tensorflow/core/lib/core/status.h"
     32 #include "tensorflow/core/lib/strings/strcat.h"
     33 #include "tensorflow/core/platform/env.h"
     34 #include "tensorflow/core/platform/logging.h"
     35 #include "tensorflow/core/platform/posix/error.h"
     36 #include "tensorflow/core/platform/posix/posix_file_system.h"
     37 
     38 namespace tensorflow {
     39 
     40 // 128KB of copy buffer
     41 constexpr size_t kPosixCopyFileBufferSize = 128 * 1024;
     42 
     43 // pread() based random-access
     44 class PosixRandomAccessFile : public RandomAccessFile {
     45  private:
     46   string filename_;
     47   int fd_;
     48 
     49  public:
     50   PosixRandomAccessFile(const string& fname, int fd)
     51       : filename_(fname), fd_(fd) {}
     52   ~PosixRandomAccessFile() override { close(fd_); }
     53 
     54   Status Read(uint64 offset, size_t n, StringPiece* result,
     55               char* scratch) const override {
     56     Status s;
     57     char* dst = scratch;
     58     while (n > 0 && s.ok()) {
     59       ssize_t r = pread(fd_, dst, n, static_cast<off_t>(offset));
     60       if (r > 0) {
     61         dst += r;
     62         n -= r;
     63         offset += r;
     64       } else if (r == 0) {
     65         s = Status(error::OUT_OF_RANGE, "Read less bytes than requested");
     66       } else if (errno == EINTR || errno == EAGAIN) {
     67         // Retry
     68       } else {
     69         s = IOError(filename_, errno);
     70       }
     71     }
     72     *result = StringPiece(scratch, dst - scratch);
     73     return s;
     74   }
     75 };
     76 
     77 class PosixWritableFile : public WritableFile {
     78  private:
     79   string filename_;
     80   FILE* file_;
     81 
     82  public:
     83   PosixWritableFile(const string& fname, FILE* f)
     84       : filename_(fname), file_(f) {}
     85 
     86   ~PosixWritableFile() override {
     87     if (file_ != nullptr) {
     88       // Ignoring any potential errors
     89       fclose(file_);
     90     }
     91   }
     92 
     93   Status Append(const StringPiece& data) override {
     94     size_t r = fwrite(data.data(), 1, data.size(), file_);
     95     if (r != data.size()) {
     96       return IOError(filename_, errno);
     97     }
     98     return Status::OK();
     99   }
    100 
    101   Status Close() override {
    102     Status result;
    103     if (fclose(file_) != 0) {
    104       result = IOError(filename_, errno);
    105     }
    106     file_ = nullptr;
    107     return result;
    108   }
    109 
    110   Status Flush() override {
    111     if (fflush(file_) != 0) {
    112       return IOError(filename_, errno);
    113     }
    114     return Status::OK();
    115   }
    116 
    117   Status Sync() override {
    118     Status s;
    119     if (fflush(file_) != 0) {
    120       s = IOError(filename_, errno);
    121     }
    122     return s;
    123   }
    124 };
    125 
    126 class PosixReadOnlyMemoryRegion : public ReadOnlyMemoryRegion {
    127  public:
    128   PosixReadOnlyMemoryRegion(const void* address, uint64 length)
    129       : address_(address), length_(length) {}
    130   ~PosixReadOnlyMemoryRegion() override {
    131     munmap(const_cast<void*>(address_), length_);
    132   }
    133   const void* data() override { return address_; }
    134   uint64 length() override { return length_; }
    135 
    136  private:
    137   const void* const address_;
    138   const uint64 length_;
    139 };
    140 
    141 Status PosixFileSystem::NewRandomAccessFile(
    142     const string& fname, std::unique_ptr<RandomAccessFile>* result) {
    143   string translated_fname = TranslateName(fname);
    144   Status s;
    145   int fd = open(translated_fname.c_str(), O_RDONLY);
    146   if (fd < 0) {
    147     s = IOError(fname, errno);
    148   } else {
    149     result->reset(new PosixRandomAccessFile(translated_fname, fd));
    150   }
    151   return s;
    152 }
    153 
    154 Status PosixFileSystem::NewWritableFile(const string& fname,
    155                                         std::unique_ptr<WritableFile>* result) {
    156   string translated_fname = TranslateName(fname);
    157   Status s;
    158   FILE* f = fopen(translated_fname.c_str(), "w");
    159   if (f == nullptr) {
    160     s = IOError(fname, errno);
    161   } else {
    162     result->reset(new PosixWritableFile(translated_fname, f));
    163   }
    164   return s;
    165 }
    166 
    167 Status PosixFileSystem::NewAppendableFile(
    168     const string& fname, std::unique_ptr<WritableFile>* result) {
    169   string translated_fname = TranslateName(fname);
    170   Status s;
    171   FILE* f = fopen(translated_fname.c_str(), "a");
    172   if (f == nullptr) {
    173     s = IOError(fname, errno);
    174   } else {
    175     result->reset(new PosixWritableFile(translated_fname, f));
    176   }
    177   return s;
    178 }
    179 
    180 Status PosixFileSystem::NewReadOnlyMemoryRegionFromFile(
    181     const string& fname, std::unique_ptr<ReadOnlyMemoryRegion>* result) {
    182   string translated_fname = TranslateName(fname);
    183   Status s = Status::OK();
    184   int fd = open(translated_fname.c_str(), O_RDONLY);
    185   if (fd < 0) {
    186     s = IOError(fname, errno);
    187   } else {
    188     struct stat st;
    189     ::fstat(fd, &st);
    190     const void* address =
    191         mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    192     if (address == MAP_FAILED) {
    193       s = IOError(fname, errno);
    194     } else {
    195       result->reset(new PosixReadOnlyMemoryRegion(address, st.st_size));
    196     }
    197     close(fd);
    198   }
    199   return s;
    200 }
    201 
    202 Status PosixFileSystem::FileExists(const string& fname) {
    203   if (access(TranslateName(fname).c_str(), F_OK) == 0) {
    204     return Status::OK();
    205   }
    206   return errors::NotFound(fname, " not found");
    207 }
    208 
    209 Status PosixFileSystem::GetChildren(const string& dir,
    210                                     std::vector<string>* result) {
    211   string translated_dir = TranslateName(dir);
    212   result->clear();
    213   DIR* d = opendir(translated_dir.c_str());
    214   if (d == nullptr) {
    215     return IOError(dir, errno);
    216   }
    217   struct dirent* entry;
    218   while ((entry = readdir(d)) != nullptr) {
    219     StringPiece basename = entry->d_name;
    220     if ((basename != ".") && (basename != "..")) {
    221       result->push_back(entry->d_name);
    222     }
    223   }
    224   closedir(d);
    225   return Status::OK();
    226 }
    227 
    228 Status PosixFileSystem::DeleteFile(const string& fname) {
    229   Status result;
    230   if (unlink(TranslateName(fname).c_str()) != 0) {
    231     result = IOError(fname, errno);
    232   }
    233   return result;
    234 }
    235 
    236 Status PosixFileSystem::CreateDir(const string& name) {
    237   Status result;
    238   if (mkdir(TranslateName(name).c_str(), 0755) != 0) {
    239     result = IOError(name, errno);
    240   }
    241   return result;
    242 }
    243 
    244 Status PosixFileSystem::DeleteDir(const string& name) {
    245   Status result;
    246   if (rmdir(TranslateName(name).c_str()) != 0) {
    247     result = IOError(name, errno);
    248   }
    249   return result;
    250 }
    251 
    252 Status PosixFileSystem::GetFileSize(const string& fname, uint64* size) {
    253   Status s;
    254   struct stat sbuf;
    255   if (stat(TranslateName(fname).c_str(), &sbuf) != 0) {
    256     *size = 0;
    257     s = IOError(fname, errno);
    258   } else {
    259     *size = sbuf.st_size;
    260   }
    261   return s;
    262 }
    263 
    264 Status PosixFileSystem::Stat(const string& fname, FileStatistics* stats) {
    265   Status s;
    266   struct stat sbuf;
    267   if (stat(TranslateName(fname).c_str(), &sbuf) != 0) {
    268     s = IOError(fname, errno);
    269   } else {
    270     stats->length = sbuf.st_size;
    271     stats->mtime_nsec = sbuf.st_mtime * 1e9;
    272     stats->is_directory = S_ISDIR(sbuf.st_mode);
    273   }
    274   return s;
    275 }
    276 
    277 Status PosixFileSystem::RenameFile(const string& src, const string& target) {
    278   Status result;
    279   if (rename(TranslateName(src).c_str(), TranslateName(target).c_str()) != 0) {
    280     result = IOError(src, errno);
    281   }
    282   return result;
    283 }
    284 
    285 Status PosixFileSystem::CopyFile(const string& src, const string& target) {
    286   string translated_src = TranslateName(src);
    287   struct stat sbuf;
    288   if (stat(translated_src.c_str(), &sbuf) != 0) {
    289     return IOError(src, errno);
    290   }
    291   int src_fd = open(translated_src.c_str(), O_RDONLY);
    292   if (src_fd < 0) {
    293     return IOError(src, errno);
    294   }
    295   string translated_target = TranslateName(target);
    296   // O_WRONLY | O_CREAT:
    297   //   Open file for write and if file does not exist, create the file.
    298   // S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH:
    299   //   Create the file with permission of 0644
    300   int target_fd = open(translated_target.c_str(), O_WRONLY | O_CREAT,
    301                        S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    302   if (target_fd < 0) {
    303     close(src_fd);
    304     return IOError(target, errno);
    305   }
    306   int rc = 0;
    307   off_t offset = 0;
    308   std::unique_ptr<char[]> buffer(new char[kPosixCopyFileBufferSize]);
    309   while (offset < sbuf.st_size) {
    310     // Use uint64 for safe compare SSIZE_MAX
    311     uint64 chunk = sbuf.st_size - offset;
    312     if (chunk > SSIZE_MAX) {
    313       chunk = SSIZE_MAX;
    314     }
    315 #if defined(__linux__) && !defined(__ANDROID__)
    316     rc = sendfile(target_fd, src_fd, &offset, static_cast<size_t>(chunk));
    317 #else
    318     if (chunk > kPosixCopyFileBufferSize) {
    319       chunk = kPosixCopyFileBufferSize;
    320     }
    321     rc = read(src_fd, buffer.get(), static_cast<size_t>(chunk));
    322     if (rc <= 0) {
    323       break;
    324     }
    325     rc = write(target_fd, buffer.get(), static_cast<size_t>(chunk));
    326     offset += chunk;
    327 #endif
    328     if (rc <= 0) {
    329       break;
    330     }
    331   }
    332 
    333   Status result = Status::OK();
    334   if (rc < 0) {
    335     result = IOError(target, errno);
    336   }
    337 
    338   // Keep the error code
    339   rc = close(target_fd);
    340   if (rc < 0 && result == Status::OK()) {
    341     result = IOError(target, errno);
    342   }
    343   rc = close(src_fd);
    344   if (rc < 0 && result == Status::OK()) {
    345     result = IOError(target, errno);
    346   }
    347 
    348   return result;
    349 }
    350 
    351 }  // namespace tensorflow
    352