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