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