Added IgnorePerm to ExtractionOptions

This commit is contained in:
Caleb Gardner
2023-04-12 07:57:57 -05:00
parent 01de43a5ae
commit 94b45c8402
+62 -11
View File
@@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
@@ -59,6 +60,21 @@ func (f File) Stat() (fs.FileInfo, error) {
return newFileInfo(f.e, f.i), nil return newFileInfo(f.e, f.i), nil
} }
// Mode returns the file's fs.FileMode
func (f File) Mode() fs.FileMode {
switch f.e.Type {
case inode.Dir:
return fs.FileMode(f.i.Perm) | fs.ModeDir
case inode.Char:
return fs.FileMode(f.i.Perm) | fs.ModeCharDevice
case inode.Block:
return fs.FileMode(f.i.Perm) | fs.ModeDevice
case inode.Sym:
return fs.FileMode(f.i.Perm) | fs.ModeSymlink
}
return fs.FileMode(f.i.Perm)
}
// Read reads the data from the file. Only works if file is a normal file. // Read reads the data from the file. Only works if file is a normal file.
func (f File) Read(p []byte) (int, error) { func (f File) Read(p []byte) (int, error) {
if f.i.Type != inode.Fil && f.i.Type != inode.EFil { if f.i.Type != inode.Fil && f.i.Type != inode.EFil {
@@ -86,7 +102,7 @@ func (f File) WriteTo(w io.Writer) (int64, error) {
return f.fullRdr.WriteTo(w) return f.fullRdr.WriteTo(w)
} }
// Close simply nils the underlying reader. Here mostly to satisfy fs.File // Close simply nils the underlying reader.
func (f *File) Close() error { func (f *File) Close() error {
f.rdr = nil f.rdr = nil
return nil return nil
@@ -204,10 +220,11 @@ func (f File) GetSymlinkFile() *File {
// ExtractionOptions are available options on how to extract. // ExtractionOptions are available options on how to extract.
type ExtractionOptions struct { type ExtractionOptions struct {
LogOutput io.Writer //Where error log should write. If nil, uses os.Stdout. Has no effect if verbose is false. LogOutput io.Writer //Where error log should write. If nil, uses os.Stdout. Has no effect if verbose is false.
DereferenceSymlink bool //Replace symlinks with the target file DereferenceSymlink bool //Replace symlinks with the target file.
UnbreakSymlink bool //Try to make sure symlinks remain unbroken when extracted, without changing the symlink UnbreakSymlink bool //Try to make sure symlinks remain unbroken when extracted, without changing the symlink.
Verbose bool //Prints extra info to log on an error Verbose bool //Prints extra info to log on an error.
FolderPerm fs.FileMode //The permissions used when creating the extraction folder IgnorePerm bool //ignore the file's permission and instead use FolderPerm.
FolderPerm fs.FileMode //The permissions used when creating the extraction folder. Defaults to 0755.
} }
// DefaultOptions is the default ExtractionOptions. // DefaultOptions is the default ExtractionOptions.
@@ -226,10 +243,9 @@ func (f File) ExtractTo(folder string) error {
// ExtractSymlink extracts the File to the folder with the DereferenceSymlink option. // ExtractSymlink extracts the File to the folder with the DereferenceSymlink option.
// If the File is a directory, it instead extracts the directory's contents to the folder. // If the File is a directory, it instead extracts the directory's contents to the folder.
func (f File) ExtractSymlink(folder string) error { func (f File) ExtractSymlink(folder string) error {
return f.ExtractWithOptions(folder, ExtractionOptions{ op := DefaultOptions()
DereferenceSymlink: true, op.DereferenceSymlink = true
FolderPerm: 0755, return f.ExtractWithOptions(folder, op)
})
} }
// ExtractWithOptions extracts the File to the given folder with the given ExtrationOptions. // ExtractWithOptions extracts the File to the given folder with the given ExtrationOptions.
@@ -276,8 +292,11 @@ func (f File) realExtract(folder string, op ExtractionOptions) error {
return return
} }
if fil.IsDir() { if fil.IsDir() {
info, _ := fil.Stat() perm := f.Mode()
err = os.Mkdir(filepath.Join(folder, fil.e.Name), info.Mode()) if op.IgnorePerm {
perm = (op.FolderPerm & fs.ModePerm) | (perm & fs.ModeType)
}
err = os.Mkdir(filepath.Join(folder, fil.e.Name), perm)
if err != nil { if err != nil {
if op.Verbose { if op.Verbose {
log.Println("Error while creating", filepath.Join(folder, fil.e.Name)) log.Println("Error while creating", filepath.Join(folder, fil.e.Name))
@@ -316,6 +335,7 @@ func (f File) realExtract(folder string, op ExtractionOptions) error {
} }
return err return err
} }
defer fil.Close()
_, err = io.Copy(fil, f) _, err = io.Copy(fil, f)
if err != nil { if err != nil {
if op.Verbose { if op.Verbose {
@@ -323,6 +343,11 @@ func (f File) realExtract(folder string, op ExtractionOptions) error {
} }
return err return err
} }
if op.IgnorePerm {
os.Chmod(fil.Name(), op.FolderPerm)
} else {
os.Chmod(fil.Name(), f.Mode())
}
case f.IsSymlink(): case f.IsSymlink():
symPath := f.SymlinkPath() symPath := f.SymlinkPath()
if op.DereferenceSymlink { if op.DereferenceSymlink {
@@ -370,7 +395,18 @@ func (f File) realExtract(folder string, op ExtractionOptions) error {
} }
return err return err
} }
if op.IgnorePerm {
os.Chmod(folder+"/"+f.e.Name, op.FolderPerm)
} else {
os.Chmod(folder+"/"+f.e.Name, f.Mode())
}
case f.isDeviceOrFifo(): case f.isDeviceOrFifo():
if runtime.GOOS == "windows" {
if op.Verbose {
log.Println(folder+"/"+f.e.Name, "ignored since it's a device link and can't be created on Windows.")
}
return nil
}
_, err = exec.LookPath("mknod") _, err = exec.LookPath("mknod")
if err != nil { if err != nil {
if op.Verbose { if op.Verbose {
@@ -384,6 +420,12 @@ func (f File) realExtract(folder string, op ExtractionOptions) error {
} else if f.i.Type == inode.Block || f.i.Type == inode.EBlock { } else if f.i.Type == inode.Block || f.i.Type == inode.EBlock {
typ = "b" typ = "b"
} else { //Fifo IPC } else { //Fifo IPC
if runtime.GOOS == "darwin" {
if op.Verbose {
log.Println(folder+"/"+f.e.Name, "ignored since it's a Fifo file and can't be created on Darwin.")
}
return nil
}
typ = "p" typ = "p"
} }
cmd := exec.Command("mknod", folder+"/"+f.e.Name, typ) cmd := exec.Command("mknod", folder+"/"+f.e.Name, typ)
@@ -402,6 +444,15 @@ func (f File) realExtract(folder string, op ExtractionOptions) error {
} }
return err return err
} }
if op.IgnorePerm {
os.Chmod(folder+"/"+f.e.Name, op.FolderPerm)
} else {
os.Chmod(folder+"/"+f.e.Name, f.Mode())
}
case f.e.Type == inode.Sock:
if op.Verbose {
log.Println(folder+"/"+f.e.Name, "ignored since it's a socket file.")
}
default: default:
return errors.New("Unsupported file type. Inode type: " + strconv.Itoa(int(f.i.Type))) return errors.New("Unsupported file type. Inode type: " + strconv.Itoa(int(f.i.Type)))
} }