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