You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
5.4 KiB
239 lines
5.4 KiB
/*
|
|
* Copyright 2012, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "FileBase.h"
|
|
#include "Log.h"
|
|
|
|
#include <sys/file.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#include <new>
|
|
|
|
using namespace bcc;
|
|
|
|
#ifdef _WIN32
|
|
// TODO: Fix flock usage under windows
|
|
#define LOCK_SH 0
|
|
#define LOCK_EX 0
|
|
#define LOCK_NB 0
|
|
#define LOCK_UN 0
|
|
|
|
int flock(int fd, int operation) {
|
|
return 0;
|
|
}
|
|
#endif // _WIN32
|
|
|
|
FileBase::FileBase(const std::string &pFilename,
|
|
unsigned pOpenFlags,
|
|
unsigned pFlags)
|
|
: mFD(-1),
|
|
mError(),
|
|
mName(pFilename), mOpenFlags(pOpenFlags),
|
|
mShouldUnlock(false),
|
|
mShouldDelete(false) {
|
|
// Process pFlags
|
|
#ifdef O_BINARY
|
|
if (pFlags & kBinary) {
|
|
mOpenFlags |= O_BINARY;
|
|
}
|
|
#endif
|
|
if (pFlags & kTruncate) {
|
|
mOpenFlags |= O_TRUNC;
|
|
}
|
|
|
|
if (pFlags & kAppend) {
|
|
mOpenFlags |= O_APPEND;
|
|
}
|
|
|
|
if (pFlags & kDeleteOnClose) {
|
|
mShouldDelete = true;
|
|
}
|
|
|
|
// Open the file.
|
|
open();
|
|
|
|
return;
|
|
}
|
|
|
|
FileBase::~FileBase() {
|
|
close();
|
|
}
|
|
|
|
bool FileBase::open() {
|
|
do {
|
|
// FIXME: Hard-coded permissions (0644) for newly created file should be
|
|
// removed and provide a way to let the user configure the value.
|
|
mFD = ::open(mName.c_str(), mOpenFlags, 0644);
|
|
if (mFD > 0) {
|
|
return true;
|
|
}
|
|
|
|
// Some errors occurred ...
|
|
if (errno != EINTR) {
|
|
detectError();
|
|
return false;
|
|
}
|
|
} while (true);
|
|
// unreachable
|
|
}
|
|
|
|
|
|
bool FileBase::checkFileIntegrity() {
|
|
// Check the file integrity by examining whether the inode referring to the mFD
|
|
// and to the file mName are the same.
|
|
struct stat fd_stat, file_stat;
|
|
|
|
// Get the file status of file descriptor mFD.
|
|
do {
|
|
if (::fstat(mFD, &fd_stat) == 0) {
|
|
break;
|
|
} else if (errno != EINTR) {
|
|
detectError();
|
|
return false;
|
|
}
|
|
} while (true);
|
|
|
|
// Get the file status of file mName.
|
|
do {
|
|
if (::stat(mName.c_str(), &file_stat) == 0) {
|
|
break;
|
|
} else if (errno != EINTR) {
|
|
detectError();
|
|
return false;
|
|
}
|
|
} while (true);
|
|
|
|
return ((fd_stat.st_dev == file_stat.st_dev) &&
|
|
(fd_stat.st_ino == file_stat.st_ino));
|
|
}
|
|
|
|
void FileBase::detectError() {
|
|
// Read error from errno.
|
|
mError.assign(errno, std::generic_category());
|
|
}
|
|
|
|
bool FileBase::lock(enum LockModeEnum pMode,
|
|
bool pNonblocking,
|
|
unsigned pMaxRetry,
|
|
useconds_t pRetryInterval) {
|
|
int lock_operation;
|
|
unsigned retry = 0;
|
|
|
|
// Check the state.
|
|
if ((mFD < 0) || hasError()) {
|
|
return false;
|
|
}
|
|
|
|
// Return immediately if it's already locked.
|
|
if (mShouldUnlock) {
|
|
return true;
|
|
}
|
|
|
|
// Determine the lock operation (2nd argument) to the flock().
|
|
if (pMode == kReadLock) {
|
|
lock_operation = LOCK_SH;
|
|
} else if (pMode == kWriteLock) {
|
|
lock_operation = LOCK_EX;
|
|
} else {
|
|
mError = std::make_error_code(std::errc::invalid_argument);
|
|
return false;
|
|
}
|
|
|
|
if (pNonblocking) {
|
|
lock_operation |= LOCK_NB;
|
|
}
|
|
|
|
do {
|
|
if (::flock(mFD, lock_operation) == 0) {
|
|
mShouldUnlock = true;
|
|
// Here we got a lock but we need to check whether the mFD still
|
|
// "represents" the filename (mName) we opened in the contructor. This
|
|
// check may failed when another process deleted the original file mFD
|
|
// mapped when we were trying to obtain the lock on the file.
|
|
if (!checkFileIntegrity()) {
|
|
if (hasError() || !reopen()) {
|
|
// Error occurred when check the file integrity or re-open the file.
|
|
return false;
|
|
} else {
|
|
// Wait a while before the next try.
|
|
::usleep(pRetryInterval);
|
|
retry++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// flock() was not performed successfully. Check the errno to see whether
|
|
// it's retry-able.
|
|
if (errno == EINTR) {
|
|
// flock() was interrupted by delivery of a signal. Restart without
|
|
// decrement the retry counter.
|
|
continue;
|
|
} else if (errno == EWOULDBLOCK) {
|
|
// The file descriptor was locked by others, wait for a while before next
|
|
// retry.
|
|
retry++;
|
|
::usleep(pRetryInterval);
|
|
} else {
|
|
// There's a fatal error occurs when perform flock(). Return immediately
|
|
// without further retry.
|
|
detectError();
|
|
return false;
|
|
}
|
|
} while (retry <= pMaxRetry);
|
|
|
|
return false;
|
|
}
|
|
|
|
void FileBase::unlock() {
|
|
if (mFD < 0) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
if (::flock(mFD, LOCK_UN) == 0) {
|
|
mShouldUnlock = false;
|
|
return;
|
|
}
|
|
} while (errno == EINTR);
|
|
|
|
detectError();
|
|
return;
|
|
}
|
|
|
|
void FileBase::close() {
|
|
if (mShouldUnlock) {
|
|
unlock();
|
|
mShouldUnlock = false;
|
|
}
|
|
if (mFD > 0) {
|
|
::close(mFD);
|
|
mFD = -1;
|
|
}
|
|
if (mShouldDelete) {
|
|
int res = ::remove(mName.c_str());
|
|
if (res != 0) {
|
|
ALOGE("Failed to remove file: %s - %s", mName.c_str(), ::strerror(res));
|
|
}
|
|
}
|
|
return;
|
|
}
|