//go:build go1.16 // +build go1.16 package os import ( "io" "io/fs" "runtime" ) type ( FileMode = fs.FileMode FileInfo = fs.FileInfo ) // 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 // 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. // A successful call returns err == nil, not err == EOF. // Because ReadFile reads the whole file, it does not treat an EOF from Read // as an error to be reported. func ReadFile(name string) ([]byte, error) { f, err := Open(name) if err != nil { return nil, err } defer f.Close() var size int if info, err := f.Stat(); err == nil { size64 := info.Size() if int64(int(size64)) == size64 { size = int(size64) } } size++ // one byte for final read at EOF // If a file claims a small size, read at least 512 bytes. // In particular, files in Linux's /proc claim size 0 but // then do not work right if read in small pieces, // so an initial read of 1 byte would not work correctly. if size < 512 { size = 512 } data := make([]byte, 0, size) for { if len(data) >= cap(data) { d := append(data[:cap(data)], 0) data = d[:len(data)] } n, err := f.Read(data[len(data):cap(data)]) data = data[:len(data)+n] if err != nil { if err == io.EOF { err = nil } return data, err } } } // WriteFile writes data to the named file, creating it if necessary. // If the file does not exist, WriteFile creates it with permissions perm (before umask); // otherwise WriteFile truncates it before writing, without changing permissions. func WriteFile(name string, data []byte, perm FileMode) error { f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm) if err != nil { return err } _, err = f.Write(data) if err1 := f.Close(); err1 != nil && err == nil { err = err1 } return err } // The defined file mode bits are the most significant bits of the FileMode. // The nine least-significant bits are the standard Unix rwxrwxrwx permissions. // The values of these bits should be considered part of the public API and // may be used in wire protocols or disk representations: they must not be // changed, although new bits might be added. const ( // The single letters are the abbreviations // used by the String method's formatting. ModeDir = fs.ModeDir // d: is a directory ModeAppend = fs.ModeAppend // a: append-only ModeExclusive = fs.ModeExclusive // l: exclusive use ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only ModeSymlink = fs.ModeSymlink // L: symbolic link ModeDevice = fs.ModeDevice // D: device file ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO) ModeSocket = fs.ModeSocket // S: Unix domain socket ModeSetuid = fs.ModeSetuid // u: setuid ModeSetgid = fs.ModeSetgid // g: setgid ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set ModeSticky = fs.ModeSticky // t: sticky ModeIrregular = fs.ModeIrregular // ?: non-regular file; nothing else is known about this file // Mask for the type bits. For regular files, none will be set. ModeType = fs.ModeType ModePerm = fs.ModePerm // Unix permission bits, 0o777 )