1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package removeall 18 19 import ( 20 "fmt" 21 "io" 22 "os" 23 "syscall" 24 25 "k8s.io/mount-utils" 26 ) 27 28 // RemoveAllOneFilesystemCommon removes the path and any children it contains, 29 // using the provided remove function. It removes everything it can but returns 30 // the first error it encounters. If the path does not exist, RemoveAll 31 // returns nil (no error). 32 // It makes sure it does not cross mount boundary, i.e. it does *not* remove 33 // files from another filesystems. Like 'rm -rf --one-file-system'. 34 // It is copied from RemoveAll() sources, with IsLikelyNotMountPoint 35 func RemoveAllOneFilesystemCommon(mounter mount.Interface, path string, remove func(string) error) error { 36 // Simple case: if Remove works, we're done. 37 err := remove(path) 38 if err == nil || os.IsNotExist(err) { 39 return nil 40 } 41 42 // Otherwise, is this a directory we need to recurse into? 43 dir, serr := os.Lstat(path) 44 if serr != nil { 45 if serr, ok := serr.(*os.PathError); ok && (os.IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { 46 return nil 47 } 48 return serr 49 } 50 if !dir.IsDir() { 51 // Not a directory; return the error from remove. 52 return err 53 } 54 55 // Directory. 56 isNotMount, err := mounter.IsLikelyNotMountPoint(path) 57 if err != nil { 58 return err 59 } 60 if !isNotMount { 61 return fmt.Errorf("cannot delete directory %s: it is a mount point", path) 62 } 63 64 fd, err := os.Open(path) 65 if err != nil { 66 if os.IsNotExist(err) { 67 // Race. It was deleted between the Lstat and Open. 68 // Return nil per RemoveAll's docs. 69 return nil 70 } 71 return err 72 } 73 74 // Remove contents & return first error. 75 err = nil 76 for { 77 names, err1 := fd.Readdirnames(100) 78 for _, name := range names { 79 err1 := RemoveAllOneFilesystemCommon(mounter, path+string(os.PathSeparator)+name, remove) 80 if err == nil { 81 err = err1 82 } 83 } 84 if err1 == io.EOF { 85 break 86 } 87 // If Readdirnames returned an error, use it. 88 if err == nil { 89 err = err1 90 } 91 if len(names) == 0 { 92 break 93 } 94 } 95 96 // Close directory, because windows won't remove opened directory. 97 fd.Close() 98 99 // Remove directory. 100 err1 := remove(path) 101 if err1 == nil || os.IsNotExist(err1) { 102 return nil 103 } 104 if err == nil { 105 err = err1 106 } 107 return err 108 } 109 110 // RemoveAllOneFilesystem removes the path and any children it contains, using 111 // the os.Remove function. It makes sure it does not cross mount boundaries, 112 // i.e. it returns an error rather than remove files from another filesystem. 113 // It removes everything it can but returns the first error it encounters. 114 // If the path does not exist, it returns nil (no error). 115 func RemoveAllOneFilesystem(mounter mount.Interface, path string) error { 116 return RemoveAllOneFilesystemCommon(mounter, path, os.Remove) 117 } 118 119 // RemoveDirsOneFilesystem removes the path and any empty subdirectories it 120 // contains, using the syscall.Rmdir function. Unlike RemoveAllOneFilesystem, 121 // RemoveDirsOneFilesystem will remove only directories and returns an error if 122 // it encounters any files in the directory tree. It makes sure it does not 123 // cross mount boundaries, i.e. it returns an error rather than remove dirs 124 // from another filesystem. It removes everything it can but returns the first 125 // error it encounters. If the path does not exist, it returns nil (no error). 126 func RemoveDirsOneFilesystem(mounter mount.Interface, path string) error { 127 return RemoveAllOneFilesystemCommon(mounter, path, syscall.Rmdir) 128 } 129