public void FsTrim() { MetaRemapStorage.FsTrim(); DataRemapStorage.FsTrim(); DuplexStorage.FsTrim(); JournalStorage.FsTrim(); CoreDataIvfcStorage.FsTrim(); FatIvfcStorage?.FsTrim(); SaveDataFileSystemCore.FsTrim(); int unusedHeaderOffset = IsFirstHeaderInUse ? 0x4000 : 0; BaseStorage.Slice(unusedHeaderOffset, 0x4000).Fill(TrimFillValue); }
private void ReadBlock(CacheBlock block, long index) { long offset = index * BlockSize; int length = BlockSize; if (Length != -1) { length = (int)Math.Min(Length - offset, length); } BaseStorage.Read(block.Buffer.AsSpan(0, length), offset); block.Length = length; block.Index = index; block.Dirty = false; }
private IStorage OpenNca2Header(long size) { const int sectorSize = NcaHeader.HeaderSectorSize; var sources = new List <IStorage>(); sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), Keyset.HeaderKey, sectorSize, true), 1, true)); for (int i = 0x400; i < size; i += sectorSize) { sources.Add(new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(i, sectorSize), Keyset.HeaderKey, sectorSize, true), 1, true)); } return(new ConcatenationStorage(sources, true)); }
public override int Read(Span <byte> destination, long offset) { int toRead = ValidateReadParamsAndGetSize(destination, offset); if (IsCompressed) { FileData.AsSpan((int)offset, toRead).CopyTo(destination); } else { long storageOffset = Offset + offset; BaseStorage.Read(destination.Slice(0, toRead), storageOffset); } return(toRead); }
public override void SetSize(long size) { //if (!IsResizable) // return 0x313802; //if (Offset < 0 || size < 0) // return 0x2F5C02; if (BaseStorage.GetSize() != Offset + _length) { throw new NotSupportedException("SubStorage cannot be resized unless it is located at the end of the base storage."); } BaseStorage.SetSize(Offset + size); _length = size; }
public IStorage OpenHeaderStorage(bool openEncrypted) { long firstSectionOffset = long.MaxValue; bool hasEnabledSection = false; // Encrypted portion continues until the first section for (int i = 0; i < NcaHeader.SectionCount; i++) { if (Header.IsSectionEnabled(i)) { hasEnabledSection = true; firstSectionOffset = Math.Min(firstSectionOffset, Header.GetSectionStartOffset(i)); } } long headerSize = hasEnabledSection ? firstSectionOffset : NcaHeader.HeaderSize; IStorage rawHeaderStorage = BaseStorage.Slice(0, headerSize); if (openEncrypted == IsEncrypted) { return(rawHeaderStorage); } IStorage header; switch (Header.Version) { case 3: header = new CachedStorage(new Aes128XtsStorage(rawHeaderStorage, KeySet.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); break; case 2: header = OpenNca2Header(headerSize, !openEncrypted); break; case 0: header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), KeySet.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); break; default: throw new NotSupportedException("Unsupported NCA version"); } return(header); }
private void InitializeRootPartition() { lock (InitLocker) { if (RootPartition != null) { return; } IStorage rootStorage = BaseStorage.Slice(Header.RootPartitionOffset); RootPartition = new XciPartition(rootStorage) { Offset = Header.RootPartitionOffset, HashValidity = Header.PartitionFsHeaderValidity }; } }
public void FsTrim() { // todo replace with a bitmap reader class when added BitArray bitmap = new DuplexBitmap(Map.GetFreeBlocksStorage(), Map.Header.JournalBlockCount + Map.Header.MainDataBlockCount).Bitmap; for (int i = 0; i < bitmap.Length; i++) { if (!bitmap[i]) { continue; } BaseStorage.Fill(SaveDataFileSystem.TrimFillValue, i * BlockSize, BlockSize); } Map.FsTrim(); }
protected override Result ReadImpl(long offset, Span <byte> destination) { if (BaseStorage == null) { return(ResultFs.SubStorageNotInitialized.Log()); } if (destination.Length == 0) { return(Result.Success); } if (!IsRangeValid(offset, destination.Length, Size)) { return(ResultFs.OutOfRange.Log()); } return(BaseStorage.Read(Offset + offset, destination)); }
protected override Result ReadImpl(out long bytesRead, long offset, Span <byte> destination, ReadOption options) { bytesRead = 0; Result rc = ValidateReadParams(out long toRead, offset, destination.Length, Mode); if (rc.IsFailure()) { return(rc); } long storageOffset = Offset + offset; BaseStorage.Read(storageOffset, destination.Slice(0, (int)toRead)); bytesRead = toRead; return(Result.Success); }
protected override Result WriteImpl(long offset, ReadOnlySpan <byte> source) { if (BaseStorage == null) { return(ResultFs.SubStorageNotInitialized.Log()); } if (source.Length == 0) { return(Result.Success); } if (!IsRangeValid(offset, source.Length, Size)) { return(ResultFs.OutOfRange.Log()); } return(BaseStorage.Write(Offset + offset, source)); }
protected override Result DoSetSize(long size) { Result rc = BaseStorage.SetSize(size); if (rc.IsFailure()) { return(rc); } rc = BaseStorage.GetSize(out long newSize); if (rc.IsFailure()) { return(rc); } Length = newSize; return(Result.Success); }
private IStorage OpenEncryptedStorage(int index) { if (!SectionExists(index)) { throw new ArgumentException(nameof(index), Messages.NcaSectionMissing); } long offset = Header.GetSectionStartOffset(index); long size = Header.GetSectionSize(index); BaseStorage.GetSize(out long baseSize).ThrowIfFailure(); if (!Util.IsSubRange(offset, size, baseSize)) { throw new InvalidDataException( $"Section offset (0x{offset:x}) and length (0x{size:x}) fall outside the total NCA length (0x{baseSize:x})."); } return(BaseStorage.Slice(offset, size)); }
protected override Result DoSetSize(long size) { Result rc = BaseStorage.SetSize(size); if (rc.IsFailure()) { return(rc); } rc = BaseStorage.GetSize(out long newSize); if (rc.IsFailure()) { return(rc); } SectorCount = (int)BitUtil.DivideUp(newSize, SectorSize); Length = newSize; return(Result.Success); }
protected override void ReadImpl(Span <byte> destination, long offset) { long inPos = offset; int outPos = 0; int remaining = destination.Length; while (remaining > 0) { int blockNum = (int)(inPos / BlockSize); int blockPos = (int)(inPos % BlockSize); long physicalOffset = Map.GetPhysicalBlock(blockNum) * BlockSize + blockPos; int bytesToRead = Math.Min(remaining, BlockSize - blockPos); BaseStorage.Read(destination.Slice(outPos, bytesToRead), physicalOffset); outPos += bytesToRead; inPos += bytesToRead; remaining -= bytesToRead; } }
protected override void WriteImpl(ReadOnlySpan <byte> source, long offset) { long inPos = offset; int outPos = 0; int remaining = source.Length; while (remaining > 0) { int blockNum = (int)(inPos / BlockSize); int blockPos = (int)(inPos % BlockSize); long physicalOffset = Map.GetPhysicalBlock(blockNum) * BlockSize + blockPos; int bytesToWrite = Math.Min(remaining, BlockSize - blockPos); BaseStorage.Write(source.Slice(outPos, bytesToWrite), physicalOffset); outPos += bytesToWrite; inPos += bytesToWrite; remaining -= bytesToWrite; } }
protected override Result WriteImpl(long offset, ReadOnlySpan <byte> source) { long blockIndex = offset / SectorSize; long hashPos = blockIndex * DigestSize; Result rc = GetSize(out long storageSize); if (rc.IsFailure()) { return(rc); } int toWrite = (int)Math.Min(source.Length, storageSize - offset); byte[] dataBuffer = ArrayPool <byte> .Shared.Rent(SectorSize); try { source.CopyTo(dataBuffer); byte[] hash = DoHash(dataBuffer, 0, toWrite); if (Type == IntegrityStorageType.Save && source.IsEmpty()) { Array.Clear(hash, 0, DigestSize); } BaseStorage.Write(offset, source); HashStorage.Write(hashPos, hash); BlockValidities[blockIndex] = Validity.Unchecked; } finally { ArrayPool <byte> .Shared.Return(dataBuffer); } return(Result.Success); }
public Result Commit(KeySet keySet) { CoreDataIvfcStorage.Flush(); FatIvfcStorage?.Flush(); Stream headerStream = BaseStorage.AsStream(); var hashData = new byte[0x3d00]; headerStream.Position = 0x300; headerStream.Read(hashData, 0, hashData.Length); var hash = new byte[Sha256.DigestSize]; Sha256.GenerateSha256Hash(hashData, hash); headerStream.Position = 0x108; headerStream.Write(hash, 0, hash.Length); if (keySet == null || keySet.DeviceUniqueSaveMacKeys[0].IsZeros()) { return(ResultFs.PreconditionViolation.Log()); } var cmacData = new byte[0x200]; var cmac = new byte[0x10]; headerStream.Position = 0x100; headerStream.Read(cmacData, 0, 0x200); Aes.CalculateCmac(cmac, cmacData, keySet.DeviceUniqueSaveMacKeys[0]); headerStream.Position = 0; headerStream.Write(cmac, 0, 0x10); headerStream.Flush(); return(Result.Success); }
protected override Result SetSizeImpl(long size) { if (BaseStorage == null) { return(ResultFs.SubStorageNotInitialized.Log()); } // todo: Add IsResizable member // if (!IsResizable) return ResultFs.SubStorageNotResizable.Log(); if (Offset < 0 || size < 0) { return(ResultFs.InvalidSize.Log()); } Result rc = BaseStorage.GetSize(out long baseSize); if (rc.IsFailure()) { return(rc); } if (baseSize != Offset + Length) { // SubStorage cannot be resized unless it is located at the end of the base storage. return(ResultFs.SubStorageNotResizableMiddleOfFile.Log()); } rc = BaseStorage.SetSize(Offset + size); if (rc.IsFailure()) { return(rc); } Length = size; return(Result.Success); }
public Result Commit(Keyset keyset) { CoreDataIvfcStorage.Flush(); FatIvfcStorage?.Flush(); Stream headerStream = BaseStorage.AsStream(); var hashData = new byte[0x3d00]; headerStream.Position = 0x300; headerStream.Read(hashData, 0, hashData.Length); var hash = new byte[Sha256.DigestSize]; Sha256.GenerateSha256Hash(hashData, hash); headerStream.Position = 0x108; headerStream.Write(hash, 0, hash.Length); if (keyset == null || keyset.SaveMacKey.IsEmpty()) { return(ResultFs.PreconditionViolation); } var cmacData = new byte[0x200]; var cmac = new byte[0x10]; headerStream.Position = 0x100; headerStream.Read(cmacData, 0, 0x200); CryptoOld.CalculateAesCmac(keyset.SaveMacKey, cmacData, 0, cmac, 0, 0x200); headerStream.Position = 0; headerStream.Write(cmac, 0, 0x10); headerStream.Flush(); return(Result.Success); }
public void FsTrim() { if (Type != IntegrityStorageType.Save) { return; } Span <byte> digest = stackalloc byte[DigestSize]; for (int i = 0; i < SectorCount; i++) { long hashPos = i * DigestSize; HashStorage.Read(digest, hashPos); if (!Util.IsEmpty(digest)) { continue; } int dataOffset = i * SectorSize; BaseStorage.Fill(SaveDataFileSystem.TrimFillValue, dataOffset, SectorSize); } }
protected override Result DoWrite(long offset, ReadOnlySpan <byte> source) { if (source.Length == 0) { return(Result.Success); } MapEntry entry = GetMapEntry(offset); long inPos = offset; int outPos = 0; int remaining = source.Length; while (remaining > 0) { long entryPos = inPos - entry.VirtualOffset; int bytesToWrite = (int)Math.Min(entry.VirtualOffsetEnd - inPos, remaining); Result rc = BaseStorage.Write(entry.PhysicalOffset + entryPos, source.Slice(outPos, bytesToWrite)); if (rc.IsFailure()) { return(rc); } outPos += bytesToWrite; inPos += bytesToWrite; remaining -= bytesToWrite; if (inPos >= entry.VirtualOffsetEnd) { entry = entry.Next; } } return(Result.Success); }
protected override Result DoSetSize(long size) { if (!IsValid()) { return(ResultFs.NotInitialized.Log()); } if (!IsResizable) { return(ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log()); } if (!IsOffsetAndSizeValid(Offset, size)) { return(ResultFs.InvalidSize.Log()); } Result rc = BaseStorage.GetSize(out long currentSize); if (rc.IsFailure()) { return(rc); } if (currentSize != Offset + Size) { // SubStorage cannot be resized unless it is located at the end of the base storage. return(ResultFs.UnsupportedSetSizeForResizableSubStorage.Log()); } rc = BaseStorage.SetSize(Offset + size); if (rc.IsFailure()) { return(rc); } Size = size; return(Result.Success); }
protected override Result DoWrite(long offset, ReadOnlySpan <byte> source) { var iterator = new AllocationTableIterator(Fat, InitialBlock); long inPos = offset; int outPos = 0; int remaining = source.Length; while (remaining > 0) { int blockNum = (int)(inPos / BlockSize); if (!iterator.Seek(blockNum)) { return(ResultFs.InvalidAllocationTableOffset.Log()); } int segmentPos = (int)(inPos - (long)iterator.VirtualBlock * BlockSize); long physicalOffset = iterator.PhysicalBlock * BlockSize + segmentPos; int remainingInSegment = iterator.CurrentSegmentSize * BlockSize - segmentPos; int bytesToWrite = Math.Min(remaining, remainingInSegment); Result rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite)); if (rc.IsFailure()) { return(rc); } outPos += bytesToWrite; inPos += bytesToWrite; remaining -= bytesToWrite; } return(Result.Success); }
public override void Flush() { BaseStorage.Flush(); }
protected override Result DoFlush() { return(BaseStorage.Flush()); }
protected override Result DoWrite(long offset, ReadOnlySpan <byte> source) { ValidateSize(source.Length, offset); return(BaseStorage.Write(offset, source)); }
protected override Result DoRead(long offset, Span <byte> destination) { ValidateSize(destination.Length, offset); return(BaseStorage.Read(offset, destination)); }
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public override void SetLength(long value) { BaseStorage.SetSize(value); _length = BaseStorage.GetSize(); }