Re-Write of the Writer to make it simpler
This commit is contained in:
@@ -2,180 +2,167 @@ package squashfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"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...).
|
type fileHolder struct {
|
||||||
//If AllowErrors is true, when errors are encountered, it just prints to the log instead of failing.
|
reader io.Reader
|
||||||
|
path string
|
||||||
|
name string
|
||||||
|
symLocation string
|
||||||
|
folder bool
|
||||||
|
symlink bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//Writer is used to creaste squashfs archives. Currently unusable
|
||||||
|
//TODO: Make usable
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
files map[string][]*File
|
structure map[string][]*fileHolder
|
||||||
symlinkTable map[string]string //[oldpath]newpath
|
symlinkTable map[string]string //[oldpath]newpath
|
||||||
symTableTemp map[string]string
|
compressionType int
|
||||||
directories []string
|
allowErrors bool //AllowErrors allows errors when adding folders and their children.
|
||||||
compression int
|
|
||||||
ResolveSymlinks bool
|
|
||||||
AllowErrors bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewWriter creates a new squashfs.Writer with the default settings (gzip compression, autoresolving symlinks, and allowErrors)
|
//NewWriter creates a new
|
||||||
func NewWriter() (*Writer, error) {
|
func NewWriter() (*Writer, error) {
|
||||||
return NewWriterWithOptions(true, true, GzipCompression)
|
return NewWriterWithOptions(GzipCompression, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewWriterWithOptions creates a new squashfs.Writer with the given options.
|
func NewWriterWithOptions(compressionType int, allowErrors bool) (*Writer, error) {
|
||||||
//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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileError struct {
|
//AddFile attempts to add an os.File to the archive at it's root.
|
||||||
err error
|
func (w *Writer) AddFile(file *os.File) error {
|
||||||
files []*File
|
return w.AddFileToFolder("/", file)
|
||||||
}
|
}
|
||||||
|
|
||||||
//convertFile converts the given os.File to a squashfs.File. Returns the errors and converted file to the channels.
|
//AddFileToFolder adds the given file to the squashfs archive, placing it inside the given folder.
|
||||||
func (w *Writer) convertFile(squashfsPath string, file *os.File, subDir bool, fileErrChan chan fileError) {
|
func (w *Writer) AddFileToFolder(folder string, file *os.File) error {
|
||||||
var out fileError
|
name := path.Base(file.Name())
|
||||||
var fil File
|
if !strings.HasSuffix(folder, "/") {
|
||||||
fil.Reader = file
|
folder += "/"
|
||||||
fil.path = squashfsPath
|
}
|
||||||
fil.name = path.Base(file.Name())
|
return w.AddFileTo(folder+name, file)
|
||||||
mode := fil.Mode()
|
}
|
||||||
|
|
||||||
if mode.IsRegular() {
|
//AddFileTo adds the given file to the squashfs archive at the given filepath.
|
||||||
fil.filType = inode.BasicFileType
|
func (w *Writer) AddFileTo(filepath string, file *os.File) error {
|
||||||
goto successExit
|
filepath = path.Clean(filepath)
|
||||||
} else if mode.IsDir() {
|
if !strings.HasPrefix(filepath, "/") {
|
||||||
fil.filType = inode.BasicSymlinkType
|
filepath = "/" + filepath
|
||||||
subDirs, err := file.Readdirnames(-1)
|
}
|
||||||
|
var holder fileHolder
|
||||||
|
holder.path, holder.name = path.Split(filepath)
|
||||||
|
holder.reader = file
|
||||||
|
stat, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
holder.folder = stat.IsDir()
|
||||||
|
holder.symlink = (stat.Mode()&os.ModeSymlink == os.ModeSymlink)
|
||||||
|
if holder.symlink {
|
||||||
|
target, err := os.Readlink(file.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if w.AllowErrors && !subDir {
|
return err
|
||||||
log.Println("Can't get sub-directories for", file.Name())
|
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
out.err = err
|
|
||||||
}
|
|
||||||
goto failExit
|
|
||||||
}
|
}
|
||||||
subDirChan := make(chan fileError)
|
holder.symLocation = target
|
||||||
for _, filName := range subDirs {
|
} else if holder.folder {
|
||||||
go func(filename string, returnChan chan fileError) {
|
subDirNames, err := file.Readdirnames(-1)
|
||||||
subFil, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
out.err = err
|
|
||||||
returnChan <- fileError{
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.convertFile(fil.Path(), subFil, true, subDirChan)
|
|
||||||
}(file.Name()+filName, subDirChan)
|
|
||||||
}
|
|
||||||
for range subDirs {
|
|
||||||
filErr := <-subDirChan
|
|
||||||
if filErr.err != nil {
|
|
||||||
if w.AllowErrors && !subDir {
|
|
||||||
log.Println("Error while adding subdirectory of", file.Name())
|
|
||||||
log.Println(filErr.err)
|
|
||||||
} else if subDir {
|
|
||||||
if out.err == nil {
|
|
||||||
out.err = filErr.err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.err = err
|
|
||||||
goto failExit
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out.files = append(out.files, filErr.files...)
|
|
||||||
}
|
|
||||||
goto successExit
|
|
||||||
} else if mode&os.ModeSymlink == os.ModeSymlink {
|
|
||||||
fil.filType = inode.BasicSymlinkType
|
|
||||||
symLocation, err := os.Readlink(file.Name())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if w.AllowErrors && !subDir {
|
return err
|
||||||
log.Println("Error while getting symlink's information for", file.Name())
|
}
|
||||||
|
dirsAdded := make([]string, 0)
|
||||||
|
for _, subDir := range subDirNames {
|
||||||
|
fil, err := os.Open(file.Name() + subDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = w.AddFileToFolder(holder.path+"/"+holder.name, fil)
|
||||||
|
if err != nil && !w.AllowErrors {
|
||||||
|
for _, dir := range dirsAdded {
|
||||||
|
w.Remove(dir)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
} else if err != nil {
|
||||||
|
log.Println("Error while adding", fil.Name())
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
} else {
|
|
||||||
out.err = err
|
|
||||||
}
|
}
|
||||||
goto failExit
|
if !w.AllowErrors {
|
||||||
}
|
dirsAdded = append(dirsAdded, holder.path+"/"+holder.name)
|
||||||
if w.ResolveSymlinks {
|
|
||||||
if val, ok := w.symlinkTable[symLocation]; ok {
|
|
||||||
symLocation = val
|
|
||||||
} else if val, ok := w.symTableTemp[symLocation]; ok {
|
|
||||||
symLocation = val
|
|
||||||
} else {
|
|
||||||
//TODO: either add the file, or place the file in this location. Maybe defer this until after all the other files are added?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO: store the symLocation inside the File somehow....
|
} else if !stat.Mode().IsRegular() {
|
||||||
|
return errors.New("Unsupported file type " + file.Name())
|
||||||
}
|
}
|
||||||
if w.AllowErrors && !subDir {
|
w.structure[holder.path] = append(w.structure[holder.path], &holder)
|
||||||
log.Println("Unsupported file type for", file.Name())
|
return nil
|
||||||
} else {
|
|
||||||
out.err = errors.New("Unsupported file type")
|
|
||||||
}
|
|
||||||
failExit: //before this is used, make sure to log or set the error.
|
|
||||||
fileErrChan <- out
|
|
||||||
return
|
|
||||||
successExit:
|
|
||||||
out.files = []*File{&fil}
|
|
||||||
fileErrChan <- out
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//AddFilesToPath adds the give os.Files to the given path within the squashfs archive.
|
//AddReaderTo adds the data from the given reader to the archive as a file located at the given filepath.
|
||||||
//If AllowErrors is true, this will ALWAYS return nil
|
//Data from the reader is not read until the squashfs archive is writen.
|
||||||
func (w *Writer) AddFilesToPath(squashfsPath string, files ...*os.File) error {
|
//If the given reader implements io.Closer, it will be closed after it is fully read.
|
||||||
squashfsPath = path.Clean(squashfsPath)
|
func (w *Writer) AddReaderTo(filepath string, reader io.Reader) error {
|
||||||
if strings.HasPrefix(squashfsPath, "/") {
|
filepath = path.Clean(filepath)
|
||||||
squashfsPath = strings.TrimPrefix(squashfsPath, "/")
|
if !strings.HasPrefix(filepath, "/") {
|
||||||
|
filepath = "/" + filepath
|
||||||
}
|
}
|
||||||
if squashfsPath == "." {
|
var holder fileHolder
|
||||||
squashfsPath = "/"
|
holder.path, holder.name = path.Split(filepath)
|
||||||
}
|
holder.reader = reader
|
||||||
fileErrChan := make(chan fileError)
|
w.structure[holder.path] = append(w.structure[holder.path], &holder)
|
||||||
for _, fil := range files {
|
return nil
|
||||||
go w.convertFile(squashfsPath, fil, false, fileErrChan)
|
|
||||||
}
|
|
||||||
return errors.New("Not yet ready")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//AddFiles adds all files given to the root directory
|
//Remove tries to remove the file(s) at the given filepath. If wildcards are used, it will remove all files that match.
|
||||||
//If AllowErrors is true, this will ALWAYS return nil
|
//Returns true if one or more files are removed.
|
||||||
func (w *Writer) AddFiles(files ...*os.File) error {
|
func (w *Writer) Remove(filepath string) bool {
|
||||||
return w.AddFilesToPath("/", files...)
|
var matchFound bool
|
||||||
|
filepath = path.Clean(filepath)
|
||||||
|
if !strings.HasPrefix(filepath, "/") {
|
||||||
|
filepath = "/" + filepath
|
||||||
|
}
|
||||||
|
dir, name := path.Split(filepath)
|
||||||
|
for structDir, files := range w.structure {
|
||||||
|
if match, _ := path.Match(dir, structDir); match {
|
||||||
|
for i, fil := range files {
|
||||||
|
if match, _ = path.Match(name, fil.name); match {
|
||||||
|
matchFound = true
|
||||||
|
if i != len(files)-1 {
|
||||||
|
w.structure[structDir] = append(w.structure[structDir][:i], w.structure[structDir][i+1:]...)
|
||||||
|
} else {
|
||||||
|
w.structure[structDir] = w.structure[structDir][:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchFound
|
||||||
}
|
}
|
||||||
|
|
||||||
//RemoveFileAt removes the file at filepath from the Writer.
|
//FixSymlinks will scan through the squashfs archive and try to find broken symlinks and fix them.
|
||||||
//If multiple files match the given filepath (such as if there are wildcards), all matching files are removed.
|
//This done by replacing the symlink with the target file and then pointing other symlinks to that file.
|
||||||
//If one or more files are removed, returns true.
|
//
|
||||||
func (w *Writer) RemoveFileAt(filepath string) bool {
|
//If this is not run before writing, you may end up with broken symlinks.
|
||||||
//TODO
|
func (w *Writer) FixSymlinks() error {
|
||||||
return false
|
return errors.New("DON'T")
|
||||||
|
}
|
||||||
|
|
||||||
|
//WriteToFilename creates the squashfs archive with the given filepath.
|
||||||
|
func (w *Writer) WriteToFilename(filepath string) error {
|
||||||
|
newFil, err := os.Create(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.WriteTo(newFil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//WriteTo attempts to write the archive to the given io.Writer.
|
||||||
|
func (w *Writer) WriteTo(write io.Writer) (int64, error) {
|
||||||
|
return 0, errors.New("I SAID DON'T")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user