232 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			232 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package asar // import "b612.me/asar"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"io"
							 | 
						||
| 
								 | 
							
									"io/ioutil"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"path/filepath"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Flag is a bit field of Entry flags.
							 | 
						||
| 
								 | 
							
								type Flag uint32
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const (
							 | 
						||
| 
								 | 
							
									// FlagNone denotes an entry with no flags.
							 | 
						||
| 
								 | 
							
									FlagNone Flag = 0
							 | 
						||
| 
								 | 
							
									// FlagDir denotes a directory entry.
							 | 
						||
| 
								 | 
							
									FlagDir Flag = 1 << iota
							 | 
						||
| 
								 | 
							
									// FlagExecutable denotes a file with the executable bit set.
							 | 
						||
| 
								 | 
							
									FlagExecutable
							 | 
						||
| 
								 | 
							
									// FlagUnpacked denotes that the entry's contents are not included in
							 | 
						||
| 
								 | 
							
									// the archive.
							 | 
						||
| 
								 | 
							
									FlagUnpacked
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Entry is a file or a folder in an ASAR archive.
							 | 
						||
| 
								 | 
							
								type Entry struct {
							 | 
						||
| 
								 | 
							
									Name     string
							 | 
						||
| 
								 | 
							
									Size     int64
							 | 
						||
| 
								 | 
							
									Offset   int64
							 | 
						||
| 
								 | 
							
									Flags    Flag
							 | 
						||
| 
								 | 
							
									Parent   *Entry
							 | 
						||
| 
								 | 
							
									Children []*Entry
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									r          io.ReaderAt
							 | 
						||
| 
								 | 
							
									baseOffset int64
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// New creates a new Entry.
							 | 
						||
| 
								 | 
							
								func New(name string, ra io.ReaderAt, size, offset int64, flags Flag) *Entry {
							 | 
						||
| 
								 | 
							
									return &Entry{
							 | 
						||
| 
								 | 
							
										Name:   name,
							 | 
						||
| 
								 | 
							
										Size:   size,
							 | 
						||
| 
								 | 
							
										Offset: offset,
							 | 
						||
| 
								 | 
							
										Flags:  flags,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										r: ra,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// FileInfo returns the os.FileInfo information about the entry.
							 | 
						||
| 
								 | 
							
								func (e *Entry) FileInfo() os.FileInfo {
							 | 
						||
| 
								 | 
							
									return fileInfo{e}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type fileInfo struct {
							 | 
						||
| 
								 | 
							
									e *Entry
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f fileInfo) Name() string {
							 | 
						||
| 
								 | 
							
									return f.e.Name
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f fileInfo) Size() int64 {
							 | 
						||
| 
								 | 
							
									return f.e.Size
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f fileInfo) Mode() os.FileMode {
							 | 
						||
| 
								 | 
							
									if f.e.Flags&FlagDir != 0 {
							 | 
						||
| 
								 | 
							
										return 0555 | os.ModeDir
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if f.e.Flags&FlagExecutable != 0 {
							 | 
						||
| 
								 | 
							
										return 0555
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return 0444
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f fileInfo) ModTime() time.Time {
							 | 
						||
| 
								 | 
							
									return time.Time{}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f fileInfo) IsDir() bool {
							 | 
						||
| 
								 | 
							
									return f.e.Flags&FlagDir != 0
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f fileInfo) Sys() interface{} {
							 | 
						||
| 
								 | 
							
									return f.e
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Path returns the file path to the entry.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// For example, given the following tree structure:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//	root
							 | 
						||
| 
								 | 
							
								//	 - sub1
							 | 
						||
| 
								 | 
							
								//	 - sub2
							 | 
						||
| 
								 | 
							
								//	   - file2.jpg
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// file2.jpg's path would be:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//	sub2/file2.jpg
							 | 
						||
| 
								 | 
							
								func (e *Entry) Path() string {
							 | 
						||
| 
								 | 
							
									if e.Parent == nil {
							 | 
						||
| 
								 | 
							
										return ""
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var p []string
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for e != nil && e.Parent != nil {
							 | 
						||
| 
								 | 
							
										p = append(p, e.Name)
							 | 
						||
| 
								 | 
							
										e = e.Parent
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									l := len(p) / 2
							 | 
						||
| 
								 | 
							
									for i := 0; i < l; i++ {
							 | 
						||
| 
								 | 
							
										j := len(p) - i - 1
							 | 
						||
| 
								 | 
							
										p[i], p[j] = p[j], p[i]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return strings.Join(p, "/")
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Open returns an *io.SectionReader of the entry's contents. nil is returned if
							 | 
						||
| 
								 | 
							
								// the entry cannot be opened (e.g. because it is a directory).
							 | 
						||
| 
								 | 
							
								func (e *Entry) Open() *io.SectionReader {
							 | 
						||
| 
								 | 
							
									if e.Flags&FlagDir != 0 || e.Flags&FlagUnpacked != 0 {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return io.NewSectionReader(e.r, e.baseOffset+e.Offset, e.Size)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// WriteTo writes the entry's contents to the given writer. nil is returned if the entry cannot
							 | 
						||
| 
								 | 
							
								// be opened (e.g. if the entry is a directory).
							 | 
						||
| 
								 | 
							
								func (e *Entry) WriteTo(w io.Writer) (n int64, err error) {
							 | 
						||
| 
								 | 
							
									r := e.Open()
							 | 
						||
| 
								 | 
							
									if r == nil {
							 | 
						||
| 
								 | 
							
										return 0, errors.New("asar: entry cannot be opened")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return io.Copy(w, r)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Bytes returns the entry's contents as a byte slice. nil is returned if the
							 | 
						||
| 
								 | 
							
								// entry cannot be read.
							 | 
						||
| 
								 | 
							
								func (e *Entry) Bytes() []byte {
							 | 
						||
| 
								 | 
							
									body := e.Open()
							 | 
						||
| 
								 | 
							
									if body == nil {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									b, err := ioutil.ReadAll(body)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return b
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Bytes returns the entry's contents as a string. nil is returned if the entry
							 | 
						||
| 
								 | 
							
								// cannot be read.
							 | 
						||
| 
								 | 
							
								func (e *Entry) String() string {
							 | 
						||
| 
								 | 
							
									body := e.Bytes()
							 | 
						||
| 
								 | 
							
									if body == nil {
							 | 
						||
| 
								 | 
							
										return ""
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return string(body)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Find searches for a sub-entry of the current entry. nil is returned if the
							 | 
						||
| 
								 | 
							
								// requested sub-entry cannot be found.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// For example, given the following tree structure:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//	root
							 | 
						||
| 
								 | 
							
								//	 - sub1
							 | 
						||
| 
								 | 
							
								//	 - sub2
							 | 
						||
| 
								 | 
							
								//	   - sub2.1
							 | 
						||
| 
								 | 
							
								//	     - file2.jpg
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The following expression would return the .jpg *Entry:
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//	root.Find("sub2", "sub2.1", "file2.jpg")
							 | 
						||
| 
								 | 
							
								func (e *Entry) Find(path ...string) *Entry {
							 | 
						||
| 
								 | 
							
								pathLoop:
							 | 
						||
| 
								 | 
							
									for _, name := range path {
							 | 
						||
| 
								 | 
							
										for _, child := range e.Children {
							 | 
						||
| 
								 | 
							
											if child.Name == name {
							 | 
						||
| 
								 | 
							
												e = child
							 | 
						||
| 
								 | 
							
												continue pathLoop
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return e
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Walk recursively walks over the entry's children. See filepath.Walk and
							 | 
						||
| 
								 | 
							
								// filepath.WalkFunc for more information.
							 | 
						||
| 
								 | 
							
								func (e *Entry) Walk(walkFn filepath.WalkFunc) error {
							 | 
						||
| 
								 | 
							
									return walk(e, "", walkFn)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func walk(e *Entry, parentPath string, walkFn filepath.WalkFunc) error {
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(e.Children); i++ {
							 | 
						||
| 
								 | 
							
										child := e.Children[i]
							 | 
						||
| 
								 | 
							
										childPath := parentPath + child.Name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										err := walkFn(childPath, child.FileInfo(), nil)
							 | 
						||
| 
								 | 
							
										if err == filepath.SkipDir {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if child.Flags&FlagDir == 0 {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if err := walk(child, childPath+"/", walkFn); err != nil {
							 | 
						||
| 
								 | 
							
											return err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func validFilename(filename string) bool {
							 | 
						||
| 
								 | 
							
									if filename == "." || filename == ".." {
							 | 
						||
| 
								 | 
							
										return false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return strings.IndexAny(filename, "\x00\\/") == -1
							 | 
						||
| 
								 | 
							
								}
							 |