package commands import ( "archive/tar" "bytes" "encoding/hex" "errors" "fmt" "io" "os" "strings" "git.cheetah.cat/cheetah/moto-flash-data/flashpart" "github.com/rs/zerolog/log" ) type MparPaxReplaceCommand struct { InputFilename string `long:"mpar" short:"m" required:"true" description:"Source Partition File"` OutputFilename string `long:"output" short:"o" required:"true" description:"Target Partition File"` FindFileName string `long:"file" short:"f" required:"true" description:"Name of the File to Find"` ReplaceWithFilename string `long:"replacewith" short:"r" required:"true" description:"Source Bytes for Replace-Action"` } func (command *MparPaxReplaceCommand) Execute(args []string) error { inputFile, err := os.Open(command.InputFilename) if err != nil { return err } defer inputFile.Close() replaceWithFile, err := os.Open(command.ReplaceWithFilename) if err != nil { return err } defer replaceWithFile.Close() inputFileStat, err := inputFile.Stat() if err != nil { return err } log.Info().Int64("fileSize", inputFileStat.Size()).Msg("Reading raw file...") pdata := make([]byte, inputFileStat.Size()) _, err = io.ReadFull(inputFile, pdata) if err != nil { return err } fpHeader := flashpart.ParseFlashPartHeader(pdata[:32]) log.Info().Msg( fmt.Sprintf("Media-ID: 0x%02X, Partition Size: 0x%08X / %d bytes, Partition Tag: %s", fpHeader.MediaID, fpHeader.TotalSize, fpHeader.TotalSize, fpHeader.VersionText, ), ) log.Info().Msg( fmt.Sprintf("Original-Full-Header-Hex: %s", hex.EncodeToString(pdata[:32])), ) if len(pdata[32:]) != len(pdata)-32 { return errors.New("something doesnt add up") } if len(pdata[32:]) != int(fpHeader.TotalSize)-32 { log.Error().Uint32("header ts", fpHeader.TotalSize).Uint32("len pdata", uint32(len(pdata))).Msg("size mismatch") return nil } breader := bytes.NewReader(pdata[32:]) tarReader := tar.NewReader(breader) temporaryPaxFile, _ := os.CreateTemp("", "pax-replace.tar") defer temporaryPaxFile.Close() tarWriter := tar.NewWriter(temporaryPaxFile) defer tarWriter.Close() for { header, err := tarReader.Next() if err == io.EOF { break } if err != nil { log.Error().Err(err) return err } if header.Typeflag == tar.TypeReg && !strings.Contains(header.Name, "._") && header.Name == command.FindFileName { log.Info().Str("fileName", header.Name).Int64("fileSize", header.Size).Msg("found file to modify") replaceWithFileStat, err := replaceWithFile.Stat() if err != nil { return err } header.Size = replaceWithFileStat.Size() header.ModTime = replaceWithFileStat.ModTime() tarWriter.WriteHeader(header) content, err := io.ReadAll(replaceWithFile) if err != nil { log.Error().Err(err) } tarWriter.Write(content) } else { tarWriter.WriteHeader(header) content, err := io.ReadAll(tarReader) if err != nil { log.Error().Err(err) } tarWriter.Write(content) } } //log.Error().Msg("no file replaced") // create new mpar with same header base _, err = temporaryPaxFile.Seek(0, 0) if err != nil { return err } temporaryPaxFileStat, err := temporaryPaxFile.Stat() if err != nil { return err } rawData := make([]byte, temporaryPaxFileStat.Size()) _, err = io.ReadFull(temporaryPaxFile, rawData) if err != nil { return err } outputFile, err := os.Create(command.OutputFilename) if err != nil { return err } defer outputFile.Close() fpHeader.AdjustRawSize(uint32(len(rawData))) log.Info().Msg( fmt.Sprintf("New-Full-Header-Hex: %s", hex.EncodeToString(fpHeader.GetBytes())), ) fpHeaderNew := flashpart.ParseFlashPartHeader(fpHeader.GetBytes()) log.Info().Msg( fmt.Sprintf("Media-ID: 0x%02X, Partition Size: 0x%08X / %d bytes, Partition Tag: %s", fpHeaderNew.MediaID, fpHeaderNew.TotalSize, fpHeaderNew.TotalSize, fpHeaderNew.VersionText, ), ) _, err = outputFile.Write(fpHeaderNew.GetBytes()) if err != nil { return err } _, err = outputFile.Write(rawData) if err != nil { return err } return nil }