Home | History | Annotate | Download | only in Support
      1 /*
      2  * Copyright 2012, 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 "bcc/Support/FileBase.h"
     18 
     19 #include <sys/file.h>
     20 #include <sys/stat.h>
     21 #include <unistd.h>
     22 
     23 #include <cerrno>
     24 #include <new>
     25 
     26 #include <utils/FileMap.h>
     27 
     28 using namespace bcc;
     29 
     30 FileBase::FileBase(const std::string &pFilename,
     31                    unsigned pOpenFlags,
     32                    unsigned pFlags)
     33   : mFD(-1),
     34     mError(),
     35     mName(pFilename), mOpenFlags(pOpenFlags),
     36     mShouldUnlock(false) {
     37   // Process pFlags
     38 #ifdef O_BINARY
     39   if (pFlags & kBinary) {
     40     mOpenFlags |= O_BINARY;
     41   }
     42 #endif
     43   if (pFlags & kTruncate) {
     44     mOpenFlags |= O_TRUNC;
     45   }
     46 
     47   if (pFlags & kAppend) {
     48     mOpenFlags |= O_APPEND;
     49   }
     50 
     51   // Open the file.
     52   open();
     53 
     54   return;
     55 }
     56 
     57 FileBase::~FileBase() {
     58   close();
     59 }
     60 
     61 bool FileBase::open() {
     62   do {
     63     // FIXME: Hard-coded permissions (0644) for newly created file should be
     64     //        removed and provide a way to let the user configure the value.
     65     mFD = ::open(mName.c_str(), mOpenFlags, 0644);
     66     if (mFD > 0) {
     67       return true;
     68     }
     69 
     70     // Some errors occurred ...
     71     if (errno != EINTR) {
     72       detectError();
     73       return false;
     74     }
     75   } while (true);
     76   // unreachable
     77 }
     78 
     79 
     80 bool FileBase::checkFileIntegrity() {
     81   // Check the file integrity by examining whether the inode referring to the mFD
     82   // and to the file mName are the same.
     83   struct stat fd_stat, file_stat;
     84 
     85   // Get the file status of file descriptor mFD.
     86   do {
     87     if (::fstat(mFD, &fd_stat) == 0) {
     88       break;
     89     } else if (errno != EINTR) {
     90       detectError();
     91       return false;
     92     }
     93   } while (true);
     94 
     95   // Get the file status of file mName.
     96   do {
     97     if (::stat(mName.c_str(), &file_stat) == 0) {
     98       break;
     99     } else if (errno != EINTR) {
    100       detectError();
    101       return false;
    102     }
    103   } while (true);
    104 
    105   return ((fd_stat.st_dev == file_stat.st_dev) &&
    106           (fd_stat.st_ino == file_stat.st_ino));
    107 }
    108 
    109 void FileBase::detectError() {
    110   // Read error from errno.
    111   mError.assign(errno, llvm::posix_category());
    112 }
    113 
    114 bool FileBase::lock(enum LockModeEnum pMode,
    115                     bool pNonblocking,
    116                     unsigned pMaxRetry,
    117                     useconds_t pRetryInterval) {
    118   int lock_operation;
    119   unsigned retry = 0;
    120 
    121   // Check the state.
    122   if ((mFD < 0) || hasError()) {
    123     return false;
    124   }
    125 
    126   // Return immediately if it's already locked.
    127   if (mShouldUnlock) {
    128     return true;
    129   }
    130 
    131   // Determine the lock operation (2nd argument) to the flock().
    132   if (pMode == kReadLock) {
    133     lock_operation = LOCK_SH;
    134   } else if (pMode == kWriteLock) {
    135     lock_operation = LOCK_EX;
    136   } else {
    137     mError.assign(llvm::errc::invalid_argument, llvm::posix_category());
    138     return false;
    139   }
    140 
    141   if (pNonblocking) {
    142     lock_operation |= LOCK_NB;
    143   }
    144 
    145   do {
    146     if (::flock(mFD, lock_operation) == 0) {
    147       mShouldUnlock = true;
    148       // Here we got a lock but we need to check whether the mFD still
    149       // "represents" the filename (mName) we opened in the contructor. This
    150       // check may failed when another process deleted the original file mFD
    151       // mapped when we were trying to obtain the lock on the file.
    152       if (!checkFileIntegrity()) {
    153         if (hasError() || !reopen()) {
    154           // Error occurred when check the file integrity or re-open the file.
    155           return false;
    156         } else {
    157           // Wait a while before the next try.
    158           ::usleep(pRetryInterval);
    159           retry++;
    160           continue;
    161         }
    162       }
    163 
    164       return true;
    165     }
    166 
    167     // flock() was not performed successfully. Check the errno to see whether
    168     // it's retry-able.
    169     if (errno == EINTR) {
    170       // flock() was interrupted by delivery of a signal. Restart without
    171       // decrement the retry counter.
    172       continue;
    173     } else if (errno == EWOULDBLOCK) {
    174       // The file descriptor was locked by others, wait for a while before next
    175       // retry.
    176       retry++;
    177       ::usleep(pRetryInterval);
    178     } else {
    179       // There's a fatal error occurs when perform flock(). Return immediately
    180       // without further retry.
    181       detectError();
    182       return false;
    183     }
    184   } while (retry <= pMaxRetry);
    185 
    186   return false;
    187 }
    188 
    189 void FileBase::unlock() {
    190   if (mFD < 0) {
    191     return;
    192   }
    193 
    194   do {
    195     if (::flock(mFD, LOCK_UN) == 0) {
    196       mShouldUnlock = false;
    197       return;
    198     }
    199   } while (errno == EINTR);
    200 
    201   detectError();
    202   return;
    203 }
    204 
    205 android::FileMap *FileBase::createMap(off_t pOffset, size_t pLength,
    206                                       bool pIsReadOnly) {
    207   if (mFD < 0 || hasError()) {
    208     return NULL;
    209   }
    210 
    211   android::FileMap *map = new (std::nothrow) android::FileMap();
    212   if (map == NULL) {
    213     mError.assign(llvm::errc::not_enough_memory, llvm::system_category());
    214     return NULL;
    215   }
    216 
    217   if (!map->create(NULL, mFD, pOffset, pLength, pIsReadOnly)) {
    218     detectError();
    219     map->release();
    220     return NULL;
    221   }
    222 
    223   return map;
    224 }
    225 
    226 size_t FileBase::getSize() {
    227   if (mFD < 0 || hasError()) {
    228     return static_cast<size_t>(-1);
    229   }
    230 
    231   struct stat file_stat;
    232   do {
    233     if (::fstat(mFD, &file_stat) == 0) {
    234       break;
    235     } else if (errno != EINTR) {
    236       detectError();
    237       return static_cast<size_t>(-1);
    238     }
    239   } while (true);
    240 
    241   return file_stat.st_size;
    242 }
    243 
    244 off_t FileBase::seek(off_t pOffset) {
    245   if ((mFD < 0) || hasError()) {
    246     return static_cast<off_t>(-1);
    247   }
    248 
    249   do {
    250     off_t result = ::lseek(mFD, pOffset, SEEK_SET);
    251     if (result == pOffset) {
    252       return result;
    253     }
    254   } while (errno == EINTR);
    255 
    256   detectError();
    257   return static_cast<off_t>(-1);
    258 }
    259 
    260 off_t FileBase::tell() {
    261   if ((mFD < 0) || hasError()) {
    262     return static_cast<off_t>(-1);
    263   }
    264 
    265   do {
    266     off_t result = ::lseek(mFD, 0, SEEK_CUR);
    267     if (result != static_cast<off_t>(-1)) {
    268       return result;
    269     }
    270   } while (errno == EINTR);
    271 
    272   detectError();
    273   return static_cast<off_t>(-1);
    274 }
    275 
    276 void FileBase::close() {
    277   if (mShouldUnlock) {
    278     unlock();
    279     mShouldUnlock = false;
    280   }
    281   if (mFD > 0) {
    282     ::close(mFD);
    283     mFD = -1;
    284   }
    285   return;
    286 }
    287