public FragmentWriter(BuilderContext context) { _context = context; _currentBlock = new byte[context.DataBlockSize]; _currentOffset = 0; _fragmentBlocks = new List<FragmentRecord>(); }
protected void FillCommonInodeData(BuilderContext context) { Inode.Mode = (ushort)Mode; Inode.UidKey = context.AllocateId(UserId); Inode.GidKey = context.AllocateId(GroupId); Inode.ModificationTime = ModificationTime; InodeNumber = (int)context.AllocateInode(); Inode.InodeNumber = (uint)InodeNumber; Inode.NumLinks = NumLinks; }
public override void Write(BuilderContext context) { if (!_written) { WriteFileData(context); WriteInode(context); _written = true; } }
private void WriteFileData(BuilderContext context) { Stream outStream = context.RawStream; bool disposeSource = false; try { if (_source == null) { _source = new FileStream(_sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read); disposeSource = true; } if (_source.Position != 0) { _source.Position = 0; } long startPos = outStream.Position; int bufferedBytes = Utilities.ReadFully(_source, context.IoBuffer, 0, context.DataBlockSize); if (bufferedBytes < context.DataBlockSize) { // Fragment - less than one complete block of data _inode.StartBlock = 0xFFFFFFFF; _inode.FragmentKey = context.WriteFragment(bufferedBytes, out _inode.FragmentOffset); _inode.FileSize = (uint)bufferedBytes; } else { // At least one full block, no fragments used _inode.FragmentKey = 0xFFFFFFFF; _lengths = new List<uint>(); _inode.StartBlock = (uint)startPos; _inode.FileSize = bufferedBytes; while (bufferedBytes > 0) { _lengths.Add(context.WriteDataBlock(context.IoBuffer, 0, bufferedBytes)); bufferedBytes = Utilities.ReadFully(_source, context.IoBuffer, 0, context.DataBlockSize); _inode.FileSize += (uint)bufferedBytes; } } } finally { if (disposeSource) { _source.Dispose(); } } }
/// <summary> /// Writes the file system to an existing stream. /// </summary> /// <param name="output">The stream to write to.</param> /// <remarks>The <c>output</c> stream must support seeking and writing.</remarks> public void Build(Stream output) { if (output == null) { throw new ArgumentNullException("output"); } if (!output.CanWrite) { throw new ArgumentException("Output stream must be writable", "output"); } if (!output.CanSeek) { throw new ArgumentException("Output stream must support seeking", "output"); } _context = new BuilderContext() { RawStream = output, DataBlockSize = DefaultBlockSize, IoBuffer = new byte[DefaultBlockSize], }; MetablockWriter inodeWriter = new MetablockWriter(); MetablockWriter dirWriter = new MetablockWriter(); FragmentWriter fragWriter = new FragmentWriter(_context); IdTableWriter idWriter = new IdTableWriter(_context); _context.AllocateInode = AllocateInode; _context.AllocateId = idWriter.AllocateId; _context.WriteDataBlock = WriteDataBlock; _context.WriteFragment = fragWriter.WriteFragment; _context.InodeWriter = inodeWriter; _context.DirectoryWriter = dirWriter; _nextInode = 1; SuperBlock superBlock = new SuperBlock(); superBlock.Magic = SuperBlock.SquashFsMagic; superBlock.CreationTime = DateTime.Now; superBlock.BlockSize = (uint)_context.DataBlockSize; superBlock.Compression = 1; // DEFLATE superBlock.BlockSizeLog2 = (ushort)Utilities.Log2(superBlock.BlockSize); superBlock.MajorVersion = 4; superBlock.MinorVersion = 0; output.Position = superBlock.Size; GetRoot().Reset(); GetRoot().Write(_context); fragWriter.Flush(); superBlock.RootInode = GetRoot().InodeRef; superBlock.InodesCount = _nextInode - 1; superBlock.FragmentsCount = (uint)fragWriter.FragmentCount; superBlock.UidGidCount = (ushort)idWriter.IdCount; superBlock.InodeTableStart = output.Position; inodeWriter.Persist(output); superBlock.DirectoryTableStart = output.Position; dirWriter.Persist(output); if (fragWriter.FragmentCount > 0) { superBlock.FragmentTableStart = output.Position; fragWriter.Persist(); } else { superBlock.FragmentTableStart = -1; } superBlock.LookupTableStart = -1; if (idWriter.IdCount > 0) { superBlock.UidGidTableStart = output.Position; idWriter.Persist(); } else { superBlock.UidGidTableStart = -1; } superBlock.ExtendedAttrsTableStart = -1; superBlock.BytesUsed = output.Position; // Pad to 4KB long end = Utilities.RoundUp(output.Position, 4 * Sizes.OneKiB); if (end != output.Position) { byte[] padding = new byte[(int)(end - output.Position)]; output.Write(padding, 0, padding.Length); } // Go back and write the superblock output.Position = 0; byte[] buffer = new byte[superBlock.Size]; superBlock.WriteTo(buffer, 0); output.Write(buffer, 0, buffer.Length); output.Position = end; }
public abstract void Write(BuilderContext context);
private void WriteInode(BuilderContext context) { if (NumLinks != 1) { throw new IOException("Extended file records (with multiple hard links) not supported"); } FillCommonInodeData(context); _inode.Type = InodeType.File; InodeRef = context.InodeWriter.Position; int totalSize = _inode.Size; _inode.WriteTo(context.IoBuffer, 0); if (_lengths != null && _lengths.Count > 0) { for (int i = 0; i < _lengths.Count; ++i) { Utilities.WriteBytesLittleEndian(_lengths[i], context.IoBuffer, _inode.Size + (i * 4)); } totalSize += _lengths.Count * 4; } context.InodeWriter.Write(context.IoBuffer, 0, totalSize); }
public IdTableWriter(BuilderContext context) { _context = context; _ids = new List<int>(); }
/// <summary> /// Writes the file system to an existing stream. /// </summary> /// <param name="output">The stream to write to.</param> /// <remarks>The <c>output</c> stream must support seeking and writing.</remarks> public void Build(Stream output) { if (output == null) { throw new ArgumentNullException("output"); } if (!output.CanWrite) { throw new ArgumentException("Output stream must be writable", "output"); } if (!output.CanSeek) { throw new ArgumentException("Output stream must support seeking", "output"); } _context = new BuilderContext() { RawStream = output, DataBlockSize = DefaultBlockSize, IoBuffer = new byte[DefaultBlockSize], }; MetablockWriter inodeWriter = new MetablockWriter(); MetablockWriter dirWriter = new MetablockWriter(); FragmentWriter fragWriter = new FragmentWriter(_context); IdTableWriter idWriter = new IdTableWriter(_context); _context.AllocateInode = AllocateInode; _context.AllocateId = idWriter.AllocateId; _context.WriteDataBlock = WriteDataBlock; _context.WriteFragment = fragWriter.WriteFragment; _context.InodeWriter = inodeWriter; _context.DirectoryWriter = dirWriter; _nextInode = 1; SuperBlock superBlock = new SuperBlock(); superBlock.Magic = SuperBlock.SquashFsMagic; superBlock.CreationTime = DateTime.Now; superBlock.BlockSize = (uint)_context.DataBlockSize; superBlock.Compression = 1; // DEFLATE superBlock.BlockSizeLog2 = (ushort)Utilities.Log2(superBlock.BlockSize); superBlock.MajorVersion = 4; superBlock.MinorVersion = 0; output.Position = superBlock.Size; GetRoot().Reset(); GetRoot().Write(_context); fragWriter.Flush(); superBlock.RootInode = GetRoot().InodeRef; superBlock.InodesCount = _nextInode - 1; superBlock.FragmentsCount = (uint)fragWriter.FragmentCount; superBlock.UidGidCount = (ushort)idWriter.IdCount; superBlock.InodeTableStart = output.Position; inodeWriter.Persist(output); superBlock.DirectoryTableStart = output.Position; dirWriter.Persist(output); if (fragWriter.FragmentCount > 0) { superBlock.FragmentTableStart = output.Position; fragWriter.Persist(); } else { superBlock.FragmentTableStart = -1; } superBlock.LookupTableStart = -1; if (idWriter.IdCount > 0) { superBlock.UidGidTableStart = output.Position; idWriter.Persist(); } else { superBlock.UidGidTableStart = -1; } superBlock.ExtendedAttrsTableStart = -1; superBlock.BytesUsed = output.Position; // Pad to 4KB long end = Utilities.RoundUp(output.Position, 4 * Sizes.OneKiB); if (end != output.Position) { byte[] padding = new byte[(int)(end - output.Position)]; output.Write(padding, 0, padding.Length); } // Go back and write the superblock output.Position = 0; byte[] buffer = new byte[superBlock.Size]; superBlock.WriteTo(buffer, 0); output.Write(buffer, 0, buffer.Length); output.Position = end; }
private void WriteDirectory(BuilderContext context) { MetadataRef startPos = context.DirectoryWriter.Position; int currentChild = 0; int numDirs = 0; while (currentChild < _children.Count) { long thisBlock = _children[currentChild].Node.InodeRef.Block; int firstInode = _children[currentChild].Node.InodeNumber; int count = 1; while (currentChild + count < _children.Count && _children[currentChild + count].Node.InodeRef.Block == thisBlock && _children[currentChild + count].Node.InodeNumber - firstInode < 0x7FFF) { ++count; } DirectoryHeader hdr = new DirectoryHeader() { Count = count - 1, InodeNumber = firstInode, StartBlock = (int)thisBlock }; hdr.WriteTo(context.IoBuffer, 0); context.DirectoryWriter.Write(context.IoBuffer, 0, hdr.Size); for (int i = 0; i < count; ++i) { Entry child = _children[currentChild + i]; DirectoryRecord record = new DirectoryRecord() { Offset = (ushort)child.Node.InodeRef.Offset, InodeNumber = (short)(child.Node.InodeNumber - firstInode), Type = child.Node.Inode.Type, Name = child.Name }; record.WriteTo(context.IoBuffer, 0); context.DirectoryWriter.Write(context.IoBuffer, 0, record.Size); if (child.Node.Inode.Type == InodeType.Directory || child.Node.Inode.Type == InodeType.ExtendedDirectory) { ++numDirs; } } currentChild += count; } long size = context.DirectoryWriter.DistanceFrom(startPos); if (size > uint.MaxValue) { throw new NotImplementedException("Writing large directories"); } NumLinks = numDirs + 2; // +1 for self, +1 for parent _inode.StartBlock = (uint)startPos.Block; _inode.Offset = (ushort)startPos.Offset; _inode.FileSize = (uint)size + 3; // For some reason, always +3 }