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