os: add DirFS, which is used by many programs to access readdir.
It's wafer-thin :-) Includes smoke test from upstream. TODO: once t.TempDir is implemented, add io/fs to the list of standard library tests to run; that's a better test.
Этот коммит содержится в:
		
							родитель
							
								
									641a7e5cb9
								
							
						
					
					
						коммит
						98a6ed8059
					
				
					 2 изменённых файлов: 120 добавлений и 1 удалений
				
			
		|  | @ -5,6 +5,7 @@ package os | ||||||
| import ( | import ( | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/fs" | 	"io/fs" | ||||||
|  | 	"runtime" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type ( | type ( | ||||||
|  | @ -12,9 +13,56 @@ type ( | ||||||
| 	FileInfo = fs.FileInfo | 	FileInfo = fs.FileInfo | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // The followings are copied from Go 1.16 official implementation: | // The followings are copied from Go 1.16 or 1.17 official implementation: | ||||||
| // https://github.com/golang/go/blob/go1.16/src/os/file.go | // https://github.com/golang/go/blob/go1.16/src/os/file.go | ||||||
| 
 | 
 | ||||||
|  | // DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir. | ||||||
|  | // | ||||||
|  | // Note that DirFS("/prefix") only guarantees that the Open calls it makes to the | ||||||
|  | // operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the | ||||||
|  | // same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside | ||||||
|  | // the /prefix tree, then using DirFS does not stop the access any more than using | ||||||
|  | // os.Open does. DirFS is therefore not a general substitute for a chroot-style security | ||||||
|  | // mechanism when the directory tree contains arbitrary content. | ||||||
|  | func DirFS(dir string) fs.FS { | ||||||
|  | 	return dirFS(dir) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func containsAny(s, chars string) bool { | ||||||
|  | 	for i := 0; i < len(s); i++ { | ||||||
|  | 		for j := 0; j < len(chars); j++ { | ||||||
|  | 			if s[i] == chars[j] { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type dirFS string | ||||||
|  | 
 | ||||||
|  | func (dir dirFS) Open(name string) (fs.File, error) { | ||||||
|  | 	if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { | ||||||
|  | 		return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid} | ||||||
|  | 	} | ||||||
|  | 	f, err := Open(string(dir) + "/" + name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err // nil fs.File | ||||||
|  | 	} | ||||||
|  | 	return f, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (dir dirFS) Stat(name string) (fs.FileInfo, error) { | ||||||
|  | 	if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { | ||||||
|  | 		return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid} | ||||||
|  | 	} | ||||||
|  | 	f, err := Stat(string(dir) + "/" + name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return f, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ReadFile reads the named file and returns the contents. | // ReadFile reads the named file and returns the contents. | ||||||
| // A successful call returns err == nil, not err == EOF. | // A successful call returns err == nil, not err == EOF. | ||||||
| // Because ReadFile reads the whole file, it does not treat an EOF from Read | // Because ReadFile reads the whole file, it does not treat an EOF from Read | ||||||
|  |  | ||||||
							
								
								
									
										71
									
								
								src/os/file_go_116_test.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										71
									
								
								src/os/file_go_116_test.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,71 @@ | ||||||
|  | // Copyright 2009 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | // +build go1.16,!baremetal,!js,!wasi | ||||||
|  | 
 | ||||||
|  | // DirFS tests copied verbatim from upstream os_test.go, and adjusted minimally to fit tinygo. | ||||||
|  | 
 | ||||||
|  | package os_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io/fs" | ||||||
|  | 	"os" | ||||||
|  | 	. "os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"runtime" | ||||||
|  | 	"testing" | ||||||
|  | 	"testing/fstest" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestDirFS(t *testing.T) { | ||||||
|  | 	if runtime.GOOS == "windows" { | ||||||
|  | 		t.Log("TODO: implement Readdir for Windows") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err := fstest.TestFS(DirFS("./testdata/dirfs"), "a", "b", "dir/x"); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Test that Open does not accept backslash as separator. | ||||||
|  | 	d := DirFS(".") | ||||||
|  | 	_, err := d.Open(`testdata\dirfs`) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf(`Open testdata\dirfs succeeded`) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestDirFSPathsValid(t *testing.T) { | ||||||
|  | 	if runtime.GOOS == "windows" { | ||||||
|  | 		t.Log("skipping on Windows") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// TODO: switch back to t.TempDir once it's implemented | ||||||
|  | 	d, err := MkdirTemp("", "TestDirFSPathsValid") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	defer Remove(d) | ||||||
|  | 	if err := os.WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	defer Remove(filepath.Join(d, "control.txt")) | ||||||
|  | 	if err := os.WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	defer Remove(filepath.Join(d, `e:xperi\ment.txt`)) | ||||||
|  | 
 | ||||||
|  | 	fsys := os.DirFS(d) | ||||||
|  | 	err = fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error { | ||||||
|  | 		if fs.ValidPath(e.Name()) { | ||||||
|  | 			t.Logf("%q ok", e.Name()) | ||||||
|  | 		} else { | ||||||
|  | 			t.Errorf("%q INVALID", e.Name()) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Dan Kegel
						Dan Kegel