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
+130 -28
View File
@@ -2,80 +2,182 @@ package squashfs
import ( import (
"errors" "errors"
"log"
"os" "os"
"path" "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 { type Writer struct {
files map[string][]*File files map[string][]*File
directories []string 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 compression int
temp []*File 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) { func NewWriter() (*Writer, error) {
return NewWriterWithOptions(true, GzipCompression) return NewWriterWithOptions(true, true, GzipCompression)
} }
//NewWriterWithOptions creates a new squashfs.Writer with the given options. //NewWriterWithOptions creates a new squashfs.Writer with the given options.
//ResolveSymlinks tries to make sure symlinks aren't broken, and if they would be //ResolveSymlinks tries to make sure symlinks aren't broken. It will either try to make the link's location work
func NewWriterWithOptions(resolveSymlinks bool, compressionType int) (*Writer, error) { func NewWriterWithOptions(resolveSymlinks, allowErrors bool, compressionType int) (*Writer, error) {
if compressionType < 0 || compressionType > 6 || compressionType == 3 { if compressionType < 0 || compressionType > 6 {
return nil, errors.New("Incompatible compression type") 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{ files: map[string][]*File{
"/": make([]*File, 0), "/": make([]*File, 0),
}, },
directories: []string{"/"}, ResolveSymlinks: resolveSymlinks,
resolveSymlinks: resolveSymlinks, AllowErrors: allowErrors,
compression: compressionType, 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 var fil File
fil.Reader = file fil.Reader = file
fil.name = path.Base(file.Name()) fil.name = path.Base(file.Name())
fil.path = squashfsPath fil.path = squashfsPath
stat, err := file.Stat() stat, err := file.Stat()
if err != nil { 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() { if stat.IsDir() {
fil.filType = inode.BasicDirectoryType
dirs, err := file.Readdirnames(-1) dirs, err := file.Readdirnames(-1)
if err != nil { 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 { 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 { if err != nil {
return err errChan <- err
} return
err = w.convertFile(fil.Path(), subFil)
if err != nil {
return err
} }
} }
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 errChan <- errors.New("Unsupported file type")
return nil return
} }
//AddFilesToPath adds the give os.Files to the given path within the squashfs archive. //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 { func (w *Writer) AddFilesToPath(squashfsPath string, files ...*os.File) error {
//TODO squashfsPath = path.Clean(squashfsPath)
return errors.New("Don't") 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 //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 { func (w *Writer) AddFiles(files ...*os.File) error {
//TODO return w.AddFilesToPath("/", files...)
return errors.New("Don't")
} }
//RemoveFileAt removes the file at filepath from the Writer. //RemoveFileAt removes the file at filepath from the Writer.