github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/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/mvdan/u-root-coreutils/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 // Skip if the input is a Stdin. 37 func (f *File) CheckPath() error { 38 if f.Options.Stdin { 39 return nil 40 } 41 42 _, err := os.Stat(f.Path) 43 if os.IsNotExist(err) { 44 return fmt.Errorf("skipping, %s does not exist", f.Path) 45 } else if os.IsPermission(err) { 46 return fmt.Errorf("skipping, %s permission denied", f.Path) 47 } 48 49 if !f.Options.Force { 50 if f.Options.Decompress { 51 if !strings.HasSuffix(f.Path, f.Options.Suffix) { 52 return fmt.Errorf("skipping, %s does not have %s suffix", f.Path, f.Options.Suffix) 53 } 54 } else { 55 if strings.HasSuffix(f.Path, f.Options.Suffix) { 56 return fmt.Errorf("skipping, %s already has %s suffix", f.Path, f.Options.Suffix) 57 } 58 } 59 } 60 return nil 61 } 62 63 // CheckOutputPath checks if output is attempting to write binary to stdout if 64 // stdout is a device. Also checks if output path already exists. Allow 65 // override via force option. 66 func (f *File) CheckOutputPath() error { 67 _, err := os.Stat(f.outputPath()) 68 if !os.IsNotExist(err) && !f.Options.Stdout && !f.Options.Test && !f.Options.Force { 69 return fmt.Errorf("skipping, %s already exist", f.outputPath()) 70 } else if os.IsPermission(err) { 71 return fmt.Errorf("skipping, %s permission denied", f.outputPath()) 72 } 73 return nil 74 } 75 76 // CheckOutputStdout checks if output is attempting to write binary to stdout 77 // if stdout is a device. 78 func (f *File) CheckOutputStdout() error { 79 if f.Options.Stdout { 80 stat, _ := os.Stdout.Stat() 81 if !f.Options.Decompress && !f.Options.Force && (stat.Mode()&os.ModeDevice) != 0 { 82 return fmt.Errorf("fatal, trying to write compressed data to a terminal/device (use -f to force)") 83 } 84 } 85 return nil 86 } 87 88 // Cleanup removes input file. Overrided with keep option. Skipped if 89 // stdout or test option is true. 90 func (f *File) Cleanup() error { 91 if !f.Options.Keep && !f.Options.Stdout && !f.Options.Test { 92 return os.Remove(f.Path) 93 } 94 return nil 95 } 96 97 // Process either compresses or decompressed the input file based on 98 // the associated file.options. 99 func (f *File) Process() error { 100 var i *os.File 101 var err error 102 103 if f.Options.Stdin { 104 i = os.Stdin 105 } else { 106 i, err = os.Open(f.Path) 107 if err != nil { 108 return err 109 } 110 defer i.Close() 111 } 112 113 // Use the uio.WriteNameCloser interface so both *os.File and 114 // uio.WriteNameClose can be assigned to var o without any type casting below. 115 var o uio.WriteNameCloser 116 117 if f.Options.Test { 118 o = uio.Discard 119 } else if f.Options.Stdout { 120 o = os.Stdout 121 } else { 122 if o, err = os.Create(f.outputPath()); err != nil { 123 return err 124 } 125 } 126 127 if f.Options.Verbose && !f.Options.Quiet { 128 fmt.Fprintf(os.Stderr, "%s to %s\n", i.Name(), o.Name()) 129 } 130 131 if f.Options.Decompress { 132 if err := Decompress(i, o, f.Options.Blocksize, f.Options.Processes); err != nil { 133 if !f.Options.Stdout { 134 o.Close() 135 } 136 return err 137 } 138 } else { 139 if err := Compress(i, o, f.Options.Level, f.Options.Blocksize, f.Options.Processes); err != nil { 140 if !f.Options.Stdout { 141 o.Close() 142 } 143 return err 144 } 145 } 146 147 if f.Options.Stdout { 148 return nil 149 } 150 return o.Close() 151 }