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 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 40int flock(int fd, int operation) { 41 return 0; 42} 43#endif // _WIN32 44 45FileBase::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 77FileBase::~FileBase() { 78 close(); 79} 80 81bool 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 100bool 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 129void FileBase::detectError() { 130 // Read error from errno. 131 mError.assign(errno, llvm::posix_category()); 132} 133 134bool 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.assign(llvm::errc::invalid_argument, llvm::posix_category()); 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 209void 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 225android::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.assign(llvm::errc::not_enough_memory, llvm::system_category()); 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 246size_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 264off_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 280off_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 296void 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