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);
        }
Beispiel #2
0
        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;
        }
Beispiel #3
0
        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));
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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;
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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
                };
            }
        }
Beispiel #8
0
        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();
        }
Beispiel #9
0
        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));
        }
Beispiel #10
0
        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);
        }
Beispiel #11
0
        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));
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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));
        }
Beispiel #14
0
        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);
        }
Beispiel #15
0
        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;
            }
        }
Beispiel #16
0
        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);
        }
Beispiel #18
0
        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);
        }
Beispiel #19
0
        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);
            }
        }
Beispiel #22
0
        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);
        }
Beispiel #23
0
        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);
        }
Beispiel #24
0
        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);
        }
Beispiel #25
0
 public override void Flush()
 {
     BaseStorage.Flush();
 }
Beispiel #26
0
 protected override Result DoFlush()
 {
     return(BaseStorage.Flush());
 }
Beispiel #27
0
 protected override Result DoWrite(long offset, ReadOnlySpan <byte> source)
 {
     ValidateSize(source.Length, offset);
     return(BaseStorage.Write(offset, source));
 }
Beispiel #28
0
 protected override Result DoRead(long offset, Span <byte> destination)
 {
     ValidateSize(destination.Length, offset);
     return(BaseStorage.Read(offset, destination));
 }
Beispiel #29
0
 public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
Beispiel #30
0
        public override void SetLength(long value)
        {
            BaseStorage.SetSize(value);

            _length = BaseStorage.GetSize();
        }