@@ -15,6 +15,7 @@ import (
1515"github.com/CalebQ42/squashfs/internal/data"
1616"github.com/CalebQ42/squashfs/internal/directory"
1717"github.com/CalebQ42/squashfs/internal/inode"
18+ "github.com/CalebQ42/squashfs/internal/threadmanager"
1819)
1920
2021// File represents a file inside a squashfs archive.
@@ -219,25 +220,48 @@ func (f File) GetSymlinkFile() *File {
219220
220221// ExtractionOptions are available options on how to extract.
221222type ExtractionOptions struct {
223+ manager * threadmanager.Manager
222224LogOutput io.Writer //Where error log should write.
223225DereferenceSymlink bool //Replace symlinks with the target file.
224226UnbreakSymlink bool //Try to make sure symlinks remain unbroken when extracted, without changing the symlink.
225227Verbose bool //Prints extra info to log on an error.
226- IgnorePerm bool //Ignore file's permissions and instead use FolderPerm .
227- FolderPerm fs.FileMode //Permission to use when making the root folder if it doesn't exist .
228+ IgnorePerm bool //Ignore file's permissions and instead use Perm .
229+ Perm fs.FileMode //Permission to use when IgnorePerm. Defaults to 0755 .
228230notFirst bool
229231}
230232
233+ func DefaultOptions () * ExtractionOptions {
234+ return & ExtractionOptions {
235+ Perm : 0755 ,
236+ }
237+ }
238+
231239// ExtractTo extracts the File to the given folder with the default options.
232240// If the File is a directory, it instead extracts the directory's contents to the folder.
233241func (f File ) ExtractTo (folder string ) error {
234- return f .realExtract (folder , & ExtractionOptions {})
242+ return f .realExtract (folder , DefaultOptions ())
243+ }
244+
245+ // ExtractVerbose extracts the File to the folder with the Verbose option.
246+ func (f File ) ExtractVerbose (folder string ) error {
247+ op := DefaultOptions ()
248+ op .Verbose = true
249+ return f .realExtract (folder , op )
250+ }
251+
252+ // ExtractIgnorePermissions extracts the File to the folder with the IgnorePerm option.
253+ func (f File ) ExtractIgnorePermissions (folder string ) error {
254+ op := DefaultOptions ()
255+ op .IgnorePerm = true
256+ return f .realExtract (folder , op )
235257}
236258
237259// ExtractSymlink extracts the File to the folder with the DereferenceSymlink option.
238260// If the File is a directory, it instead extracts the directory's contents to the folder.
239261func (f File ) ExtractSymlink (folder string ) error {
240- return f .realExtract (folder , & ExtractionOptions {})
262+ op := DefaultOptions ()
263+ op .DereferenceSymlink = true
264+ return f .realExtract (folder , op )
241265}
242266
243267// ExtractWithOptions extracts the File to the given folder with the given ExtrationOptions.
@@ -250,18 +274,17 @@ func (f File) ExtractWithOptions(folder string, op *ExtractionOptions) error {
250274}
251275
252276func (f File ) realExtract (folder string , op * ExtractionOptions ) (err error ) {
277+ if op .manager == nil {
278+ op .manager = threadmanager .NewManager (runtime .NumCPU ())
279+ }
253280extDir := folder + "/" + f .e .Name
254281if ! op .notFirst {
255282op .notFirst = true
256283if f .IsDir () {
257284extDir = folder
258285_ , err = os .Open (folder )
259286if err != nil && os .IsNotExist (err ) {
260- if op .IgnorePerm {
261- err = os .Mkdir (extDir , op .FolderPerm | (f .Mode ()& fs .ModeType ))
262- } else {
263- err = os .Mkdir (extDir , f .Mode ())
264- }
287+ err = os .Mkdir (extDir , op .Perm )
265288}
266289if err != nil {
267290if op .Verbose {
@@ -274,17 +297,19 @@ func (f File) realExtract(folder string, op *ExtractionOptions) (err error) {
274297switch {
275298case f .IsDir ():
276299if folder != extDir && f .e .Name != "" {
277- if op .IgnorePerm {
278- err = os .Mkdir (extDir , op .FolderPerm | (f .Mode ()& fs .ModeType ))
279- } else {
280- err = os .Mkdir (extDir , f .Mode ())
281- }
300+ //First extract it with a permisive permission.
301+ err = os .Mkdir (extDir , op .Perm )
282302if err != nil {
283303if op .Verbose {
284304log .Println ("Error while making directory" , extDir )
285305}
286306return
287307}
308+ //Then set it to it's actual permissions once we're done with it
309+ if ! op .IgnorePerm {
310+ defer os .Chmod (extDir , f .Mode ())
311+ defer os .Chown (extDir , int (f .r .ids [f .i .UidInd ]), int (f .r .ids [f .i .GidInd ]))
312+ }
288313}
289314var filFS * FS
290315filFS , err = f .FS ()
@@ -324,6 +349,8 @@ func (f File) realExtract(folder string, op *ExtractionOptions) (err error) {
324349//Then we extract the files.
325350for i = 0 ; i < len (files ); i ++ {
326351go func (index int ) {
352+ n := op .manager .Lock ()
353+ defer op .manager .Unlock (n )
327354subF , goErr := f .r .newFile (files [index ], filFS )
328355if goErr != nil {
329356if op .Verbose {
@@ -368,9 +395,10 @@ func (f File) realExtract(folder string, op *ExtractionOptions) (err error) {
368395return err
369396}
370397if op .IgnorePerm {
371- os .Chmod (fil . Name () , op .FolderPerm | (f .Mode ()& fs .ModeType ))
398+ os .Chmod (extDir , op .Perm | (f .Mode ()& fs .ModeType ))
372399} else {
373- os .Chmod (fil .Name (), f .Mode ())
400+ os .Chmod (extDir , f .Mode ())
401+ os .Chown (extDir , int (f .r .ids [f .i .UidInd ]), int (f .r .ids [f .i .GidInd ]))
374402}
375403case f .IsSymlink ():
376404symPath := f .SymlinkPath ()
@@ -420,9 +448,10 @@ func (f File) realExtract(folder string, op *ExtractionOptions) (err error) {
420448return err
421449}
422450if op .IgnorePerm {
423- os .Chmod (extDir , op .FolderPerm | (f .Mode ()& fs .ModeType ))
451+ os .Chmod (extDir , op .Perm | (f .Mode ()& fs .ModeType ))
424452} else {
425453os .Chmod (extDir , f .Mode ())
454+ os .Chown (extDir , int (f .r .ids [f .i .UidInd ]), int (f .r .ids [f .i .GidInd ]))
426455}
427456case f .isDeviceOrFifo ():
428457if runtime .GOOS == "windows" {
@@ -469,9 +498,10 @@ func (f File) realExtract(folder string, op *ExtractionOptions) (err error) {
469498return err
470499}
471500if op .IgnorePerm {
472- os .Chmod (extDir , op .FolderPerm | (f .Mode ()& fs .ModeType ))
501+ os .Chmod (extDir , op .Perm | (f .Mode ()& fs .ModeType ))
473502} else {
474503os .Chmod (extDir , f .Mode ())
504+ os .Chown (extDir , int (f .r .ids [f .i .UidInd ]), int (f .r .ids [f .i .GidInd ]))
475505}
476506case f .e .Type == inode .Sock :
477507if op .Verbose {
0 commit comments