os.go (3944B)
1 // Copyright 2017 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // Package os provides template functions for interacting with the operating 15 // system. 16 package os 17 18 import ( 19 "errors" 20 "fmt" 21 _os "os" 22 "path/filepath" 23 24 "github.com/bep/overlayfs" 25 "github.com/gohugoio/hugo/deps" 26 "github.com/spf13/afero" 27 "github.com/spf13/cast" 28 ) 29 30 // New returns a new instance of the os-namespaced template functions. 31 func New(d *deps.Deps) *Namespace { 32 var readFileFs, workFs afero.Fs 33 34 // The docshelper script does not have or need all the dependencies set up. 35 if d.PathSpec != nil { 36 readFileFs = overlayfs.New(overlayfs.Options{ 37 Fss: []afero.Fs{ 38 d.PathSpec.BaseFs.Work, 39 d.PathSpec.BaseFs.Content.Fs, 40 }, 41 }) 42 // See #9599 43 workFs = d.PathSpec.BaseFs.WorkDir 44 } 45 46 return &Namespace{ 47 readFileFs: readFileFs, 48 workFs: workFs, 49 deps: d, 50 } 51 } 52 53 // Namespace provides template functions for the "os" namespace. 54 type Namespace struct { 55 readFileFs afero.Fs 56 workFs afero.Fs 57 deps *deps.Deps 58 } 59 60 // Getenv retrieves the value of the environment variable named by the key. 61 // It returns the value, which will be empty if the variable is not present. 62 func (ns *Namespace) Getenv(key any) (string, error) { 63 skey, err := cast.ToStringE(key) 64 if err != nil { 65 return "", nil 66 } 67 68 if err = ns.deps.ExecHelper.Sec().CheckAllowedGetEnv(skey); err != nil { 69 return "", err 70 } 71 72 return _os.Getenv(skey), nil 73 } 74 75 // readFile reads the file named by filename in the given filesystem 76 // and returns the contents as a string. 77 func readFile(fs afero.Fs, filename string) (string, error) { 78 filename = filepath.Clean(filename) 79 if filename == "" || filename == "." || filename == string(_os.PathSeparator) { 80 return "", errors.New("invalid filename") 81 } 82 83 b, err := afero.ReadFile(fs, filename) 84 if err != nil { 85 return "", err 86 } 87 88 return string(b), nil 89 } 90 91 // ReadFile reads the file named by filename relative to the configured WorkingDir. 92 // It returns the contents as a string. 93 // There is an upper size limit set at 1 megabytes. 94 func (ns *Namespace) ReadFile(i any) (string, error) { 95 s, err := cast.ToStringE(i) 96 if err != nil { 97 return "", err 98 } 99 100 if ns.deps.PathSpec != nil { 101 s = ns.deps.PathSpec.RelPathify(s) 102 } 103 104 return readFile(ns.readFileFs, s) 105 } 106 107 // ReadDir lists the directory contents relative to the configured WorkingDir. 108 func (ns *Namespace) ReadDir(i any) ([]_os.FileInfo, error) { 109 path, err := cast.ToStringE(i) 110 if err != nil { 111 return nil, err 112 } 113 114 list, err := afero.ReadDir(ns.workFs, path) 115 if err != nil { 116 return nil, fmt.Errorf("failed to read directory %q: %s", path, err) 117 } 118 119 return list, nil 120 } 121 122 // FileExists checks whether a file exists under the given path. 123 func (ns *Namespace) FileExists(i any) (bool, error) { 124 path, err := cast.ToStringE(i) 125 if err != nil { 126 return false, err 127 } 128 129 if path == "" { 130 return false, errors.New("fileExists needs a path to a file") 131 } 132 133 status, err := afero.Exists(ns.readFileFs, path) 134 if err != nil { 135 return false, err 136 } 137 138 return status, nil 139 } 140 141 // Stat returns the os.FileInfo structure describing file. 142 func (ns *Namespace) Stat(i any) (_os.FileInfo, error) { 143 path, err := cast.ToStringE(i) 144 if err != nil { 145 return nil, err 146 } 147 148 if path == "" { 149 return nil, errors.New("fileStat needs a path to a file") 150 } 151 152 r, err := ns.readFileFs.Stat(path) 153 if err != nil { 154 return nil, err 155 } 156 157 return r, nil 158 }