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