123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- package tools
- import (
- "fmt"
- "go-common/library/log"
- "os"
- "path/filepath"
- "time"
- )
- type zipSections struct {
- beforeSigningBlock []byte
- signingBlock []byte
- signingBlockOffset int64
- centraDir []byte
- centralDirOffset int64
- eocd []byte
- eocdOffset int64
- }
- type transform func(*zipSections) (*zipSections, error)
- func (z *zipSections) writeTo(output string, transform transform) (err error) {
- f, err := os.Create(output)
- if err != nil {
- return
- }
- defer f.Close()
- newZip, err := transform(z)
- if err != nil {
- return
- }
- for _, s := range [][]byte{
- newZip.beforeSigningBlock,
- newZip.signingBlock,
- newZip.centraDir,
- newZip.eocd} {
- _, err := f.Write(s)
- if err != nil {
- return err
- }
- }
- return
- }
- var _debug bool
- // GenerateChannelApk generate a specified channel apk
- func GenerateChannelApk(out string, channel string, extras map[string]string, input string, force bool, debug bool) (output string, err error) {
- _debug = debug
- if len(input) == 0 {
- err = fmt.Errorf("Error: no input file specified")
- log.Error("no input file specified, error(%v)", err)
- return
- }
- if _, err = os.Stat(input); os.IsNotExist(err) {
- return
- }
- if len(out) == 0 {
- out = filepath.Dir(input)
- } else {
- var fi os.FileInfo
- err = os.MkdirAll(out, 0755)
- if err != nil {
- log.Error("os.MkdirAll(%s) error(%v)", out, err)
- return
- }
- fi, err = os.Stat(out)
- println("error %v", err)
- if os.IsNotExist(err) || !fi.IsDir() {
- err = fmt.Errorf("Error: output %s is neither exist nor a dir", out)
- log.Error("output %s is neither exist nor a dir, error(%v)", out, err)
- return
- }
- }
- if channel == "" {
- err = fmt.Errorf("Error: no channel specified")
- log.Error("no channel specified, error(%v)", err)
- return
- }
- //TODO: add new option for generating new channel from channelled apk
- if c, _ := readChannelInfo(input); len(c.Channel) != 0 {
- err = fmt.Errorf("Error: file %s is registered a channel block %s", filepath.Base(input), c.String())
- log.Error("file %s is registered a channel block %s, error(%v)", filepath.Base(input), c.String(), err)
- return
- }
- var start time.Time
- if _debug {
- start = time.Now()
- }
- z, err := newZipSections(input)
- if err != nil {
- err = fmt.Errorf("Error occurred on parsing apk %s, %s", input, err)
- log.Error("Error occurred on parsing apk %s, error(%v)", input, err)
- return
- }
- name, ext := fileNameAndExt(input)
- output = filepath.Join(out, name+"-"+channel+ext)
- c := ChannelInfo{Channel: channel, Extras: extras}
- err = gen(c, z, output, force)
- if err != nil {
- err = fmt.Errorf("Error occurred on generating channel %s, %s", channel, err)
- log.Error("Error occurred on generating channel %s, error(%v)", input, err)
- return
- }
- if _debug {
- println("Consume", time.Since(start).String())
- } else {
- println("Done!")
- }
- return
- }
- func newZipSections(input string) (z zipSections, err error) {
- in, err := os.Open(input)
- if err != nil {
- return
- }
- defer in.Close()
- // read eocd
- eocd, eocdOffset, err := findEndOfCentralDirectoryRecord(in)
- if err != nil {
- return
- }
- centralDirOffset := getEocdCentralDirectoryOffset(eocd)
- centralDirSize := getEocdCentralDirectorySize(eocd)
- z.eocd = eocd
- z.eocdOffset = eocdOffset
- z.centralDirOffset = int64(centralDirOffset)
- // read signing block
- signingBlock, signingBlockOffset, err := findApkSigningBlock(in, centralDirOffset)
- if err != nil {
- return
- }
- z.signingBlock = signingBlock
- z.signingBlockOffset = signingBlockOffset
- // read bytes before signing block
- //TODO: waste too large memory
- if signingBlockOffset >= 64*1024*1024 {
- fmt.Print("Warning: maybe waste large memory on processing this apk! ")
- fmt.Println("Before APK Signing Block bytes size is", signingBlockOffset/1024/1024, "MB")
- }
- beforeSigningBlock := make([]byte, signingBlockOffset)
- n, err := in.ReadAt(beforeSigningBlock, 0)
- if err != nil {
- return
- }
- if int64(n) != signingBlockOffset {
- return z, fmt.Errorf("Read bytes count mismatched! Expect %d, but %d", signingBlockOffset, n)
- }
- z.beforeSigningBlock = beforeSigningBlock
- centralDir := make([]byte, centralDirSize)
- n, err = in.ReadAt(centralDir, int64(centralDirOffset))
- if uint32(n) != centralDirSize {
- return z, fmt.Errorf("Read bytes count mismatched! Expect %d, but %d", centralDirSize, n)
- }
- z.centraDir = centralDir
- if _debug {
- fmt.Printf("signingBlockOffset=%d, signingBlockLenth=%d\n"+
- "centralDirOffset=%d, centralDirSize=%d\n"+
- "eocdOffset=%d, eocdLenthe=%d\n",
- signingBlockOffset,
- len(signingBlock),
- centralDirOffset,
- centralDirSize,
- eocdOffset,
- len(eocd))
- }
- return
- }
- func gen(info ChannelInfo, sections zipSections, output string, force bool) (err error) {
- fi, err := os.Stat(output)
- if err != nil && !os.IsNotExist(err) {
- return err
- }
- if fi != nil {
- if !force {
- return fmt.Errorf("file already exists %s", output)
- }
- println("Force generating channel", info.Channel)
- }
- var s time.Time
- if _debug {
- s = time.Now()
- }
- err = sections.writeTo(output, newTransform(info))
- if _debug {
- fmt.Printf(" write %s consume %s", output, time.Since(s).String())
- fmt.Println()
- }
- return
- }
- func newTransform(info ChannelInfo) transform {
- return func(zip *zipSections) (*zipSections, error) {
- newBlock, diffSize, err := makeSigningBlockWithChannelInfo(info, zip.signingBlock)
- if err != nil {
- return nil, err
- }
- newzip := new(zipSections)
- newzip.beforeSigningBlock = zip.beforeSigningBlock
- newzip.signingBlock = newBlock
- newzip.signingBlockOffset = zip.signingBlockOffset
- newzip.centraDir = zip.centraDir
- newzip.centralDirOffset = zip.centralDirOffset
- newzip.eocdOffset = zip.eocdOffset
- newzip.eocd = makeEocd(zip.eocd, uint32(int64(diffSize)+zip.centralDirOffset))
- return newzip, nil
- }
- }
|