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