github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/gzip/file.go (about)

     1  // Copyright 2017-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gzip
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/u-root/u-root/pkg/uio"
    13  )
    14  
    15  // File is a file path to be compressed or decompressed.
    16  type File struct {
    17  	Path    string
    18  	Options *Options
    19  }
    20  
    21  // outputPath removes the path suffix on decompress and adds it on compress.
    22  // In the case of when options stdout or test are enabled it returns the path
    23  // as is.
    24  func (f *File) outputPath() string {
    25  	if f.Options.Stdout || f.Options.Test {
    26  		return f.Path
    27  	} else if f.Options.Decompress {
    28  		return f.Path[:len(f.Path)-len(f.Options.Suffix)]
    29  	}
    30  	return f.Path + f.Options.Suffix
    31  }
    32  
    33  // CheckPath validates the input file path. Checks on compression
    34  // if the path has the correct suffix, and on decompression checks
    35  // that it doesn't have the suffix. Allows override by force option.
    36  func (f *File) CheckPath() error {
    37  	_, err := os.Stat(f.Path)
    38  	if os.IsNotExist(err) {
    39  		return fmt.Errorf("skipping, %s does not exist", f.Path)
    40  	} else if os.IsPermission(err) {
    41  		return fmt.Errorf("skipping, %s permission denied", f.Path)
    42  	}
    43  
    44  	if !f.Options.Force {
    45  		if f.Options.Decompress {
    46  			if !strings.HasSuffix(f.Path, f.Options.Suffix) {
    47  				return fmt.Errorf("skipping, %s does not have %s suffix", f.Path, f.Options.Suffix)
    48  			}
    49  		} else {
    50  			if strings.HasSuffix(f.Path, f.Options.Suffix) {
    51  				return fmt.Errorf("skipping, %s already has %s suffix", f.Path, f.Options.Suffix)
    52  			}
    53  		}
    54  	}
    55  	return nil
    56  }
    57  
    58  // CheckOutputPath checks if output is attempting to write binary to stdout if
    59  // stdout is a device. Also checks if output path already exists. Allow
    60  // override via force option.
    61  func (f *File) CheckOutputPath() error {
    62  	_, err := os.Stat(f.outputPath())
    63  	if !os.IsNotExist(err) && !f.Options.Stdout && !f.Options.Test && !f.Options.Force {
    64  		return fmt.Errorf("skipping, %s already exist", f.outputPath())
    65  	} else if os.IsPermission(err) {
    66  		return fmt.Errorf("skipping, %s permission denied", f.outputPath())
    67  	}
    68  	return nil
    69  }
    70  
    71  // CheckOutputStdout checks if output is attempting to write binary to stdout
    72  // if stdout is a device.
    73  func (f *File) CheckOutputStdout() error {
    74  	if f.Options.Stdout {
    75  		stat, _ := os.Stdout.Stat()
    76  		if !f.Options.Decompress && !f.Options.Force && (stat.Mode()&os.ModeDevice) != 0 {
    77  			return fmt.Errorf("fatal, trying to write compressed data to a terminal/device (use -f to force)")
    78  		}
    79  	}
    80  	return nil
    81  }
    82  
    83  // Cleanup removes input file. Overrided with keep option. Skipped if
    84  // stdout or test option is true.
    85  func (f *File) Cleanup() error {
    86  	if !f.Options.Keep && !f.Options.Stdout && !f.Options.Test {
    87  		return os.Remove(f.Path)
    88  	}
    89  	return nil
    90  }
    91  
    92  // Process either compresses or decompressed the input file based on
    93  // the associated file.options.
    94  func (f *File) Process() error {
    95  	i, err := os.Open(f.Path)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	defer i.Close()
   100  
   101  	// Use the uio.WriteNameCloser interface so both *os.File and
   102  	// uio.WriteNameClose can be assigned to var o without any type casting below.
   103  	var o uio.WriteNameCloser
   104  
   105  	if f.Options.Test {
   106  		o = uio.Discard
   107  	} else if f.Options.Stdout {
   108  		o = os.Stdout
   109  	} else {
   110  		if o, err = os.Create(f.outputPath()); err != nil {
   111  			return err
   112  		}
   113  	}
   114  
   115  	if f.Options.Verbose && !f.Options.Quiet {
   116  		fmt.Fprintf(os.Stderr, "%s to %s\n", i.Name(), o.Name())
   117  	}
   118  
   119  	if f.Options.Decompress {
   120  		if err := Decompress(i, o, f.Options.Blocksize, f.Options.Processes); err != nil {
   121  			if !f.Options.Stdout {
   122  				o.Close()
   123  			}
   124  			return err
   125  		}
   126  	} else {
   127  		if err := Compress(i, o, f.Options.Level, f.Options.Blocksize, f.Options.Processes); err != nil {
   128  			if !f.Options.Stdout {
   129  				o.Close()
   130  			}
   131  			return err
   132  		}
   133  	}
   134  
   135  	if f.Options.Stdout {
   136  		return nil
   137  	}
   138  	return o.Close()
   139  }