/// <summary> /// Scan FAT o miniFAT for free sectors to reuse. /// </summary> /// <param name="sType">Type of sector to look for</param> /// <returns>A stack of available sectors or minisectors already allocated</returns> internal Queue<Sector> FindFreeSectors(SectorType sType) { var freeList = new Queue<Sector>(); if (sType == SectorType.Normal) { var fatChain = GetSectorChain(-1, SectorType.FAT); var fatStream = new StreamView(fatChain, GetSectorSize(), _header.FATSectorsNumber*GetSectorSize(), SourceStream); var idx = 0; while (idx < _sectors.Count) { var id = fatStream.ReadInt32(); if (id == Sector.FreeSector) { if (_sectors[idx] == null) { var sector = new Sector(GetSectorSize(), SourceStream) {Id = idx}; _sectors[idx] = sector; } freeList.Enqueue(_sectors[idx]); } idx++; } } else { var miniFAT = GetSectorChain(_header.FirstMiniFATSectorId, SectorType.Normal); var miniFATView = new StreamView(miniFAT, GetSectorSize(), _header.MiniFATSectorsNumber*Sector.MinisectorSize, SourceStream); var miniStream = GetSectorChain(RootEntry.StartSector, SectorType.Normal); var miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, SourceStream); long ptr = 0; var nMinisectors = (int) (miniStreamView.Length/Sector.MinisectorSize); while (ptr < nMinisectors) { //AssureLength(miniStreamView, (int)miniFATView.Length); var id = miniFATView.ReadInt32(); ptr += 4; if (id != Sector.FreeSector) continue; var miniSector = new Sector(Sector.MinisectorSize, SourceStream) { Id = (int) ((ptr - 4)/4), Type = SectorType.Mini }; miniStreamView.Seek(miniSector.Id*Sector.MinisectorSize, SeekOrigin.Begin); miniStreamView.Read(miniSector.GetData(), 0, Sector.MinisectorSize); freeList.Enqueue(miniSector); } } return freeList; }
/// <summary> /// Setup the DIFAT sector chain /// </summary> /// <param name="faTsectorChain">A FAT sector chain</param> private void SetDIFATSectorChain(List<Sector> faTsectorChain) { // Get initial sector's count _header.FATSectorsNumber = faTsectorChain.Count; // Allocate Sectors foreach (var s in faTsectorChain) { if (s.Id != -1) continue; _sectors.Add(s); s.Id = _sectors.Count - 1; s.Type = SectorType.FAT; } // Sector count... var nCurrentSectors = _sectors.Count; // Temp DIFAT count var nDIFATSectors = (int) _header.DIFATSectorsNumber; if (faTsectorChain.Count > HeaderDIFATEntriesCount) { nDIFATSectors = Ceiling((double) (faTsectorChain.Count - HeaderDIFATEntriesCount)/_difatSectorFATEntriesCount); nDIFATSectors = LowSaturation(nDIFATSectors - (int) _header.DIFATSectorsNumber); //required DIFAT } // ...sum with new required DIFAT sectors count nCurrentSectors += nDIFATSectors; // ReCheck FAT bias while (_header.FATSectorsNumber*_fatSectorEntriesCount < nCurrentSectors) { var extraFATSector = new Sector(GetSectorSize(), SourceStream); _sectors.Add(extraFATSector); extraFATSector.Id = _sectors.Count - 1; extraFATSector.Type = SectorType.FAT; faTsectorChain.Add(extraFATSector); _header.FATSectorsNumber++; nCurrentSectors++; //... so, adding a FAT sector may induce DIFAT sectors to increase by one // and consequently this may induce ANOTHER FAT sector (TO-THINK: May this condition occure ?) if (nDIFATSectors*_difatSectorFATEntriesCount >= (_header.FATSectorsNumber > HeaderDIFATEntriesCount ? _header.FATSectorsNumber - HeaderDIFATEntriesCount : 0)) continue; nDIFATSectors++; nCurrentSectors++; } var difatSectors = GetSectorChain(-1, SectorType.DIFAT); var difatStream = new StreamView(difatSectors, GetSectorSize(), SourceStream); // Write DIFAT Sectors (if required) // Save room for the following chaining for (var i = 0; i < faTsectorChain.Count; i++) { if (i < HeaderDIFATEntriesCount) _header.DIFAT[i] = faTsectorChain[i].Id; else { // room for DIFAT chaining at the end of any DIFAT sector (4 bytes) if (i != HeaderDIFATEntriesCount && (i - HeaderDIFATEntriesCount)%_difatSectorFATEntriesCount == 0) { var temp = new byte[sizeof (int)]; difatStream.Write(temp, 0, sizeof (int)); } difatStream.Write(BitConverter.GetBytes(faTsectorChain[i].Id), 0, sizeof (int)); } } // Allocate room for DIFAT sectors foreach (var sector in difatStream.BaseSectorChain) { if (sector.Id != -1) continue; _sectors.Add(sector); sector.Id = _sectors.Count - 1; sector.Type = SectorType.DIFAT; } _header.DIFATSectorsNumber = (uint) nDIFATSectors; // Chain first sector if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0) { _header.FirstDIFATSectorId = difatStream.BaseSectorChain[0].Id; // Update header information _header.DIFATSectorsNumber = (uint) difatStream.BaseSectorChain.Count; // Write chaining information at the end of DIFAT Sectors for (var i = 0; i < difatStream.BaseSectorChain.Count - 1; i++) { Buffer.BlockCopy( BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id), 0, difatStream.BaseSectorChain[i].GetData(), GetSectorSize() - sizeof (int), 4); } Buffer.BlockCopy( BitConverter.GetBytes(Sector.Endofchain), 0, difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(), GetSectorSize() - sizeof (int), sizeof (int) ); } else _header.FirstDIFATSectorId = Sector.Endofchain; // Mark DIFAT Sectors in FAT var fatSv = new StreamView(faTsectorChain, GetSectorSize(), _header.FATSectorsNumber*GetSectorSize(), SourceStream); for (var i = 0; i < _header.DIFATSectorsNumber; i++) { fatSv.Seek(difatStream.BaseSectorChain[i].Id*4, SeekOrigin.Begin); fatSv.Write(BitConverter.GetBytes(Sector.DifSector), 0, 4); } for (var i = 0; i < _header.FATSectorsNumber; i++) { fatSv.Seek(fatSv.BaseSectorChain[i].Id*4, SeekOrigin.Begin); fatSv.Write(BitConverter.GetBytes(Sector.FATSector), 0, 4); } _header.FATSectorsNumber = fatSv.BaseSectorChain.Count; }
/// <summary> /// Get a standard sector chain /// </summary> /// <param name="secId">First SecID of the required chain</param> /// <returns>A list of sectors</returns> /// <exception cref="CFCorruptedFileException">Raised when the file is corrupt</exception> private List<Sector> GetNormalSectorChain(int secId) { var result = new List<Sector>(); var nextSecId = secId; var fatSectors = GetFatSectorChain(); var fatStream = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count*GetSectorSize(), SourceStream); while (true) { if (nextSecId == Sector.Endofchain) break; if (nextSecId >= _sectors.Count) throw new CFCorruptedFileException( string.Format( "Next Sector ID reference an out of range sector. NextID : {0} while sector count {1}", nextSecId, _sectors.Count)); var sector = _sectors[nextSecId]; if (sector == null) { sector = new Sector(GetSectorSize(), SourceStream) {Id = nextSecId, Type = SectorType.Normal}; _sectors[nextSecId] = sector; } result.Add(sector); fatStream.Seek(nextSecId*4, SeekOrigin.Begin); var next = fatStream.ReadInt32(); if (next != nextSecId) nextSecId = next; else throw new CFCorruptedFileException("Cyclic sector chain found. File is corrupted"); } return result; }
/// <summary> /// Get a mini sector chain /// </summary> /// <param name="sectorId">First sector id of the required chain</param> /// <returns>A list of mini sectors (64 bytes)</returns> private List<Sector> GetMiniSectorChain(int sectorId) { var result = new List<Sector>(); if (sectorId == Sector.Endofchain) return result; var miniFAT = GetNormalSectorChain(_header.FirstMiniFATSectorId); var miniStream = GetNormalSectorChain(RootEntry.StartSector); var miniFATView = new StreamView(miniFAT, GetSectorSize(), _header.MiniFATSectorsNumber*Sector.MinisectorSize, SourceStream); var miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, SourceStream); var miniFATReader = new BinaryReader(miniFATView); var nextSectorId = sectorId; while (true) { if (nextSectorId == Sector.Endofchain) break; var miniSector = new Sector(Sector.MinisectorSize, SourceStream) { Id = nextSectorId, Type = SectorType.Mini }; miniStreamView.Seek(nextSectorId*Sector.MinisectorSize, SeekOrigin.Begin); miniStreamView.Read(miniSector.GetData(), 0, Sector.MinisectorSize); result.Add(miniSector); miniFATView.Seek(nextSectorId*4, SeekOrigin.Begin); nextSectorId = miniFATReader.ReadInt32(); } return result; }
/// <summary> /// Get the FAT sector chain /// </summary> /// <returns>List of FAT sectors</returns> private List<Sector> GetFatSectorChain() { const int numberOfHeaderFATEntry = 109; //Number of FAT sectors id in the header var result = new List<Sector>(); int nextSecId; var difatSectors = GetDifatSectorChain(); var idx = 0; // Read FAT entries from the header Fat entry array (max 109 entries) while (idx < _header.FATSectorsNumber && idx < numberOfHeaderFATEntry) { nextSecId = _header.DIFAT[idx]; var sector = _sectors[nextSecId]; if (sector == null) { sector = new Sector(GetSectorSize(), SourceStream) {Id = nextSecId, Type = SectorType.FAT}; _sectors[nextSecId] = sector; } result.Add(sector); idx++; } //Is there any DIFAT sector containing other FAT entries ? if (difatSectors.Count <= 0) return result; var difatStream = new StreamView ( difatSectors, GetSectorSize(), _header.FATSectorsNumber > numberOfHeaderFATEntry ? (_header.FATSectorsNumber - numberOfHeaderFATEntry)*4 : 0, SourceStream ); var nextDIFATSectorBuffer = new byte[4]; difatStream.Read(nextDIFATSectorBuffer, 0, 4); nextSecId = BitConverter.ToInt32(nextDIFATSectorBuffer, 0); var i = 0; var numberOfFatHeaderEntries = numberOfHeaderFATEntry; while (numberOfFatHeaderEntries < _header.FATSectorsNumber) { if (difatStream.Position == ((GetSectorSize() - 4) + i*GetSectorSize())) { difatStream.Seek(4, SeekOrigin.Current); i++; continue; } var sector = _sectors[nextSecId]; if (sector == null) { sector = new Sector(GetSectorSize(), SourceStream) {Type = SectorType.FAT, Id = nextSecId}; _sectors[nextSecId] = sector; //UUU } result.Add(sector); difatStream.Read(nextDIFATSectorBuffer, 0, 4); nextSecId = BitConverter.ToInt32(nextDIFATSectorBuffer, 0); numberOfFatHeaderEntries++; } return result; }
/// <summary> /// Get the DIFAT Sector chain /// </summary> /// <returns>A list of DIFAT sectors</returns> /// <exception cref="CFCorruptedFileException">Raised when DIFAT sectors count is mismatched</exception> private List<Sector> GetDifatSectorChain() { var result = new List<Sector>(); if (_header.DIFATSectorsNumber == 0) return result; var validationCount = (int) _header.DIFATSectorsNumber; var sector = _sectors[_header.FirstDIFATSectorId]; if (sector == null) //Lazy loading { sector = new Sector(GetSectorSize(), SourceStream) { Type = SectorType.DIFAT, Id = _header.FirstDIFATSectorId }; _sectors[_header.FirstDIFATSectorId] = sector; } result.Add(sector); while (validationCount >= 0) { var nextSecId = BitConverter.ToInt32(sector.GetData(), GetSectorSize() - 4); // Strictly speaking, the following condition is not correct from // a specification point of view: // only ENDOFCHAIN should break DIFAT chain but // a lot of existing compound files use FREESECT as DIFAT chain termination if (nextSecId == Sector.FreeSector || nextSecId == Sector.Endofchain) break; validationCount--; if (validationCount < 0) { Close(); throw new CFCorruptedFileException("DIFAT sectors count mismatched. Corrupted compound file"); } sector = _sectors[nextSecId]; if (sector == null) { sector = new Sector(GetSectorSize(), SourceStream) {Id = nextSecId}; _sectors[nextSecId] = sector; } result.Add(sector); } return result; }