//#include <err.h>
#include <locale.h>
#include <stdlib.h>
-#include <string.h>
+#include <cstring>
+#include <sys/param.h>
#elif _USE_IO == _USE_IO_MACOS
typedef SInt32 ssize_t;
mWriteFile(nil),
mWriteFilename(mReadFilename + ",") // XXX Do some more work here like resolving symlinks/aliases etc.
{
+ debug("atomicfile", "%p construct name=%s", this, mReadFilename.c_str());
// We only support databases with string names of non-zero length.
if (inDbName.dbLocation() != nil || inDbName.dbName().length() == 0)
CssmError::throwMe(CSSMERR_DL_INVALID_DB_LOCATION);
AtomicFile::~AtomicFile()
{
// Assume there are no more running theads in this object.
+ debug("atomicfile", "%p destroyed", this);
// Try hard to clean up as much as possible.
try
void
AtomicFile::close()
{
+ debug("atomicfile", "%p close", this);
StLock<Mutex> _(mReadLock);
// If we have no read file we have nothing to close.
// If we never had or no longer have an open read file. Open it now.
if (mReadFile == nil)
{
- mReadFile = new OpenFile(mReadFilename, false, false, 0);
+ mReadFile = new OpenFile(mReadFilename, false, false, 0, 0);
mOpenFileMap.insert(OpenFileMap::value_type(mReadFile->versionId(), mReadFile));
}
- // Note that mReadFile->isDirty() might actually return true here, but all that mean is
+ // Note that mReadFile->isDirty() might actually return true here, but all that means is
// that we are looking at data that was commited after we opened the file which might
// happen in a few miliseconds anyway.
// XXX This is a potential infinite loop.
for (;;)
{
- aReadFile = new OpenFile(mReadFilename, true, true, 0);
+ aReadFile = new OpenFile(mReadFilename, true, true, 0, 0);
if (!aReadFile->isDirty())
break;
StLock<Mutex> _(mReadLock);
// Create mReadFilename until the lock has been aquired on a non-dirty file.
- aReadFile = new OpenFile(mReadFilename, false, true, 1);
+ aReadFile = new OpenFile(mReadFilename, false, true, 1, 0666);
// Open mWriteFile for writing.
- mWriteFile = new OpenFile(mWriteFilename, true, false, aReadFile->versionId() + 1);
+ mWriteFile = new OpenFile(mWriteFilename, true, false, aReadFile->versionId() + 1, 0666);
// Insert aReadFile into the map (do this after opening mWriteFile just in case that throws).
mOpenFileMap.insert(OpenFileMap::value_type(-1, aReadFile));
// XXX This is a potential infinite loop.
for (;;)
{
- aReadFile = new OpenFile(mReadFilename, true, true, 0);
+ aReadFile = new OpenFile(mReadFilename, true, true, 0, 0);
if (!aReadFile->isDirty())
break;
StLock<Mutex> _(mReadLock);
// Open mWriteFile for writing.
- mWriteFile = new OpenFile(mWriteFilename, true, false, aReadFile->versionId() + 1);
+ mWriteFile = new OpenFile(mWriteFilename, true, false, aReadFile->versionId() + 1, aReadFile->mode());
// Insert aReadFile into the map (do this after opening mWriteFile just in case that throws).
mOpenFileMap.insert(OpenFileMap::value_type(-1, aReadFile));
AtomicFile::VersionId
AtomicFile::commit()
{
+ debug("atomicfile", "%p commit", this);
StLock<Mutex> _(mReadLock);
if (mWriteFile == nil)
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
// Close all unused files (in particular aOpenFile) and remove them from mOpenFileMap
endWrite();
+ debug("atomicfile", "%p commit done", this);
return aVersionId;
}
catch (...)
unlink(mWriteFilename);
}catch(...) {}
endWrite();
+ debug("atomicfile", "%p commit failed, rethrowing", this);
throw;
}
}
void
AtomicFile::rollback()
{
+ debug("atomicfile", "%p rollback", this);
StLock<Mutex> _(mReadLock);
if (mWriteFile == nil)
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
if (mCreating)
unlink(mReadFilename);
endWrite();
+ debug("atomicfile", "%p rollback complete", this);
}
catch(...)
{
unlink(mWriteFilename);
}catch(...) {}
endWrite();
+ debug("atomicfile", "%p rollback failed, rethrowing", this);
throw;
}
}
// AtomicFile::OpenFile implementation
-AtomicFile::OpenFile::OpenFile(const string &inFilename, bool write, bool lock, VersionId inVersionId) :
+AtomicFile::OpenFile::OpenFile(const string &inFilename, bool write, bool lock, VersionId inVersionId, mode_t mode) :
mUseCount(0),
mVersionId(inVersionId),
mAddress(NULL),
mLength(0)
{
- int flags, mode = 0;
+ int flags;
if (write && lock)
{
flags = O_RDWR;
else if (write && !lock)
{
flags = O_WRONLY|O_CREAT|O_TRUNC;
- mode = 0666;
mState = Write;
}
else if (!write && lock)
{
flags = O_WRONLY|O_CREAT|O_TRUNC|O_EXCL;
- mode = 0666;
mState = Create;
}
else
flags = O_RDONLY;
mState = Read;
}
+ debug("atomicfile", "%p openfile(%s,%s%s,%d,0x%x) -> flags=0x%x, state=%d",
+ this, inFilename.c_str(), write ? "write" : "read", lock ? ",lock" : "",
+ inVersionId, mode, flags, mState);
mFileRef = ::open(inFilename.c_str(), flags, mode);
if (mFileRef == -1)
{
int error = errno;
+ debug("atomicfile", "%p openfile open failed(errno=%d)", this, error);
#if _USE_IO == _USE_IO_POSIX
// Do the obvious error code translations here.
// Now try the open again.
mFileRef = ::open(inFilename.c_str(), flags, mode);
+ debug("atomicfile", "%p openfile reopen %s (%d)",
+ this, (mFileRef == -1) ? "failed" : "ok", errno);
error = mFileRef == -1 ? errno : 0;
if (error == ENOENT)
CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED);
void
AtomicFile::OpenFile::close()
{
+ IFDEBUG(if (mState != Closed) debug("atomicfile", "%p openfile closing(ref=%d)",
+ this, mFileRef));
int error = 0;
if (mAddress != NULL)
{
#if _USE_IO == _USE_IO_POSIX
+ debug("atomicfile", "%p openfile is unmapping %p:%ld", this, mAddress, mLength);
if (::munmap(const_cast<uint8 *>(mAddress), mLength) == -1)
error = errno;
#else
+ debug("atomicfile", "%p openfile deleting %p", this, mAddress);
delete[] mAddress;
#endif
#endif
}
+mode_t
+AtomicFile::OpenFile::mode()
+{
+ struct stat st;
+ if (::fstat(mFileRef, &st) == -1)
+ UnixError::throwMe(errno);
+ return st.st_mode;
+}
+
+
AtomicFile::VersionId
AtomicFile::OpenFile::readVersionId()
{
void
AtomicFile::OpenFile::mkpath(const std::string &inFilename)
{
- char *path = const_cast<char *>(inFilename.c_str()); // @@@ Const_cast is a lie!!!
+ const char *path = inFilename.c_str();
struct stat sb;
- char *slash;
- mode_t dir_mode = (0777 & ~umask(0)) | S_IWUSR | S_IXUSR;
-
- slash = path;
+ char dirPath[MAXPATHLEN];
+ size_t slash = 0;
for (;;)
{
- slash += strspn(slash, "/");
- slash += strcspn(slash, "/");
+ slash += strspn(path + slash, "/");
+ slash += strcspn(path + slash, "/");
- if (*slash == '\0')
+ if (path[slash] == '\0')
break;
- *slash = '\0';
+ if (slash >= MAXPATHLEN)
+ UnixError::throwMe(ENAMETOOLONG);
+ strncpy(dirPath, path, slash);
+ dirPath[slash] = '\0';
- if (stat(path, &sb))
+ if (stat(dirPath, &sb))
{
- if (errno != ENOENT || mkdir(path, dir_mode))
- UnixError::throwMe(errno);
- /* The mkdir() and umask() calls both honor only the low
- nine bits, so if you try to set a mode including the
- sticky, setuid, setgid bits you lose them. So chmod(). */
- if (chmod(path, dir_mode) == -1)
+ if (errno != ENOENT || mkdir(dirPath, 0777))
UnixError::throwMe(errno);
}
else if (!S_ISDIR(sb.st_mode))
CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED); // @@@ Should be is a directory
-
- *slash = '/';
}
}