43fe4f91a2
Adding files to the Writer should work properly now (except symlinks) Threaded the adding of files. Added ability to ignore errors when adding files.
190 lines
5.2 KiB
Go
190 lines
5.2 KiB
Go
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...).
|
|
//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
|
|
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, autoresolving symlinks, and allowErrors)
|
|
func NewWriter() (*Writer, error) {
|
|
return NewWriterWithOptions(true, true, GzipCompression)
|
|
}
|
|
|
|
//NewWriterWithOptions creates a new squashfs.Writer with the given options.
|
|
//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")
|
|
}
|
|
if compressionType == 3 {
|
|
return nil, errors.New("Lzo compression is not currently supported")
|
|
}
|
|
out := Writer{
|
|
files: map[string][]*File{
|
|
"/": make([]*File, 0),
|
|
},
|
|
ResolveSymlinks: resolveSymlinks,
|
|
AllowErrors: allowErrors,
|
|
compression: compressionType,
|
|
}
|
|
if resolveSymlinks {
|
|
out.symlinkTable = make(map[string]string)
|
|
}
|
|
return &out, nil
|
|
}
|
|
|
|
//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 {
|
|
if w.AllowErrors {
|
|
log.Println("Error while getting FileInfo for", file.Name()+":")
|
|
log.Println(err)
|
|
err = nil
|
|
}
|
|
errChan <- err
|
|
return
|
|
}
|
|
if stat.IsDir() {
|
|
fil.filType = inode.BasicDirectoryType
|
|
dirs, err := file.Readdirnames(-1)
|
|
if err != nil {
|
|
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 {
|
|
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 {
|
|
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
|
|
}
|
|
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 {
|
|
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 {
|
|
return w.AddFilesToPath("/", files...)
|
|
}
|
|
|
|
//RemoveFileAt removes the file at filepath from the Writer.
|
|
//If multiple files match the given filepath (such as if there are wildcards), all matching files are removed.
|
|
//If one or more files are removed, returns true.
|
|
func (w *Writer) RemoveFileAt(filepath string) bool {
|
|
//TODO
|
|
return false
|
|
}
|