public override (byte[], int) Read(VFile file, int length, long offset) { //Prevent overflow to say... next tar header int actual_read_length = Math.Min((int)(file.Size - offset), length); byte[] buffer = new byte[length]; int bytesread = 0; TarState state = states[file.AbsolutePath]; using (FileStream fs = File.OpenRead(filepath)) using (BinaryReader reader = new BinaryReader(fs)) { reader.BaseStream.Position = state.offset_in_tar_file + offset; bytesread = reader.Read(buffer, bytesread, actual_read_length); } return(buffer, bytesread); }
public override bool Load_statemap() { states.Clear(); fileList.Clear(); folderList.Clear(); folderList.Add("/", VFolder.RootFolder); if (!File.Exists(statemapPath)) { return(false); } using (StreamReader reader = File.OpenText(statemapPath)) { string s; while ((s = reader.ReadLine()) != null) { if (s == "" || s.StartsWith("#")) { continue; } string[] rawStates = s.Split('\0'); TarState state = new TarState(); state.type = rawStates[0][0]; state.fullFilepath = rawStates[1]; state.offset_in_tar_file = long.Parse(rawStates[2]); state.size = long.Parse(rawStates[3]); if (state.type == '0') { fileList.Add(state.fullFilepath, new VFile(state.fullFilepath, state.size)); } else { folderList.Add(state.fullFilepath, new VFolder(state.fullFilepath, state.size)); } states.Add(state.fullFilepath, state); } } return(true); }
public override bool Generate_statemap() { //Long filepath related variables, longPathName is used as cache for long filepath readed. bool hasLongFilepath = false; string stored_Longname = ""; //PAX Extended filename bool hasPAXFileName = false; string stored_PAX_Filename = ""; //PAX Global extended filename bool hasPAXGlobalFileName = false; string stored_PAX_Global_Filename = ""; using (FileStream fs = File.OpenRead(filepath)) using (BinaryReader reader = new BinaryReader(fs, Encoding.UTF8)) { while (!Skip_emprty_blocks(reader) && reader.BaseStream.Position < reader.BaseStream.Length) { long start_position = reader.BaseStream.Position; //check header integrity if (!verify_header(reader)) { throw new FileCorruoptedException( $"The provided tar file might be corrupted (Header position: 0x{reader.BaseStream.Position.ToString("X")})"); } reader.BaseStream.Position = start_position; current_state = new TarState(); current_state.fullFilepath = ByteUtil.To_readable_string(reader.ReadBytes(100)).Trim('\0'); if (states.ContainsKey(current_state.fullFilepath)) { throw new ParseErrorException("Dupelicated filepath found in tar file, There is currently no plan to support hardlink/softlink"); } //Skip metadata that has no use to this program //File mode, Owner's numeric user ID and Group's numeric user ID reader.BaseStream.Position += 24; //File size current_state.size = Read_file_size(reader); //Skip metadata that has no use to this program //Last modification time in numeric Unix time format (octal) and Checksum for header record reader.BaseStream.Position += 20; //Type flag char type_flag = reader.ReadChar(); if (type_flag == '\0') { type_flag = TarType.File; } if (type_flag == TarType.File || //File type_flag == TarType.Directory || //Directory type_flag == TarType.GNU_Longname || //GNU_Longname type_flag == TarType.PAX_Extended_header || //PAX Extended header type_flag == TarType.PAX_Global_extended_header) //PAX Global extended header { current_state.type = type_flag; } else { throw new NotSupportedException("This Tar file is currently not supported, type:" + type_flag); } //Skip metadata that has no use to this program //Name of linked file reader.BaseStream.Position += 100; //This is the end of v7 header //Check for ustar marker if (ByteUtil.To_readable_string(reader.ReadBytes(6)) == "ustar" && reader.ReadByte() == char.MinValue) { //Skip metadata that has no use to this program //UStar version "00", Owner user name, Owner group name, Device major number and Device minor number reader.BaseStream.Position += 82; //filepath prefix current_state.fullFilepath = ByteUtil.To_readable_string(reader.ReadBytes(155)).Trim('\0') + current_state.fullFilepath; if (states.ContainsKey(current_state.fullFilepath)) { throw new ParseErrorException("Dupelicated filepath found in tar file, There is currently no plan to support hardlink/softlink"); } } //Restore saved file name if there is any. if (hasLongFilepath) { current_state.fullFilepath = stored_Longname; hasLongFilepath = false; } else if (hasPAXFileName) { current_state.fullFilepath = stored_PAX_Filename; hasPAXFileName = false; } else if (hasPAXGlobalFileName) { current_state.fullFilepath = stored_PAX_Global_Filename; } //final processing current_state.fullFilepath = VFile.Unify_filepath(current_state.fullFilepath); long roundup_filesize = current_state.size; if (current_state.size % 512 > 0) { roundup_filesize += 512 - current_state.size % 512; } current_state.offset_in_tar_file = start_position + 512; //512 == size of tar header roundup reader.BaseStream.Position = current_state.offset_in_tar_file; if (type_flag == TarType.GNU_Longname) { //read longPathName before we skip it. hasLongFilepath = true; //TODO: support long path loger than 2147483647 bytes (aka 2147483647 UTF-8 characters) (I doubt I will do this in a decade) stored_Longname = ByteUtil.To_readable_string(reader.ReadBytes((int)current_state.size)).Trim('\0'); } else if (type_flag == TarType.PAX_Extended_header) { //PAX Extended header (bool, string)result = read_PAX_header(reader, current_state.size); hasPAXFileName = result.Item1; stored_PAX_Filename = result.Item2; } else if (type_flag == TarType.PAX_Global_extended_header) { //PAX Global extended header (bool, string)result = read_PAX_header(reader, current_state.size); hasPAXGlobalFileName = result.Item1; stored_PAX_Global_Filename = result.Item2; } else { states.Add(current_state.fullFilepath, current_state); } reader.BaseStream.Position = current_state.offset_in_tar_file + roundup_filesize; //Register the folder/file we read. if (type_flag == TarType.Directory) { //This is a folder VFolder folder = new VFolder(current_state.fullFilepath, current_state.size); folderList.Add(folder.AbsolutePath, folder); } else if (type_flag == TarType.File) { //This is a file VFile file = new VFile(current_state.fullFilepath, current_state.size); folderList[file.Prefix].Files.Add(file); fileList.Add(file.AbsolutePath, file); } else if (type_flag != TarType.GNU_Longname && type_flag != TarType.PAX_Extended_header && type_flag != TarType.PAX_Global_extended_header) { //It is also not other supported flag??? throw new FileCorruoptedException($"Unknown type flag detected {(char)type_flag}, it's probably (99.9%) due to developer not taken care of the code."); } } return(true); } }