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