Ejemplo n.º 1
0
        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);
                }
        }