Home | History | Annotate | Download | only in file
      1 /*
      2  * Copyright (C) 2018 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 "lang_id/common/file/mmap.h"
     18 
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 #include <stdint.h>
     22 #include <string.h>
     23 #include <sys/mman.h>
     24 #include <sys/stat.h>
     25 #include <unistd.h>
     26 
     27 #include "lang_id/common/lite_base/logging.h"
     28 #include "lang_id/common/lite_base/macros.h"
     29 
     30 namespace libtextclassifier3 {
     31 namespace mobile {
     32 
     33 namespace {
     34 inline string GetLastSystemError() {
     35   return string(strerror(errno));
     36 }
     37 
     38 inline MmapHandle GetErrorMmapHandle() {
     39   return MmapHandle(nullptr, 0);
     40 }
     41 
     42 class FileCloser {
     43  public:
     44   explicit FileCloser(int fd) : fd_(fd) {}
     45   ~FileCloser() {
     46     int result = close(fd_);
     47     if (result != 0) {
     48       const string last_error = GetLastSystemError();
     49       SAFTM_LOG(ERROR) << "Error closing file descriptor: " << last_error;
     50     }
     51   }
     52  private:
     53   const int fd_;
     54 
     55   SAFTM_DISALLOW_COPY_AND_ASSIGN(FileCloser);
     56 };
     57 }  // namespace
     58 
     59 MmapHandle MmapFile(const string &filename) {
     60   int fd = open(filename.c_str(), O_RDONLY);
     61 
     62   if (fd < 0) {
     63     const string last_error = GetLastSystemError();
     64     SAFTM_LOG(ERROR) << "Error opening " << filename << ": " << last_error;
     65     return GetErrorMmapHandle();
     66   }
     67 
     68   // Make sure we close fd no matter how we exit this function.  As the man page
     69   // for mmap clearly states: "closing the file descriptor does not unmap the
     70   // region."  Hence, we can close fd as soon as we return from here.
     71   FileCloser file_closer(fd);
     72 
     73   return MmapFile(fd);
     74 }
     75 
     76 MmapHandle MmapFile(int fd) {
     77   // Get file stats to obtain file size.
     78   struct stat sb;
     79   if (fstat(fd, &sb) != 0) {
     80     const string last_error = GetLastSystemError();
     81     SAFTM_LOG(ERROR) << "Unable to stat fd: " << last_error;
     82     return GetErrorMmapHandle();
     83   }
     84   size_t file_size_in_bytes = static_cast<size_t>(sb.st_size);
     85 
     86   // Perform actual mmap.
     87   void *mmap_addr = mmap(
     88 
     89       // Let system pick address for mmapp-ed data.
     90       nullptr,
     91 
     92       // Mmap all bytes from the file.
     93       file_size_in_bytes,
     94 
     95       // One can read / write the mapped data (but see MAP_PRIVATE below).
     96       // Normally, we expect only to read it, but in the future, we may want to
     97       // write it, to fix e.g., endianness differences.
     98       PROT_READ | PROT_WRITE,
     99 
    100       // Updates to mmaped data are *not* propagated to actual file.
    101       // AFAIK(salcianu) that's anyway not possible on Android.
    102       MAP_PRIVATE,
    103 
    104       // Descriptor of file to mmap.
    105       fd,
    106 
    107       // Map bytes right from the beginning of the file.  This, and
    108       // file_size_in_bytes (2nd argument) means we map all bytes from the file.
    109       0);
    110   if (mmap_addr == MAP_FAILED) {
    111     const string last_error = GetLastSystemError();
    112     SAFTM_LOG(ERROR) << "Error while mmapping: " << last_error;
    113     return GetErrorMmapHandle();
    114   }
    115 
    116   return MmapHandle(mmap_addr, file_size_in_bytes);
    117 }
    118 
    119 bool Unmap(MmapHandle mmap_handle) {
    120   if (!mmap_handle.ok()) {
    121     // Unmapping something that hasn't been mapped is trivially successful.
    122     return true;
    123   }
    124   if (munmap(mmap_handle.start(), mmap_handle.num_bytes()) != 0) {
    125     const string last_error = GetLastSystemError();
    126     SAFTM_LOG(ERROR) << "Error during Unmap / munmap: " << last_error;
    127     return false;
    128   }
    129   return true;
    130 }
    131 
    132 }  // namespace mobile
    133 }  // namespace nlp_saft
    134