go.etcd.io/etcd@v3.3.27+incompatible/pkg/fileutil/fileutil.go (about) 1 // Copyright 2015 The etcd Authors 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 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package fileutil implements utility functions related to files and paths. 16 package fileutil 17 18 import ( 19 "fmt" 20 "io" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "sort" 25 26 "github.com/coreos/pkg/capnslog" 27 ) 28 29 const ( 30 // PrivateFileMode grants owner to read/write a file. 31 PrivateFileMode = 0600 32 ) 33 34 var ( 35 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "pkg/fileutil") 36 ) 37 38 // IsDirWriteable checks if dir is writable by writing and removing a file 39 // to dir. It returns nil if dir is writable. 40 func IsDirWriteable(dir string) error { 41 f := filepath.Join(dir, ".touch") 42 if err := ioutil.WriteFile(f, []byte(""), PrivateFileMode); err != nil { 43 return err 44 } 45 return os.Remove(f) 46 } 47 48 // ReadDir returns the filenames in the given directory in sorted order. 49 func ReadDir(dirpath string) ([]string, error) { 50 dir, err := os.Open(dirpath) 51 if err != nil { 52 return nil, err 53 } 54 defer dir.Close() 55 names, err := dir.Readdirnames(-1) 56 if err != nil { 57 return nil, err 58 } 59 sort.Strings(names) 60 return names, nil 61 } 62 63 // TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory 64 // does not exists. TouchDirAll also ensures the given directory is writable. 65 func TouchDirAll(dir string) error { 66 // If path is already a directory, MkdirAll does nothing and returns nil, so, 67 // first check if dir exist with an expected permission mode. 68 if Exist(dir) { 69 err := CheckDirPermission(dir, PrivateDirMode) 70 if err != nil { 71 plog.Warningf("check file permission: %v", err) 72 } 73 } else { 74 err := os.MkdirAll(dir, PrivateDirMode) 75 if err != nil { 76 // if mkdirAll("a/text") and "text" is not 77 // a directory, this will return syscall.ENOTDIR 78 return err 79 } 80 } 81 82 return IsDirWriteable(dir) 83 } 84 85 // CreateDirAll is similar to TouchDirAll but returns error 86 // if the deepest directory was not empty. 87 func CreateDirAll(dir string) error { 88 err := TouchDirAll(dir) 89 if err == nil { 90 var ns []string 91 ns, err = ReadDir(dir) 92 if err != nil { 93 return err 94 } 95 if len(ns) != 0 { 96 err = fmt.Errorf("expected %q to be empty, got %q", dir, ns) 97 } 98 } 99 return err 100 } 101 102 func Exist(name string) bool { 103 _, err := os.Stat(name) 104 return err == nil 105 } 106 107 // ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily 108 // shorten the length of the file. 109 func ZeroToEnd(f *os.File) error { 110 // TODO: support FALLOC_FL_ZERO_RANGE 111 off, err := f.Seek(0, io.SeekCurrent) 112 if err != nil { 113 return err 114 } 115 lenf, lerr := f.Seek(0, io.SeekEnd) 116 if lerr != nil { 117 return lerr 118 } 119 if err = f.Truncate(off); err != nil { 120 return err 121 } 122 // make sure blocks remain allocated 123 if err = Preallocate(f, lenf, true); err != nil { 124 return err 125 } 126 _, err = f.Seek(off, io.SeekStart) 127 return err 128 } 129 130 // CheckDirPermission checks permission on an existing dir. 131 // Returns error if dir is empty or exist with a different permission than specified. 132 func CheckDirPermission(dir string, perm os.FileMode) error { 133 if !Exist(dir) { 134 return fmt.Errorf("directory %q empty, cannot check permission.", dir) 135 } 136 //check the existing permission on the directory 137 dirInfo, err := os.Stat(dir) 138 if err != nil { 139 return err 140 } 141 dirMode := dirInfo.Mode().Perm() 142 if dirMode != perm { 143 err = fmt.Errorf("directory %q exist, but the permission is %q. The recommended permission is %q to prevent possible unprivileged access to the data.", dir, dirInfo.Mode(), os.FileMode(PrivateDirMode)) 144 return err 145 } 146 return nil 147 }