mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 14:00:32 +08:00
306 lines
7.5 KiB
C++
306 lines
7.5 KiB
C++
#include "compoundfile.h"
|
|
#include "cfexception.h"
|
|
|
|
using namespace CFCPP;
|
|
|
|
CompoundFile::CompoundFile() :
|
|
CompoundFile(CFSVersion::Ver_3, CFSConfiguration::Default)
|
|
{}
|
|
|
|
CompoundFile::CompoundFile(const std::wstring &fileName, CFSUpdateMode updateMode, CFSConfiguration configParameters)
|
|
{
|
|
configuration = configParameters;
|
|
validationExceptionEnabled = !configParameters.HasFlag(CFSConfiguration.NoValidationException);
|
|
sectorRecycle = configParameters.HasFlag(CFSConfiguration.SectorRecycle);
|
|
updateMode = updateMode;
|
|
eraseFreeSectors = configParameters.HasFlag(CFSConfiguration.EraseFreeSectors);
|
|
|
|
LoadFile(fileName);
|
|
|
|
DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
|
|
FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
|
|
}
|
|
|
|
CompoundFile::CompoundFile(CFSVersion cfsVersion, CFSConfiguration configFlags) : header((ushort)cfsVersion)
|
|
{
|
|
configuration = configFlags;
|
|
|
|
sectorRecycle = configFlags & CFSConfiguration::SectorRecycle;
|
|
bool eraseFreeSectors = configFlags & CFSConfiguration::EraseFreeSectors;
|
|
|
|
// if (cfsVersion == CFSVersion::Ver_4)
|
|
// sectors.OnVer3SizeLimitReached += new Ver3SizeLimitReached(OnSizeLimitReached);
|
|
|
|
|
|
DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
|
|
FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
|
|
|
|
//Root --
|
|
IDirectoryEntry rootDir = DirectoryEntry.New("Root Entry", StgType.StgRoot, directoryEntries);
|
|
rootDir.StgColor = StgColor.Black;
|
|
//InsertNewDirectoryEntry(rootDir);
|
|
|
|
rootStorage = new CFStorage(this, rootDir);
|
|
}
|
|
|
|
CompoundFile::CompoundFile(const std::wstring &fileName)
|
|
{
|
|
sectorRecycle = false;
|
|
updateMode = CFSUpdateMode::ReadOnly;
|
|
eraseFreeSectors = false;
|
|
|
|
LoadFile(fileName);
|
|
|
|
DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
|
|
FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
|
|
}
|
|
|
|
CompoundFile::CompoundFile(Stream stream)
|
|
{
|
|
LoadStream(stream);
|
|
|
|
DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
|
|
FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
|
|
}
|
|
|
|
void CompoundFile::OnSizeLimitReached()
|
|
{
|
|
Sector rangeLockSector(GetSectorSize(), sourceStream);
|
|
|
|
rangeLockSector.type = SectorType::RangeLockSector;
|
|
|
|
_transactionLockAdded = true;
|
|
_lockSectorId = rangeLockSector.Id();
|
|
sectors.Add(rangeLockSector);
|
|
}
|
|
|
|
void CompoundFile::Commit()
|
|
{
|
|
Commit(false);
|
|
}
|
|
|
|
void CompoundFile::Commit(bool releaseMemory)
|
|
{
|
|
if (_disposed)
|
|
throw new CFDisposedException("Compound File closed: cannot commit data");
|
|
|
|
if (updateMode != CFSUpdateMode::Update)
|
|
throw new CFInvalidOperation("Cannot commit data in Read-Only update mode");
|
|
#if !defined(FLAT_WRITE)
|
|
|
|
int sId = -1;
|
|
int sCount = 0;
|
|
int bufOffset = 0;
|
|
#endif
|
|
int sSize = GetSectorSize();
|
|
|
|
if (header.majorVersion != (ushort)CFSVersion::Ver_3)
|
|
CheckForLockSector();
|
|
|
|
sourceStream.Seek(0, SeekOrigin.Begin);
|
|
sourceStream.Write((byte[])Array.CreateInstance(typeof(byte), GetSectorSize()), 0, sSize);
|
|
|
|
CommitDirectory();
|
|
|
|
bool gap = true;
|
|
|
|
|
|
for (int i = 0; i < sectors.largeArraySlices.size(); i++)
|
|
{
|
|
#if FLAT_WRITE
|
|
|
|
//Note:
|
|
//Here sectors should not be loaded dynamically because
|
|
//if they are null it means that no change has involved them;
|
|
|
|
Sector s = (Sector)sectors[i];
|
|
|
|
if (s != null && s.DirtyFlag)
|
|
{
|
|
if (gap)
|
|
sourceStream.Seek((long)((long)(sSize) + (long)i * (long)sSize), SeekOrigin.Begin);
|
|
|
|
sourceStream.Write(s.GetData(), 0, sSize);
|
|
sourceStream.Flush();
|
|
s.DirtyFlag = false;
|
|
gap = false;
|
|
|
|
}
|
|
else
|
|
{
|
|
gap = true;
|
|
}
|
|
|
|
if (s != null && releaseMemory)
|
|
{
|
|
|
|
s.ReleaseData();
|
|
s = null;
|
|
sectors[i] = null;
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
Sector s = sectors[i] as Sector;
|
|
|
|
|
|
if (s != null && s.DirtyFlag && flushingQueue.Count < (int)(buffer.Length / sSize))
|
|
{
|
|
//First of a block of contiguous sectors, mark id, start enqueuing
|
|
|
|
if (gap)
|
|
{
|
|
sId = s.Id;
|
|
gap = false;
|
|
}
|
|
|
|
flushingQueue.Enqueue(s);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//Found a gap, stop enqueuing, flush a write operation
|
|
|
|
gap = true;
|
|
sCount = flushingQueue.Count;
|
|
|
|
if (sCount == 0) continue;
|
|
|
|
bufOffset = 0;
|
|
while (flushingQueue.Count > 0)
|
|
{
|
|
Sector r = flushingQueue.Dequeue();
|
|
Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize);
|
|
r.DirtyFlag = false;
|
|
|
|
if (releaseMemory)
|
|
{
|
|
r.ReleaseData();
|
|
}
|
|
|
|
bufOffset += sSize;
|
|
}
|
|
|
|
sourceStream.Seek(((long)sSize + (long)sId * (long)sSize), SeekOrigin.Begin);
|
|
sourceStream.Write(buffer, 0, sCount * sSize);
|
|
|
|
|
|
|
|
//Console.WriteLine("W - " + (int)(sCount * sSize ));
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !FLAT_WRITE
|
|
sCount = flushingQueue.Count;
|
|
bufOffset = 0;
|
|
|
|
while (flushingQueue.Count > 0)
|
|
{
|
|
Sector r = flushingQueue.Dequeue();
|
|
Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize);
|
|
r.DirtyFlag = false;
|
|
|
|
if (releaseMemory)
|
|
{
|
|
r.ReleaseData();
|
|
r = null;
|
|
}
|
|
|
|
bufOffset += sSize;
|
|
}
|
|
|
|
if (sCount != 0)
|
|
{
|
|
sourceStream.Seek((long)sSize + (long)sId * (long)sSize, SeekOrigin.Begin);
|
|
sourceStream.Write(buffer, 0, sCount * sSize);
|
|
//Console.WriteLine("W - " + (int)(sCount * sSize));
|
|
}
|
|
|
|
#endif
|
|
|
|
// Seek to beginning position and save header (first 512 or 4096 bytes)
|
|
sourceStream.Seek(0, SeekOrigin.Begin);
|
|
header.Write(sourceStream);
|
|
|
|
sourceStream.SetLength((long)(sectors.Count + 1) * sSize);
|
|
sourceStream.Flush();
|
|
|
|
if (releaseMemory)
|
|
GC.Collect();
|
|
|
|
//}
|
|
//catch (Exception ex)
|
|
//{
|
|
// throw new CFException("Internal error while committing data", ex);
|
|
//}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load compound file from an existing stream.
|
|
/// </summary>
|
|
/// <param name="stream">Stream to load compound file from</param>
|
|
private void Load(Stream stream)
|
|
{
|
|
try
|
|
{
|
|
this.header = new Header();
|
|
this.directoryEntries = new List<IDirectoryEntry>();
|
|
|
|
this.sourceStream = stream;
|
|
|
|
header.Read(stream);
|
|
|
|
int n_sector = Ceiling(((double)(stream.Length - GetSectorSize()) / (double)GetSectorSize()));
|
|
|
|
if (stream.Length > 0x7FFFFF0)
|
|
this._transactionLockAllocated = true;
|
|
|
|
|
|
sectors = new SectorCollection();
|
|
//sectors = new ArrayList();
|
|
for (int i = 0; i < n_sector; i++)
|
|
{
|
|
sectors.Add(null);
|
|
}
|
|
|
|
LoadDirectories();
|
|
|
|
this.rootStorage
|
|
= new CFStorage(this, directoryEntries[0]);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (stream != null && closeStream)
|
|
stream.Close();
|
|
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CompoundFile::GetSectorSize()
|
|
{
|
|
|
|
return 2 << (header.sectorShift - 1);
|
|
}
|
|
|
|
void CompoundFile::CheckForLockSector()
|
|
{
|
|
//If transaction lock has been added and not yet allocated in the FAT...
|
|
if (_transactionLockAdded && !_transactionLockAllocated)
|
|
{
|
|
StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream);
|
|
|
|
fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin);
|
|
fatStream.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4);
|
|
|
|
_transactionLockAllocated = true;
|
|
}
|
|
}
|