#include <security_filedb/AtomicFile.h>
#include <CommonCrypto/CommonDigest.h>
#include <security_cdsa_utilities/cssmerrors.h>
#include <Security/cssm.h>
#include <Security/SecRandom.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <copyfile.h>
#include <sandbox.h>
#include <set>
#include <assert.h>
#define kAtomicFileMaxBlockSize INT_MAX
AtomicFile::AtomicFile(const std::string &inPath) :
mPath(inPath)
{
pathSplit(inPath, mDir, mFile);
if (mDir.length() == 0)
{
const char* buffer = getwd(NULL);
mDir = buffer;
free((void*) buffer);
}
mDir += '/';
struct statfs info;
int result = statfs(mDir.c_str(), &info);
if (result == -1) {
mIsLocalFileSystem = false; }
else
{
mIsLocalFileSystem = (info.f_flags & MNT_LOCAL) != 0;
if (mIsLocalFileSystem)
{
CC_SHA1_CTX ctx;
CC_SHA1_Init(&ctx);
CC_SHA1_Update(&ctx, (const void*) mFile.c_str(), (CC_LONG)mFile.length());
u_int8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(digest, &ctx);
u_int32_t hash = (digest[0] << 24) | (digest[1] << 16) | (digest[2] << 8) | digest[3];
char buffer[256];
sprintf(buffer, "%08X", hash);
mLockFilePath = mDir + ".fl" + buffer;
}
}
}
AtomicFile::~AtomicFile()
{
}
void
AtomicFile::performDelete()
{
AtomicLockedFile lock(*this);
if (::unlink(mPath.c_str()) != 0)
{
int error = errno;
secinfo("atomicfile", "unlink %s: %s", mPath.c_str(), strerror(error));
if (error == ENOENT)
CssmError::throwMe(CSSMERR_DL_DATASTORE_DOESNOT_EXIST);
else
UnixError::throwMe(error);
}
::unlink(mLockFilePath.c_str());
}
void
AtomicFile::rename(const std::string &inNewPath)
{
const char *path = mPath.c_str();
const char *newPath = inNewPath.c_str();
AtomicLockedFile lock(*this);
if (::rename(path, newPath) != 0)
{
int error = errno;
secinfo("atomicfile", "rename(%s, %s): %s", path, newPath, strerror(error));
UnixError::throwMe(error);
}
}
RefPointer<AtomicTempFile>
AtomicFile::create(mode_t mode)
{
const char *path = mPath.c_str();
mkpath(mDir);
RefPointer<AtomicLockedFile> lock(new AtomicLockedFile(*this));
int fileRef = ropen(path, O_WRONLY|O_CREAT|O_EXCL, mode);
if (fileRef == -1)
{
int error = errno;
secinfo("atomicfile", "open %s: %s", path, strerror(error));
if (error == EACCES)
CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED);
else if (error == EEXIST)
CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS);
else
UnixError::throwMe(error);
}
rclose(fileRef);
try
{
RefPointer<AtomicTempFile> temp(new AtomicTempFile(*this, lock, mode));
secinfo("atomicfile", "%p created %s", this, path);
return temp;
}
catch (...)
{
if (::unlink(path) == -1)
{
secnotice("atomicfile", "unlink %s: %s", path, strerror(errno));
}
throw;
}
}
RefPointer<AtomicTempFile>
AtomicFile::write()
{
RefPointer<AtomicLockedFile> lock(new AtomicLockedFile(*this));
return new AtomicTempFile(*this, lock);
}
RefPointer<AtomicBufferedFile>
AtomicFile::read()
{
return new AtomicBufferedFile(mPath, mIsLocalFileSystem);
}
mode_t
AtomicFile::mode() const
{
const char *path = mPath.c_str();
struct stat st;
if (::stat(path, &st) == -1)
{
int error = errno;
secinfo("atomicfile", "stat %s: %s", path, strerror(error));
UnixError::throwMe(error);
}
return st.st_mode;
}
void
AtomicFile::pathSplit(const std::string &inFull, std::string &outDir, std::string &outFile)
{
std::string::size_type slash, len = inFull.size();
slash = inFull.rfind('/');
if (slash == std::string::npos)
{
outDir = "";
outFile = inFull;
}
else if (slash + 1 == len)
{
outDir = inFull;
outFile = "";
}
else
{
outDir = inFull.substr(0, slash + 1);
outFile = inFull.substr(slash + 1, len);
}
}
void
AtomicFile::mkpath(const std::string &inDir, mode_t mode)
{
struct stat st;
int result = stat(inDir.c_str(), &st);
if (result == 0) {
if ((st.st_mode & S_IFDIR) == 0)
{
syslog(LOG_ALERT, "Needed a directory at %s, but the file that was there was not one.\n", inDir.c_str());
UnixError::throwMe(ENOTDIR);
}
}
else
{
result = mkpath_np(inDir.c_str(), 0777); if (result != 0)
{
UnixError::throwMe(result);
}
}
result = stat(inDir.c_str(), &st);
if (result != 0)
{
UnixError::throwMe(errno);
}
if ((st.st_mode & S_IFDIR) == 0)
{
syslog(LOG_ALERT, "Failed to create a directory when we asked for one to be created at %s\n", inDir.c_str());
UnixError::throwMe(ENOTDIR);
}
}
int
AtomicFile::ropen(const char *const name, int flags, mode_t mode)
{
bool isCreate = (flags & O_CREAT) != 0;
bool checkForRead = false;
bool checkForWrite = false;
int fd, tries_left = 4 ;
if (!isCreate)
{
switch (flags & O_ACCMODE)
{
case O_RDONLY:
checkForRead = true;
break;
case O_WRONLY:
checkForWrite = true;
break;
case O_RDWR:
checkForRead = true;
checkForWrite = true;
break;
}
if (checkForRead)
{
int result = sandbox_check(getpid(), "file-read-data", (sandbox_filter_type) (SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT), name);
if (result != 0)
{
secdebug("atomicfile", "sandboxing rejected read access to %s", name);
return -1;
}
}
if (checkForWrite)
{
int result = sandbox_check(getpid(), "file-write-data", (sandbox_filter_type) (SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT), name);
if (result != 0)
{
secdebug("atomicfile", "sandboxing rejected write access to %s", name);
return -1;
}
}
}
do
{
fd = ::open(name, flags, mode);
} while (fd < 0 && (errno == EINTR || (errno == ENFILE && --tries_left >= 0)));
return fd;
}
int
AtomicFile::rclose(int fd)
{
int result;
do
{
result = ::close(fd);
} while(result && errno == EINTR);
return result;
}
AtomicBufferedFile::AtomicBufferedFile(const std::string &inPath, bool isLocal) :
mPath(inPath),
mFileRef(-1),
mBuffer(NULL),
mLength(0)
{
}
AtomicBufferedFile::~AtomicBufferedFile()
{
if (mFileRef >= 0)
{
__unused int rv = AtomicFile::rclose(mFileRef);
assert(rv == 0);
secinfo("atomicfile", "%p closed %s", this, mPath.c_str());
}
if (mBuffer)
{
secinfo("atomicfile", "%p free %s buffer %p", this, mPath.c_str(), mBuffer);
unloadBuffer();
}
}
off_t
AtomicBufferedFile::open()
{
const char *path = mPath.c_str();
if (mFileRef >= 0)
{
secinfo("atomicfile", "open %s: already open, closing and reopening", path);
close();
}
mFileRef = AtomicFile::ropen(path, O_RDONLY, 0);
if (mFileRef == -1)
{
int error = errno;
secinfo("atomicfile", "open %s: %s", path, strerror(error));
if (error == ENOENT)
CssmError::throwMe(CSSMERR_DL_DATASTORE_DOESNOT_EXIST);
else if (error == EACCES)
CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED);
else
UnixError::throwMe(error);
}
struct stat st;
int result = fstat(mFileRef, &st);
if (result == 0)
{
mLength = st.st_size;
}
else
{
int error = errno;
secinfo("atomicfile", "lseek(%s, END): %s", path, strerror(error));
AtomicFile::rclose(mFileRef);
mFileRef = -1;
UnixError::throwMe(error);
}
secinfo("atomicfile", "%p opened %s: %qd bytes", this, path, mLength);
return mLength;
}
void
AtomicBufferedFile::unloadBuffer()
{
if(mBuffer) {
delete [] mBuffer;
mBuffer = NULL;
}
}
void
AtomicBufferedFile::loadBuffer()
{
mBuffer = new uint8[(size_t) mLength];
if(lseek(mFileRef, 0, SEEK_SET) < 0) {
int error = errno;
secinfo("atomicfile", "lseek(%s, BEGINNING): %s", mPath.c_str(), strerror(error));
UnixError::throwMe(error);
}
ssize_t pos = 0;
ssize_t bytesToRead = (ssize_t)mLength;
while (bytesToRead > 0)
{
ssize_t bytesRead = ::read(mFileRef, mBuffer + pos, bytesToRead);
if (bytesRead == -1)
{
if (errno != EINTR)
{
int error = errno;
secinfo("atomicfile", "read(%s, %zd): %s", mPath.c_str(), bytesToRead, strerror(error));
AtomicFile::rclose(mFileRef);
mFileRef = -1;
UnixError::throwMe(error);
}
}
else
{
bytesToRead -= bytesRead;
pos += bytesRead;
}
}
}
const uint8 *
AtomicBufferedFile::read(off_t inOffset, off_t inLength, off_t &outLength)
{
if (mFileRef < 0)
{
secinfo("atomicfile", "read %s: file yet not opened, opening", mPath.c_str());
open();
}
off_t bytesLeft = inLength;
if (mBuffer)
{
secinfo("atomicfile", "%p free %s buffer %p", this, mPath.c_str(), mBuffer);
unloadBuffer();
}
loadBuffer();
secinfo("atomicfile", "%p allocated %s buffer %p size %qd", this, mPath.c_str(), mBuffer, bytesLeft);
off_t maxEnd = inOffset + inLength;
if (maxEnd > mLength)
{
maxEnd = mLength;
}
outLength = maxEnd - inOffset;
return mBuffer + inOffset;
}
void
AtomicBufferedFile::close()
{
if (mFileRef < 0)
{
secinfo("atomicfile", "close %s: already closed", mPath.c_str());
}
else
{
int result = AtomicFile::rclose(mFileRef);
mFileRef = -1;
if (result == -1)
{
int error = errno;
secnotice("atomicfile", "close %s: %s", mPath.c_str(), strerror(errno));
UnixError::throwMe(error);
}
secinfo("atomicfile", "%p closed %s", this, mPath.c_str());
}
}
AtomicTempFile::AtomicTempFile(AtomicFile &inFile, const RefPointer<AtomicLockedFile> &inLockedFile, mode_t mode) :
mFile(inFile),
mLockedFile(inLockedFile),
mCreating(true)
{
create(mode);
}
AtomicTempFile::AtomicTempFile(AtomicFile &inFile, const RefPointer<AtomicLockedFile> &inLockedFile) :
mFile(inFile),
mLockedFile(inLockedFile),
mCreating(false)
{
create(mFile.mode());
}
AtomicTempFile::~AtomicTempFile()
{
if (mFileRef >= 0)
rollback();
}
void
AtomicTempFile::create(mode_t mode)
{
string dir = mFile.dir();
int i = (int)dir.length() - 1;
while (i >= 0 && dir[i] == '/')
{
i -= 1;
}
i += 1;
const char* temp = _amkrtemp((dir.substr(0, i) + "/" + mFile.file()).c_str());
if (temp == NULL)
{
UnixError::throwMe(errno);
}
mPath = temp;
free((void*) temp);
const char *path = mPath.c_str();
mFileRef = AtomicFile::ropen(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
if (mFileRef == -1)
{
int error = errno;
secnotice("atomicfile", "create %s: %s", path, strerror(error));
if (error == EACCES)
CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED);
else
UnixError::throwMe(error);
}
if (!mCreating)
{
if (::fchmod(mFileRef, mode))
{
int error = errno;
secnotice("atomicfile", "fchmod %s: %s", path, strerror(error));
UnixError::throwMe(error);
}
}
secinfo("atomicfile", "%p created %s", this, path);
}
void
AtomicTempFile::write(AtomicFile::OffsetType inOffsetType, off_t inOffset, const uint32 inData)
{
uint32 aData = htonl(inData);
write(inOffsetType, inOffset, reinterpret_cast<uint8 *>(&aData), sizeof(aData));
}
void
AtomicTempFile::write(AtomicFile::OffsetType inOffsetType, off_t inOffset,
const uint32 *inData, uint32 inCount)
{
#ifdef HOST_LONG_IS_NETWORK_LONG
const uint32 *aBuffer = inData;
#else
auto_array<uint32> aBuffer(inCount);
for (uint32 i = 0; i < inCount; i++)
aBuffer.get()[i] = htonl(inData[i]);
#endif
write(inOffsetType, inOffset, reinterpret_cast<const uint8 *>(aBuffer.get()),
inCount * sizeof(*inData));
}
void
AtomicTempFile::write(AtomicFile::OffsetType inOffsetType, off_t inOffset, const uint8 *inData, size_t inLength)
{
off_t pos;
if (inOffsetType == AtomicFile::FromEnd)
{
pos = ::lseek(mFileRef, 0, SEEK_END);
if (pos == -1)
{
int error = errno;
secnotice("atomicfile", "lseek(%s, %qd): %s", mPath.c_str(), inOffset, strerror(error));
UnixError::throwMe(error);
}
}
else if (inOffsetType == AtomicFile::FromStart)
pos = inOffset;
else
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
off_t bytesLeft = inLength;
const uint8 *ptr = inData;
while (bytesLeft)
{
size_t toWrite = bytesLeft > kAtomicFileMaxBlockSize ? kAtomicFileMaxBlockSize : size_t(bytesLeft);
ssize_t bytesWritten = ::pwrite(mFileRef, ptr, toWrite, pos);
if (bytesWritten == -1)
{
int error = errno;
if (error == EINTR)
{
secnotice("atomicfile", "write %s: interrupted, retrying", mPath.c_str());
continue;
}
secnotice("atomicfile", "write %s: %s", mPath.c_str(), strerror(error));
UnixError::throwMe(error);
}
if (bytesWritten == 0)
{
secnotice("atomicfile", "write %s: 0 bytes written", mPath.c_str());
CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
}
secdebug("atomicfile", "%p wrote %s %ld bytes from %p", this, mPath.c_str(), bytesWritten, ptr);
bytesLeft -= bytesWritten;
ptr += bytesWritten;
pos += bytesWritten;
}
}
void
AtomicTempFile::fsync()
{
if (mFileRef < 0)
{
secnotice("atomicfile", "fsync %s: already closed", mPath.c_str());
}
else
{
int result;
do
{
result = ::fsync(mFileRef);
} while (result && errno == EINTR);
if (result == -1)
{
int error = errno;
secnotice("atomicfile", "fsync %s: %s", mPath.c_str(), strerror(errno));
UnixError::throwMe(error);
}
secinfo("atomicfile", "%p fsynced %s", this, mPath.c_str());
}
}
void
AtomicTempFile::close()
{
if (mFileRef < 0)
{
secnotice("atomicfile", "close %s: already closed", mPath.c_str());
}
else
{
int result = AtomicFile::rclose(mFileRef);
mFileRef = -1;
if (result == -1)
{
int error = errno;
secnotice("atomicfile", "close %s: %s", mPath.c_str(), strerror(errno));
UnixError::throwMe(error);
}
secinfo("atomicfile", "%p closed %s", this, mPath.c_str());
}
}
void
AtomicTempFile::commit()
{
try
{
fsync();
close();
const char *oldPath = mPath.c_str();
const char *newPath = mFile.path().c_str();
copyfile_state_t s;
s = copyfile_state_alloc();
if(copyfile(newPath, oldPath, s, COPYFILE_SECURITY | COPYFILE_NOFOLLOW) == -1) secnotice("atomicfile", "copyfile (%s, %s): %s", oldPath, newPath, strerror(errno));
copyfile_state_free(s);
::utimes(oldPath, NULL);
if (::rename(oldPath, newPath) == -1)
{
int error = errno;
secnotice("atomicfile", "rename (%s, %s): %s", oldPath, newPath, strerror(errno));
UnixError::throwMe(error);
}
secnotice("atomicfile", "%p commited %s to %s", this, oldPath, newPath);
mLockedFile = NULL;
}
catch (...)
{
rollback();
throw;
}
}
void
AtomicTempFile::rollback() throw()
{
if (mFileRef >= 0)
{
AtomicFile::rclose(mFileRef);
mFileRef = -1;
}
const char *path = mPath.c_str();
if (::unlink(path) == -1)
{
secnotice("atomicfile", "unlink %s: %s", path, strerror(errno));
}
if (mCreating)
{
const char *path = mFile.path().c_str();
if (::unlink(path) == -1)
{
secnotice("atomicfile", "unlink %s: %s", path, strerror(errno));
}
}
}
FileLocker::~FileLocker()
{
}
LocalFileLocker::LocalFileLocker(AtomicFile &inFile) :
mPath(inFile.lockFileName())
{
}
LocalFileLocker::~LocalFileLocker()
{
}
#ifndef NDEBUG
static double GetTime()
{
struct timeval t;
gettimeofday(&t, NULL);
return ((double) t.tv_sec) + ((double) t.tv_usec) / 1000000.0;
}
#endif
void
LocalFileLocker::lock(mode_t mode)
{
struct stat st;
do
{
mLockFile = open(mPath.c_str(), O_RDONLY | O_CREAT, mode);
if (mLockFile == -1)
{
UnixError::throwMe(errno);
}
IFDEBUG(double startTime = GetTime());
int result = flock(mLockFile, LOCK_EX);
IFDEBUG(double endTime = GetTime());
IFDEBUG(secnotice("atomictime", "Waited %.4f milliseconds for file lock", (endTime - startTime) * 1000.0));
if (result == -1)
{
UnixError::throwMe(errno);
}
result = fstat(mLockFile, &st);
if (result == -1)
{
UnixError::throwMe(errno);
}
if (st.st_nlink == 0) {
close(mLockFile);
}
} while (st.st_nlink == 0);
}
void
LocalFileLocker::unlock()
{
flock(mLockFile, LOCK_UN);
close(mLockFile);
}
NetworkFileLocker::NetworkFileLocker(AtomicFile &inFile) :
mDir(inFile.dir()),
mPath(inFile.dir() + "lck~" + inFile.file())
{
}
NetworkFileLocker::~NetworkFileLocker()
{
}
std::string
NetworkFileLocker::unique(mode_t mode)
{
static const int randomPart = 16;
std::string::size_type dirSize = mDir.size();
std::string fullname(dirSize + randomPart + 2, '\0');
fullname.replace(0, dirSize, mDir);
fullname[dirSize] = '~';
char buf[randomPart];
struct stat filebuf;
int result, fd = -1;
for (int retries = 0; retries < 10; ++retries)
{
MacOSError::check(SecRandomCopyBytes(kSecRandomDefault, randomPart, buf));
for (int ix = 0; ix < randomPart; ++ix)
{
char ch = buf[ix] & 0x3f;
fullname[ix + dirSize + 1] = ch +
( ch < 26 ? 'A'
: ch < 26 + 26 ? 'a' - 26
: ch < 26 + 26 + 10 ? '0' - 26 - 26
: ch == 26 + 26 + 10 ? '-' - 26 - 26 - 10
: '_' - 26 - 26 - 11);
}
result = lstat(fullname.c_str(), &filebuf);
if (result && errno == ENAMETOOLONG)
{
do
fullname.erase(fullname.end() - 1);
while((result = lstat(fullname.c_str(), &filebuf)) && errno == ENAMETOOLONG && fullname.size() > dirSize + 8);
}
if (result && errno == ENOENT)
{
fd = AtomicFile::ropen(fullname.c_str(), O_WRONLY|O_CREAT|O_EXCL, mode);
if (fd >= 0 || errno != EEXIST)
break;
}
}
if (fd < 0)
{
int error = errno;
::syslog(LOG_ERR, "Couldn't create temp file %s: %s", fullname.c_str(), strerror(error));
secnotice("atomicfile", "Couldn't create temp file %s: %s", fullname.c_str(), strerror(error));
UnixError::throwMe(error);
}
write(fd, "0", 1);
AtomicFile::rclose(fd);
return fullname;
}
int
NetworkFileLocker::rlink(const char *const old, const char *const newn, struct stat &sto)
{
int result = ::link(old,newn);
if (result)
{
int serrno = errno;
if (::lstat(old, &sto) == 0)
{
struct stat stn;
if (::lstat(newn, &stn) == 0
&& sto.st_dev == stn.st_dev
&& sto.st_ino == stn.st_ino
&& sto.st_uid == stn.st_uid
&& sto.st_gid == stn.st_gid
&& !S_ISLNK(sto.st_mode))
{
return 0;
}
else
result = 1;
}
errno = serrno;
}
return result;
}
int
NetworkFileLocker::myrename(const char *const old, const char *const newn)
{
struct stat stbuf;
int fd = -1;
int ret;
ret = rlink(old, newn, stbuf);
if (ret > 0)
{
if (stbuf.st_nlink < 2 && (errno == EXDEV || errno == ENOTSUP))
{
fd = AtomicFile::ropen(newn, O_WRONLY|O_CREAT|O_EXCL, stbuf.st_mode);
if (fd >= 0)
ret = 0;
}
}
int serrno = errno;
::unlink(old);
if (fd > 0)
AtomicFile::rclose(fd);
errno = serrno;
return ret;
}
int
NetworkFileLocker::xcreat(const char *const name, mode_t mode, time_t &tim)
{
std::string uniqueName = unique(mode);
const char *uniquePath = uniqueName.c_str();
struct stat stbuf;
stat(uniquePath, &stbuf);
tim = stbuf.st_mtime;
return myrename(uniquePath, name);
}
void
NetworkFileLocker::lock(mode_t mode)
{
const char *path = mPath.c_str();
bool triedforce = false;
struct stat stbuf;
time_t t, locktimeout = 1024;
bool doSyslog = false;
bool failed = false;
int retries = 0;
while (!failed)
{
if (doSyslog)
::syslog(LOG_NOTICE, "Locking %s", path);
else
doSyslog = true;
secinfo("atomicfile", "Locking %s", path);
if (!xcreat(path, mode, t))
{
break;
}
switch(errno)
{
case EEXIST:
if (!lstat(path, &stbuf) && stbuf.st_size <= 16 && locktimeout
&& !lstat(path, &stbuf) && locktimeout < t - stbuf.st_mtime)
{
if (triedforce)
{
failed = true;
break;
}
else if (S_ISDIR(stbuf.st_mode) || ::unlink(path))
{
triedforce=true;
::syslog(LOG_ERR, "Forced unlock denied on %s", path);
secnotice("atomicfile", "Forced unlock denied on %s", path);
}
else
{
::syslog(LOG_ERR, "Forcing lock on %s", path);
secnotice("atomicfile", "Forcing lock on %s", path);
sleep(16 );
break;
}
}
else
triedforce = false;
retries = 0;
usleep(250000);
break;
case ENOSPC:
#ifdef EDQUOT
case EDQUOT:
#endif
case ENOENT:
case ENOTDIR:
case EIO:
if(++retries < (256 + 1))
usleep(250000);
else
failed = true;
break;
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
if (mPath.size() > mDir.size() + 8)
{
secnotice("atomicfile", "Truncating %s and retrying lock", path);
mPath.erase(mPath.end() - 1);
path = mPath.c_str();
retries = 0;
break;
}
#endif
default:
failed = true;
break;
}
}
if (failed)
{
int error = errno;
::syslog(LOG_ERR, "Lock failure on %s: %s", path, strerror(error));
secnotice("atomicfile", "Lock failure on %s: %s", path, strerror(error));
UnixError::throwMe(error);
}
}
void
NetworkFileLocker::unlock()
{
const char *path = mPath.c_str();
if (::unlink(path) == -1)
{
secnotice("atomicfile", "unlink %s: %s", path, strerror(errno));
}
}
AtomicLockedFile::AtomicLockedFile(AtomicFile &inFile)
{
if (inFile.isOnLocalFileSystem())
{
mFileLocker = new LocalFileLocker(inFile);
}
else
{
mFileLocker = new NetworkFileLocker(inFile);
}
lock();
}
AtomicLockedFile::~AtomicLockedFile()
{
unlock();
delete mFileLocker;
}
void
AtomicLockedFile::lock(mode_t mode)
{
mFileLocker->lock(mode);
}
void AtomicLockedFile::unlock() throw()
{
mFileLocker->unlock();
}
#undef kAtomicFileMaxBlockSize