FileBase.cpp revision 331310e1f3f86a795f78e42b3f03558a43829f09
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 31using namespace bcc; 32 33FileBase::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 65FileBase::~FileBase() { 66 close(); 67} 68 69bool 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 88bool 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 117void FileBase::detectError() { 118 // Read error from errno. 119 mError.assign(errno, llvm::posix_category()); 120} 121 122bool 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 197void 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 213android::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 234size_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 252off_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 268off_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 284void 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