From 3f1b2a8d1edb9eef313f04f1c4cf721baa5fe1e7 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Fri, 29 Jan 2021 12:55:57 -0600 Subject: [PATCH] Restructure for 1.16 io/fs interface --- direntry_fileinfo.go | 126 ++++ file.go | 1036 +++++++++++++++---------------- file2.go | 157 +++++ fs.go | 89 +++ internal/directory/directory.go | 51 +- reader.go | 193 +++--- reader_test.go | 42 +- 7 files changed, 1004 insertions(+), 690 deletions(-) create mode 100644 direntry_fileinfo.go create mode 100644 file2.go create mode 100644 fs.go diff --git a/direntry_fileinfo.go b/direntry_fileinfo.go new file mode 100644 index 0000000..9e2100e --- /dev/null +++ b/direntry_fileinfo.go @@ -0,0 +1,126 @@ +package squashfs + +import ( + "io" + "io/fs" + "time" + + "github.com/CalebQ42/squashfs/internal/directory" + "github.com/CalebQ42/squashfs/internal/inode" +) + +type DirEntry struct { + en *directory.Entry + parent *FS + r *Reader +} + +func (r *Reader) newDirEntry(en *directory.Entry, parent *FS) *DirEntry { + return &DirEntry{ + en: en, + parent: parent, + r: r, + } +} + +func (d DirEntry) Name() string { + return d.en.Name +} + +func (d DirEntry) IsDir() bool { + return d.en.Type == inode.DirType +} + +func (d DirEntry) Type() fs.FileMode { + switch d.en.Type { + case inode.DirType: + return fs.ModeDir + case inode.SymType: + return fs.ModeSymlink + default: + return 0 + } +} + +func (d DirEntry) Info() (fs.FileInfo, error) { + in, err := d.r.getInodeFromEntry(d.en) + if err != nil { + return nil, err + } + return &FileInfo{ + name: d.en.Name, + i: in, + parent: d.parent, + r: d.r, + }, nil +} + +//GetInodeFromEntry returns the inode associated with a given directory.Entry +func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) { + br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.InodeOffset))) + if err != nil { + return nil, err + } + _, err = br.Seek(int64(en.InodeBlockOffset), io.SeekStart) + if err != nil { + return nil, err + } + i, err := inode.ProcessInode(br, r.super.BlockSize) + if err != nil { + return nil, err + } + return i, nil +} + +type FileInfo struct { + i *inode.Inode + parent *FS + r *Reader + name string +} + +func (f FileInfo) Name() string { + return f.name +} + +func (f FileInfo) Size() int64 { + switch f.i.Type { + case inode.FileType: + return int64(f.i.Info.(inode.File).Size) + case inode.ExtFileType: + return int64(f.i.Info.(inode.ExtFile).Size) + } + return 0 +} + +func (f FileInfo) Mode() fs.FileMode { + mode := fs.FileMode(f.i.Permissions) + switch f.i.Type { + case inode.DirType | inode.ExtDirType: + return mode | fs.ModeDir + case inode.ExtDirType: + return mode | fs.ModeDir + case inode.SymType: + return mode | fs.ModeSymlink + case inode.ExtSymType: + return mode | fs.ModeSymlink + } + return mode +} + +func (f FileInfo) ModTime() time.Time { + return time.Unix(int64(f.i.ModifiedTime), 0) +} + +func (f FileInfo) IsDir() bool { + return f.i.Type == inode.DirType || f.i.Type == inode.ExtDirType +} + +func (f FileInfo) Sys() interface{} { + return &File{ + name: f.name, + i: f.i, + r: f.r, + parent: f.parent, + } +} diff --git a/file.go b/file.go index 6b391d2..1a82475 100644 --- a/file.go +++ b/file.go @@ -1,566 +1,516 @@ -package squashfs +// package squashfs -import ( - "errors" - "fmt" - "io" - "os" - "path" - "strings" - "time" +// import ( +// "errors" +// "fmt" +// "io" +// "os" +// "path" +// "strings" +// "time" - "github.com/CalebQ42/squashfs/internal/directory" - "github.com/CalebQ42/squashfs/internal/inode" -) +// "github.com/CalebQ42/squashfs/internal/directory" +// "github.com/CalebQ42/squashfs/internal/inode" +// ) -//TODO: implement fs.FS, fs.ReadDirFile, fs.ReadFileFS, fs.StatFS, fs.SubFS with 1.16 +// //TODO: implement fs.FS, fs.ReadDirFile, fs.ReadFileFS, fs.StatFS, fs.SubFS with 1.16 -var ( - //ErrNotDirectory is returned when you're trying to do directory things with a non-directory - errNotDirectory = errors.New("File is not a directory") - //ErrNotFile is returned when you're trying to do file things with a directory - errNotFile = errors.New("File is not a file") - //ErrNotReading is returned when running functions that are only meant to be used when reading a squashfs - errNotReading = errors.New("Function only supported when reading a squashfs") - //ErrBrokenSymlink is returned when using ExtractWithOptions with the unbreakSymlink set to true, but the symlink's file cannot be extracted. - ErrBrokenSymlink = errors.New("Extracted symlink is probably broken") -) +// var ( +// //ErrNotDirectory is returned when you're trying to do directory things with a non-directory +// errNotDirectory = errors.New("File is not a directory") +// //ErrNotFile is returned when you're trying to do file things with a directory +// errNotFile = errors.New("File is not a file") +// //ErrNotReading is returned when running functions that are only meant to be used when reading a squashfs +// errNotReading = errors.New("Function only supported when reading a squashfs") +// //ErrBrokenSymlink is returned when using ExtractWithOptions with the unbreakSymlink set to true, but the symlink's file cannot be extracted. +// ErrBrokenSymlink = errors.New("Extracted symlink is probably broken") +// ) -//File is the main way to interact with files within squashfs, or when putting files into a squashfs. -//File can be either a file or folder. When reading from a squashfs, it reads from the datablocks. -//When writing, this holds the information on WHERE the file will be placed inside the archive. -// -//If copying data from a squashfs, the returned reader from io.Sys() implements io.WriterTo which -//will be significantly faster then calling Read directly. -//Ex: use io.Sys().(io.Reader) for io.Copy instead of using the File directly. -// -//Implements os.FileInfo and io.Reader -type File struct { - reader io.Reader - Parent *File - r *Reader //Underlying reader. When writing, will probably be an os.File. When reading this is kept nil UNTIL reading to save memory. - in *inode.Inode - name string - dir string - filType int //The file's type, using inode types. +// //File is the main way to interact with files within squashfs, or when putting files into a squashfs. +// //File can be either a file or folder. When reading from a squashfs, it reads from the datablocks. +// //When writing, this holds the information on WHERE the file will be placed inside the archive. +// // +// //If copying data from a squashfs, the returned reader from io.Sys() implements io.WriterTo which +// //will be significantly faster then calling Read directly. +// //Ex: use io.Sys().(io.Reader) for io.Copy instead of using the File directly. +// // +// //Implements os.FileInfo and io.Reader +// type File struct { +// reader io.Reader +// Parent *File +// r *Reader //Underlying reader. When writing, will probably be an os.File. When reading this is kept nil UNTIL reading to save memory. +// in *inode.Inode +// name string +// dir string +// filType int //The file's type, using inode types. -} - -//get a File from a directory.entry -func (r *Reader) newFileFromDirEntry(entry *directory.Entry) (fil *File, err error) { - fil = new(File) - fil.in, err = r.getInodeFromEntry(entry) - if err != nil { - return nil, err - } - fil.name = entry.Name - fil.r = r - fil.filType = fil.in.Type - return -} - -//Name is the file's name -func (f *File) Name() string { - return f.name -} - -//Size is the complete size of the file. Zero if it's not a file. -func (f *File) Size() int64 { - switch f.filType { - case inode.FileType: - return int64(f.in.Info.(inode.File).Size) - case inode.ExtFileType: - return int64(f.in.Info.(inode.ExtFile).Size) - default: - return 0 - } -} - -//ModTime is the time of last modification. -func (f *File) ModTime() time.Time { - return time.Unix(int64(f.in.Header.ModifiedTime), 0) -} - -//Sys returns the underlying reader. If the reader isn't initialized, it will initialize it. -//If called on something other then a file, returns nil. -func (f *File) Sys() interface{} { - if !f.IsFile() { - return nil - } - if f.reader == nil && f.r != nil { - var err error - f.reader, err = f.r.newFileReader(f.in) - if err != nil { - return nil - } - } - return f.reader -} - -//TODO: Implement below when 1.16 drops to satisfy fs.File - -//Stat simply returns the file. It's simply here to satisfy fs.File -// func (f *File) Stat() (fs.FileInfo, error) { -// return f, nil // } -//Close does nothing. It's simply here to satisfy fs.File -//TODO: add actual implementation -// func (f *File) Close() error { +// //get a File from a directory.entry +// func (r *Reader) newFileFromDirEntry(entry *directory.Entry) (fil *File, err error) { +// fil = new(File) +// fil.in, err = r.getInodeFromEntry(entry) +// if err != nil { +// return nil, err +// } +// fil.name = entry.Name +// fil.r = r +// fil.filType = fil.in.Type +// return +// } + +// //Name is the file's name +// func (f *File) Name() string { +// return f.name +// } + +// //Size is the complete size of the file. Zero if it's not a file. +// func (f *File) Size() int64 { +// switch f.filType { +// case inode.FileType: +// return int64(f.in.Info.(inode.File).Size) +// case inode.ExtFileType: +// return int64(f.in.Info.(inode.ExtFile).Size) +// default: +// return 0 +// } +// } + +// //ModTime is the time of last modification. +// func (f *File) ModTime() time.Time { +// return time.Unix(int64(f.in.Header.ModifiedTime), 0) +// } + +// //Sys returns the underlying reader. If the reader isn't initialized, it will initialize it. +// //If called on something other then a file, returns nil. +// func (f *File) Sys() interface{} { +// if !f.IsFile() { +// return nil +// } +// if f.reader == nil && f.r != nil { +// var err error +// f.reader, err = f.r.newFileReader(f.in) +// if err != nil { +// return nil +// } +// } +// return f.reader +// } + +// //TODO: Implement below when 1.16 drops to satisfy fs.File + +// //Stat simply returns the file. It's simply here to satisfy fs.File +// // func (f *File) Stat() (fs.FileInfo, error) { +// // return f, nil +// // } + +// //Close does nothing. It's simply here to satisfy fs.File +// //TODO: add actual implementation +// // func (f *File) Close() error { +// // return nil +// // } + +// //GetChildren returns a *squashfs.File slice of every direct child of the directory. If the File is not a directory, will return ErrNotDirectory +// func (f *File) GetChildren() (children []*File, err error) { +// children = make([]*File, 0) +// if f.r == nil { +// return nil, errNotReading +// } +// if !f.IsDir() { +// return nil, errNotDirectory +// } +// dir, err := f.r.readDirFromInode(f.in) +// if err != nil { +// return +// } +// var fil *File +// for _, entry := range dir.Entries { +// fil, err = f.r.newFileFromDirEntry(&entry) +// if err != nil { +// return +// } +// fil.Parent = f +// if f.name != "" { +// fil.dir = f.Path() +// } +// children = append(children, fil) +// } +// return +// } + +// //GetChildrenRecursively returns ALL children. Goes down ALL folder paths. +// func (f *File) GetChildrenRecursively() (children []*File, err error) { +// children = make([]*File, 0) +// if f.r == nil { +// return nil, errNotReading +// } +// if !f.IsDir() { +// return nil, errNotDirectory +// } +// children, err = f.GetChildren() +// if err != nil { +// return +// } +// var childFolders []*File +// for _, child := range children { +// if child.IsDir() { +// childFolders = append(childFolders, child) +// } +// } +// for _, folds := range childFolders { +// var childs []*File +// childs, err = folds.GetChildrenRecursively() +// if err != nil { +// fmt.Println(err) +// return +// } +// children = append(children, childs...) +// } +// return +// } + +// //Path returns the path of the file within the archive. +// func (f *File) Path() string { +// if f.name == "" { +// return f.dir +// } +// return f.dir + "/" + f.name +// } + +// //GetFileAtPath tries to return the File at the given path, relative to the file. +// //Returns nil if called on something other then a folder, OR if the path goes oustide the archive. +// //Allows wildcards supported by path.Match (namely * and ?) and will return the FIRST file that matches. +// func (f *File) GetFileAtPath(dirPath string) *File { +// dirPath = path.Clean(dirPath) +// dirPath = strings.TrimPrefix(dirPath, "/") +// if dirPath == "" || dirPath == "." { +// return f +// } +// if dirPath != "." && !f.IsDir() { +// return nil +// } +// split := strings.Split(dirPath, "/") +// if split[0] == ".." && f.name == "" { +// return nil +// } else if split[0] == ".." { +// if f.Parent != nil { +// return f.Parent.GetFileAtPath(strings.Join(split[1:], "/")) +// } +// return nil +// } +// children, err := f.GetChildren() +// if err != nil { +// return nil +// } +// for _, child := range children { +// eq, _ := path.Match(split[0], child.name) +// if eq { +// return child.GetFileAtPath(strings.Join(split[1:], "/")) +// } +// } // return nil // } -//GetChildren returns a *squashfs.File slice of every direct child of the directory. If the File is not a directory, will return ErrNotDirectory -func (f *File) GetChildren() (children []*File, err error) { - children = make([]*File, 0) - if f.r == nil { - return nil, errNotReading - } - if !f.IsDir() { - return nil, errNotDirectory - } - dir, err := f.r.readDirFromInode(f.in) - if err != nil { - return - } - var fil *File - for _, entry := range dir.Entries { - fil, err = f.r.newFileFromDirEntry(&entry) - if err != nil { - return - } - fil.Parent = f - if f.name != "" { - fil.dir = f.Path() - } - children = append(children, fil) - } - return -} +// //TODO: add with 1.16 +// //Open is the same as GetFileAtPath to implement fs.FS +// // func (f *File) Open(name string) (fs.File, error) { +// // tmp := f.GetFileAtPath(name) +// // if tmp == nil { +// // return tmp, fs.ErrNotExist +// // } +// // return tmp, nil +// // } -//GetChildrenRecursively returns ALL children. Goes down ALL folder paths. -func (f *File) GetChildrenRecursively() (children []*File, err error) { - children = make([]*File, 0) - if f.r == nil { - return nil, errNotReading - } - if !f.IsDir() { - return nil, errNotDirectory - } - children, err = f.GetChildren() - if err != nil { - return - } - var childFolders []*File - for _, child := range children { - if child.IsDir() { - childFolders = append(childFolders, child) - } - } - for _, folds := range childFolders { - var childs []*File - childs, err = folds.GetChildrenRecursively() - if err != nil { - fmt.Println(err) - return - } - children = append(children, childs...) - } - return -} - -//Path returns the path of the file within the archive. -func (f *File) Path() string { - if f.name == "" { - return f.dir - } - return f.dir + "/" + f.name -} - -//GetFileAtPath tries to return the File at the given path, relative to the file. -//Returns nil if called on something other then a folder, OR if the path goes oustide the archive. -//Allows wildcards supported by path.Match (namely * and ?) and will return the FIRST file that matches. -func (f *File) GetFileAtPath(dirPath string) *File { - dirPath = path.Clean(dirPath) - dirPath = strings.TrimPrefix(dirPath, "/") - if dirPath == "" || dirPath == "." { - return f - } - if dirPath != "." && !f.IsDir() { - return nil - } - split := strings.Split(dirPath, "/") - if split[0] == ".." && f.name == "" { - return nil - } else if split[0] == ".." { - if f.Parent != nil { - return f.Parent.GetFileAtPath(strings.Join(split[1:], "/")) - } - return nil - } - children, err := f.GetChildren() - if err != nil { - return nil - } - for _, child := range children { - eq, _ := path.Match(split[0], child.name) - if eq { - return child.GetFileAtPath(strings.Join(split[1:], "/")) - } - } - return nil -} - -//TODO: add with 1.16 -//Open is the same as GetFileAtPath to implement fs.FS -// func (f *File) Open(name string) (fs.File, error) { -// tmp := f.GetFileAtPath(name) -// if tmp == nil { -// return tmp, fs.ErrNotExist -// } -// return tmp, nil +// //IsDir returns if the file is a directory. +// func (f *File) IsDir() bool { +// return f.filType == inode.DirType || f.filType == inode.ExtDirType // } -//IsDir returns if the file is a directory. -func (f *File) IsDir() bool { - return f.filType == inode.DirType || f.filType == inode.ExtDirType -} +// //IsSymlink returns if the file is a symlink. +// func (f *File) IsSymlink() bool { +// return f.filType == inode.SymType || f.filType == inode.ExtSymType +// } -//IsSymlink returns if the file is a symlink. -func (f *File) IsSymlink() bool { - return f.filType == inode.SymType || f.filType == inode.ExtSymType -} +// //IsFile returns if the file is a file. +// func (f *File) IsFile() bool { +// return f.filType == inode.FileType || f.filType == inode.ExtFileType +// } -//IsFile returns if the file is a file. -func (f *File) IsFile() bool { - return f.filType == inode.FileType || f.filType == inode.ExtFileType -} +// //SymlinkPath returns the path the symlink is pointing to. If the file ISN'T a symlink, will return an empty string. +// //If a path begins with "/" then the symlink is pointing to an absolute path (starting from root, and not a file inside the archive) +// func (f *File) SymlinkPath() string { +// switch f.filType { +// case inode.SymType: +// return f.in.Info.(inode.Sym).Path +// case inode.ExtSymType: +// return f.in.Info.(inode.ExtSym).Path +// default: +// return "" +// } +// } -//SymlinkPath returns the path the symlink is pointing to. If the file ISN'T a symlink, will return an empty string. -//If a path begins with "/" then the symlink is pointing to an absolute path (starting from root, and not a file inside the archive) -func (f *File) SymlinkPath() string { - switch f.filType { - case inode.SymType: - return f.in.Info.(inode.Sym).Path - case inode.ExtSymType: - return f.in.Info.(inode.ExtSym).Path - default: - return "" - } -} +// //GetSymlinkFile tries to return the squashfs.File associated with the symlink. If the file isn't a symlink +// //or the symlink points to a location outside the archive, nil is returned. +// func (f *File) GetSymlinkFile() *File { +// if !f.IsSymlink() { +// return nil +// } +// if strings.HasSuffix(f.SymlinkPath(), "/") { +// return nil +// } +// return f.Parent.GetFileAtPath(f.SymlinkPath()) +// } -//GetSymlinkFile tries to return the squashfs.File associated with the symlink. If the file isn't a symlink -//or the symlink points to a location outside the archive, nil is returned. -func (f *File) GetSymlinkFile() *File { - if !f.IsSymlink() { - return nil - } - if strings.HasSuffix(f.SymlinkPath(), "/") { - return nil - } - return f.Parent.GetFileAtPath(f.SymlinkPath()) -} +// //GetSymlinkFileRecursive tries to return the squasfs.File associated with the symlink. It will recursively +// //try to get the symlink's file. This will return either a non-symlink File, or nil. +// func (f *File) GetSymlinkFileRecursive() *File { +// if !f.IsSymlink() { +// return nil +// } +// if strings.HasSuffix(f.SymlinkPath(), "/") { +// return nil +// } +// sym := f +// for { +// sym = sym.GetSymlinkFile() +// if sym == nil { +// return nil +// } +// if !sym.IsSymlink() { +// return sym +// } +// } +// } -//GetSymlinkFileRecursive tries to return the squasfs.File associated with the symlink. It will recursively -//try to get the symlink's file. This will return either a non-symlink File, or nil. -func (f *File) GetSymlinkFileRecursive() *File { - if !f.IsSymlink() { - return nil - } - if strings.HasSuffix(f.SymlinkPath(), "/") { - return nil - } - sym := f - for { - sym = sym.GetSymlinkFile() - if sym == nil { - return nil - } - if !sym.IsSymlink() { - return sym - } - } -} +// //Mode returns the os.FileMode of the File. Sets mode bits for directories and symlinks. +// func (f *File) Mode() os.FileMode { +// mode := os.FileMode(f.in.Header.Permissions) +// switch { +// case f.IsDir(): +// mode = mode | os.ModeDir +// case f.IsSymlink(): +// mode = mode | os.ModeSymlink +// } +// return mode +// } -//Mode returns the os.FileMode of the File. Sets mode bits for directories and symlinks. -func (f *File) Mode() os.FileMode { - mode := os.FileMode(f.in.Header.Permissions) - switch { - case f.IsDir(): - mode = mode | os.ModeDir - case f.IsSymlink(): - mode = mode | os.ModeSymlink - } - return mode -} +// //ExtractTo extracts the file to the given path. This is the same as ExtractWithOptions(path, false, false, os.ModePerm, false). +// //Will NOT try to keep symlinks valid, folders extracted will have the permissions set by the squashfs, but the folder to make path will have full permissions (777). +// // +// //Will try it's best to extract all files, and if any errors come up, they will be appended to the error slice that's returned. +// func (f *File) ExtractTo(path string) []error { +// return f.ExtractWithOptions(path, false, false, os.ModePerm, false) +// } -//ExtractTo extracts the file to the given path. This is the same as ExtractWithOptions(path, false, false, os.ModePerm, false). -//Will NOT try to keep symlinks valid, folders extracted will have the permissions set by the squashfs, but the folder to make path will have full permissions (777). -// -//Will try it's best to extract all files, and if any errors come up, they will be appended to the error slice that's returned. -func (f *File) ExtractTo(path string) []error { - return f.ExtractWithOptions(path, false, false, os.ModePerm, false) -} +// //ExtractSymlink is similar to ExtractTo, but when it extracts a symlink, it instead extracts the file associated with the symlink in it's place. +// //This is the same as ExtractWithOptions(path, true, false, os.ModePerm, false) +// func (f *File) ExtractSymlink(path string) []error { +// return f.ExtractWithOptions(path, true, false, os.ModePerm, false) +// } -//ExtractSymlink is similar to ExtractTo, but when it extracts a symlink, it instead extracts the file associated with the symlink in it's place. -//This is the same as ExtractWithOptions(path, true, false, os.ModePerm, false) -func (f *File) ExtractSymlink(path string) []error { - return f.ExtractWithOptions(path, true, false, os.ModePerm, false) -} +// //ExtractWithOptions will extract the file to the given path, while allowing customization on how it works. ExtractTo is the "default" options. +// //Will try it's best to extract all files, and if any errors come up, they will be appended to the error slice that's returned. +// //Should only return multiple errors if extracting a folder. +// // +// //If dereferenceSymlink is set, instead of extracting a symlink, it will extract the file the symlink is pointed to in it's place. +// //If both dereferenceSymlink and unbreakSymlink is set, dereferenceSymlink takes precendence. +// // +// //If unbreakSymlink is set, it will also try to extract the symlink's associated file. WARNING: the symlink's file may have to go up the directory to work. +// //If unbreakSymlink is set and the file cannot be extracted, a ErrBrokenSymlink will be appended to the returned error slice. +// // +// //folderPerm only applies to the folders created to get to path. Folders from the archive are given the correct permissions defined by the archive. +// func (f *File) ExtractWithOptions(path string, dereferenceSymlink, unbreakSymlink bool, folderPerm os.FileMode, verbose bool) (errs []error) { +// errs = make([]error, 0) +// err := os.MkdirAll(path, folderPerm) +// if err != nil { +// return []error{err} +// } +// switch { +// case f.IsDir(): +// if f.name != "" { +// //TODO: check if folder is present, and if so, try to set it's permission +// err = os.Mkdir(path+"/"+f.name, os.ModePerm) +// if err != nil { +// if verbose { +// fmt.Println("Error while making: ", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } +// var fil *os.File +// fil, err = os.Open(path + "/" + f.name) +// if err != nil { +// if verbose { +// fmt.Println("Error while opening:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } +// fil.Chown(int(f.r.idTable[f.in.Header.UID]), int(f.r.idTable[f.in.Header.GID])) +// //don't mention anything when it fails. Because it fails often. Probably has something to do about uid & gid 0 +// // if err != nil { +// // if verbose { +// // fmt.Println("Error while changing owner:", path+"/"+f.Name) +// // fmt.Println(err) +// // } +// // errs = append(errs, err) +// // } +// err = fil.Chmod(f.Mode()) +// if err != nil { +// if verbose { +// fmt.Println("Error while changing owner:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// } +// } +// var children []*File +// children, err = f.GetChildren() +// if err != nil { +// if verbose { +// fmt.Println("Error getting children for:", f.Path()) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } +// finishChan := make(chan []error) +// for _, child := range children { +// go func(child *File) { +// if f.name == "" { +// finishChan <- child.ExtractWithOptions(path, dereferenceSymlink, unbreakSymlink, folderPerm, verbose) +// } else { +// finishChan <- child.ExtractWithOptions(path+"/"+f.name, dereferenceSymlink, unbreakSymlink, folderPerm, verbose) +// } +// }(child) +// } +// for range children { +// errs = append(errs, (<-finishChan)...) +// } +// return +// case f.IsFile(): +// var fil *os.File +// fil, err = os.Create(path + "/" + f.name) +// if os.IsExist(err) { +// err = os.Remove(path + "/" + f.name) +// if err != nil { +// if verbose { +// fmt.Println("Error while making:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } +// fil, err = os.Create(path + "/" + f.name) +// if err != nil { +// if verbose { +// fmt.Println("Error while making:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } +// } else if err != nil { +// if verbose { +// fmt.Println("Error while making:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } //Since we will be reading from the file +// _, err = io.Copy(fil, f.Sys().(io.Reader)) +// if err != nil { +// if verbose { +// fmt.Println("Error while Copying data to:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// return +// } +// fil.Chown(int(f.r.idTable[f.in.Header.UID]), int(f.r.idTable[f.in.Header.GID])) +// //don't mention anything when it fails. Because it fails often. Probably has something to do about uid & gid 0 +// // if err != nil { +// // if verbose { +// // fmt.Println("Error while changing owner:", path+"/"+f.Name) +// // fmt.Println(err) +// // } +// // errs = append(errs, err) +// // return +// // } +// err = fil.Chmod(f.Mode()) +// if err != nil { +// if verbose { +// fmt.Println("Error while setting permissions for:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// } +// return +// case f.IsSymlink(): +// symPath := f.SymlinkPath() +// if dereferenceSymlink { +// fil := f.GetSymlinkFile() +// if fil == nil { +// if verbose { +// fmt.Println("Symlink path(", symPath, ") is outside the archive:"+path+"/"+f.name) +// } +// return +// } +// fil.name = f.name +// extracSymErrs := fil.ExtractWithOptions(path, dereferenceSymlink, unbreakSymlink, folderPerm, verbose) +// if len(extracSymErrs) > 0 { +// if verbose { +// fmt.Println("Error(s) while extracting the symlink's file:", path+"/"+f.name) +// fmt.Println(extracSymErrs) +// } +// errs = append(errs, extracSymErrs...) +// } +// return +// } else if unbreakSymlink { +// fil := f.GetSymlinkFile() +// if fil != nil { +// symPath = path + "/" + symPath +// paths := strings.Split(symPath, "/") +// extracSymErrs := fil.ExtractWithOptions(strings.Join(paths[:len(paths)-1], "/"), dereferenceSymlink, unbreakSymlink, folderPerm, verbose) +// if len(extracSymErrs) > 0 { +// if verbose { +// fmt.Println("Error(s) while extracting the symlink's file:", path+"/"+f.name) +// fmt.Println(extracSymErrs) +// } +// errs = append(errs, extracSymErrs...) +// } +// } else { +// if verbose { +// fmt.Println("Symlink path(", symPath, ") is outside the archive:"+path+"/"+f.name) +// } +// return +// } +// } +// err = os.Symlink(f.SymlinkPath(), path+"/"+f.name) +// if err != nil { +// if verbose { +// fmt.Println("Error while making symlink:", path+"/"+f.name) +// fmt.Println(err) +// } +// errs = append(errs, err) +// } +// } +// return +// } -//ExtractWithOptions will extract the file to the given path, while allowing customization on how it works. ExtractTo is the "default" options. -//Will try it's best to extract all files, and if any errors come up, they will be appended to the error slice that's returned. -//Should only return multiple errors if extracting a folder. -// -//If dereferenceSymlink is set, instead of extracting a symlink, it will extract the file the symlink is pointed to in it's place. -//If both dereferenceSymlink and unbreakSymlink is set, dereferenceSymlink takes precendence. -// -//If unbreakSymlink is set, it will also try to extract the symlink's associated file. WARNING: the symlink's file may have to go up the directory to work. -//If unbreakSymlink is set and the file cannot be extracted, a ErrBrokenSymlink will be appended to the returned error slice. -// -//folderPerm only applies to the folders created to get to path. Folders from the archive are given the correct permissions defined by the archive. -func (f *File) ExtractWithOptions(path string, dereferenceSymlink, unbreakSymlink bool, folderPerm os.FileMode, verbose bool) (errs []error) { - errs = make([]error, 0) - err := os.MkdirAll(path, folderPerm) - if err != nil { - return []error{err} - } - switch { - case f.IsDir(): - if f.name != "" { - //TODO: check if folder is present, and if so, try to set it's permission - err = os.Mkdir(path+"/"+f.name, os.ModePerm) - if err != nil { - if verbose { - fmt.Println("Error while making: ", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - return - } - var fil *os.File - fil, err = os.Open(path + "/" + f.name) - if err != nil { - if verbose { - fmt.Println("Error while opening:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - return - } - fil.Chown(int(f.r.idTable[f.in.Header.UID]), int(f.r.idTable[f.in.Header.GID])) - //don't mention anything when it fails. Because it fails often. Probably has something to do about uid & gid 0 - // if err != nil { - // if verbose { - // fmt.Println("Error while changing owner:", path+"/"+f.Name) - // fmt.Println(err) - // } - // errs = append(errs, err) - // } - err = fil.Chmod(f.Mode()) - if err != nil { - if verbose { - fmt.Println("Error while changing owner:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - } - } - var children []*File - children, err = f.GetChildren() - if err != nil { - if verbose { - fmt.Println("Error getting children for:", f.Path()) - fmt.Println(err) - } - errs = append(errs, err) - return - } - finishChan := make(chan []error) - for _, child := range children { - go func(child *File) { - if f.name == "" { - finishChan <- child.ExtractWithOptions(path, dereferenceSymlink, unbreakSymlink, folderPerm, verbose) - } else { - finishChan <- child.ExtractWithOptions(path+"/"+f.name, dereferenceSymlink, unbreakSymlink, folderPerm, verbose) - } - }(child) - } - for range children { - errs = append(errs, (<-finishChan)...) - } - return - case f.IsFile(): - var fil *os.File - fil, err = os.Create(path + "/" + f.name) - if os.IsExist(err) { - err = os.Remove(path + "/" + f.name) - if err != nil { - if verbose { - fmt.Println("Error while making:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - return - } - fil, err = os.Create(path + "/" + f.name) - if err != nil { - if verbose { - fmt.Println("Error while making:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - return - } - } else if err != nil { - if verbose { - fmt.Println("Error while making:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - return - } //Since we will be reading from the file - _, err = io.Copy(fil, f.Sys().(io.Reader)) - if err != nil { - if verbose { - fmt.Println("Error while Copying data to:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - return - } - fil.Chown(int(f.r.idTable[f.in.Header.UID]), int(f.r.idTable[f.in.Header.GID])) - //don't mention anything when it fails. Because it fails often. Probably has something to do about uid & gid 0 - // if err != nil { - // if verbose { - // fmt.Println("Error while changing owner:", path+"/"+f.Name) - // fmt.Println(err) - // } - // errs = append(errs, err) - // return - // } - err = fil.Chmod(f.Mode()) - if err != nil { - if verbose { - fmt.Println("Error while setting permissions for:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - } - return - case f.IsSymlink(): - symPath := f.SymlinkPath() - if dereferenceSymlink { - fil := f.GetSymlinkFile() - if fil == nil { - if verbose { - fmt.Println("Symlink path(", symPath, ") is outside the archive:"+path+"/"+f.name) - } - return - } - fil.name = f.name - extracSymErrs := fil.ExtractWithOptions(path, dereferenceSymlink, unbreakSymlink, folderPerm, verbose) - if len(extracSymErrs) > 0 { - if verbose { - fmt.Println("Error(s) while extracting the symlink's file:", path+"/"+f.name) - fmt.Println(extracSymErrs) - } - errs = append(errs, extracSymErrs...) - } - return - } else if unbreakSymlink { - fil := f.GetSymlinkFile() - if fil != nil { - symPath = path + "/" + symPath - paths := strings.Split(symPath, "/") - extracSymErrs := fil.ExtractWithOptions(strings.Join(paths[:len(paths)-1], "/"), dereferenceSymlink, unbreakSymlink, folderPerm, verbose) - if len(extracSymErrs) > 0 { - if verbose { - fmt.Println("Error(s) while extracting the symlink's file:", path+"/"+f.name) - fmt.Println(extracSymErrs) - } - errs = append(errs, extracSymErrs...) - } - } else { - if verbose { - fmt.Println("Symlink path(", symPath, ") is outside the archive:"+path+"/"+f.name) - } - return - } - } - err = os.Symlink(f.SymlinkPath(), path+"/"+f.name) - if err != nil { - if verbose { - fmt.Println("Error while making symlink:", path+"/"+f.name) - fmt.Println(err) - } - errs = append(errs, err) - } - } - return -} - -//Read from the file. Doesn't do anything fancy, just pases it to the underlying io.Reader. If a directory, return io.EOF. -func (f *File) Read(p []byte) (int, error) { - if !f.IsFile() { - return 0, io.EOF - } - var err error - if f.reader == nil && f.r != nil { - f.reader, err = f.r.newFileReader(f.in) - if err != nil { - return 0, err - } - } - return f.reader.Read(p) -} - -//ReadDirFromInode returns a fully populated Directory from a given Inode. -//If the given inode is not a directory it returns an error. -func (r *Reader) readDirFromInode(i *inode.Inode) (*directory.Directory, error) { - var offset uint32 - var metaOffset uint16 - var size uint32 - switch i.Type { - case inode.DirType: - offset = i.Info.(inode.Dir).DirectoryIndex - metaOffset = i.Info.(inode.Dir).DirectoryOffset - size = uint32(i.Info.(inode.Dir).DirectorySize) - case inode.ExtDirType: - offset = i.Info.(inode.ExtDir).DirectoryIndex - metaOffset = i.Info.(inode.ExtDir).DirectoryOffset - size = i.Info.(inode.ExtDir).DirectorySize - default: - return nil, errors.New("Not a directory inode") - } - br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset))) - if err != nil { - return nil, err - } - _, err = br.Seek(int64(metaOffset), io.SeekStart) - if err != nil { - return nil, err - } - dir, err := directory.NewDirectory(br, size) - if err != nil { - return dir, err - } - return dir, nil -} - -//GetInodeFromEntry returns the inode associated with a given directory.Entry -func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) { - br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset))) - if err != nil { - return nil, err - } - _, err = br.Seek(int64(en.Offset), io.SeekStart) - if err != nil { - return nil, err - } - i, err := inode.ProcessInode(br, r.super.BlockSize) - if err != nil { - return nil, err - } - return i, nil -} +// //Read from the file. Doesn't do anything fancy, just pases it to the underlying io.Reader. If a directory, return io.EOF. +// func (f *File) Read(p []byte) (int, error) { +// if !f.IsFile() { +// return 0, io.EOF +// } +// var err error +// if f.reader == nil && f.r != nil { +// f.reader, err = f.r.newFileReader(f.in) +// if err != nil { +// return 0, err +// } +// } +// return f.reader.Read(p) +// } diff --git a/file2.go b/file2.go new file mode 100644 index 0000000..7f8bec2 --- /dev/null +++ b/file2.go @@ -0,0 +1,157 @@ +package squashfs + +import ( + "errors" + "io" + "io/fs" + + "github.com/CalebQ42/squashfs/internal/directory" + "github.com/CalebQ42/squashfs/internal/inode" +) + +type File struct { + i *inode.Inode + parent *FS + r *Reader + reader *fileReader + name string + dirsRead int +} + +func (f FileInfo) File() (file *File, err error) { + file = &File{ + name: f.name, + r: f.r, + parent: f.parent, + i: f.i, + } + file.reader, err = f.r.newFileReader(f.i) + return +} + +func (r *Reader) newFileFromDirEntry(en *directory.Entry, parent *FS) (file *File, err error) { + file = &File{ + name: en.Name, + r: r, + parent: parent, + } + file.i, err = r.getInodeFromEntry(en) + if err != nil { + return nil, err + } + file.reader, err = r.newFileReader(file.i) + return +} + +func (f *File) Stat() (fs.FileInfo, error) { + return &FileInfo{ + i: f.i, + name: f.name, + parent: f.parent, + r: f.r, + }, nil +} + +func (f *File) Read(p []byte) (int, error) { + if f.i.Type == inode.FileType || f.i.Type == inode.ExtFileType { + if f.reader == nil { + return 0, fs.ErrClosed + } + return f.reader.Read(p) + } + return 0, errors.New("Can only read files") +} + +func (f *File) WriteTo(w io.Writer) (int64, error) { + if f.i.Type == inode.FileType || f.i.Type == inode.ExtFileType { + if f.reader == nil { + return 0, fs.ErrClosed + } + return f.reader.WriteTo(w) + } + return 0, errors.New("Can only read files") +} + +func (f *File) Close() error { + f.reader = nil + return nil +} + +func (f *File) ReadDir(n int) ([]fs.DirEntry, error) { + if !f.IsDir() { + return nil, errors.New("File is not a directory") + } + ffs, err := f.FS() + if err != nil { + return nil, err + } + var beg, end int + if n <= 0 { + beg, end = 0, len(ffs.entries) + } else { + beg, end = f.dirsRead, f.dirsRead+n + if end > len(ffs.entries) { + end = len(ffs.entries) + err = io.EOF + } + } + out := make([]fs.DirEntry, end-beg) + for i, ent := range ffs.entries[beg:end] { + out[i] = f.r.newDirEntry(ent, ffs) + } + return out, err +} + +func (f File) FS() (*FS, error) { + if !f.IsDir() { + return nil, errors.New("File is not a directory") + } + ents, err := f.r.readDirFromInode(f.i) + if err != nil { + return nil, err + } + return &FS{ + entries: ents, + parent: f.parent, + r: f.r, + }, nil +} + +func (f File) IsDir() bool { + return f.i.Type == inode.DirType || f.i.Type == inode.ExtDirType +} + +func (f File) Path() + +//ReadDirFromInode returns a fully populated Directory from a given Inode. +//If the given inode is not a directory it returns an error. +func (r *Reader) readDirFromInode(i *inode.Inode) ([]*directory.Entry, error) { + var offset uint32 + var metaOffset uint16 + var size uint32 + switch i.Type { + case inode.DirType: + offset = i.Info.(inode.Dir).DirectoryIndex + metaOffset = i.Info.(inode.Dir).DirectoryOffset + size = uint32(i.Info.(inode.Dir).DirectorySize) + case inode.ExtDirType: + offset = i.Info.(inode.ExtDir).DirectoryIndex + metaOffset = i.Info.(inode.ExtDir).DirectoryOffset + size = i.Info.(inode.ExtDir).DirectorySize + default: + return nil, errors.New("Not a directory inode") + } + br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset))) + if err != nil { + return nil, err + } + _, err = br.Seek(int64(metaOffset), io.SeekStart) + if err != nil { + return nil, err + } + ents, err := directory.NewDirectory(br, size) + if err != nil { + return nil, err + } + return ents, nil +} diff --git a/fs.go b/fs.go new file mode 100644 index 0000000..88a2a41 --- /dev/null +++ b/fs.go @@ -0,0 +1,89 @@ +package squashfs + +import ( + "io/fs" + "path" + "strings" + + "github.com/CalebQ42/squashfs/internal/directory" +) + +type FS struct { + r *Reader + parent *FS + entries []*directory.Entry +} + +func (f FS) Open(name string) (fs.File, error) { + if !fs.ValidPath(name) { + return nil, &fs.PathError{ + Op: "open", + Path: name, + Err: fs.ErrInvalid, + } + } + name = path.Clean(strings.TrimPrefix(name, "/")) + split := strings.Split(name, "/") + for i := 0; i < len(f.entries); i++ { + if match, _ := path.Match(split[0], f.entries[i].Name); match { + if len(split) == 1 { + return f.r.newFileFromDirEntry(f.entries[i], &f) + } + sub, err := f.Sub(split[0]) + if err != nil { + if pathErr, ok := err.(*fs.PathError); ok { + pathErr.Op = "open" + pathErr.Path = name + return nil, err + } + return nil, &fs.PathError{ + Op: "open", + Path: name, + Err: err, + } + } + fil, err := sub.Open(strings.Join(split[1:], "/")) + if err != nil { + if pathErr, ok := err.(*fs.PathError); ok { + if pathErr.Err == fs.ErrNotExist { + continue + } + pathErr.Op = "open" + pathErr.Path = name + return nil, err + } + return nil, &fs.PathError{ + Op: "open", + Path: name, + Err: err, + } + } + return fil, nil + } + } + return nil, &fs.PathError{ + Op: "open", + Path: name, + Err: fs.ErrNotExist, + } +} + +func (f FS) Glob(pattern string) ([]string, error) { + return nil, nil +} + +func (f FS) ReadDir(name string) ([]DirEntry, error) { + return nil, nil +} + +func (f FS) ReadFile(name string) ([]byte, error) { + return nil, nil +} + +func (f FS) Stat(name string) ([]byte, error) { + return nil, nil +} + +func (f FS) Sub(dir string) (fs.FS, error) { + return nil, nil +} diff --git a/internal/directory/directory.go b/internal/directory/directory.go index 44da60d..67311ea 100644 --- a/internal/directory/directory.go +++ b/internal/directory/directory.go @@ -23,38 +23,33 @@ type EntryRaw struct { //Entry is an entry in a directory. type Entry struct { - *Header - Name string - EntryRaw + Name string + InodeOffset uint32 + InodeBlockOffset int16 + Type uint16 } //NewEntry creates a new directory entry -func NewEntry(rdr io.Reader) (Entry, error) { - var entry Entry - err := binary.Read(rdr, binary.LittleEndian, &entry.EntryRaw) +func NewEntry(rdr io.Reader) (*Entry, error) { + var raw EntryRaw + err := binary.Read(rdr, binary.LittleEndian, &raw) if err != nil { - return Entry{}, err + return nil, err } - tmp := make([]byte, entry.EntryRaw.NameSize+1) + tmp := make([]byte, raw.NameSize+1) err = binary.Read(rdr, binary.LittleEndian, &tmp) if err != nil { - return Entry{}, err + return nil, err } - entry.Name = string(tmp) - return entry, err -} - -//Directory is an entry in the directory table of a squashfs. -//Will only have multiple headers if there are more then 256 entries -type Directory struct { - Headers []Header - Entries []Entry + return &Entry{ + InodeBlockOffset: raw.InodeOffset, + Type: raw.Type, + Name: string(tmp), + }, nil } //NewDirectory reads the directory from rdr -func NewDirectory(base io.Reader, size uint32) (*Directory, error) { - var dir Directory - var err error +func NewDirectory(base io.Reader, size uint32) (entries []*Entry, err error) { tmp := make([]byte, size) base.Read(tmp) rdr := bytes.NewBuffer(tmp) @@ -62,6 +57,7 @@ func NewDirectory(base io.Reader, size uint32) (*Directory, error) { var hdr Header err = binary.Read(rdr, binary.LittleEndian, &hdr) if err == io.ErrUnexpectedEOF { + err = nil break } else if err != nil { return nil, err @@ -71,24 +67,21 @@ func NewDirectory(base io.Reader, size uint32) (*Directory, error) { if hdr.Count%256 > 0 { headers++ } - dir.Headers = append(dir.Headers, hdr) for i := uint32(0); i < hdr.Count; i++ { if i != 0 && i%256 == 0 { - var newHdr Header - err = binary.Read(rdr, binary.LittleEndian, &newHdr) + err = binary.Read(rdr, binary.LittleEndian, &hdr) if err != nil { return nil, err } - dir.Headers = append(dir.Headers, newHdr) } - var ent Entry + var ent *Entry ent, err = NewEntry(rdr) if err != nil { return nil, err } - ent.Header = &dir.Headers[len(dir.Headers)-1] - dir.Entries = append(dir.Entries, ent) + ent.InodeOffset = hdr.InodeOffset + entries = append(entries, ent) } } - return &dir, nil + return } diff --git a/reader.go b/reader.go index 81d41c9..5b7be30 100644 --- a/reader.go +++ b/reader.go @@ -8,7 +8,6 @@ import ( "time" "github.com/CalebQ42/squashfs/internal/compression" - "github.com/CalebQ42/squashfs/internal/inode" ) const ( @@ -159,117 +158,117 @@ func (r *Reader) ModTime() time.Time { } //ExtractTo tries to extract ALL files to the given path. This is the same as getting the root folder and extracting that. -func (r *Reader) ExtractTo(path string) []error { - if r.root == nil { - _, err := r.GetRootFolder() - if err != nil { - return []error{err} - } - } - return r.root.ExtractTo(path) -} +// func (r *Reader) ExtractTo(path string) []error { +// if r.root == nil { +// _, err := r.GetRootFolder() +// if err != nil { +// return []error{err} +// } +// } +// return r.root.ExtractTo(path) +// } //GetRootFolder returns a squashfs.File that references the root directory of the squashfs archive. func (r *Reader) GetRootFolder() (*File, error) { if r.root != nil { return r.root, nil } - mr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef) - if err != nil { - return nil, err - } + // mr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef) + // if err != nil { + // return nil, err + // } var root File - root.in, err = inode.ProcessInode(mr, r.super.BlockSize) - if err != nil { - return nil, err - } - root.dir = "/" - root.filType = root.in.Type - root.r = r + // root.in, err = inode.ProcessInode(mr, r.super.BlockSize) + // if err != nil { + // return nil, err + // } + // root.dir = "/" + // root.filType = root.in.Type + // root.r = r r.root = &root return r.root, nil } //GetAllFiles returns a slice of ALL files and folders contained in the squashfs. -func (r *Reader) GetAllFiles() (fils []*File, err error) { - if r.root == nil { - _, err := r.GetRootFolder() - if err != nil { - return nil, err - } - } - return r.root.GetChildrenRecursively() -} +// func (r *Reader) GetAllFiles() (fils []*File, err error) { +// if r.root == nil { +// _, err := r.GetRootFolder() +// if err != nil { +// return nil, err +// } +// } +// return r.root.GetChildrenRecursively() +// } //FindFile returns the first file (in the same order as Reader.GetAllFiles) that the given function returns true for. Returns nil if nothing is found. -func (r *Reader) FindFile(query func(*File) bool) *File { - if r.root == nil { - _, err := r.GetRootFolder() - if err != nil { - return nil - } - } - fils, err := r.root.GetChildren() - if err != nil { - return nil - } - var childrenDirs []*File - for _, fil := range fils { - if query(fil) { - return fil - } - if fil.IsDir() { - childrenDirs = append(childrenDirs, fil) - } - } - for len(childrenDirs) != 0 { - var tmp []*File - for _, dirs := range childrenDirs { - chil, err := dirs.GetChildren() - if err != nil { - return nil - } - for _, child := range chil { - if query(child) { - return child - } - if child.IsDir() { - tmp = append(tmp, child) - } - } - } - childrenDirs = tmp - } - return nil -} +// func (r *Reader) FindFile(query func(*File) bool) *File { +// if r.root == nil { +// _, err := r.GetRootFolder() +// if err != nil { +// return nil +// } +// } +// fils, err := r.root.GetChildren() +// if err != nil { +// return nil +// } +// var childrenDirs []*File +// for _, fil := range fils { +// if query(fil) { +// return fil +// } +// if fil.IsDir() { +// childrenDirs = append(childrenDirs, fil) +// } +// } +// for len(childrenDirs) != 0 { +// var tmp []*File +// for _, dirs := range childrenDirs { +// chil, err := dirs.GetChildren() +// if err != nil { +// return nil +// } +// for _, child := range chil { +// if query(child) { +// return child +// } +// if child.IsDir() { +// tmp = append(tmp, child) +// } +// } +// } +// childrenDirs = tmp +// } +// return nil +// } //FindAll returns all files where the given function returns true. -func (r *Reader) FindAll(query func(*File) bool) (all []*File) { - if r.root == nil { - _, err := r.GetRootFolder() - if err != nil { - return nil - } - } - fils, err := r.root.GetChildrenRecursively() - if err != nil { - return nil - } - for _, fil := range fils { - if query(fil) { - all = append(all, fil) - } - } - return -} +// func (r *Reader) FindAll(query func(*File) bool) (all []*File) { +// if r.root == nil { +// _, err := r.GetRootFolder() +// if err != nil { +// return nil +// } +// } +// fils, err := r.root.GetChildrenRecursively() +// if err != nil { +// return nil +// } +// for _, fil := range fils { +// if query(fil) { +// all = append(all, fil) +// } +// } +// return +// } //GetFileAtPath will return the file at the given path. If the file cannot be found, will return nil. -func (r *Reader) GetFileAtPath(filepath string) *File { - if r.root == nil { - _, err := r.GetRootFolder() - if err != nil { - return nil - } - } - return r.root.GetFileAtPath(filepath) -} +// func (r *Reader) GetFileAtPath(filepath string) *File { +// if r.root == nil { +// _, err := r.GetRootFolder() +// if err != nil { +// return nil +// } +// } +// return r.root.GetFileAtPath(filepath) +// } diff --git a/reader_test.go b/reader_test.go index 8480e0a..d0a8e07 100644 --- a/reader_test.go +++ b/reader_test.go @@ -34,18 +34,18 @@ func TestSquashfs(t *testing.T) { t.Fatal(err) } fmt.Println("stuff", rdr.super.CompressionType) - fil := rdr.GetFileAtPath("*.desktop") - if fil == nil { - t.Fatal("Can't find desktop fil") - } - errs := fil.ExtractTo(wd + "/testing") - if len(errs) > 0 { - t.Fatal(errs) - } - errs = rdr.ExtractTo(wd + "/testing/" + squashfsName + ".d") - if len(errs) > 0 { - t.Fatal(errs) - } + // fil := rdr.GetFileAtPath("*.desktop") + // if fil == nil { + // t.Fatal("Can't find desktop fil") + // } + // errs := fil.ExtractTo(wd + "/testing") + // if len(errs) > 0 { + // t.Fatal(errs) + // } + // errs = rdr.ExtractTo(wd + "/testing/" + squashfsName + ".d") + // if len(errs) > 0 { + // t.Fatal(errs) + // } t.Fatal("No Problems") } @@ -131,7 +131,7 @@ func BenchmarkDragRace(b *testing.B) { } else if err != nil { b.Fatal(err) } - stat, _ := aiFil.Stat() + // stat, _ := aiFil.Stat() ai := goappimage.NewAppImage(wd + "/testing/" + appImageName) os.RemoveAll(wd + "/testing/unsquashFirefox") os.RemoveAll(wd + "/testing/firefox") @@ -143,14 +143,14 @@ func BenchmarkDragRace(b *testing.B) { } unsquashTime := time.Since(start) start = time.Now() - rdr, err := NewSquashfsReader(io.NewSectionReader(aiFil, ai.Offset, stat.Size()-ai.Offset)) - if err != nil { - b.Fatal(err) - } - errs := rdr.ExtractTo(wd + "/testing/firefox") - if len(errs) > 0 { - b.Fatal(errs) - } + // rdr, err := NewSquashfsReader(io.NewSectionReader(aiFil, ai.Offset, stat.Size()-ai.Offset)) + // if err != nil { + // b.Fatal(err) + // } + // errs := rdr.ExtractTo(wd + "/testing/firefox") + // if len(errs) > 0 { + // b.Fatal(errs) + // } libTime := time.Since(start) b.Log("Unsqushfs:", unsquashTime.Round(time.Millisecond)) b.Log("Library:", libTime.Round(time.Millisecond))