1 // Copyright 2009 The Go 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 os_test 6 7 import ( 8 "internal/testenv" 9 "io/ioutil" 10 . "os" 11 "path/filepath" 12 "runtime" 13 "syscall" 14 "testing" 15 ) 16 17 var isReadonlyError = func(error) bool { return false } 18 19 func TestMkdirAll(t *testing.T) { 20 tmpDir := TempDir() 21 path := tmpDir + "/_TestMkdirAll_/dir/./dir2" 22 err := MkdirAll(path, 0777) 23 if err != nil { 24 t.Fatalf("MkdirAll %q: %s", path, err) 25 } 26 defer RemoveAll(tmpDir + "/_TestMkdirAll_") 27 28 // Already exists, should succeed. 29 err = MkdirAll(path, 0777) 30 if err != nil { 31 t.Fatalf("MkdirAll %q (second time): %s", path, err) 32 } 33 34 // Make file. 35 fpath := path + "/file" 36 f, err := Create(fpath) 37 if err != nil { 38 t.Fatalf("create %q: %s", fpath, err) 39 } 40 defer f.Close() 41 42 // Can't make directory named after file. 43 err = MkdirAll(fpath, 0777) 44 if err == nil { 45 t.Fatalf("MkdirAll %q: no error", fpath) 46 } 47 perr, ok := err.(*PathError) 48 if !ok { 49 t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err) 50 } 51 if filepath.Clean(perr.Path) != filepath.Clean(fpath) { 52 t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath)) 53 } 54 55 // Can't make subdirectory of file. 56 ffpath := fpath + "/subdir" 57 err = MkdirAll(ffpath, 0777) 58 if err == nil { 59 t.Fatalf("MkdirAll %q: no error", ffpath) 60 } 61 perr, ok = err.(*PathError) 62 if !ok { 63 t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err) 64 } 65 if filepath.Clean(perr.Path) != filepath.Clean(fpath) { 66 t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath)) 67 } 68 69 if runtime.GOOS == "windows" { 70 path := tmpDir + `\_TestMkdirAll_\dir\.\dir2\` 71 err := MkdirAll(path, 0777) 72 if err != nil { 73 t.Fatalf("MkdirAll %q: %s", path, err) 74 } 75 } 76 } 77 78 func TestRemoveAll(t *testing.T) { 79 tmpDir := TempDir() 80 // Work directory. 81 path := tmpDir + "/_TestRemoveAll_" 82 fpath := path + "/file" 83 dpath := path + "/dir" 84 85 // Make directory with 1 file and remove. 86 if err := MkdirAll(path, 0777); err != nil { 87 t.Fatalf("MkdirAll %q: %s", path, err) 88 } 89 fd, err := Create(fpath) 90 if err != nil { 91 t.Fatalf("create %q: %s", fpath, err) 92 } 93 fd.Close() 94 if err = RemoveAll(path); err != nil { 95 t.Fatalf("RemoveAll %q (first): %s", path, err) 96 } 97 if _, err = Lstat(path); err == nil { 98 t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path) 99 } 100 101 // Make directory with file and subdirectory and remove. 102 if err = MkdirAll(dpath, 0777); err != nil { 103 t.Fatalf("MkdirAll %q: %s", dpath, err) 104 } 105 fd, err = Create(fpath) 106 if err != nil { 107 t.Fatalf("create %q: %s", fpath, err) 108 } 109 fd.Close() 110 fd, err = Create(dpath + "/file") 111 if err != nil { 112 t.Fatalf("create %q: %s", fpath, err) 113 } 114 fd.Close() 115 if err = RemoveAll(path); err != nil { 116 t.Fatalf("RemoveAll %q (second): %s", path, err) 117 } 118 if _, err := Lstat(path); err == nil { 119 t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path) 120 } 121 122 // Determine if we should run the following test. 123 testit := true 124 if runtime.GOOS == "windows" { 125 // Chmod is not supported under windows. 126 testit = false 127 } else { 128 // Test fails as root. 129 testit = Getuid() != 0 130 } 131 if testit { 132 // Make directory with file and subdirectory and trigger error. 133 if err = MkdirAll(dpath, 0777); err != nil { 134 t.Fatalf("MkdirAll %q: %s", dpath, err) 135 } 136 137 for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} { 138 fd, err = Create(s) 139 if err != nil { 140 t.Fatalf("create %q: %s", s, err) 141 } 142 fd.Close() 143 } 144 if err = Chmod(dpath, 0); err != nil { 145 t.Fatalf("Chmod %q 0: %s", dpath, err) 146 } 147 148 // No error checking here: either RemoveAll 149 // will or won't be able to remove dpath; 150 // either way we want to see if it removes fpath 151 // and path/zzz. Reasons why RemoveAll might 152 // succeed in removing dpath as well include: 153 // * running as root 154 // * running on a file system without permissions (FAT) 155 RemoveAll(path) 156 Chmod(dpath, 0777) 157 158 for _, s := range []string{fpath, path + "/zzz"} { 159 if _, err = Lstat(s); err == nil { 160 t.Fatalf("Lstat %q succeeded after partial RemoveAll", s) 161 } 162 } 163 } 164 if err = RemoveAll(path); err != nil { 165 t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err) 166 } 167 if _, err = Lstat(path); err == nil { 168 t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path) 169 } 170 } 171 172 func TestMkdirAllWithSymlink(t *testing.T) { 173 testenv.MustHaveSymlink(t) 174 175 tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-") 176 if err != nil { 177 t.Fatal(err) 178 } 179 defer RemoveAll(tmpDir) 180 181 dir := tmpDir + "/dir" 182 err = Mkdir(dir, 0755) 183 if err != nil { 184 t.Fatalf("Mkdir %s: %s", dir, err) 185 } 186 187 link := tmpDir + "/link" 188 err = Symlink("dir", link) 189 if err != nil { 190 t.Fatalf("Symlink %s: %s", link, err) 191 } 192 193 path := link + "/foo" 194 err = MkdirAll(path, 0755) 195 if err != nil { 196 t.Errorf("MkdirAll %q: %s", path, err) 197 } 198 } 199 200 func TestMkdirAllAtSlash(t *testing.T) { 201 switch runtime.GOOS { 202 case "android", "plan9", "windows": 203 t.Skipf("skipping on %s", runtime.GOOS) 204 case "darwin": 205 switch runtime.GOARCH { 206 case "arm", "arm64": 207 t.Skipf("skipping on darwin/%s, mkdir returns EPERM", runtime.GOARCH) 208 } 209 } 210 RemoveAll("/_go_os_test") 211 const dir = "/_go_os_test/dir" 212 err := MkdirAll(dir, 0777) 213 if err != nil { 214 pathErr, ok := err.(*PathError) 215 // common for users not to be able to write to / 216 if ok && (pathErr.Err == syscall.EACCES || isReadonlyError(pathErr.Err)) { 217 t.Skipf("could not create %v: %v", dir, err) 218 } 219 t.Fatalf(`MkdirAll "/_go_os_test/dir": %v, %s`, err, pathErr.Err) 220 } 221 RemoveAll("/_go_os_test") 222 } 223