More work on Writer

Adding files to the Writer should work properly now (except symlinks)
Threaded the adding of files.
Added ability to ignore errors when adding files.
This commit is contained in:
Caleb Gardner
2021-01-03 04:39:39 -06:00
parent 9524a2c192
commit 43fe4f91a2
2 changed files with 131 additions and 29 deletions
+1 -1
View File
@@ -15,4 +15,4 @@ require (
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
)
)
+130 -28
View File
@@ -2,80 +2,182 @@ package squashfs
import (
"errors"
"log"
"os"
"path"
"sort"
"strings"
"github.com/CalebQ42/squashfs/internal/inode"
)
//Writer is an interface to write a squashfs. Doesn't write until you call Write (TODO: maybe not do Write...)
//Writer is an interface to write a squashfs. Doesn't write until you call Write (TODO: maybe not do Write...).
//If AllowErrors is true, when errors are encountered, it just prints to the log instead of failing.
type Writer struct {
files map[string][]*File
directories []string
resolveSymlinks bool
symlinkTable map[string]string //symlinkTable holds info about symlink'd to files that had to be moved from their original position. [originalpath]newpath
ResolveSymlinks bool
AllowErrors bool
compression int
temp []*File
}
//NewWriter creates a new squashfs.Writer with the default settings (gzip compression and autoresolving symlinks)
//NewWriter creates a new squashfs.Writer with the default settings (gzip compression, autoresolving symlinks, and allowErrors)
func NewWriter() (*Writer, error) {
return NewWriterWithOptions(true, GzipCompression)
return NewWriterWithOptions(true, true, GzipCompression)
}
//NewWriterWithOptions creates a new squashfs.Writer with the given options.
//ResolveSymlinks tries to make sure symlinks aren't broken, and if they would be
func NewWriterWithOptions(resolveSymlinks bool, compressionType int) (*Writer, error) {
if compressionType < 0 || compressionType > 6 || compressionType == 3 {
return nil, errors.New("Incompatible compression type")
//ResolveSymlinks tries to make sure symlinks aren't broken. It will either try to make the link's location work
func NewWriterWithOptions(resolveSymlinks, allowErrors bool, compressionType int) (*Writer, error) {
if compressionType < 0 || compressionType > 6 {
return nil, errors.New("Incorrect compression type")
}
return &Writer{
if compressionType == 3 {
return nil, errors.New("Lzo compression is not currently supported")
}
out := Writer{
files: map[string][]*File{
"/": make([]*File, 0),
},
directories: []string{"/"},
resolveSymlinks: resolveSymlinks,
ResolveSymlinks: resolveSymlinks,
AllowErrors: allowErrors,
compression: compressionType,
}, nil
}
if resolveSymlinks {
out.symlinkTable = make(map[string]string)
}
return &out, nil
}
func (w *Writer) convertFile(squashfsPath string, file *os.File) error {
//convertFile converts the given os.File to a squashfs.File and then adds it to the Writer's temp File slice.
func (w *Writer) convertFile(squashfsPath string, file *os.File, errChan chan error) {
var fil File
fil.Reader = file
fil.name = path.Base(file.Name())
fil.path = squashfsPath
stat, err := file.Stat()
if err != nil {
return err
if w.AllowErrors {
log.Println("Error while getting FileInfo for", file.Name()+":")
log.Println(err)
err = nil
}
errChan <- err
return
}
defer func() { w.temp = append(w.temp, &fil) }()
if stat.IsDir() {
fil.filType = inode.BasicDirectoryType
dirs, err := file.Readdirnames(-1)
if err != nil {
return err
if w.AllowErrors {
log.Println("Error when getting directory names for", file.Name()+":")
log.Println(err)
err = nil
}
errChan <- err
return
}
subDirErrChan := make(chan error)
for _, dir := range dirs {
subFil, err := os.Open(file.Name() + dir)
go func(newFilename string, errChan chan error) {
subFil, err := os.Open(file.Name() + newFilename)
if err != nil {
if w.AllowErrors {
log.Println("Error when opening sub-directory", subFil.Name()+":")
log.Println(err)
err = nil
}
errChan <- err
return
}
subDirErrChan := make(chan error)
w.convertFile(fil.Path(), subFil, subDirErrChan)
errChan <- <-subDirErrChan
return
}(dir, subDirErrChan)
}
for range dirs {
err = <-subDirErrChan
if err != nil {
return err
}
err = w.convertFile(fil.Path(), subFil)
if err != nil {
return err
errChan <- err
return
}
}
w.temp = append(w.temp, &fil)
errChan <- nil
return
} else if stat.Mode().IsRegular() {
fil.filType = inode.BasicFileType
w.temp = append(w.temp, &fil)
errChan <- nil
return
} else if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
linkLocation, err := os.Readlink(file.Name())
if err != nil {
if w.AllowErrors {
log.Println("Error when reading symlink's target", file.Name()+":")
log.Println(err)
err = nil
}
errChan <- err
return
}
if w.ResolveSymlinks {
if w.symlinkTable[linkLocation] != "" {
linkLocation = w.symlinkTable[linkLocation]
}
}
//TODO: finish symlink support
}
//TODO: reg files & symlinks
return nil
errChan <- errors.New("Unsupported file type")
return
}
//AddFilesToPath adds the give os.Files to the given path within the squashfs archive.
//If AllowErrors is true, this will ALWAYS return nil
func (w *Writer) AddFilesToPath(squashfsPath string, files ...*os.File) error {
//TODO
return errors.New("Don't")
squashfsPath = path.Clean(squashfsPath)
if strings.HasPrefix(squashfsPath, "/") {
squashfsPath = strings.TrimPrefix(squashfsPath, "/")
}
if squashfsPath == "." {
squashfsPath = "/"
}
errChan := make(chan error)
for _, fil := range files {
go w.convertFile(squashfsPath, fil, errChan)
}
var firstError error
for range files {
err := <-errChan
if firstError != nil && err != nil {
firstError = err
}
}
if firstError != nil {
w.temp = nil
return firstError
}
for _, tempFil := range w.temp {
if tempFil.path != "/" {
ind := sort.SearchStrings(w.directories, tempFil.path)
if ind == len(w.directories) {
w.directories = append(w.directories, tempFil.path)
sort.Strings(w.directories)
}
}
w.files[tempFil.path] = append(w.files[tempFil.path], tempFil)
}
w.temp = nil
return nil
}
//AddFiles adds all files given to the root directory
//If AllowErrors is true, this will ALWAYS return nil
func (w *Writer) AddFiles(files ...*os.File) error {
//TODO
return errors.New("Don't")
return w.AddFilesToPath("/", files...)
}
//RemoveFileAt removes the file at filepath from the Writer.