2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 // AtomicFile.cpp - Description t.b.d.
23 #define _CPP_ATOMICFILE
26 #include <Security/AtomicFile.h>
27 #include <Security/DbName.h>
35 #if _USE_IO == _USE_IO_POSIX
37 #include <sys/types.h>
45 #include <sys/param.h>
47 #elif _USE_IO == _USE_IO_MACOS
48 typedef SInt32 ssize_t
;
53 AtomicFile::AtomicFile(const DbName
&inDbName
) :
55 mReadFilename(inDbName
.dbName()),
57 mWriteFilename(mReadFilename
+ ",") // XXX Do some more work here like resolving symlinks/aliases etc.
59 debug("atomicfile", "%p construct name=%s", this, mReadFilename
.c_str());
60 // We only support databases with string names of non-zero length.
61 if (inDbName
.dbLocation() != nil
|| inDbName
.dbName().length() == 0)
62 CssmError::throwMe(CSSMERR_DL_INVALID_DB_LOCATION
);
65 AtomicFile::~AtomicFile()
67 // Assume there are no more running theads in this object.
68 debug("atomicfile", "%p destroyed", this);
70 // Try hard to clean up as much as possible.
73 // Rollback any pending write.
79 // Close and delete all files in mOpenFileMap
80 for (OpenFileMap::iterator it
= mOpenFileMap
.begin(); it
!= mOpenFileMap
.end(); it
++)
98 debug("atomicfile", "%p close", this);
99 StLock
<Mutex
> _(mReadLock
);
101 // If we have no read file we have nothing to close.
102 if (mReadFile
== nil
)
105 // Remember mReadFile and set it to nil, so that it will be closed after any pending write completes
106 OpenFile
*aOpenFile
= mReadFile
;
109 // If aOpenFile has a zero use count no other thread is currently using it,
110 // so we can safely remove it from the map.
111 if (aOpenFile
->mUseCount
== 0)
113 // Do not close any files (nor remove them from the map) while some thread is writing
114 // since doing so might release the lock we are holding.
115 if (mWriteLock
.tryLock())
117 // Release the write lock immediately since tryLock just aquired it and we don't want to write.
120 // Remove aOpenFile from the map of open files.
121 mOpenFileMap
.erase(aOpenFile
->versionId());
136 AtomicFile::VersionId
137 AtomicFile::enterRead(const uint8
*&outFileAddress
, size_t &outLength
)
139 StLock
<Mutex
> _(mReadLock
);
141 // If we already have a read file check if it is still current.
142 if (mReadFile
!= nil
)
144 if (mReadFile
->isDirty())
146 // Remember mReadFile and set it to nil in case an exception is thrown
147 OpenFile
*aOpenFile
= mReadFile
;
150 // If aOpenFile has a zero use count no other thread is currently using it,
151 // so we can safely remove it from the map.
152 if (aOpenFile
->mUseCount
== 0)
154 // Do not close any files (nor remove them from the map) while some thread is writing
155 // since doing so might release the lock we are holding.
156 if (mWriteLock
.tryLock())
158 // Release the write lock immediately since tryLock just aquired it and we don't want to write.
161 // Remove aOpenFile from the map of open files.
162 mOpenFileMap
.erase(aOpenFile
->versionId());
178 // If we never had or no longer have an open read file. Open it now.
179 if (mReadFile
== nil
)
181 mReadFile
= new OpenFile(mReadFilename
, false, false, 0, 0);
182 mOpenFileMap
.insert(OpenFileMap::value_type(mReadFile
->versionId(), mReadFile
));
184 // Note that mReadFile->isDirty() might actually return true here, but all that means is
185 // that we are looking at data that was commited after we opened the file which might
186 // happen in a few miliseconds anyway.
188 // Bump up the use count of our OpenFile.
189 mReadFile
->mUseCount
++;
191 // Return the length of the file and the mapped address.
192 outLength
= mReadFile
->length();
193 outFileAddress
= mReadFile
->address();
194 return mReadFile
->versionId();
198 AtomicFile::exitRead(VersionId inVersionId
)
200 StLock
<Mutex
> _(mReadLock
);
201 OpenFileMap::iterator it
= mOpenFileMap
.find(inVersionId
);
202 // If the inVersionId is not in the map anymore something really bad happned.
203 if (it
== mOpenFileMap
.end())
204 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
206 OpenFile
*aOpenFile
= it
->second
;
207 aOpenFile
->mUseCount
--;
209 // Don't close the current active file even if its mUseCount hits 0 since someone
210 // else will probably request it soon.
211 if (aOpenFile
->mUseCount
== 0 && aOpenFile
!= mReadFile
)
213 // Do not close any files (nor remove them from the map) while some thread is writing
214 // since doing so might release the lock we are holding.
215 if (mWriteLock
.tryLock())
217 // Release the write lock immidiatly since tryLock just aquired it and we don't want to write.
220 // Remove from the map, close and delete aOpenFile.
221 mOpenFileMap
.erase(it
);
236 bool AtomicFile::isDirty(VersionId inVersionId
)
238 StLock
<Mutex
> _(mReadLock
);
239 OpenFileMap::iterator it
= mOpenFileMap
.find(inVersionId
);
240 // If the inVersionId is not in the map anymore something really bad happned.
241 if (it
== mOpenFileMap
.end())
242 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
244 return it
->second
->isDirty();
248 AtomicFile::performDelete()
250 // Prevent any other threads in this process from writing.
253 OpenFile
*aReadFile
= nil
;
256 // Keep reopening mReadFilename until the lock has been aquired on a non-dirty file.
257 // XXX This is a potential infinite loop.
260 aReadFile
= new OpenFile(mReadFilename
, true, true, 0, 0);
261 if (!aReadFile
->isDirty())
269 // Aquire the read lock so no other thread will open the file
270 StLock
<Mutex
> _(mReadLock
);
273 unlink(mReadFilename
);
275 // Clear our current mReadFile since it refers to the deleted file.
278 // Mark the old file as modified
279 aReadFile
->setDirty();
281 // Close any open files.
290 VersionId aVersionId
= aReadFile
->versionId();
292 mOpenFileMap
.erase(aVersionId
);
302 AtomicFile::VersionId
303 AtomicFile::enterCreate(FileRef
&outWriteRef
)
305 // Prevent any other threads in this process from writing.
307 OpenFile
*aReadFile
= nil
;
310 // No threads can read during creation
311 StLock
<Mutex
> _(mReadLock
);
313 // Create mReadFilename until the lock has been aquired on a non-dirty file.
314 aReadFile
= new OpenFile(mReadFilename
, false, true, 1, 0666);
316 // Open mWriteFile for writing.
317 mWriteFile
= new OpenFile(mWriteFilename
, true, false, aReadFile
->versionId() + 1, 0666);
319 // Insert aReadFile into the map (do this after opening mWriteFile just in case that throws).
320 mOpenFileMap
.insert(OpenFileMap::value_type(-1, aReadFile
));
322 outWriteRef
= mWriteFile
->fileRef();
323 mCreating
= true; // So rollback() will delete mReadFileName.
324 return aReadFile
->versionId();
328 // Make sure we don't thow during cleanup since that would clobber the original
329 // error and prevent us from releasing mWriteLock
337 // XXX We should only unlink if we know that no one else is currently creating the file.
338 //unlink(mReadFilename);
339 mOpenFileMap
.erase(-1);
349 unlink(mWriteFilename
);
355 catch(...) {} // Do not throw since we already have an error.
357 // Release the write lock and remove any unused files from the map
363 AtomicFile::VersionId
364 AtomicFile::enterWrite(const uint8
*&outFileAddress
, size_t &outLength
, FileRef
&outWriteRef
)
366 // Wait for all other threads in this process to finish writing.
368 mCreating
= false; // So rollback() will not delete mReadFileName.
369 OpenFile
*aReadFile
= nil
;
372 // Keep reopening mReadFilename until the lock has been aquired on a non-dirty file.
373 // XXX This is a potential infinite loop.
376 aReadFile
= new OpenFile(mReadFilename
, true, true, 0, 0);
377 if (!aReadFile
->isDirty())
385 // We have the write lock on the file now we start modifying our shared data
386 // stuctures so aquire the read lock.
387 StLock
<Mutex
> _(mReadLock
);
389 // Open mWriteFile for writing.
390 mWriteFile
= new OpenFile(mWriteFilename
, true, false, aReadFile
->versionId() + 1, aReadFile
->mode());
392 // Insert aReadFile into the map (do this after opening mWriteFile just in case that throws).
393 mOpenFileMap
.insert(OpenFileMap::value_type(-1, aReadFile
));
395 outWriteRef
= mWriteFile
->fileRef();
396 outLength
= aReadFile
->length();
397 outFileAddress
= aReadFile
->address();
398 return aReadFile
->versionId();
402 // Make sure we don't thow during cleanup since that would clobber the original
403 // error and prevent us from releasing mWriteLock
411 mOpenFileMap
.erase(-1);
421 unlink(mWriteFilename
);
427 catch(...) {} // Do not throw since we already have an error.
429 // Release the write lock and remove any unused files from the map
435 AtomicFile::VersionId
438 debug("atomicfile", "%p commit", this);
439 StLock
<Mutex
> _(mReadLock
);
440 if (mWriteFile
== nil
)
441 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
445 VersionId aVersionId
= mWriteFile
->versionId();
450 OpenFileMap::iterator it
= mOpenFileMap
.find(-1);
451 if (it
== mOpenFileMap
.end())
452 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
454 // First rename the file and them mark the old one as modified
455 rename(mWriteFilename
, mReadFilename
);
456 OpenFile
*aOpenFile
= it
->second
;
458 // Clear our current mReadFile since it refers to the old file.
461 // Mark the old file as modified
462 aOpenFile
->setDirty();
464 // Close all unused files (in particular aOpenFile) and remove them from mOpenFileMap
466 debug("atomicfile", "%p commit done", this);
471 // Unlink the new file to rollback the transaction and close any open files.
474 unlink(mWriteFilename
);
477 debug("atomicfile", "%p commit failed, rethrowing", this);
483 AtomicFile::rollback()
485 debug("atomicfile", "%p rollback", this);
486 StLock
<Mutex
> _(mReadLock
);
487 if (mWriteFile
== nil
)
488 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
496 // First rename the file and them mark the old one as modified
497 unlink(mWriteFilename
);
499 unlink(mReadFilename
);
501 debug("atomicfile", "%p rollback complete", this);
505 // Unlink the new file to rollback the transaction and close any open files.
508 unlink(mWriteFilename
);
511 debug("atomicfile", "%p rollback failed, rethrowing", this);
516 // This private function is called by a successfull commit(), rollback() or performDelete() as well
517 // as by a failed enterWrite() or enterCreate().
519 AtomicFile::endWrite()
523 // We need to go in and close and delete all unused files from the queue
524 stack
<VersionId
> aDeleteList
;
525 OpenFileMap::iterator it
;
526 for (it
= mOpenFileMap
.begin();
527 it
!= mOpenFileMap
.end();
530 OpenFile
*aOpenFile
= it
->second
;
531 // If aOpenFile is unused and it is not the mReadFile schedule it for close and removal.
532 // Note that if this is being called after a commit mReadFile will have been set to nil.
533 if (aOpenFile
!= mReadFile
&& aOpenFile
->mUseCount
== 0)
534 aDeleteList
.push(it
->first
);
537 // Remove everything that was scheduled for removal
538 while (!aDeleteList
.empty())
540 it
= mOpenFileMap
.find(aDeleteList
.top());
548 mOpenFileMap
.erase(it
);
570 AtomicFile::rename(const string
&inSrcFilename
, const string
&inDestFilename
)
572 if (::rename(inSrcFilename
.c_str(), inDestFilename
.c_str()))
573 UnixError::throwMe(errno
);
577 AtomicFile::unlink(const string
&inFilename
)
579 if (::unlink(inFilename
.c_str()))
580 UnixError::throwMe(errno
);
584 AtomicFile::write(OffsetType inOffsetType
, uint32 inOffset
, const uint32 inData
)
586 uint32 aData
= htonl(inData
);
587 write(inOffsetType
, inOffset
, reinterpret_cast<uint8
*>(&aData
), sizeof(aData
));
591 AtomicFile::write(OffsetType inOffsetType
, uint32 inOffset
,
592 const uint32
*inData
, uint32 inCount
)
594 #ifdef HOST_LONG_IS_NETWORK_LONG
595 // XXX Optimize this for the case where hl == nl
596 const uint32
*aBuffer
= inData
;
598 auto_array
<uint32
> aBuffer(inCount
);
599 for (uint32 i
= 0; i
< inCount
; i
++)
600 aBuffer
.get()[i
] = htonl(inData
[i
]);
603 write(inOffsetType
, inOffset
, reinterpret_cast<const uint8
*>(aBuffer
.get()),
604 inCount
* sizeof(*inData
));
608 AtomicFile::write(OffsetType inOffsetType
, uint32 inOffset
, const uint8
*inData
, uint32 inLength
)
610 // Seriously paranoid check.
611 if (mWriteFile
== nil
)
612 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
614 if (inOffsetType
!= None
)
616 if (::lseek(mWriteFile
->mFileRef
, inOffset
, inOffsetType
== FromStart
? SEEK_SET
: SEEK_CUR
) == -1)
617 UnixError::throwMe(errno
);
620 if (::write(mWriteFile
->mFileRef
, reinterpret_cast<const char *>(inData
),
621 inLength
) != static_cast<ssize_t
>(inLength
))
622 UnixError::throwMe(errno
);
625 // AtomicFile::OpenFile implementation
627 AtomicFile::OpenFile::OpenFile(const string
&inFilename
, bool write
, bool lock
, VersionId inVersionId
, mode_t mode
) :
629 mVersionId(inVersionId
),
639 else if (write
&& !lock
)
641 flags
= O_WRONLY
|O_CREAT
|O_TRUNC
;
644 else if (!write
&& lock
)
646 flags
= O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
;
654 debug("atomicfile", "%p openfile(%s,%s%s,%d,0x%x) -> flags=0x%x, state=%d",
655 this, inFilename
.c_str(), write
? "write" : "read", lock
? ",lock" : "",
656 inVersionId
, mode
, flags
, mState
);
658 mFileRef
= ::open(inFilename
.c_str(), flags
, mode
);
662 debug("atomicfile", "%p openfile open failed(errno=%d)", this, error
);
664 #if _USE_IO == _USE_IO_POSIX
665 // Do the obvious error code translations here.
668 // Throw CSSMERR_DL_DATASTORE_DOESNOT_EXIST even in Write state since it means someone threw away our parent directory.
669 if (mState
== ReadWrite
|| mState
== Read
|| mState
== Write
)
670 CssmError::throwMe(CSSMERR_DL_DATASTORE_DOESNOT_EXIST
);
671 if (mState
== Create
)
673 // Attempt to create the path to inFilename since one or more of the directories
674 // in the path do not yet exist.
677 // Now try the open again.
678 mFileRef
= ::open(inFilename
.c_str(), flags
, mode
);
679 debug("atomicfile", "%p openfile reopen %s (%d)",
680 this, (mFileRef
== -1) ? "failed" : "ok", errno
);
681 error
= mFileRef
== -1 ? errno
: 0;
683 CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED
);
688 CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED
);
691 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
694 // Check if we are still in an error state.
696 UnixError::throwMe(errno
);
699 // If this is a new file write out the versionId
700 if (mState
== Create
)
701 writeVersionId(mVersionId
);
703 // If this is a temp output file we are done.
709 mLength
= ::lseek(mFileRef
, 0, SEEK_END
);
710 if (mLength
== static_cast<size_t>(-1))
711 UnixError::throwMe(errno
);
714 // XXX What to set versionId to?
716 return; // No point in mapping a zero length file.
719 #if _USE_IO == _USE_IO_POSIX
720 // Lock the file if required.
726 mLock
.l_pid
= getpid();
727 mLock
.l_type
= F_WRLCK
;
728 mLock
.l_whence
= SEEK_SET
;
730 // Keep trying to obtain the lock if we get interupted.
733 if (::fcntl(mFileRef
, F_SETLKW
, reinterpret_cast<int>(&mLock
)) == -1)
739 if (error
!= ENOTSUP
)
740 UnixError::throwMe(error
);
742 // XXX Filesystem does not support locking with fcntl use an alternative.
752 if (mState
!= Create
)
754 mAddress
= reinterpret_cast<const uint8
*>
755 (::mmap(0, mLength
, PROT_READ
, MAP_FILE
|MAP_SHARED
,
757 if (mAddress
== reinterpret_cast<const uint8
*>(-1))
760 UnixError::throwMe(errno
);
763 mVersionId
= readVersionId();
766 if (mState
!= Create
)
768 mAddress
= reinterpret_cast<const uint8
*>(-1);
769 auto_array
<char> aBuffer(mLength
);
770 if (::read(mFileRef
, aBuffer
.get(), mLength
) != mLength
)
771 UnixError::throwMe(errno
);
773 mAddress
= reinterpret_cast<const uint8
*>(aBuffer
.release());
774 mVersionId
= readVersionId();
780 if (mState
!= Closed
)
786 AtomicFile::OpenFile::~OpenFile()
792 AtomicFile::OpenFile::close()
794 IFDEBUG(if (mState
!= Closed
) debug("atomicfile", "%p openfile closing(ref=%d)",
797 if (mAddress
!= NULL
)
799 #if _USE_IO == _USE_IO_POSIX
800 debug("atomicfile", "%p openfile is unmapping %p:%ld", this, mAddress
, mLength
);
801 if (::munmap(const_cast<uint8
*>(mAddress
), mLength
) == -1)
804 debug("atomicfile", "%p openfile deleting %p", this, mAddress
);
812 writeVersionId(mVersionId
);
814 if (mState
!= Closed
)
817 if (::close(mFileRef
) == -1)
822 UnixError::throwMe(error
);
826 AtomicFile::OpenFile::isDirty()
828 if (mAddress
== NULL
)
829 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
831 return (mVersionId
!= readVersionId()) || mVersionId
== 0;
834 // Set the files dirty bit (requires the file to be writeable and locked).
836 AtomicFile::OpenFile::setDirty()
838 if (mState
!= ReadWrite
&& mState
!= Create
)
839 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
845 AtomicFile::OpenFile::unlock()
847 // XXX This should be called.
854 mLock
.l_pid
= getpid();
855 mLock
.l_type
= F_UNLCK
;
856 mLock
.l_whence
= SEEK_SET
;
857 if (::fcntl(mFileRef
, F_SETLK
, reinterpret_cast<int>(&mLock
)) == -1)
858 UnixError::throwMe(errno
);
864 AtomicFile::OpenFile::mode()
867 if (::fstat(mFileRef
, &st
) == -1)
868 UnixError::throwMe(errno
);
873 AtomicFile::VersionId
874 AtomicFile::OpenFile::readVersionId()
879 // Read the VersionId
880 if (mAddress
== NULL
)
882 // Seek to the end of the file minus 4
884 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
886 if (::lseek(mFileRef
, mLength
- 4, SEEK_SET
) == -1)
887 UnixError::throwMe(errno
);
889 ptr
= reinterpret_cast<uint8
*>(buf
);
890 if (::read(mFileRef
, buf
, 4) != 4)
891 UnixError::throwMe(errno
);
895 ptr
= mAddress
+ mLength
- 4;
897 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
900 VersionId aVersionId
= 0;
901 for (int i
= 0; i
< 4; i
++)
903 aVersionId
= (aVersionId
<< 8) + ptr
[i
];
910 AtomicFile::OpenFile::writeVersionId(VersionId inVersionId
)
912 if (mState
== ReadWrite
)
914 // Seek to the end of the file minus 4
916 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
918 if (::lseek(mFileRef
, mLength
- 4, SEEK_SET
) == -1)
919 UnixError::throwMe(errno
);
921 else /* if (mState == Create || mState == Write) */
923 // Seek to the end of the file.
924 if (::lseek(mFileRef
, 0, SEEK_END
) == -1)
925 UnixError::throwMe(errno
);
929 // Serialize the VersionId
930 for (int i
= 3; i
>= 0; i
--)
932 buf
[i
] = inVersionId
& 0xff;
933 inVersionId
= inVersionId
>> 8;
936 // Write the VersionId
937 if (::write(mFileRef
, reinterpret_cast<char *>(buf
), 4) != 4)
938 UnixError::throwMe(errno
);
942 AtomicFile::OpenFile::mkpath(const std::string
&inFilename
)
944 const char *path
= inFilename
.c_str();
946 char dirPath
[MAXPATHLEN
];
951 slash
+= strspn(path
+ slash
, "/");
952 slash
+= strcspn(path
+ slash
, "/");
954 if (path
[slash
] == '\0')
957 if (slash
>= MAXPATHLEN
)
958 UnixError::throwMe(ENAMETOOLONG
);
959 strncpy(dirPath
, path
, slash
);
960 dirPath
[slash
] = '\0';
962 if (stat(dirPath
, &sb
))
964 if (errno
!= ENOENT
|| mkdir(dirPath
, 0777))
965 UnixError::throwMe(errno
);
967 else if (!S_ISDIR(sb
.st_mode
))
968 CssmError::throwMe(CSSM_ERRCODE_OS_ACCESS_DENIED
); // @@@ Should be is a directory
974 // Constructor uglyness to work around C++ language limitations.
975 struct AtomicFileRef::InitArg
977 AtomicFile::VersionId versionId
;
978 const uint8
*address
;
982 AtomicFileRef::~AtomicFileRef()
986 AtomicFileRef::AtomicFileRef(AtomicFile
&inAtomicFile
, const InitArg
&inInitArg
) :
987 mVersionId(inInitArg
.versionId
),
988 mAtomicFile(inAtomicFile
),
989 mAddress(inInitArg
.address
),
990 mLength(inInitArg
.length
)
994 AtomicFileReadRef::~AtomicFileReadRef()
997 mAtomicFile
.exitRead(mVersionId
);
1003 AtomicFileRef::InitArg
1004 AtomicFileReadRef::enterRead(AtomicFile
&inAtomicFile
)
1007 anInitArg
.versionId
= inAtomicFile
.enterRead(anInitArg
.address
, anInitArg
.length
);
1011 AtomicFileReadRef::AtomicFileReadRef(AtomicFile
&inAtomicFile
) :
1012 AtomicFileRef(inAtomicFile
, enterRead(inAtomicFile
))
1016 AtomicFileWriteRef::~AtomicFileWriteRef()
1020 mAtomicFile
.rollback();
1028 AtomicFileRef::InitArg
1029 AtomicFileWriteRef::enterWrite(AtomicFile
&inAtomicFile
, AtomicFile::FileRef
&outWriteFileRef
)
1032 anInitArg
.versionId
= inAtomicFile
.enterWrite(anInitArg
.address
, anInitArg
.length
, outWriteFileRef
);
1036 AtomicFileWriteRef::AtomicFileWriteRef(AtomicFile
&inAtomicFile
) :
1037 AtomicFileRef(inAtomicFile
, enterWrite(inAtomicFile
, mFileRef
))