diff options
author | hiddenpath <hiddenpath@yandex-team.com> | 2024-04-02 23:50:23 +0300 |
---|---|---|
committer | hiddenpath <hiddenpath@yandex-team.com> | 2024-04-03 00:02:31 +0300 |
commit | 8923c6d2c438e0aeed2e06b8b0275e1864eeee33 (patch) | |
tree | 6b5e476699fc0be5091cb650654ef5f602c8afff /contrib/go/_std_1.22/src/os | |
parent | d18afd09df2a08cd023012593b46109b77713a6c (diff) | |
download | ydb-8923c6d2c438e0aeed2e06b8b0275e1864eeee33.tar.gz |
Update golang to 1.22.1
2967d19c907adf59101a1f47b4208bd0b04a6186
Diffstat (limited to 'contrib/go/_std_1.22/src/os')
75 files changed, 9546 insertions, 0 deletions
diff --git a/contrib/go/_std_1.22/src/os/dir.go b/contrib/go/_std_1.22/src/os/dir.go new file mode 100644 index 0000000000..5306bcb3ba --- /dev/null +++ b/contrib/go/_std_1.22/src/os/dir.go @@ -0,0 +1,125 @@ +// Copyright 2016 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. + +package os + +import ( + "io/fs" + "sort" +) + +type readdirMode int + +const ( + readdirName readdirMode = iota + readdirDirEntry + readdirFileInfo +) + +// Readdir reads the contents of the directory associated with file and +// returns a slice of up to n FileInfo values, as would be returned +// by Lstat, in directory order. Subsequent calls on the same file will yield +// further FileInfos. +// +// If n > 0, Readdir returns at most n FileInfo structures. In this case, if +// Readdir returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is io.EOF. +// +// If n <= 0, Readdir returns all the FileInfo from the directory in +// a single slice. In this case, if Readdir succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil error. If it encounters an error before the end of the +// directory, Readdir returns the FileInfo read until that point +// and a non-nil error. +// +// Most clients are better served by the more efficient ReadDir method. +func (f *File) Readdir(n int) ([]FileInfo, error) { + if f == nil { + return nil, ErrInvalid + } + _, _, infos, err := f.readdir(n, readdirFileInfo) + if infos == nil { + // Readdir has historically always returned a non-nil empty slice, never nil, + // even on error (except misuse with nil receiver above). + // Keep it that way to avoid breaking overly sensitive callers. + infos = []FileInfo{} + } + return infos, err +} + +// Readdirnames reads the contents of the directory associated with file +// and returns a slice of up to n names of files in the directory, +// in directory order. Subsequent calls on the same file will yield +// further names. +// +// If n > 0, Readdirnames returns at most n names. In this case, if +// Readdirnames returns an empty slice, it will return a non-nil error +// explaining why. At the end of a directory, the error is io.EOF. +// +// If n <= 0, Readdirnames returns all the names from the directory in +// a single slice. In this case, if Readdirnames succeeds (reads all +// the way to the end of the directory), it returns the slice and a +// nil error. If it encounters an error before the end of the +// directory, Readdirnames returns the names read until that point and +// a non-nil error. +func (f *File) Readdirnames(n int) (names []string, err error) { + if f == nil { + return nil, ErrInvalid + } + names, _, _, err = f.readdir(n, readdirName) + if names == nil { + // Readdirnames has historically always returned a non-nil empty slice, never nil, + // even on error (except misuse with nil receiver above). + // Keep it that way to avoid breaking overly sensitive callers. + names = []string{} + } + return names, err +} + +// A DirEntry is an entry read from a directory +// (using the ReadDir function or a File's ReadDir method). +type DirEntry = fs.DirEntry + +// ReadDir reads the contents of the directory associated with the file f +// and returns a slice of DirEntry values in directory order. +// Subsequent calls on the same file will yield later DirEntry records in the directory. +// +// If n > 0, ReadDir returns at most n DirEntry records. +// In this case, if ReadDir returns an empty slice, it will return an error explaining why. +// At the end of a directory, the error is io.EOF. +// +// If n <= 0, ReadDir returns all the DirEntry records remaining in the directory. +// When it succeeds, it returns a nil error (not io.EOF). +func (f *File) ReadDir(n int) ([]DirEntry, error) { + if f == nil { + return nil, ErrInvalid + } + _, dirents, _, err := f.readdir(n, readdirDirEntry) + if dirents == nil { + // Match Readdir and Readdirnames: don't return nil slices. + dirents = []DirEntry{} + } + return dirents, err +} + +// testingForceReadDirLstat forces ReadDir to call Lstat, for testing that code path. +// This can be difficult to provoke on some Unix systems otherwise. +var testingForceReadDirLstat bool + +// ReadDir reads the named directory, +// returning all its directory entries sorted by filename. +// If an error occurs reading the directory, +// ReadDir returns the entries it was able to read before the error, +// along with the error. +func ReadDir(name string) ([]DirEntry, error) { + f, err := Open(name) + if err != nil { + return nil, err + } + defer f.Close() + + dirs, err := f.ReadDir(-1) + sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) + return dirs, err +} diff --git a/contrib/go/_std_1.22/src/os/dir_darwin.go b/contrib/go/_std_1.22/src/os/dir_darwin.go new file mode 100644 index 0000000000..e6d5bda24b --- /dev/null +++ b/contrib/go/_std_1.22/src/os/dir_darwin.go @@ -0,0 +1,140 @@ +// 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. + +package os + +import ( + "io" + "runtime" + "syscall" + "unsafe" +) + +// Auxiliary information if the File describes a directory +type dirInfo struct { + dir uintptr // Pointer to DIR structure from dirent.h +} + +func (d *dirInfo) close() { + if d.dir == 0 { + return + } + closedir(d.dir) + d.dir = 0 +} + +func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { + if f.dirinfo == nil { + dir, call, errno := f.pfd.OpenDir() + if errno != nil { + return nil, nil, nil, &PathError{Op: call, Path: f.name, Err: errno} + } + f.dirinfo = &dirInfo{ + dir: dir, + } + } + d := f.dirinfo + + size := n + if size <= 0 { + size = 100 + n = -1 + } + + var dirent syscall.Dirent + var entptr *syscall.Dirent + for len(names)+len(dirents)+len(infos) < size || n == -1 { + if errno := readdir_r(d.dir, &dirent, &entptr); errno != 0 { + if errno == syscall.EINTR { + continue + } + return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno} + } + if entptr == nil { // EOF + break + } + // Darwin may return a zero inode when a directory entry has been + // deleted but not yet removed from the directory. The man page for + // getdirentries(2) states that programs are responsible for skipping + // those entries: + // + // Users of getdirentries() should skip entries with d_fileno = 0, + // as such entries represent files which have been deleted but not + // yet removed from the directory entry. + // + if dirent.Ino == 0 { + continue + } + name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] + for i, c := range name { + if c == 0 { + name = name[:i] + break + } + } + // Check for useless names before allocating a string. + if string(name) == "." || string(name) == ".." { + continue + } + if mode == readdirName { + names = append(names, string(name)) + } else if mode == readdirDirEntry { + de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type)) + if IsNotExist(err) { + // File disappeared between readdir and stat. + // Treat as if it didn't exist. + continue + } + if err != nil { + return nil, dirents, nil, err + } + dirents = append(dirents, de) + } else { + info, err := lstat(f.name + "/" + string(name)) + if IsNotExist(err) { + // File disappeared between readdir + stat. + // Treat as if it didn't exist. + continue + } + if err != nil { + return nil, nil, infos, err + } + infos = append(infos, info) + } + runtime.KeepAlive(f) + } + + if n > 0 && len(names)+len(dirents)+len(infos) == 0 { + return nil, nil, nil, io.EOF + } + return names, dirents, infos, nil +} + +func dtToType(typ uint8) FileMode { + switch typ { + case syscall.DT_BLK: + return ModeDevice + case syscall.DT_CHR: + return ModeDevice | ModeCharDevice + case syscall.DT_DIR: + return ModeDir + case syscall.DT_FIFO: + return ModeNamedPipe + case syscall.DT_LNK: + return ModeSymlink + case syscall.DT_REG: + return 0 + case syscall.DT_SOCK: + return ModeSocket + } + return ^FileMode(0) +} + +// Implemented in syscall/syscall_darwin.go. + +//go:linkname closedir syscall.closedir +func closedir(dir uintptr) (err error) + +//go:linkname readdir_r syscall.readdir_r +func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno) diff --git a/contrib/go/_std_1.22/src/os/dir_unix.go b/contrib/go/_std_1.22/src/os/dir_unix.go new file mode 100644 index 0000000000..266a78acaf --- /dev/null +++ b/contrib/go/_std_1.22/src/os/dir_unix.go @@ -0,0 +1,198 @@ +// 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. + +//go:build aix || dragonfly || freebsd || (js && wasm) || wasip1 || linux || netbsd || openbsd || solaris + +package os + +import ( + "io" + "runtime" + "sync" + "syscall" + "unsafe" +) + +// Auxiliary information if the File describes a directory +type dirInfo struct { + buf *[]byte // buffer for directory I/O + nbuf int // length of buf; return value from Getdirentries + bufp int // location of next record in buf. +} + +const ( + // More than 5760 to work around https://golang.org/issue/24015. + blockSize = 8192 +) + +var dirBufPool = sync.Pool{ + New: func() any { + // The buffer must be at least a block long. + buf := make([]byte, blockSize) + return &buf + }, +} + +func (d *dirInfo) close() { + if d.buf != nil { + dirBufPool.Put(d.buf) + d.buf = nil + } +} + +func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { + // If this file has no dirinfo, create one. + if f.dirinfo == nil { + f.dirinfo = new(dirInfo) + f.dirinfo.buf = dirBufPool.Get().(*[]byte) + } + d := f.dirinfo + + // Change the meaning of n for the implementation below. + // + // The n above was for the public interface of "if n <= 0, + // Readdir returns all the FileInfo from the directory in a + // single slice". + // + // But below, we use only negative to mean looping until the + // end and positive to mean bounded, with positive + // terminating at 0. + if n == 0 { + n = -1 + } + + for n != 0 { + // Refill the buffer if necessary + if d.bufp >= d.nbuf { + d.bufp = 0 + var errno error + d.nbuf, errno = f.pfd.ReadDirent(*d.buf) + runtime.KeepAlive(f) + if errno != nil { + return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno} + } + if d.nbuf <= 0 { + break // EOF + } + } + + // Drain the buffer + buf := (*d.buf)[d.bufp:d.nbuf] + reclen, ok := direntReclen(buf) + if !ok || reclen > uint64(len(buf)) { + break + } + rec := buf[:reclen] + d.bufp += int(reclen) + ino, ok := direntIno(rec) + if !ok { + break + } + // When building to wasip1, the host runtime might be running on Windows + // or might expose a remote file system which does not have the concept + // of inodes. Therefore, we cannot make the assumption that it is safe + // to skip entries with zero inodes. + if ino == 0 && runtime.GOOS != "wasip1" { + continue + } + const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name)) + namlen, ok := direntNamlen(rec) + if !ok || namoff+namlen > uint64(len(rec)) { + break + } + name := rec[namoff : namoff+namlen] + for i, c := range name { + if c == 0 { + name = name[:i] + break + } + } + // Check for useless names before allocating a string. + if string(name) == "." || string(name) == ".." { + continue + } + if n > 0 { // see 'n == 0' comment above + n-- + } + if mode == readdirName { + names = append(names, string(name)) + } else if mode == readdirDirEntry { + de, err := newUnixDirent(f.name, string(name), direntType(rec)) + if IsNotExist(err) { + // File disappeared between readdir and stat. + // Treat as if it didn't exist. + continue + } + if err != nil { + return nil, dirents, nil, err + } + dirents = append(dirents, de) + } else { + info, err := lstat(f.name + "/" + string(name)) + if IsNotExist(err) { + // File disappeared between readdir + stat. + // Treat as if it didn't exist. + continue + } + if err != nil { + return nil, nil, infos, err + } + infos = append(infos, info) + } + } + + if n > 0 && len(names)+len(dirents)+len(infos) == 0 { + return nil, nil, nil, io.EOF + } + return names, dirents, infos, nil +} + +// readInt returns the size-bytes unsigned integer in native byte order at offset off. +func readInt(b []byte, off, size uintptr) (u uint64, ok bool) { + if len(b) < int(off+size) { + return 0, false + } + if isBigEndian { + return readIntBE(b[off:], size), true + } + return readIntLE(b[off:], size), true +} + +func readIntBE(b []byte, size uintptr) uint64 { + switch size { + case 1: + return uint64(b[0]) + case 2: + _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[1]) | uint64(b[0])<<8 + case 4: + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24 + case 8: + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 + default: + panic("syscall: readInt with unsupported size") + } +} + +func readIntLE(b []byte, size uintptr) uint64 { + switch size { + case 1: + return uint64(b[0]) + case 2: + _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 + case 4: + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 + case 8: + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + default: + panic("syscall: readInt with unsupported size") + } +} diff --git a/contrib/go/_std_1.22/src/os/dir_windows.go b/contrib/go/_std_1.22/src/os/dir_windows.go new file mode 100644 index 0000000000..4485dffdb1 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/dir_windows.go @@ -0,0 +1,204 @@ +// 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. + +package os + +import ( + "internal/syscall/windows" + "io" + "io/fs" + "runtime" + "sync" + "syscall" + "unsafe" +) + +// Auxiliary information if the File describes a directory +type dirInfo struct { + // buf is a slice pointer so the slice header + // does not escape to the heap when returning + // buf to dirBufPool. + buf *[]byte // buffer for directory I/O + bufp int // location of next record in buf + vol uint32 + class uint32 // type of entries in buf + path string // absolute directory path, empty if the file system supports FILE_ID_BOTH_DIR_INFO +} + +const ( + // dirBufSize is the size of the dirInfo buffer. + // The buffer must be big enough to hold at least a single entry. + // The filename alone can be 512 bytes (MAX_PATH*2), and the fixed part of + // the FILE_ID_BOTH_DIR_INFO structure is 105 bytes, so dirBufSize + // should not be set below 1024 bytes (512+105+safety buffer). + // Windows 8.1 and earlier only works with buffer sizes up to 64 kB. + dirBufSize = 64 * 1024 // 64kB +) + +var dirBufPool = sync.Pool{ + New: func() any { + // The buffer must be at least a block long. + buf := make([]byte, dirBufSize) + return &buf + }, +} + +func (d *dirInfo) close() { + if d.buf != nil { + dirBufPool.Put(d.buf) + d.buf = nil + } +} + +// allowReadDirFileID indicates whether File.readdir should try to use FILE_ID_BOTH_DIR_INFO +// if the underlying file system supports it. +// Useful for testing purposes. +var allowReadDirFileID = true + +func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { + // If this file has no dirinfo, create one. + if file.dirinfo == nil { + // vol is used by os.SameFile. + // It is safe to query it once and reuse the value. + // Hard links are not allowed to reference files in other volumes. + // Junctions and symbolic links can reference files and directories in other volumes, + // but the reparse point should still live in the parent volume. + var vol, flags uint32 + err = windows.GetVolumeInformationByHandle(file.pfd.Sysfd, nil, 0, &vol, nil, &flags, nil, 0) + runtime.KeepAlive(file) + if err != nil { + err = &PathError{Op: "readdir", Path: file.name, Err: err} + return + } + file.dirinfo = new(dirInfo) + file.dirinfo.buf = dirBufPool.Get().(*[]byte) + file.dirinfo.vol = vol + if allowReadDirFileID && flags&windows.FILE_SUPPORTS_OPEN_BY_FILE_ID != 0 { + file.dirinfo.class = windows.FileIdBothDirectoryRestartInfo + } else { + file.dirinfo.class = windows.FileFullDirectoryRestartInfo + // Set the directory path for use by os.SameFile, as it is possible that + // the file system supports retrieving the file ID using GetFileInformationByHandle. + file.dirinfo.path = file.name + if !isAbs(file.dirinfo.path) { + // If the path is relative, we need to convert it to an absolute path + // in case the current directory changes between this call and a + // call to os.SameFile. + file.dirinfo.path, err = syscall.FullPath(file.dirinfo.path) + if err != nil { + err = &PathError{Op: "readdir", Path: file.name, Err: err} + return + } + } + } + } + d := file.dirinfo + wantAll := n <= 0 + if wantAll { + n = -1 + } + for n != 0 { + // Refill the buffer if necessary + if d.bufp == 0 { + err = windows.GetFileInformationByHandleEx(file.pfd.Sysfd, d.class, (*byte)(unsafe.Pointer(&(*d.buf)[0])), uint32(len(*d.buf))) + runtime.KeepAlive(file) + if err != nil { + if err == syscall.ERROR_NO_MORE_FILES { + break + } + if err == syscall.ERROR_FILE_NOT_FOUND && + (d.class == windows.FileIdBothDirectoryRestartInfo || d.class == windows.FileFullDirectoryRestartInfo) { + // GetFileInformationByHandleEx doesn't document the return error codes when the info class is FileIdBothDirectoryRestartInfo, + // but MS-FSA 2.1.5.6.3 [1] specifies that the underlying file system driver should return STATUS_NO_SUCH_FILE when + // reading an empty root directory, which is mapped to ERROR_FILE_NOT_FOUND by Windows. + // Note that some file system drivers may never return this error code, as the spec allows to return the "." and ".." + // entries in such cases, making the directory appear non-empty. + // The chances of false positive are very low, as we know that the directory exists, else GetVolumeInformationByHandle + // would have failed, and that the handle is still valid, as we haven't closed it. + // See go.dev/issue/61159. + // [1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/fa8194e0-53ec-413b-8315-e8fa85396fd8 + break + } + if s, _ := file.Stat(); s != nil && !s.IsDir() { + err = &PathError{Op: "readdir", Path: file.name, Err: syscall.ENOTDIR} + } else { + err = &PathError{Op: "GetFileInformationByHandleEx", Path: file.name, Err: err} + } + return + } + if d.class == windows.FileIdBothDirectoryRestartInfo { + d.class = windows.FileIdBothDirectoryInfo + } else if d.class == windows.FileFullDirectoryRestartInfo { + d.class = windows.FileFullDirectoryInfo + } + } + // Drain the buffer + var islast bool + for n != 0 && !islast { + var nextEntryOffset uint32 + var nameslice []uint16 + entry := unsafe.Pointer(&(*d.buf)[d.bufp]) + if d.class == windows.FileIdBothDirectoryInfo { + info := (*windows.FILE_ID_BOTH_DIR_INFO)(entry) + nextEntryOffset = info.NextEntryOffset + nameslice = unsafe.Slice(&info.FileName[0], info.FileNameLength/2) + } else { + info := (*windows.FILE_FULL_DIR_INFO)(entry) + nextEntryOffset = info.NextEntryOffset + nameslice = unsafe.Slice(&info.FileName[0], info.FileNameLength/2) + } + d.bufp += int(nextEntryOffset) + islast = nextEntryOffset == 0 + if islast { + d.bufp = 0 + } + if (len(nameslice) == 1 && nameslice[0] == '.') || + (len(nameslice) == 2 && nameslice[0] == '.' && nameslice[1] == '.') { + // Ignore "." and ".." and avoid allocating a string for them. + continue + } + name := syscall.UTF16ToString(nameslice) + if mode == readdirName { + names = append(names, name) + } else { + var f *fileStat + if d.class == windows.FileIdBothDirectoryInfo { + f = newFileStatFromFileIDBothDirInfo((*windows.FILE_ID_BOTH_DIR_INFO)(entry)) + } else { + f = newFileStatFromFileFullDirInfo((*windows.FILE_FULL_DIR_INFO)(entry)) + // Defer appending the entry name to the parent directory path until + // it is really needed, to avoid allocating a string that may not be used. + // It is currently only used in os.SameFile. + f.appendNameToPath = true + f.path = d.path + } + f.name = name + f.vol = d.vol + if mode == readdirDirEntry { + dirents = append(dirents, dirEntry{f}) + } else { + infos = append(infos, f) + } + } + n-- + } + } + if !wantAll && len(names)+len(dirents)+len(infos) == 0 { + return nil, nil, nil, io.EOF + } + return names, dirents, infos, nil +} + +type dirEntry struct { + fs *fileStat +} + +func (de dirEntry) Name() string { return de.fs.Name() } +func (de dirEntry) IsDir() bool { return de.fs.IsDir() } +func (de dirEntry) Type() FileMode { return de.fs.Mode().Type() } +func (de dirEntry) Info() (FileInfo, error) { return de.fs, nil } + +func (de dirEntry) String() string { + return fs.FormatDirEntry(de) +} diff --git a/contrib/go/_std_1.22/src/os/dirent_linux.go b/contrib/go/_std_1.22/src/os/dirent_linux.go new file mode 100644 index 0000000000..74a3431121 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/dirent_linux.go @@ -0,0 +1,51 @@ +// Copyright 2020 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. + +package os + +import ( + "syscall" + "unsafe" +) + +func direntIno(buf []byte) (uint64, bool) { + return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino)) +} + +func direntReclen(buf []byte) (uint64, bool) { + return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen)) +} + +func direntNamlen(buf []byte) (uint64, bool) { + reclen, ok := direntReclen(buf) + if !ok { + return 0, false + } + return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true +} + +func direntType(buf []byte) FileMode { + off := unsafe.Offsetof(syscall.Dirent{}.Type) + if off >= uintptr(len(buf)) { + return ^FileMode(0) // unknown + } + typ := buf[off] + switch typ { + case syscall.DT_BLK: + return ModeDevice + case syscall.DT_CHR: + return ModeDevice | ModeCharDevice + case syscall.DT_DIR: + return ModeDir + case syscall.DT_FIFO: + return ModeNamedPipe + case syscall.DT_LNK: + return ModeSymlink + case syscall.DT_REG: + return 0 + case syscall.DT_SOCK: + return ModeSocket + } + return ^FileMode(0) // unknown +} diff --git a/contrib/go/_std_1.22/src/os/endian_little.go b/contrib/go/_std_1.22/src/os/endian_little.go new file mode 100644 index 0000000000..a7cf1cdda8 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/endian_little.go @@ -0,0 +1,9 @@ +// Copyright 2016 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. +// +//go:build 386 || amd64 || arm || arm64 || loong64 || ppc64le || mips64le || mipsle || riscv64 || wasm + +package os + +const isBigEndian = false diff --git a/contrib/go/_std_1.22/src/os/env.go b/contrib/go/_std_1.22/src/os/env.go new file mode 100644 index 0000000000..63ad5ab4bd --- /dev/null +++ b/contrib/go/_std_1.22/src/os/env.go @@ -0,0 +1,141 @@ +// Copyright 2010 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. + +// General environment variables. + +package os + +import ( + "internal/testlog" + "syscall" +) + +// Expand replaces ${var} or $var in the string based on the mapping function. +// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv). +func Expand(s string, mapping func(string) string) string { + var buf []byte + // ${} is all ASCII, so bytes are fine for this operation. + i := 0 + for j := 0; j < len(s); j++ { + if s[j] == '$' && j+1 < len(s) { + if buf == nil { + buf = make([]byte, 0, 2*len(s)) + } + buf = append(buf, s[i:j]...) + name, w := getShellName(s[j+1:]) + if name == "" && w > 0 { + // Encountered invalid syntax; eat the + // characters. + } else if name == "" { + // Valid syntax, but $ was not followed by a + // name. Leave the dollar character untouched. + buf = append(buf, s[j]) + } else { + buf = append(buf, mapping(name)...) + } + j += w + i = j + 1 + } + } + if buf == nil { + return s + } + return string(buf) + s[i:] +} + +// ExpandEnv replaces ${var} or $var in the string according to the values +// of the current environment variables. References to undefined +// variables are replaced by the empty string. +func ExpandEnv(s string) string { + return Expand(s, Getenv) +} + +// isShellSpecialVar reports whether the character identifies a special +// shell variable such as $*. +func isShellSpecialVar(c uint8) bool { + switch c { + case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return true + } + return false +} + +// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore. +func isAlphaNum(c uint8) bool { + return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' +} + +// getShellName returns the name that begins the string and the number of bytes +// consumed to extract it. If the name is enclosed in {}, it's part of a ${} +// expansion and two more bytes are needed than the length of the name. +func getShellName(s string) (string, int) { + switch { + case s[0] == '{': + if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' { + return s[1:2], 3 + } + // Scan to closing brace + for i := 1; i < len(s); i++ { + if s[i] == '}' { + if i == 1 { + return "", 2 // Bad syntax; eat "${}" + } + return s[1:i], i + 1 + } + } + return "", 1 // Bad syntax; eat "${" + case isShellSpecialVar(s[0]): + return s[0:1], 1 + } + // Scan alphanumerics. + var i int + for i = 0; i < len(s) && isAlphaNum(s[i]); i++ { + } + return s[:i], i +} + +// Getenv retrieves the value of the environment variable named by the key. +// It returns the value, which will be empty if the variable is not present. +// To distinguish between an empty value and an unset value, use LookupEnv. +func Getenv(key string) string { + testlog.Getenv(key) + v, _ := syscall.Getenv(key) + return v +} + +// LookupEnv retrieves the value of the environment variable named +// by the key. If the variable is present in the environment the +// value (which may be empty) is returned and the boolean is true. +// Otherwise the returned value will be empty and the boolean will +// be false. +func LookupEnv(key string) (string, bool) { + testlog.Getenv(key) + return syscall.Getenv(key) +} + +// Setenv sets the value of the environment variable named by the key. +// It returns an error, if any. +func Setenv(key, value string) error { + err := syscall.Setenv(key, value) + if err != nil { + return NewSyscallError("setenv", err) + } + return nil +} + +// Unsetenv unsets a single environment variable. +func Unsetenv(key string) error { + return syscall.Unsetenv(key) +} + +// Clearenv deletes all environment variables. +func Clearenv() { + syscall.Clearenv() +} + +// Environ returns a copy of strings representing the environment, +// in the form "key=value". +func Environ() []string { + return syscall.Environ() +} diff --git a/contrib/go/_std_1.22/src/os/error.go b/contrib/go/_std_1.22/src/os/error.go new file mode 100644 index 0000000000..62ede9ded3 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/error.go @@ -0,0 +1,141 @@ +// 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. + +package os + +import ( + "internal/poll" + "io/fs" +) + +// Portable analogs of some common system call errors. +// +// Errors returned from this package may be tested against these errors +// with errors.Is. +var ( + // ErrInvalid indicates an invalid argument. + // Methods on File will return this error when the receiver is nil. + ErrInvalid = fs.ErrInvalid // "invalid argument" + + ErrPermission = fs.ErrPermission // "permission denied" + ErrExist = fs.ErrExist // "file already exists" + ErrNotExist = fs.ErrNotExist // "file does not exist" + ErrClosed = fs.ErrClosed // "file already closed" + + ErrNoDeadline = errNoDeadline() // "file type does not support deadline" + ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout" +) + +func errNoDeadline() error { return poll.ErrNoDeadline } + +// errDeadlineExceeded returns the value for os.ErrDeadlineExceeded. +// This error comes from the internal/poll package, which is also +// used by package net. Doing it this way ensures that the net +// package will return os.ErrDeadlineExceeded for an exceeded deadline, +// as documented by net.Conn.SetDeadline, without requiring any extra +// work in the net package and without requiring the internal/poll +// package to import os (which it can't, because that would be circular). +func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded } + +type timeout interface { + Timeout() bool +} + +// PathError records an error and the operation and file path that caused it. +type PathError = fs.PathError + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string + Err error +} + +func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() } + +func (e *SyscallError) Unwrap() error { return e.Err } + +// Timeout reports whether this error represents a timeout. +func (e *SyscallError) Timeout() bool { + t, ok := e.Err.(timeout) + return ok && t.Timeout() +} + +// NewSyscallError returns, as an error, a new SyscallError +// with the given system call name and error details. +// As a convenience, if err is nil, NewSyscallError returns nil. +func NewSyscallError(syscall string, err error) error { + if err == nil { + return nil + } + return &SyscallError{syscall, err} +} + +// IsExist returns a boolean indicating whether the error is known to report +// that a file or directory already exists. It is satisfied by ErrExist as +// well as some syscall errors. +// +// This function predates errors.Is. It only supports errors returned by +// the os package. New code should use errors.Is(err, fs.ErrExist). +func IsExist(err error) bool { + return underlyingErrorIs(err, ErrExist) +} + +// IsNotExist returns a boolean indicating whether the error is known to +// report that a file or directory does not exist. It is satisfied by +// ErrNotExist as well as some syscall errors. +// +// This function predates errors.Is. It only supports errors returned by +// the os package. New code should use errors.Is(err, fs.ErrNotExist). +func IsNotExist(err error) bool { + return underlyingErrorIs(err, ErrNotExist) +} + +// IsPermission returns a boolean indicating whether the error is known to +// report that permission is denied. It is satisfied by ErrPermission as well +// as some syscall errors. +// +// This function predates errors.Is. It only supports errors returned by +// the os package. New code should use errors.Is(err, fs.ErrPermission). +func IsPermission(err error) bool { + return underlyingErrorIs(err, ErrPermission) +} + +// IsTimeout returns a boolean indicating whether the error is known +// to report that a timeout occurred. +// +// This function predates errors.Is, and the notion of whether an +// error indicates a timeout can be ambiguous. For example, the Unix +// error EWOULDBLOCK sometimes indicates a timeout and sometimes does not. +// New code should use errors.Is with a value appropriate to the call +// returning the error, such as os.ErrDeadlineExceeded. +func IsTimeout(err error) bool { + terr, ok := underlyingError(err).(timeout) + return ok && terr.Timeout() +} + +func underlyingErrorIs(err, target error) bool { + // Note that this function is not errors.Is: + // underlyingError only unwraps the specific error-wrapping types + // that it historically did, not all errors implementing Unwrap(). + err = underlyingError(err) + if err == target { + return true + } + // To preserve prior behavior, only examine syscall errors. + e, ok := err.(syscallErrorType) + return ok && e.Is(target) +} + +// underlyingError returns the underlying error for known os error types. +func underlyingError(err error) error { + switch err := err.(type) { + case *PathError: + return err.Err + case *LinkError: + return err.Err + case *SyscallError: + return err.Err + } + return err +} diff --git a/contrib/go/_std_1.22/src/os/error_errno.go b/contrib/go/_std_1.22/src/os/error_errno.go new file mode 100644 index 0000000000..c8140461a4 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/error_errno.go @@ -0,0 +1,11 @@ +// Copyright 2019 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. + +//go:build !plan9 + +package os + +import "syscall" + +type syscallErrorType = syscall.Errno diff --git a/contrib/go/_std_1.22/src/os/error_posix.go b/contrib/go/_std_1.22/src/os/error_posix.go new file mode 100644 index 0000000000..b159c036c1 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/error_posix.go @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +//go:build unix || (js && wasm) || wasip1 || windows + +package os + +import "syscall" + +// wrapSyscallError takes an error and a syscall name. If the error is +// a syscall.Errno, it wraps it in an os.SyscallError using the syscall name. +func wrapSyscallError(name string, err error) error { + if _, ok := err.(syscall.Errno); ok { + err = NewSyscallError(name, err) + } + return err +} diff --git a/contrib/go/_std_1.22/src/os/exec.go b/contrib/go/_std_1.22/src/os/exec.go new file mode 100644 index 0000000000..ed5a75c4d1 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec.go @@ -0,0 +1,180 @@ +// 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. + +package os + +import ( + "errors" + "internal/testlog" + "runtime" + "sync" + "sync/atomic" + "syscall" + "time" +) + +// ErrProcessDone indicates a Process has finished. +var ErrProcessDone = errors.New("os: process already finished") + +// Process stores the information about a process created by StartProcess. +type Process struct { + Pid int + handle uintptr // handle is accessed atomically on Windows + isdone atomic.Bool // process has been successfully waited on + sigMu sync.RWMutex // avoid race between wait and signal +} + +func newProcess(pid int, handle uintptr) *Process { + p := &Process{Pid: pid, handle: handle} + runtime.SetFinalizer(p, (*Process).Release) + return p +} + +func (p *Process) setDone() { + p.isdone.Store(true) +} + +func (p *Process) done() bool { + return p.isdone.Load() +} + +// ProcAttr holds the attributes that will be applied to a new process +// started by StartProcess. +type ProcAttr struct { + // If Dir is non-empty, the child changes into the directory before + // creating the process. + Dir string + // If Env is non-nil, it gives the environment variables for the + // new process in the form returned by Environ. + // If it is nil, the result of Environ will be used. + Env []string + // Files specifies the open files inherited by the new process. The + // first three entries correspond to standard input, standard output, and + // standard error. An implementation may support additional entries, + // depending on the underlying operating system. A nil entry corresponds + // to that file being closed when the process starts. + // On Unix systems, StartProcess will change these File values + // to blocking mode, which means that SetDeadline will stop working + // and calling Close will not interrupt a Read or Write. + Files []*File + + // Operating system-specific process creation attributes. + // Note that setting this field means that your program + // may not execute properly or even compile on some + // operating systems. + Sys *syscall.SysProcAttr +} + +// A Signal represents an operating system signal. +// The usual underlying implementation is operating system-dependent: +// on Unix it is syscall.Signal. +type Signal interface { + String() string + Signal() // to distinguish from other Stringers +} + +// Getpid returns the process id of the caller. +func Getpid() int { return syscall.Getpid() } + +// Getppid returns the process id of the caller's parent. +func Getppid() int { return syscall.Getppid() } + +// FindProcess looks for a running process by its pid. +// +// The Process it returns can be used to obtain information +// about the underlying operating system process. +// +// On Unix systems, FindProcess always succeeds and returns a Process +// for the given pid, regardless of whether the process exists. To test whether +// the process actually exists, see whether p.Signal(syscall.Signal(0)) reports +// an error. +func FindProcess(pid int) (*Process, error) { + return findProcess(pid) +} + +// StartProcess starts a new process with the program, arguments and attributes +// specified by name, argv and attr. The argv slice will become os.Args in the +// new process, so it normally starts with the program name. +// +// If the calling goroutine has locked the operating system thread +// with runtime.LockOSThread and modified any inheritable OS-level +// thread state (for example, Linux or Plan 9 name spaces), the new +// process will inherit the caller's thread state. +// +// StartProcess is a low-level interface. The os/exec package provides +// higher-level interfaces. +// +// If there is an error, it will be of type *PathError. +func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) { + testlog.Open(name) + return startProcess(name, argv, attr) +} + +// Release releases any resources associated with the Process p, +// rendering it unusable in the future. +// Release only needs to be called if Wait is not. +func (p *Process) Release() error { + return p.release() +} + +// Kill causes the Process to exit immediately. Kill does not wait until +// the Process has actually exited. This only kills the Process itself, +// not any other processes it may have started. +func (p *Process) Kill() error { + return p.kill() +} + +// Wait waits for the Process to exit, and then returns a +// ProcessState describing its status and an error, if any. +// Wait releases any resources associated with the Process. +// On most operating systems, the Process must be a child +// of the current process or an error will be returned. +func (p *Process) Wait() (*ProcessState, error) { + return p.wait() +} + +// Signal sends a signal to the Process. +// Sending Interrupt on Windows is not implemented. +func (p *Process) Signal(sig Signal) error { + return p.signal(sig) +} + +// UserTime returns the user CPU time of the exited process and its children. +func (p *ProcessState) UserTime() time.Duration { + return p.userTime() +} + +// SystemTime returns the system CPU time of the exited process and its children. +func (p *ProcessState) SystemTime() time.Duration { + return p.systemTime() +} + +// Exited reports whether the program has exited. +// On Unix systems this reports true if the program exited due to calling exit, +// but false if the program terminated due to a signal. +func (p *ProcessState) Exited() bool { + return p.exited() +} + +// Success reports whether the program exited successfully, +// such as with exit status 0 on Unix. +func (p *ProcessState) Success() bool { + return p.success() +} + +// Sys returns system-dependent exit information about +// the process. Convert it to the appropriate underlying +// type, such as syscall.WaitStatus on Unix, to access its contents. +func (p *ProcessState) Sys() any { + return p.sys() +} + +// SysUsage returns system-dependent resource usage information about +// the exited process. Convert it to the appropriate underlying +// type, such as *syscall.Rusage on Unix, to access its contents. +// (On Unix, *syscall.Rusage matches struct rusage as defined in the +// getrusage(2) manual page.) +func (p *ProcessState) SysUsage() any { + return p.sysUsage() +} diff --git a/contrib/go/_std_1.22/src/os/exec/exec.go b/contrib/go/_std_1.22/src/os/exec/exec.go new file mode 100644 index 0000000000..c88ee7f52c --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec/exec.go @@ -0,0 +1,1303 @@ +// 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. + +// Package exec runs external commands. It wraps os.StartProcess to make it +// easier to remap stdin and stdout, connect I/O with pipes, and do other +// adjustments. +// +// Unlike the "system" library call from C and other languages, the +// os/exec package intentionally does not invoke the system shell and +// does not expand any glob patterns or handle other expansions, +// pipelines, or redirections typically done by shells. The package +// behaves more like C's "exec" family of functions. To expand glob +// patterns, either call the shell directly, taking care to escape any +// dangerous input, or use the path/filepath package's Glob function. +// To expand environment variables, use package os's ExpandEnv. +// +// Note that the examples in this package assume a Unix system. +// They may not run on Windows, and they do not run in the Go Playground +// used by golang.org and godoc.org. +// +// # Executables in the current directory +// +// The functions Command and LookPath look for a program +// in the directories listed in the current path, following the +// conventions of the host operating system. +// Operating systems have for decades included the current +// directory in this search, sometimes implicitly and sometimes +// configured explicitly that way by default. +// Modern practice is that including the current directory +// is usually unexpected and often leads to security problems. +// +// To avoid those security problems, as of Go 1.19, this package will not resolve a program +// using an implicit or explicit path entry relative to the current directory. +// That is, if you run exec.LookPath("go"), it will not successfully return +// ./go on Unix nor .\go.exe on Windows, no matter how the path is configured. +// Instead, if the usual path algorithms would result in that answer, +// these functions return an error err satisfying errors.Is(err, ErrDot). +// +// For example, consider these two program snippets: +// +// path, err := exec.LookPath("prog") +// if err != nil { +// log.Fatal(err) +// } +// use(path) +// +// and +// +// cmd := exec.Command("prog") +// if err := cmd.Run(); err != nil { +// log.Fatal(err) +// } +// +// These will not find and run ./prog or .\prog.exe, +// no matter how the current path is configured. +// +// Code that always wants to run a program from the current directory +// can be rewritten to say "./prog" instead of "prog". +// +// Code that insists on including results from relative path entries +// can instead override the error using an errors.Is check: +// +// path, err := exec.LookPath("prog") +// if errors.Is(err, exec.ErrDot) { +// err = nil +// } +// if err != nil { +// log.Fatal(err) +// } +// use(path) +// +// and +// +// cmd := exec.Command("prog") +// if errors.Is(cmd.Err, exec.ErrDot) { +// cmd.Err = nil +// } +// if err := cmd.Run(); err != nil { +// log.Fatal(err) +// } +// +// Setting the environment variable GODEBUG=execerrdot=0 +// disables generation of ErrDot entirely, temporarily restoring the pre-Go 1.19 +// behavior for programs that are unable to apply more targeted fixes. +// A future version of Go may remove support for this variable. +// +// Before adding such overrides, make sure you understand the +// security implications of doing so. +// See https://go.dev/blog/path-security for more information. +package exec + +import ( + "bytes" + "context" + "errors" + "internal/godebug" + "internal/syscall/execenv" + "io" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "syscall" + "time" +) + +// Error is returned by LookPath when it fails to classify a file as an +// executable. +type Error struct { + // Name is the file name for which the error occurred. + Name string + // Err is the underlying error. + Err error +} + +func (e *Error) Error() string { + return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error() +} + +func (e *Error) Unwrap() error { return e.Err } + +// ErrWaitDelay is returned by (*Cmd).Wait if the process exits with a +// successful status code but its output pipes are not closed before the +// command's WaitDelay expires. +var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete") + +// wrappedError wraps an error without relying on fmt.Errorf. +type wrappedError struct { + prefix string + err error +} + +func (w wrappedError) Error() string { + return w.prefix + ": " + w.err.Error() +} + +func (w wrappedError) Unwrap() error { + return w.err +} + +// Cmd represents an external command being prepared or run. +// +// A Cmd cannot be reused after calling its Run, Output or CombinedOutput +// methods. +type Cmd struct { + // Path is the path of the command to run. + // + // This is the only field that must be set to a non-zero + // value. If Path is relative, it is evaluated relative + // to Dir. + Path string + + // Args holds command line arguments, including the command as Args[0]. + // If the Args field is empty or nil, Run uses {Path}. + // + // In typical use, both Path and Args are set by calling Command. + Args []string + + // Env specifies the environment of the process. + // Each entry is of the form "key=value". + // If Env is nil, the new process uses the current process's + // environment. + // If Env contains duplicate environment keys, only the last + // value in the slice for each duplicate key is used. + // As a special case on Windows, SYSTEMROOT is always added if + // missing and not explicitly set to the empty string. + Env []string + + // Dir specifies the working directory of the command. + // If Dir is the empty string, Run runs the command in the + // calling process's current directory. + Dir string + + // Stdin specifies the process's standard input. + // + // If Stdin is nil, the process reads from the null device (os.DevNull). + // + // If Stdin is an *os.File, the process's standard input is connected + // directly to that file. + // + // Otherwise, during the execution of the command a separate + // goroutine reads from Stdin and delivers that data to the command + // over a pipe. In this case, Wait does not complete until the goroutine + // stops copying, either because it has reached the end of Stdin + // (EOF or a read error), or because writing to the pipe returned an error, + // or because a nonzero WaitDelay was set and expired. + Stdin io.Reader + + // Stdout and Stderr specify the process's standard output and error. + // + // If either is nil, Run connects the corresponding file descriptor + // to the null device (os.DevNull). + // + // If either is an *os.File, the corresponding output from the process + // is connected directly to that file. + // + // Otherwise, during the execution of the command a separate goroutine + // reads from the process over a pipe and delivers that data to the + // corresponding Writer. In this case, Wait does not complete until the + // goroutine reaches EOF or encounters an error or a nonzero WaitDelay + // expires. + // + // If Stdout and Stderr are the same writer, and have a type that can + // be compared with ==, at most one goroutine at a time will call Write. + Stdout io.Writer + Stderr io.Writer + + // ExtraFiles specifies additional open files to be inherited by the + // new process. It does not include standard input, standard output, or + // standard error. If non-nil, entry i becomes file descriptor 3+i. + // + // ExtraFiles is not supported on Windows. + ExtraFiles []*os.File + + // SysProcAttr holds optional, operating system-specific attributes. + // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. + SysProcAttr *syscall.SysProcAttr + + // Process is the underlying process, once started. + Process *os.Process + + // ProcessState contains information about an exited process. + // If the process was started successfully, Wait or Run will + // populate its ProcessState when the command completes. + ProcessState *os.ProcessState + + // ctx is the context passed to CommandContext, if any. + ctx context.Context + + Err error // LookPath error, if any. + + // If Cancel is non-nil, the command must have been created with + // CommandContext and Cancel will be called when the command's + // Context is done. By default, CommandContext sets Cancel to + // call the Kill method on the command's Process. + // + // Typically a custom Cancel will send a signal to the command's + // Process, but it may instead take other actions to initiate cancellation, + // such as closing a stdin or stdout pipe or sending a shutdown request on a + // network socket. + // + // If the command exits with a success status after Cancel is + // called, and Cancel does not return an error equivalent to + // os.ErrProcessDone, then Wait and similar methods will return a non-nil + // error: either an error wrapping the one returned by Cancel, + // or the error from the Context. + // (If the command exits with a non-success status, or Cancel + // returns an error that wraps os.ErrProcessDone, Wait and similar methods + // continue to return the command's usual exit status.) + // + // If Cancel is set to nil, nothing will happen immediately when the command's + // Context is done, but a nonzero WaitDelay will still take effect. That may + // be useful, for example, to work around deadlocks in commands that do not + // support shutdown signals but are expected to always finish quickly. + // + // Cancel will not be called if Start returns a non-nil error. + Cancel func() error + + // If WaitDelay is non-zero, it bounds the time spent waiting on two sources + // of unexpected delay in Wait: a child process that fails to exit after the + // associated Context is canceled, and a child process that exits but leaves + // its I/O pipes unclosed. + // + // The WaitDelay timer starts when either the associated Context is done or a + // call to Wait observes that the child process has exited, whichever occurs + // first. When the delay has elapsed, the command shuts down the child process + // and/or its I/O pipes. + // + // If the child process has failed to exit — perhaps because it ignored or + // failed to receive a shutdown signal from a Cancel function, or because no + // Cancel function was set — then it will be terminated using os.Process.Kill. + // + // Then, if the I/O pipes communicating with the child process are still open, + // those pipes are closed in order to unblock any goroutines currently blocked + // on Read or Write calls. + // + // If pipes are closed due to WaitDelay, no Cancel call has occurred, + // and the command has otherwise exited with a successful status, Wait and + // similar methods will return ErrWaitDelay instead of nil. + // + // If WaitDelay is zero (the default), I/O pipes will be read until EOF, + // which might not occur until orphaned subprocesses of the command have + // also closed their descriptors for the pipes. + WaitDelay time.Duration + + // childIOFiles holds closers for any of the child process's + // stdin, stdout, and/or stderr files that were opened by the Cmd itself + // (not supplied by the caller). These should be closed as soon as they + // are inherited by the child process. + childIOFiles []io.Closer + + // parentIOPipes holds closers for the parent's end of any pipes + // connected to the child's stdin, stdout, and/or stderr streams + // that were opened by the Cmd itself (not supplied by the caller). + // These should be closed after Wait sees the command and copying + // goroutines exit, or after WaitDelay has expired. + parentIOPipes []io.Closer + + // goroutine holds a set of closures to execute to copy data + // to and/or from the command's I/O pipes. + goroutine []func() error + + // If goroutineErr is non-nil, it receives the first error from a copying + // goroutine once all such goroutines have completed. + // goroutineErr is set to nil once its error has been received. + goroutineErr <-chan error + + // If ctxResult is non-nil, it receives the result of watchCtx exactly once. + ctxResult <-chan ctxResult + + // The stack saved when the Command was created, if GODEBUG contains + // execwait=2. Used for debugging leaks. + createdByStack []byte + + // For a security release long ago, we created x/sys/execabs, + // which manipulated the unexported lookPathErr error field + // in this struct. For Go 1.19 we exported the field as Err error, + // above, but we have to keep lookPathErr around for use by + // old programs building against new toolchains. + // The String and Start methods look for an error in lookPathErr + // in preference to Err, to preserve the errors that execabs sets. + // + // In general we don't guarantee misuse of reflect like this, + // but the misuse of reflect was by us, the best of various bad + // options to fix the security problem, and people depend on + // those old copies of execabs continuing to work. + // The result is that we have to leave this variable around for the + // rest of time, a compatibility scar. + // + // See https://go.dev/blog/path-security + // and https://go.dev/issue/43724 for more context. + lookPathErr error +} + +// A ctxResult reports the result of watching the Context associated with a +// running command (and sending corresponding signals if needed). +type ctxResult struct { + err error + + // If timer is non-nil, it expires after WaitDelay has elapsed after + // the Context is done. + // + // (If timer is nil, that means that the Context was not done before the + // command completed, or no WaitDelay was set, or the WaitDelay already + // expired and its effect was already applied.) + timer *time.Timer +} + +var execwait = godebug.New("#execwait") +var execerrdot = godebug.New("execerrdot") + +// Command returns the Cmd struct to execute the named program with +// the given arguments. +// +// It sets only the Path and Args in the returned structure. +// +// If name contains no path separators, Command uses LookPath to +// resolve name to a complete path if possible. Otherwise it uses name +// directly as Path. +// +// The returned Cmd's Args field is constructed from the command name +// followed by the elements of arg, so arg should not include the +// command name itself. For example, Command("echo", "hello"). +// Args[0] is always name, not the possibly resolved Path. +// +// On Windows, processes receive the whole command line as a single string +// and do their own parsing. Command combines and quotes Args into a command +// line string with an algorithm compatible with applications using +// CommandLineToArgvW (which is the most common way). Notable exceptions are +// msiexec.exe and cmd.exe (and thus, all batch files), which have a different +// unquoting algorithm. In these or other similar cases, you can do the +// quoting yourself and provide the full command line in SysProcAttr.CmdLine, +// leaving Args empty. +func Command(name string, arg ...string) *Cmd { + cmd := &Cmd{ + Path: name, + Args: append([]string{name}, arg...), + } + + if v := execwait.Value(); v != "" { + if v == "2" { + // Obtain the caller stack. (This is equivalent to runtime/debug.Stack, + // copied to avoid importing the whole package.) + stack := make([]byte, 1024) + for { + n := runtime.Stack(stack, false) + if n < len(stack) { + stack = stack[:n] + break + } + stack = make([]byte, 2*len(stack)) + } + + if i := bytes.Index(stack, []byte("\nos/exec.Command(")); i >= 0 { + stack = stack[i+1:] + } + cmd.createdByStack = stack + } + + runtime.SetFinalizer(cmd, func(c *Cmd) { + if c.Process != nil && c.ProcessState == nil { + debugHint := "" + if c.createdByStack == nil { + debugHint = " (set GODEBUG=execwait=2 to capture stacks for debugging)" + } else { + os.Stderr.WriteString("GODEBUG=execwait=2 detected a leaked exec.Cmd created by:\n") + os.Stderr.Write(c.createdByStack) + os.Stderr.WriteString("\n") + debugHint = "" + } + panic("exec: Cmd started a Process but leaked without a call to Wait" + debugHint) + } + }) + } + + if filepath.Base(name) == name { + lp, err := LookPath(name) + if lp != "" { + // Update cmd.Path even if err is non-nil. + // If err is ErrDot (especially on Windows), lp may include a resolved + // extension (like .exe or .bat) that should be preserved. + cmd.Path = lp + } + if err != nil { + cmd.Err = err + } + } else if runtime.GOOS == "windows" && filepath.IsAbs(name) { + // We may need to add a filename extension from PATHEXT + // or verify an extension that is already present. + // Since the path is absolute, its extension should be unambiguous + // and independent of cmd.Dir, and we can go ahead and update cmd.Path to + // reflect it. + // + // Note that we cannot add an extension here for relative paths, because + // cmd.Dir may be set after we return from this function and that may cause + // the command to resolve to a different extension. + lp, err := lookExtensions(name, "") + if lp != "" { + cmd.Path = lp + } + if err != nil { + cmd.Err = err + } + } + return cmd +} + +// CommandContext is like Command but includes a context. +// +// The provided context is used to interrupt the process +// (by calling cmd.Cancel or os.Process.Kill) +// if the context becomes done before the command completes on its own. +// +// CommandContext sets the command's Cancel function to invoke the Kill method +// on its Process, and leaves its WaitDelay unset. The caller may change the +// cancellation behavior by modifying those fields before starting the command. +func CommandContext(ctx context.Context, name string, arg ...string) *Cmd { + if ctx == nil { + panic("nil Context") + } + cmd := Command(name, arg...) + cmd.ctx = ctx + cmd.Cancel = func() error { + return cmd.Process.Kill() + } + return cmd +} + +// String returns a human-readable description of c. +// It is intended only for debugging. +// In particular, it is not suitable for use as input to a shell. +// The output of String may vary across Go releases. +func (c *Cmd) String() string { + if c.Err != nil || c.lookPathErr != nil { + // failed to resolve path; report the original requested path (plus args) + return strings.Join(c.Args, " ") + } + // report the exact executable path (plus args) + b := new(strings.Builder) + b.WriteString(c.Path) + for _, a := range c.Args[1:] { + b.WriteByte(' ') + b.WriteString(a) + } + return b.String() +} + +// interfaceEqual protects against panics from doing equality tests on +// two interfaces with non-comparable underlying types. +func interfaceEqual(a, b any) bool { + defer func() { + recover() + }() + return a == b +} + +func (c *Cmd) argv() []string { + if len(c.Args) > 0 { + return c.Args + } + return []string{c.Path} +} + +func (c *Cmd) childStdin() (*os.File, error) { + if c.Stdin == nil { + f, err := os.Open(os.DevNull) + if err != nil { + return nil, err + } + c.childIOFiles = append(c.childIOFiles, f) + return f, nil + } + + if f, ok := c.Stdin.(*os.File); ok { + return f, nil + } + + pr, pw, err := os.Pipe() + if err != nil { + return nil, err + } + + c.childIOFiles = append(c.childIOFiles, pr) + c.parentIOPipes = append(c.parentIOPipes, pw) + c.goroutine = append(c.goroutine, func() error { + _, err := io.Copy(pw, c.Stdin) + if skipStdinCopyError(err) { + err = nil + } + if err1 := pw.Close(); err == nil { + err = err1 + } + return err + }) + return pr, nil +} + +func (c *Cmd) childStdout() (*os.File, error) { + return c.writerDescriptor(c.Stdout) +} + +func (c *Cmd) childStderr(childStdout *os.File) (*os.File, error) { + if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { + return childStdout, nil + } + return c.writerDescriptor(c.Stderr) +} + +// writerDescriptor returns an os.File to which the child process +// can write to send data to w. +// +// If w is nil, writerDescriptor returns a File that writes to os.DevNull. +func (c *Cmd) writerDescriptor(w io.Writer) (*os.File, error) { + if w == nil { + f, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0) + if err != nil { + return nil, err + } + c.childIOFiles = append(c.childIOFiles, f) + return f, nil + } + + if f, ok := w.(*os.File); ok { + return f, nil + } + + pr, pw, err := os.Pipe() + if err != nil { + return nil, err + } + + c.childIOFiles = append(c.childIOFiles, pw) + c.parentIOPipes = append(c.parentIOPipes, pr) + c.goroutine = append(c.goroutine, func() error { + _, err := io.Copy(w, pr) + pr.Close() // in case io.Copy stopped due to write error + return err + }) + return pw, nil +} + +func closeDescriptors(closers []io.Closer) { + for _, fd := range closers { + fd.Close() + } +} + +// Run starts the specified command and waits for it to complete. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the command starts but does not complete successfully, the error is of +// type *ExitError. Other error types may be returned for other situations. +// +// If the calling goroutine has locked the operating system thread +// with runtime.LockOSThread and modified any inheritable OS-level +// thread state (for example, Linux or Plan 9 name spaces), the new +// process will inherit the caller's thread state. +func (c *Cmd) Run() error { + if err := c.Start(); err != nil { + return err + } + return c.Wait() +} + +// Start starts the specified command but does not wait for it to complete. +// +// If Start returns successfully, the c.Process field will be set. +// +// After a successful call to Start the Wait method must be called in +// order to release associated system resources. +func (c *Cmd) Start() error { + // Check for doubled Start calls before we defer failure cleanup. If the prior + // call to Start succeeded, we don't want to spuriously close its pipes. + if c.Process != nil { + return errors.New("exec: already started") + } + + started := false + defer func() { + closeDescriptors(c.childIOFiles) + c.childIOFiles = nil + + if !started { + closeDescriptors(c.parentIOPipes) + c.parentIOPipes = nil + } + }() + + if c.Path == "" && c.Err == nil && c.lookPathErr == nil { + c.Err = errors.New("exec: no command") + } + if c.Err != nil || c.lookPathErr != nil { + if c.lookPathErr != nil { + return c.lookPathErr + } + return c.Err + } + lp := c.Path + if runtime.GOOS == "windows" && !filepath.IsAbs(c.Path) { + // If c.Path is relative, we had to wait until now + // to resolve it in case c.Dir was changed. + // (If it is absolute, we already resolved its extension in Command + // and shouldn't need to do so again.) + // + // Unfortunately, we cannot write the result back to c.Path because programs + // may assume that they can call Start concurrently with reading the path. + // (It is safe and non-racy to do so on Unix platforms, and users might not + // test with the race detector on all platforms; + // see https://go.dev/issue/62596.) + // + // So we will pass the fully resolved path to os.StartProcess, but leave + // c.Path as is: missing a bit of logging information seems less harmful + // than triggering a surprising data race, and if the user really cares + // about that bit of logging they can always use LookPath to resolve it. + var err error + lp, err = lookExtensions(c.Path, c.Dir) + if err != nil { + return err + } + } + if c.Cancel != nil && c.ctx == nil { + return errors.New("exec: command with a non-nil Cancel was not created with CommandContext") + } + if c.ctx != nil { + select { + case <-c.ctx.Done(): + return c.ctx.Err() + default: + } + } + + childFiles := make([]*os.File, 0, 3+len(c.ExtraFiles)) + stdin, err := c.childStdin() + if err != nil { + return err + } + childFiles = append(childFiles, stdin) + stdout, err := c.childStdout() + if err != nil { + return err + } + childFiles = append(childFiles, stdout) + stderr, err := c.childStderr(stdout) + if err != nil { + return err + } + childFiles = append(childFiles, stderr) + childFiles = append(childFiles, c.ExtraFiles...) + + env, err := c.environ() + if err != nil { + return err + } + + c.Process, err = os.StartProcess(lp, c.argv(), &os.ProcAttr{ + Dir: c.Dir, + Files: childFiles, + Env: env, + Sys: c.SysProcAttr, + }) + if err != nil { + return err + } + started = true + + // Don't allocate the goroutineErr channel unless there are goroutines to start. + if len(c.goroutine) > 0 { + goroutineErr := make(chan error, 1) + c.goroutineErr = goroutineErr + + type goroutineStatus struct { + running int + firstErr error + } + statusc := make(chan goroutineStatus, 1) + statusc <- goroutineStatus{running: len(c.goroutine)} + for _, fn := range c.goroutine { + go func(fn func() error) { + err := fn() + + status := <-statusc + if status.firstErr == nil { + status.firstErr = err + } + status.running-- + if status.running == 0 { + goroutineErr <- status.firstErr + } else { + statusc <- status + } + }(fn) + } + c.goroutine = nil // Allow the goroutines' closures to be GC'd when they complete. + } + + // If we have anything to do when the command's Context expires, + // start a goroutine to watch for cancellation. + // + // (Even if the command was created by CommandContext, a helper library may + // have explicitly set its Cancel field back to nil, indicating that it should + // be allowed to continue running after cancellation after all.) + if (c.Cancel != nil || c.WaitDelay != 0) && c.ctx != nil && c.ctx.Done() != nil { + resultc := make(chan ctxResult) + c.ctxResult = resultc + go c.watchCtx(resultc) + } + + return nil +} + +// watchCtx watches c.ctx until it is able to send a result to resultc. +// +// If c.ctx is done before a result can be sent, watchCtx calls c.Cancel, +// and/or kills cmd.Process it after c.WaitDelay has elapsed. +// +// watchCtx manipulates c.goroutineErr, so its result must be received before +// c.awaitGoroutines is called. +func (c *Cmd) watchCtx(resultc chan<- ctxResult) { + select { + case resultc <- ctxResult{}: + return + case <-c.ctx.Done(): + } + + var err error + if c.Cancel != nil { + if interruptErr := c.Cancel(); interruptErr == nil { + // We appear to have successfully interrupted the command, so any + // program behavior from this point may be due to ctx even if the + // command exits with code 0. + err = c.ctx.Err() + } else if errors.Is(interruptErr, os.ErrProcessDone) { + // The process already finished: we just didn't notice it yet. + // (Perhaps c.Wait hadn't been called, or perhaps it happened to race with + // c.ctx being cancelled.) Don't inject a needless error. + } else { + err = wrappedError{ + prefix: "exec: canceling Cmd", + err: interruptErr, + } + } + } + if c.WaitDelay == 0 { + resultc <- ctxResult{err: err} + return + } + + timer := time.NewTimer(c.WaitDelay) + select { + case resultc <- ctxResult{err: err, timer: timer}: + // c.Process.Wait returned and we've handed the timer off to c.Wait. + // It will take care of goroutine shutdown from here. + return + case <-timer.C: + } + + killed := false + if killErr := c.Process.Kill(); killErr == nil { + // We appear to have killed the process. c.Process.Wait should return a + // non-nil error to c.Wait unless the Kill signal races with a successful + // exit, and if that does happen we shouldn't report a spurious error, + // so don't set err to anything here. + killed = true + } else if !errors.Is(killErr, os.ErrProcessDone) { + err = wrappedError{ + prefix: "exec: killing Cmd", + err: killErr, + } + } + + if c.goroutineErr != nil { + select { + case goroutineErr := <-c.goroutineErr: + // Forward goroutineErr only if we don't have reason to believe it was + // caused by a call to Cancel or Kill above. + if err == nil && !killed { + err = goroutineErr + } + default: + // Close the child process's I/O pipes, in case it abandoned some + // subprocess that inherited them and is still holding them open + // (see https://go.dev/issue/23019). + // + // We close the goroutine pipes only after we have sent any signals we're + // going to send to the process (via Signal or Kill above): if we send + // SIGKILL to the process, we would prefer for it to die of SIGKILL, not + // SIGPIPE. (However, this may still cause any orphaned subprocesses to + // terminate with SIGPIPE.) + closeDescriptors(c.parentIOPipes) + // Wait for the copying goroutines to finish, but report ErrWaitDelay for + // the error: any other error here could result from closing the pipes. + _ = <-c.goroutineErr + if err == nil { + err = ErrWaitDelay + } + } + + // Since we have already received the only result from c.goroutineErr, + // set it to nil to prevent awaitGoroutines from blocking on it. + c.goroutineErr = nil + } + + resultc <- ctxResult{err: err} +} + +// An ExitError reports an unsuccessful exit by a command. +type ExitError struct { + *os.ProcessState + + // Stderr holds a subset of the standard error output from the + // Cmd.Output method if standard error was not otherwise being + // collected. + // + // If the error output is long, Stderr may contain only a prefix + // and suffix of the output, with the middle replaced with + // text about the number of omitted bytes. + // + // Stderr is provided for debugging, for inclusion in error messages. + // Users with other needs should redirect Cmd.Stderr as needed. + Stderr []byte +} + +func (e *ExitError) Error() string { + return e.ProcessState.String() +} + +// Wait waits for the command to exit and waits for any copying to +// stdin or copying from stdout or stderr to complete. +// +// The command must have been started by Start. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the command fails to run or doesn't complete successfully, the +// error is of type *ExitError. Other error types may be +// returned for I/O problems. +// +// If any of c.Stdin, c.Stdout or c.Stderr are not an *os.File, Wait also waits +// for the respective I/O loop copying to or from the process to complete. +// +// Wait releases any resources associated with the Cmd. +func (c *Cmd) Wait() error { + if c.Process == nil { + return errors.New("exec: not started") + } + if c.ProcessState != nil { + return errors.New("exec: Wait was already called") + } + + state, err := c.Process.Wait() + if err == nil && !state.Success() { + err = &ExitError{ProcessState: state} + } + c.ProcessState = state + + var timer *time.Timer + if c.ctxResult != nil { + watch := <-c.ctxResult + timer = watch.timer + // If c.Process.Wait returned an error, prefer that. + // Otherwise, report any error from the watchCtx goroutine, + // such as a Context cancellation or a WaitDelay overrun. + if err == nil && watch.err != nil { + err = watch.err + } + } + + if goroutineErr := c.awaitGoroutines(timer); err == nil { + // Report an error from the copying goroutines only if the program otherwise + // exited normally on its own. Otherwise, the copying error may be due to the + // abnormal termination. + err = goroutineErr + } + closeDescriptors(c.parentIOPipes) + c.parentIOPipes = nil + + return err +} + +// awaitGoroutines waits for the results of the goroutines copying data to or +// from the command's I/O pipes. +// +// If c.WaitDelay elapses before the goroutines complete, awaitGoroutines +// forcibly closes their pipes and returns ErrWaitDelay. +// +// If timer is non-nil, it must send to timer.C at the end of c.WaitDelay. +func (c *Cmd) awaitGoroutines(timer *time.Timer) error { + defer func() { + if timer != nil { + timer.Stop() + } + c.goroutineErr = nil + }() + + if c.goroutineErr == nil { + return nil // No running goroutines to await. + } + + if timer == nil { + if c.WaitDelay == 0 { + return <-c.goroutineErr + } + + select { + case err := <-c.goroutineErr: + // Avoid the overhead of starting a timer. + return err + default: + } + + // No existing timer was started: either there is no Context associated with + // the command, or c.Process.Wait completed before the Context was done. + timer = time.NewTimer(c.WaitDelay) + } + + select { + case <-timer.C: + closeDescriptors(c.parentIOPipes) + // Wait for the copying goroutines to finish, but ignore any error + // (since it was probably caused by closing the pipes). + _ = <-c.goroutineErr + return ErrWaitDelay + + case err := <-c.goroutineErr: + return err + } +} + +// Output runs the command and returns its standard output. +// Any returned error will usually be of type *ExitError. +// If c.Stderr was nil, Output populates ExitError.Stderr. +func (c *Cmd) Output() ([]byte, error) { + if c.Stdout != nil { + return nil, errors.New("exec: Stdout already set") + } + var stdout bytes.Buffer + c.Stdout = &stdout + + captureErr := c.Stderr == nil + if captureErr { + c.Stderr = &prefixSuffixSaver{N: 32 << 10} + } + + err := c.Run() + if err != nil && captureErr { + if ee, ok := err.(*ExitError); ok { + ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes() + } + } + return stdout.Bytes(), err +} + +// CombinedOutput runs the command and returns its combined standard +// output and standard error. +func (c *Cmd) CombinedOutput() ([]byte, error) { + if c.Stdout != nil { + return nil, errors.New("exec: Stdout already set") + } + if c.Stderr != nil { + return nil, errors.New("exec: Stderr already set") + } + var b bytes.Buffer + c.Stdout = &b + c.Stderr = &b + err := c.Run() + return b.Bytes(), err +} + +// StdinPipe returns a pipe that will be connected to the command's +// standard input when the command starts. +// The pipe will be closed automatically after Wait sees the command exit. +// A caller need only call Close to force the pipe to close sooner. +// For example, if the command being run will not exit until standard input +// is closed, the caller must close the pipe. +func (c *Cmd) StdinPipe() (io.WriteCloser, error) { + if c.Stdin != nil { + return nil, errors.New("exec: Stdin already set") + } + if c.Process != nil { + return nil, errors.New("exec: StdinPipe after process started") + } + pr, pw, err := os.Pipe() + if err != nil { + return nil, err + } + c.Stdin = pr + c.childIOFiles = append(c.childIOFiles, pr) + c.parentIOPipes = append(c.parentIOPipes, pw) + return pw, nil +} + +// StdoutPipe returns a pipe that will be connected to the command's +// standard output when the command starts. +// +// Wait will close the pipe after seeing the command exit, so most callers +// need not close the pipe themselves. It is thus incorrect to call Wait +// before all reads from the pipe have completed. +// For the same reason, it is incorrect to call Run when using StdoutPipe. +// See the example for idiomatic usage. +func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { + if c.Stdout != nil { + return nil, errors.New("exec: Stdout already set") + } + if c.Process != nil { + return nil, errors.New("exec: StdoutPipe after process started") + } + pr, pw, err := os.Pipe() + if err != nil { + return nil, err + } + c.Stdout = pw + c.childIOFiles = append(c.childIOFiles, pw) + c.parentIOPipes = append(c.parentIOPipes, pr) + return pr, nil +} + +// StderrPipe returns a pipe that will be connected to the command's +// standard error when the command starts. +// +// Wait will close the pipe after seeing the command exit, so most callers +// need not close the pipe themselves. It is thus incorrect to call Wait +// before all reads from the pipe have completed. +// For the same reason, it is incorrect to use Run when using StderrPipe. +// See the StdoutPipe example for idiomatic usage. +func (c *Cmd) StderrPipe() (io.ReadCloser, error) { + if c.Stderr != nil { + return nil, errors.New("exec: Stderr already set") + } + if c.Process != nil { + return nil, errors.New("exec: StderrPipe after process started") + } + pr, pw, err := os.Pipe() + if err != nil { + return nil, err + } + c.Stderr = pw + c.childIOFiles = append(c.childIOFiles, pw) + c.parentIOPipes = append(c.parentIOPipes, pr) + return pr, nil +} + +// prefixSuffixSaver is an io.Writer which retains the first N bytes +// and the last N bytes written to it. The Bytes() methods reconstructs +// it with a pretty error message. +type prefixSuffixSaver struct { + N int // max size of prefix or suffix + prefix []byte + suffix []byte // ring buffer once len(suffix) == N + suffixOff int // offset to write into suffix + skipped int64 + + // TODO(bradfitz): we could keep one large []byte and use part of it for + // the prefix, reserve space for the '... Omitting N bytes ...' message, + // then the ring buffer suffix, and just rearrange the ring buffer + // suffix when Bytes() is called, but it doesn't seem worth it for + // now just for error messages. It's only ~64KB anyway. +} + +func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) { + lenp := len(p) + p = w.fill(&w.prefix, p) + + // Only keep the last w.N bytes of suffix data. + if overage := len(p) - w.N; overage > 0 { + p = p[overage:] + w.skipped += int64(overage) + } + p = w.fill(&w.suffix, p) + + // w.suffix is full now if p is non-empty. Overwrite it in a circle. + for len(p) > 0 { // 0, 1, or 2 iterations. + n := copy(w.suffix[w.suffixOff:], p) + p = p[n:] + w.skipped += int64(n) + w.suffixOff += n + if w.suffixOff == w.N { + w.suffixOff = 0 + } + } + return lenp, nil +} + +// fill appends up to len(p) bytes of p to *dst, such that *dst does not +// grow larger than w.N. It returns the un-appended suffix of p. +func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) { + if remain := w.N - len(*dst); remain > 0 { + add := min(len(p), remain) + *dst = append(*dst, p[:add]...) + p = p[add:] + } + return p +} + +func (w *prefixSuffixSaver) Bytes() []byte { + if w.suffix == nil { + return w.prefix + } + if w.skipped == 0 { + return append(w.prefix, w.suffix...) + } + var buf bytes.Buffer + buf.Grow(len(w.prefix) + len(w.suffix) + 50) + buf.Write(w.prefix) + buf.WriteString("\n... omitting ") + buf.WriteString(strconv.FormatInt(w.skipped, 10)) + buf.WriteString(" bytes ...\n") + buf.Write(w.suffix[w.suffixOff:]) + buf.Write(w.suffix[:w.suffixOff]) + return buf.Bytes() +} + +// environ returns a best-effort copy of the environment in which the command +// would be run as it is currently configured. If an error occurs in computing +// the environment, it is returned alongside the best-effort copy. +func (c *Cmd) environ() ([]string, error) { + var err error + + env := c.Env + if env == nil { + env, err = execenv.Default(c.SysProcAttr) + if err != nil { + env = os.Environ() + // Note that the non-nil err is preserved despite env being overridden. + } + + if c.Dir != "" { + switch runtime.GOOS { + case "windows", "plan9": + // Windows and Plan 9 do not use the PWD variable, so we don't need to + // keep it accurate. + default: + // On POSIX platforms, PWD represents “an absolute pathname of the + // current working directory.” Since we are changing the working + // directory for the command, we should also update PWD to reflect that. + // + // Unfortunately, we didn't always do that, so (as proposed in + // https://go.dev/issue/50599) to avoid unintended collateral damage we + // only implicitly update PWD when Env is nil. That way, we're much + // less likely to override an intentional change to the variable. + if pwd, absErr := filepath.Abs(c.Dir); absErr == nil { + env = append(env, "PWD="+pwd) + } else if err == nil { + err = absErr + } + } + } + } + + env, dedupErr := dedupEnv(env) + if err == nil { + err = dedupErr + } + return addCriticalEnv(env), err +} + +// Environ returns a copy of the environment in which the command would be run +// as it is currently configured. +func (c *Cmd) Environ() []string { + // Intentionally ignore errors: environ returns a best-effort environment no matter what. + env, _ := c.environ() + return env +} + +// dedupEnv returns a copy of env with any duplicates removed, in favor of +// later values. +// Items not of the normal environment "key=value" form are preserved unchanged. +// Except on Plan 9, items containing NUL characters are removed, and +// an error is returned along with the remaining values. +func dedupEnv(env []string) ([]string, error) { + return dedupEnvCase(runtime.GOOS == "windows", runtime.GOOS == "plan9", env) +} + +// dedupEnvCase is dedupEnv with a case option for testing. +// If caseInsensitive is true, the case of keys is ignored. +// If nulOK is false, items containing NUL characters are allowed. +func dedupEnvCase(caseInsensitive, nulOK bool, env []string) ([]string, error) { + // Construct the output in reverse order, to preserve the + // last occurrence of each key. + var err error + out := make([]string, 0, len(env)) + saw := make(map[string]bool, len(env)) + for n := len(env); n > 0; n-- { + kv := env[n-1] + + // Reject NUL in environment variables to prevent security issues (#56284); + // except on Plan 9, which uses NUL as os.PathListSeparator (#56544). + if !nulOK && strings.IndexByte(kv, 0) != -1 { + err = errors.New("exec: environment variable contains NUL") + continue + } + + i := strings.Index(kv, "=") + if i == 0 { + // We observe in practice keys with a single leading "=" on Windows. + // TODO(#49886): Should we consume only the first leading "=" as part + // of the key, or parse through arbitrarily many of them until a non-"="? + i = strings.Index(kv[1:], "=") + 1 + } + if i < 0 { + if kv != "" { + // The entry is not of the form "key=value" (as it is required to be). + // Leave it as-is for now. + // TODO(#52436): should we strip or reject these bogus entries? + out = append(out, kv) + } + continue + } + k := kv[:i] + if caseInsensitive { + k = strings.ToLower(k) + } + if saw[k] { + continue + } + + saw[k] = true + out = append(out, kv) + } + + // Now reverse the slice to restore the original order. + for i := 0; i < len(out)/2; i++ { + j := len(out) - i - 1 + out[i], out[j] = out[j], out[i] + } + + return out, err +} + +// addCriticalEnv adds any critical environment variables that are required +// (or at least almost always required) on the operating system. +// Currently this is only used for Windows. +func addCriticalEnv(env []string) []string { + if runtime.GOOS != "windows" { + return env + } + for _, kv := range env { + k, _, ok := strings.Cut(kv, "=") + if !ok { + continue + } + if strings.EqualFold(k, "SYSTEMROOT") { + // We already have it. + return env + } + } + return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT")) +} + +// ErrDot indicates that a path lookup resolved to an executable +// in the current directory due to ‘.’ being in the path, either +// implicitly or explicitly. See the package documentation for details. +// +// Note that functions in this package do not return ErrDot directly. +// Code should use errors.Is(err, ErrDot), not err == ErrDot, +// to test whether a returned error err is due to this condition. +var ErrDot = errors.New("cannot run executable found relative to current directory") diff --git a/contrib/go/_std_1.22/src/os/exec/exec_unix.go b/contrib/go/_std_1.22/src/os/exec/exec_unix.go new file mode 100644 index 0000000000..3ed672a744 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec/exec_unix.go @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +//go:build !plan9 && !windows + +package exec + +import ( + "io/fs" + "syscall" +) + +// skipStdinCopyError optionally specifies a function which reports +// whether the provided stdin copy error should be ignored. +func skipStdinCopyError(err error) bool { + // Ignore EPIPE errors copying to stdin if the program + // completed successfully otherwise. + // See Issue 9173. + pe, ok := err.(*fs.PathError) + return ok && + pe.Op == "write" && pe.Path == "|1" && + pe.Err == syscall.EPIPE +} diff --git a/contrib/go/_std_1.22/src/os/exec/exec_windows.go b/contrib/go/_std_1.22/src/os/exec/exec_windows.go new file mode 100644 index 0000000000..e7a2ee6c9d --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec/exec_windows.go @@ -0,0 +1,23 @@ +// Copyright 2017 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. + +package exec + +import ( + "io/fs" + "syscall" +) + +// skipStdinCopyError optionally specifies a function which reports +// whether the provided stdin copy error should be ignored. +func skipStdinCopyError(err error) bool { + // Ignore ERROR_BROKEN_PIPE and ERROR_NO_DATA errors copying + // to stdin if the program completed successfully otherwise. + // See Issue 20445. + const _ERROR_NO_DATA = syscall.Errno(0xe8) + pe, ok := err.(*fs.PathError) + return ok && + pe.Op == "write" && pe.Path == "|1" && + (pe.Err == syscall.ERROR_BROKEN_PIPE || pe.Err == _ERROR_NO_DATA) +} diff --git a/contrib/go/_std_1.22/src/os/exec/lp_unix.go b/contrib/go/_std_1.22/src/os/exec/lp_unix.go new file mode 100644 index 0000000000..3787132078 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec/lp_unix.go @@ -0,0 +1,88 @@ +// Copyright 2010 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. + +//go:build unix + +package exec + +import ( + "errors" + "internal/syscall/unix" + "io/fs" + "os" + "path/filepath" + "strings" + "syscall" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = errors.New("executable file not found in $PATH") + +func findExecutable(file string) error { + d, err := os.Stat(file) + if err != nil { + return err + } + m := d.Mode() + if m.IsDir() { + return syscall.EISDIR + } + err = unix.Eaccess(file, unix.X_OK) + // ENOSYS means Eaccess is not available or not implemented. + // EPERM can be returned by Linux containers employing seccomp. + // In both cases, fall back to checking the permission bits. + if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) { + return err + } + if m&0111 != 0 { + return nil + } + return fs.ErrPermission +} + +// LookPath searches for an executable named file in the +// directories named by the PATH environment variable. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// Otherwise, on success, the result is an absolute path. +// +// In older versions of Go, LookPath could return a path relative to the current directory. +// As of Go 1.19, LookPath will instead return that path along with an error satisfying +// errors.Is(err, ErrDot). See the package documentation for more details. +func LookPath(file string) (string, error) { + // NOTE(rsc): I wish we could use the Plan 9 behavior here + // (only bypass the path if file begins with / or ./ or ../) + // but that would not match all the Unix shells. + + if strings.Contains(file, "/") { + err := findExecutable(file) + if err == nil { + return file, nil + } + return "", &Error{file, err} + } + path := os.Getenv("PATH") + for _, dir := range filepath.SplitList(path) { + if dir == "" { + // Unix shell semantics: path element "" means "." + dir = "." + } + path := filepath.Join(dir, file) + if err := findExecutable(path); err == nil { + if !filepath.IsAbs(path) { + if execerrdot.Value() != "0" { + return path, &Error{file, ErrDot} + } + execerrdot.IncNonDefault() + } + return path, nil + } + } + return "", &Error{file, ErrNotFound} +} + +// lookExtensions is a no-op on non-Windows platforms, since +// they do not restrict executables to specific extensions. +func lookExtensions(path, dir string) (string, error) { + return path, nil +} diff --git a/contrib/go/_std_1.22/src/os/exec/lp_windows.go b/contrib/go/_std_1.22/src/os/exec/lp_windows.go new file mode 100644 index 0000000000..698a97c40f --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec/lp_windows.go @@ -0,0 +1,212 @@ +// Copyright 2010 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. + +package exec + +import ( + "errors" + "io/fs" + "os" + "path/filepath" + "strings" + "syscall" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = errors.New("executable file not found in %PATH%") + +func chkStat(file string) error { + d, err := os.Stat(file) + if err != nil { + return err + } + if d.IsDir() { + return fs.ErrPermission + } + return nil +} + +func hasExt(file string) bool { + i := strings.LastIndex(file, ".") + if i < 0 { + return false + } + return strings.LastIndexAny(file, `:\/`) < i +} + +func findExecutable(file string, exts []string) (string, error) { + if len(exts) == 0 { + return file, chkStat(file) + } + if hasExt(file) { + if chkStat(file) == nil { + return file, nil + } + // Keep checking exts below, so that programs with weird names + // like "foo.bat.exe" will resolve instead of failing. + } + for _, e := range exts { + if f := file + e; chkStat(f) == nil { + return f, nil + } + } + if hasExt(file) { + return "", fs.ErrNotExist + } + return "", ErrNotFound +} + +// LookPath searches for an executable named file in the +// directories named by the PATH environment variable. +// LookPath also uses PATHEXT environment variable to match +// a suitable candidate. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// Otherwise, on success, the result is an absolute path. +// +// In older versions of Go, LookPath could return a path relative to the current directory. +// As of Go 1.19, LookPath will instead return that path along with an error satisfying +// errors.Is(err, ErrDot). See the package documentation for more details. +func LookPath(file string) (string, error) { + return lookPath(file, pathExt()) +} + +// lookExtensions finds windows executable by its dir and path. +// It uses LookPath to try appropriate extensions. +// lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. +// +// If the path already has an extension found in PATHEXT, +// lookExtensions returns it directly without searching +// for additional extensions. For example, +// "C:\foo\example.com" would be returned as-is even if the +// program is actually "C:\foo\example.com.exe". +func lookExtensions(path, dir string) (string, error) { + if filepath.Base(path) == path { + path = "." + string(filepath.Separator) + path + } + exts := pathExt() + if ext := filepath.Ext(path); ext != "" { + for _, e := range exts { + if strings.EqualFold(ext, e) { + // Assume that path has already been resolved. + return path, nil + } + } + } + if dir == "" { + return lookPath(path, exts) + } + if filepath.VolumeName(path) != "" { + return lookPath(path, exts) + } + if len(path) > 1 && os.IsPathSeparator(path[0]) { + return lookPath(path, exts) + } + dirandpath := filepath.Join(dir, path) + // We assume that LookPath will only add file extension. + lp, err := lookPath(dirandpath, exts) + if err != nil { + return "", err + } + ext := strings.TrimPrefix(lp, dirandpath) + return path + ext, nil +} + +func pathExt() []string { + var exts []string + x := os.Getenv(`PATHEXT`) + if x != "" { + for _, e := range strings.Split(strings.ToLower(x), `;`) { + if e == "" { + continue + } + if e[0] != '.' { + e = "." + e + } + exts = append(exts, e) + } + } else { + exts = []string{".com", ".exe", ".bat", ".cmd"} + } + return exts +} + +// lookPath implements LookPath for the given PATHEXT list. +func lookPath(file string, exts []string) (string, error) { + if strings.ContainsAny(file, `:\/`) { + f, err := findExecutable(file, exts) + if err == nil { + return f, nil + } + return "", &Error{file, err} + } + + // On Windows, creating the NoDefaultCurrentDirectoryInExePath + // environment variable (with any value or no value!) signals that + // path lookups should skip the current directory. + // In theory we are supposed to call NeedCurrentDirectoryForExePathW + // "as the registry location of this environment variable can change" + // but that seems exceedingly unlikely: it would break all users who + // have configured their environment this way! + // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-needcurrentdirectoryforexepathw + // See also go.dev/issue/43947. + var ( + dotf string + dotErr error + ) + if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found { + if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { + if execerrdot.Value() == "0" { + execerrdot.IncNonDefault() + return f, nil + } + dotf, dotErr = f, &Error{file, ErrDot} + } + } + + path := os.Getenv("path") + for _, dir := range filepath.SplitList(path) { + if dir == "" { + // Skip empty entries, consistent with what PowerShell does. + // (See https://go.dev/issue/61493#issuecomment-1649724826.) + continue + } + + if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { + if dotErr != nil { + // https://go.dev/issue/53536: if we resolved a relative path implicitly, + // and it is the same executable that would be resolved from the explicit %PATH%, + // prefer the explicit name for the executable (and, likely, no error) instead + // of the equivalent implicit name with ErrDot. + // + // Otherwise, return the ErrDot for the implicit path as soon as we find + // out that the explicit one doesn't match. + dotfi, dotfiErr := os.Lstat(dotf) + fi, fiErr := os.Lstat(f) + if dotfiErr != nil || fiErr != nil || !os.SameFile(dotfi, fi) { + return dotf, dotErr + } + } + + if !filepath.IsAbs(f) { + if execerrdot.Value() != "0" { + // If this is the same relative path that we already found, + // dotErr is non-nil and we already checked it above. + // Otherwise, record this path as the one to which we must resolve, + // with or without a dotErr. + if dotErr == nil { + dotf, dotErr = f, &Error{file, ErrDot} + } + continue + } + execerrdot.IncNonDefault() + } + return f, nil + } + } + + if dotErr != nil { + return dotf, dotErr + } + return "", &Error{file, ErrNotFound} +} diff --git a/contrib/go/_std_1.22/src/os/exec/ya.make b/contrib/go/_std_1.22/src/os/exec/ya.make new file mode 100644 index 0000000000..8a8e4e2c88 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec/ya.make @@ -0,0 +1,15 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + exec.go + exec_unix.go + lp_unix.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + exec.go + exec_windows.go + lp_windows.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/os/exec_posix.go b/contrib/go/_std_1.22/src/os/exec_posix.go new file mode 100644 index 0000000000..4f9ea08cde --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec_posix.go @@ -0,0 +1,136 @@ +// 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. + +//go:build unix || (js && wasm) || wasip1 || windows + +package os + +import ( + "internal/itoa" + "internal/syscall/execenv" + "runtime" + "syscall" +) + +// The only signal values guaranteed to be present in the os package on all +// systems are os.Interrupt (send the process an interrupt) and os.Kill (force +// the process to exit). On Windows, sending os.Interrupt to a process with +// os.Process.Signal is not implemented; it will return an error instead of +// sending a signal. +var ( + Interrupt Signal = syscall.SIGINT + Kill Signal = syscall.SIGKILL +) + +func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) { + // If there is no SysProcAttr (ie. no Chroot or changed + // UID/GID), double-check existence of the directory we want + // to chdir into. We can make the error clearer this way. + if attr != nil && attr.Sys == nil && attr.Dir != "" { + if _, err := Stat(attr.Dir); err != nil { + pe := err.(*PathError) + pe.Op = "chdir" + return nil, pe + } + } + + sysattr := &syscall.ProcAttr{ + Dir: attr.Dir, + Env: attr.Env, + Sys: attr.Sys, + } + if sysattr.Env == nil { + sysattr.Env, err = execenv.Default(sysattr.Sys) + if err != nil { + return nil, err + } + } + sysattr.Files = make([]uintptr, 0, len(attr.Files)) + for _, f := range attr.Files { + sysattr.Files = append(sysattr.Files, f.Fd()) + } + + pid, h, e := syscall.StartProcess(name, argv, sysattr) + + // Make sure we don't run the finalizers of attr.Files. + runtime.KeepAlive(attr) + + if e != nil { + return nil, &PathError{Op: "fork/exec", Path: name, Err: e} + } + + return newProcess(pid, h), nil +} + +func (p *Process) kill() error { + return p.Signal(Kill) +} + +// ProcessState stores information about a process, as reported by Wait. +type ProcessState struct { + pid int // The process's id. + status syscall.WaitStatus // System-dependent status info. + rusage *syscall.Rusage +} + +// Pid returns the process id of the exited process. +func (p *ProcessState) Pid() int { + return p.pid +} + +func (p *ProcessState) exited() bool { + return p.status.Exited() +} + +func (p *ProcessState) success() bool { + return p.status.ExitStatus() == 0 +} + +func (p *ProcessState) sys() any { + return p.status +} + +func (p *ProcessState) sysUsage() any { + return p.rusage +} + +func (p *ProcessState) String() string { + if p == nil { + return "<nil>" + } + status := p.Sys().(syscall.WaitStatus) + res := "" + switch { + case status.Exited(): + code := status.ExitStatus() + if runtime.GOOS == "windows" && uint(code) >= 1<<16 { // windows uses large hex numbers + res = "exit status " + itoa.Uitox(uint(code)) + } else { // unix systems use small decimal integers + res = "exit status " + itoa.Itoa(code) // unix + } + case status.Signaled(): + res = "signal: " + status.Signal().String() + case status.Stopped(): + res = "stop signal: " + status.StopSignal().String() + if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 { + res += " (trap " + itoa.Itoa(status.TrapCause()) + ")" + } + case status.Continued(): + res = "continued" + } + if status.CoreDump() { + res += " (core dumped)" + } + return res +} + +// ExitCode returns the exit code of the exited process, or -1 +// if the process hasn't exited or was terminated by a signal. +func (p *ProcessState) ExitCode() int { + // return -1 if the process hasn't started. + if p == nil { + return -1 + } + return p.status.ExitStatus() +} diff --git a/contrib/go/_std_1.22/src/os/exec_unix.go b/contrib/go/_std_1.22/src/os/exec_unix.go new file mode 100644 index 0000000000..36b320df18 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec_unix.go @@ -0,0 +1,104 @@ +// 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. + +//go:build unix || (js && wasm) || wasip1 + +package os + +import ( + "errors" + "runtime" + "syscall" + "time" +) + +func (p *Process) wait() (ps *ProcessState, err error) { + if p.Pid == -1 { + return nil, syscall.EINVAL + } + + // If we can block until Wait4 will succeed immediately, do so. + ready, err := p.blockUntilWaitable() + if err != nil { + return nil, err + } + if ready { + // Mark the process done now, before the call to Wait4, + // so that Process.signal will not send a signal. + p.setDone() + // Acquire a write lock on sigMu to wait for any + // active call to the signal method to complete. + p.sigMu.Lock() + p.sigMu.Unlock() + } + + var ( + status syscall.WaitStatus + rusage syscall.Rusage + pid1 int + e error + ) + for { + pid1, e = syscall.Wait4(p.Pid, &status, 0, &rusage) + if e != syscall.EINTR { + break + } + } + if e != nil { + return nil, NewSyscallError("wait", e) + } + p.setDone() + ps = &ProcessState{ + pid: pid1, + status: status, + rusage: &rusage, + } + return ps, nil +} + +func (p *Process) signal(sig Signal) error { + if p.Pid == -1 { + return errors.New("os: process already released") + } + if p.Pid == 0 { + return errors.New("os: process not initialized") + } + p.sigMu.RLock() + defer p.sigMu.RUnlock() + if p.done() { + return ErrProcessDone + } + s, ok := sig.(syscall.Signal) + if !ok { + return errors.New("os: unsupported signal type") + } + if e := syscall.Kill(p.Pid, s); e != nil { + if e == syscall.ESRCH { + return ErrProcessDone + } + return e + } + return nil +} + +func (p *Process) release() error { + // NOOP for unix. + p.Pid = -1 + // no need for a finalizer anymore + runtime.SetFinalizer(p, nil) + return nil +} + +func findProcess(pid int) (p *Process, err error) { + // NOOP for unix. + return newProcess(pid, 0), nil +} + +func (p *ProcessState) userTime() time.Duration { + return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond +} + +func (p *ProcessState) systemTime() time.Duration { + return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond +} diff --git a/contrib/go/_std_1.22/src/os/exec_windows.go b/contrib/go/_std_1.22/src/os/exec_windows.go new file mode 100644 index 0000000000..061a12b10f --- /dev/null +++ b/contrib/go/_std_1.22/src/os/exec_windows.go @@ -0,0 +1,175 @@ +// 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. + +package os + +import ( + "errors" + "internal/syscall/windows" + "runtime" + "sync/atomic" + "syscall" + "time" +) + +func (p *Process) wait() (ps *ProcessState, err error) { + handle := atomic.LoadUintptr(&p.handle) + s, e := syscall.WaitForSingleObject(syscall.Handle(handle), syscall.INFINITE) + switch s { + case syscall.WAIT_OBJECT_0: + break + case syscall.WAIT_FAILED: + return nil, NewSyscallError("WaitForSingleObject", e) + default: + return nil, errors.New("os: unexpected result from WaitForSingleObject") + } + var ec uint32 + e = syscall.GetExitCodeProcess(syscall.Handle(handle), &ec) + if e != nil { + return nil, NewSyscallError("GetExitCodeProcess", e) + } + var u syscall.Rusage + e = syscall.GetProcessTimes(syscall.Handle(handle), &u.CreationTime, &u.ExitTime, &u.KernelTime, &u.UserTime) + if e != nil { + return nil, NewSyscallError("GetProcessTimes", e) + } + p.setDone() + defer p.Release() + return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil +} + +func (p *Process) signal(sig Signal) error { + handle := atomic.LoadUintptr(&p.handle) + if handle == uintptr(syscall.InvalidHandle) { + return syscall.EINVAL + } + if p.done() { + return ErrProcessDone + } + if sig == Kill { + var terminationHandle syscall.Handle + e := syscall.DuplicateHandle(^syscall.Handle(0), syscall.Handle(handle), ^syscall.Handle(0), &terminationHandle, syscall.PROCESS_TERMINATE, false, 0) + if e != nil { + return NewSyscallError("DuplicateHandle", e) + } + runtime.KeepAlive(p) + defer syscall.CloseHandle(terminationHandle) + e = syscall.TerminateProcess(syscall.Handle(terminationHandle), 1) + return NewSyscallError("TerminateProcess", e) + } + // TODO(rsc): Handle Interrupt too? + return syscall.Errno(syscall.EWINDOWS) +} + +func (p *Process) release() error { + handle := atomic.SwapUintptr(&p.handle, uintptr(syscall.InvalidHandle)) + if handle == uintptr(syscall.InvalidHandle) { + return syscall.EINVAL + } + e := syscall.CloseHandle(syscall.Handle(handle)) + if e != nil { + return NewSyscallError("CloseHandle", e) + } + // no need for a finalizer anymore + runtime.SetFinalizer(p, nil) + return nil +} + +func findProcess(pid int) (p *Process, err error) { + const da = syscall.STANDARD_RIGHTS_READ | + syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE + h, e := syscall.OpenProcess(da, false, uint32(pid)) + if e != nil { + return nil, NewSyscallError("OpenProcess", e) + } + return newProcess(pid, uintptr(h)), nil +} + +func init() { + cmd := windows.UTF16PtrToString(syscall.GetCommandLine()) + if len(cmd) == 0 { + arg0, _ := Executable() + Args = []string{arg0} + } else { + Args = commandLineToArgv(cmd) + } +} + +// appendBSBytes appends n '\\' bytes to b and returns the resulting slice. +func appendBSBytes(b []byte, n int) []byte { + for ; n > 0; n-- { + b = append(b, '\\') + } + return b +} + +// readNextArg splits command line string cmd into next +// argument and command line remainder. +func readNextArg(cmd string) (arg []byte, rest string) { + var b []byte + var inquote bool + var nslash int + for ; len(cmd) > 0; cmd = cmd[1:] { + c := cmd[0] + switch c { + case ' ', '\t': + if !inquote { + return appendBSBytes(b, nslash), cmd[1:] + } + case '"': + b = appendBSBytes(b, nslash/2) + if nslash%2 == 0 { + // use "Prior to 2008" rule from + // http://daviddeley.com/autohotkey/parameters/parameters.htm + // section 5.2 to deal with double double quotes + if inquote && len(cmd) > 1 && cmd[1] == '"' { + b = append(b, c) + cmd = cmd[1:] + } + inquote = !inquote + } else { + b = append(b, c) + } + nslash = 0 + continue + case '\\': + nslash++ + continue + } + b = appendBSBytes(b, nslash) + nslash = 0 + b = append(b, c) + } + return appendBSBytes(b, nslash), "" +} + +// commandLineToArgv splits a command line into individual argument +// strings, following the Windows conventions documented +// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV +func commandLineToArgv(cmd string) []string { + var args []string + for len(cmd) > 0 { + if cmd[0] == ' ' || cmd[0] == '\t' { + cmd = cmd[1:] + continue + } + var arg []byte + arg, cmd = readNextArg(cmd) + args = append(args, string(arg)) + } + return args +} + +func ftToDuration(ft *syscall.Filetime) time.Duration { + n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals + return time.Duration(n*100) * time.Nanosecond +} + +func (p *ProcessState) userTime() time.Duration { + return ftToDuration(&p.rusage.UserTime) +} + +func (p *ProcessState) systemTime() time.Duration { + return ftToDuration(&p.rusage.KernelTime) +} diff --git a/contrib/go/_std_1.22/src/os/executable.go b/contrib/go/_std_1.22/src/os/executable.go new file mode 100644 index 0000000000..cc3134af1c --- /dev/null +++ b/contrib/go/_std_1.22/src/os/executable.go @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +package os + +// Executable returns the path name for the executable that started +// the current process. There is no guarantee that the path is still +// pointing to the correct executable. If a symlink was used to start +// the process, depending on the operating system, the result might +// be the symlink or the path it pointed to. If a stable result is +// needed, path/filepath.EvalSymlinks might help. +// +// Executable returns an absolute path unless an error occurred. +// +// The main use case is finding resources located relative to an +// executable. +func Executable() (string, error) { + return executable() +} diff --git a/contrib/go/_std_1.22/src/os/executable_darwin.go b/contrib/go/_std_1.22/src/os/executable_darwin.go new file mode 100644 index 0000000000..dae9f4ee18 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/executable_darwin.go @@ -0,0 +1,29 @@ +// Copyright 2016 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. + +package os + +import "errors" + +var executablePath string // set by ../runtime/os_darwin.go + +var initCwd, initCwdErr = Getwd() + +func executable() (string, error) { + ep := executablePath + if len(ep) == 0 { + return ep, errors.New("cannot find executable path") + } + if ep[0] != '/' { + if initCwdErr != nil { + return ep, initCwdErr + } + if len(ep) > 2 && ep[0:2] == "./" { + // skip "./" + ep = ep[2:] + } + ep = initCwd + "/" + ep + } + return ep, nil +} diff --git a/contrib/go/_std_1.22/src/os/executable_procfs.go b/contrib/go/_std_1.22/src/os/executable_procfs.go new file mode 100644 index 0000000000..94e674e364 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/executable_procfs.go @@ -0,0 +1,37 @@ +// Copyright 2016 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. + +//go:build linux || netbsd + +package os + +import ( + "errors" + "runtime" +) + +func executable() (string, error) { + var procfn string + switch runtime.GOOS { + default: + return "", errors.New("Executable not implemented for " + runtime.GOOS) + case "linux", "android": + procfn = "/proc/self/exe" + case "netbsd": + procfn = "/proc/curproc/exe" + } + path, err := Readlink(procfn) + + // When the executable has been deleted then Readlink returns a + // path appended with " (deleted)". + return stringsTrimSuffix(path, " (deleted)"), err +} + +// stringsTrimSuffix is the same as strings.TrimSuffix. +func stringsTrimSuffix(s, suffix string) string { + if len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix { + return s[:len(s)-len(suffix)] + } + return s +} diff --git a/contrib/go/_std_1.22/src/os/executable_windows.go b/contrib/go/_std_1.22/src/os/executable_windows.go new file mode 100644 index 0000000000..fc5cf86005 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/executable_windows.go @@ -0,0 +1,32 @@ +// Copyright 2016 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. + +package os + +import ( + "internal/syscall/windows" + "syscall" +) + +func getModuleFileName(handle syscall.Handle) (string, error) { + n := uint32(1024) + var buf []uint16 + for { + buf = make([]uint16, n) + r, err := windows.GetModuleFileName(handle, &buf[0], n) + if err != nil { + return "", err + } + if r < n { + break + } + // r == n means n not big enough + n += 1024 + } + return syscall.UTF16ToString(buf), nil +} + +func executable() (string, error) { + return getModuleFileName(0) +} diff --git a/contrib/go/_std_1.22/src/os/file.go b/contrib/go/_std_1.22/src/os/file.go new file mode 100644 index 0000000000..090ffba4dc --- /dev/null +++ b/contrib/go/_std_1.22/src/os/file.go @@ -0,0 +1,824 @@ +// 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. + +// Package os provides a platform-independent interface to operating system +// functionality. The design is Unix-like, although the error handling is +// Go-like; failing calls return values of type error rather than error numbers. +// Often, more information is available within the error. For example, +// if a call that takes a file name fails, such as Open or Stat, the error +// will include the failing file name when printed and will be of type +// *PathError, which may be unpacked for more information. +// +// The os interface is intended to be uniform across all operating systems. +// Features not generally available appear in the system-specific package syscall. +// +// Here is a simple example, opening a file and reading some of it. +// +// file, err := os.Open("file.go") // For read access. +// if err != nil { +// log.Fatal(err) +// } +// +// If the open fails, the error string will be self-explanatory, like +// +// open file.go: no such file or directory +// +// The file's data can then be read into a slice of bytes. Read and +// Write take their byte counts from the length of the argument slice. +// +// data := make([]byte, 100) +// count, err := file.Read(data) +// if err != nil { +// log.Fatal(err) +// } +// fmt.Printf("read %d bytes: %q\n", count, data[:count]) +// +// Note: The maximum number of concurrent operations on a File may be limited by +// the OS or the system. The number should be high, but exceeding it may degrade +// performance or cause other issues. +package os + +import ( + "errors" + "internal/poll" + "internal/safefilepath" + "internal/testlog" + "io" + "io/fs" + "runtime" + "syscall" + "time" + "unsafe" +) + +// Name returns the name of the file as presented to Open. +func (f *File) Name() string { return f.name } + +// Stdin, Stdout, and Stderr are open Files pointing to the standard input, +// standard output, and standard error file descriptors. +// +// Note that the Go runtime writes to standard error for panics and crashes; +// closing Stderr may cause those messages to go elsewhere, perhaps +// to a file opened later. +var ( + Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") + Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout") + Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr") +) + +// Flags to OpenFile wrapping those of the underlying system. Not all +// flags may be implemented on a given system. +const ( + // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified. + O_RDONLY int = syscall.O_RDONLY // open the file read-only. + O_WRONLY int = syscall.O_WRONLY // open the file write-only. + O_RDWR int = syscall.O_RDWR // open the file read-write. + // The remaining values may be or'ed in to control behavior. + O_APPEND int = syscall.O_APPEND // append data to the file when writing. + O_CREATE int = syscall.O_CREAT // create a new file if none exists. + O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist. + O_SYNC int = syscall.O_SYNC // open for synchronous I/O. + O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened. +) + +// Seek whence values. +// +// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd. +const ( + SEEK_SET int = 0 // seek relative to the origin of the file + SEEK_CUR int = 1 // seek relative to the current offset + SEEK_END int = 2 // seek relative to the end +) + +// LinkError records an error during a link or symlink or rename +// system call and the paths that caused it. +type LinkError struct { + Op string + Old string + New string + Err error +} + +func (e *LinkError) Error() string { + return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error() +} + +func (e *LinkError) Unwrap() error { + return e.Err +} + +// Read reads up to len(b) bytes from the File and stores them in b. +// It returns the number of bytes read and any error encountered. +// At end of file, Read returns 0, io.EOF. +func (f *File) Read(b []byte) (n int, err error) { + if err := f.checkValid("read"); err != nil { + return 0, err + } + n, e := f.read(b) + return n, f.wrapErr("read", e) +} + +// ReadAt reads len(b) bytes from the File starting at byte offset off. +// It returns the number of bytes read and the error, if any. +// ReadAt always returns a non-nil error when n < len(b). +// At end of file, that error is io.EOF. +func (f *File) ReadAt(b []byte, off int64) (n int, err error) { + if err := f.checkValid("read"); err != nil { + return 0, err + } + + if off < 0 { + return 0, &PathError{Op: "readat", Path: f.name, Err: errors.New("negative offset")} + } + + for len(b) > 0 { + m, e := f.pread(b, off) + if e != nil { + err = f.wrapErr("read", e) + break + } + n += m + b = b[m:] + off += int64(m) + } + return +} + +// ReadFrom implements io.ReaderFrom. +func (f *File) ReadFrom(r io.Reader) (n int64, err error) { + if err := f.checkValid("write"); err != nil { + return 0, err + } + n, handled, e := f.readFrom(r) + if !handled { + return genericReadFrom(f, r) // without wrapping + } + return n, f.wrapErr("write", e) +} + +// noReadFrom can be embedded alongside another type to +// hide the ReadFrom method of that other type. +type noReadFrom struct{} + +// ReadFrom hides another ReadFrom method. +// It should never be called. +func (noReadFrom) ReadFrom(io.Reader) (int64, error) { + panic("can't happen") +} + +// fileWithoutReadFrom implements all the methods of *File other +// than ReadFrom. This is used to permit ReadFrom to call io.Copy +// without leading to a recursive call to ReadFrom. +type fileWithoutReadFrom struct { + noReadFrom + *File +} + +func genericReadFrom(f *File, r io.Reader) (int64, error) { + return io.Copy(fileWithoutReadFrom{File: f}, r) +} + +// Write writes len(b) bytes from b to the File. +// It returns the number of bytes written and an error, if any. +// Write returns a non-nil error when n != len(b). +func (f *File) Write(b []byte) (n int, err error) { + if err := f.checkValid("write"); err != nil { + return 0, err + } + n, e := f.write(b) + if n < 0 { + n = 0 + } + if n != len(b) { + err = io.ErrShortWrite + } + + epipecheck(f, e) + + if e != nil { + err = f.wrapErr("write", e) + } + + return n, err +} + +var errWriteAtInAppendMode = errors.New("os: invalid use of WriteAt on file opened with O_APPEND") + +// WriteAt writes len(b) bytes to the File starting at byte offset off. +// It returns the number of bytes written and an error, if any. +// WriteAt returns a non-nil error when n != len(b). +// +// If file was opened with the O_APPEND flag, WriteAt returns an error. +func (f *File) WriteAt(b []byte, off int64) (n int, err error) { + if err := f.checkValid("write"); err != nil { + return 0, err + } + if f.appendMode { + return 0, errWriteAtInAppendMode + } + + if off < 0 { + return 0, &PathError{Op: "writeat", Path: f.name, Err: errors.New("negative offset")} + } + + for len(b) > 0 { + m, e := f.pwrite(b, off) + if e != nil { + err = f.wrapErr("write", e) + break + } + n += m + b = b[m:] + off += int64(m) + } + return +} + +// WriteTo implements io.WriterTo. +func (f *File) WriteTo(w io.Writer) (n int64, err error) { + if err := f.checkValid("read"); err != nil { + return 0, err + } + n, handled, e := f.writeTo(w) + if handled { + return n, f.wrapErr("read", e) + } + return genericWriteTo(f, w) // without wrapping +} + +// noWriteTo can be embedded alongside another type to +// hide the WriteTo method of that other type. +type noWriteTo struct{} + +// WriteTo hides another WriteTo method. +// It should never be called. +func (noWriteTo) WriteTo(io.Writer) (int64, error) { + panic("can't happen") +} + +// fileWithoutWriteTo implements all the methods of *File other +// than WriteTo. This is used to permit WriteTo to call io.Copy +// without leading to a recursive call to WriteTo. +type fileWithoutWriteTo struct { + noWriteTo + *File +} + +func genericWriteTo(f *File, w io.Writer) (int64, error) { + return io.Copy(w, fileWithoutWriteTo{File: f}) +} + +// Seek sets the offset for the next Read or Write on file to offset, interpreted +// according to whence: 0 means relative to the origin of the file, 1 means +// relative to the current offset, and 2 means relative to the end. +// It returns the new offset and an error, if any. +// The behavior of Seek on a file opened with O_APPEND is not specified. +func (f *File) Seek(offset int64, whence int) (ret int64, err error) { + if err := f.checkValid("seek"); err != nil { + return 0, err + } + r, e := f.seek(offset, whence) + if e == nil && f.dirinfo != nil && r != 0 { + e = syscall.EISDIR + } + if e != nil { + return 0, f.wrapErr("seek", e) + } + return r, nil +} + +// WriteString is like Write, but writes the contents of string s rather than +// a slice of bytes. +func (f *File) WriteString(s string) (n int, err error) { + b := unsafe.Slice(unsafe.StringData(s), len(s)) + return f.Write(b) +} + +// Mkdir creates a new directory with the specified name and permission +// bits (before umask). +// If there is an error, it will be of type *PathError. +func Mkdir(name string, perm FileMode) error { + longName := fixLongPath(name) + e := ignoringEINTR(func() error { + return syscall.Mkdir(longName, syscallMode(perm)) + }) + + if e != nil { + return &PathError{Op: "mkdir", Path: name, Err: e} + } + + // mkdir(2) itself won't handle the sticky bit on *BSD and Solaris + if !supportsCreateWithStickyBit && perm&ModeSticky != 0 { + e = setStickyBit(name) + + if e != nil { + Remove(name) + return e + } + } + + return nil +} + +// setStickyBit adds ModeSticky to the permission bits of path, non atomic. +func setStickyBit(name string) error { + fi, err := Stat(name) + if err != nil { + return err + } + return Chmod(name, fi.Mode()|ModeSticky) +} + +// Chdir changes the current working directory to the named directory. +// If there is an error, it will be of type *PathError. +func Chdir(dir string) error { + if e := syscall.Chdir(dir); e != nil { + testlog.Open(dir) // observe likely non-existent directory + return &PathError{Op: "chdir", Path: dir, Err: e} + } + if log := testlog.Logger(); log != nil { + wd, err := Getwd() + if err == nil { + log.Chdir(wd) + } + } + return nil +} + +// Open opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// If there is an error, it will be of type *PathError. +func Open(name string) (*File, error) { + return OpenFile(name, O_RDONLY, 0) +} + +// Create creates or truncates the named file. If the file already exists, +// it is truncated. If the file does not exist, it is created with mode 0666 +// (before umask). If successful, methods on the returned File can +// be used for I/O; the associated file descriptor has mode O_RDWR. +// If there is an error, it will be of type *PathError. +func Create(name string) (*File, error) { + return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) +} + +// OpenFile is the generalized open call; most users will use Open +// or Create instead. It opens the named file with specified flag +// (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag +// is passed, it is created with mode perm (before umask). If successful, +// methods on the returned File can be used for I/O. +// If there is an error, it will be of type *PathError. +func OpenFile(name string, flag int, perm FileMode) (*File, error) { + testlog.Open(name) + f, err := openFileNolog(name, flag, perm) + if err != nil { + return nil, err + } + f.appendMode = flag&O_APPEND != 0 + + return f, nil +} + +// lstat is overridden in tests. +var lstat = Lstat + +// Rename renames (moves) oldpath to newpath. +// If newpath already exists and is not a directory, Rename replaces it. +// OS-specific restrictions may apply when oldpath and newpath are in different directories. +// Even within the same directory, on non-Unix platforms Rename is not an atomic operation. +// If there is an error, it will be of type *LinkError. +func Rename(oldpath, newpath string) error { + return rename(oldpath, newpath) +} + +// Readlink returns the destination of the named symbolic link. +// If there is an error, it will be of type *PathError. +// +// If the link destination is relative, Readlink returns the relative path +// without resolving it to an absolute one. +func Readlink(name string) (string, error) { + return readlink(name) +} + +// Many functions in package syscall return a count of -1 instead of 0. +// Using fixCount(call()) instead of call() corrects the count. +func fixCount(n int, err error) (int, error) { + if n < 0 { + n = 0 + } + return n, err +} + +// checkWrapErr is the test hook to enable checking unexpected wrapped errors of poll.ErrFileClosing. +// It is set to true in the export_test.go for tests (including fuzz tests). +var checkWrapErr = false + +// wrapErr wraps an error that occurred during an operation on an open file. +// It passes io.EOF through unchanged, otherwise converts +// poll.ErrFileClosing to ErrClosed and wraps the error in a PathError. +func (f *File) wrapErr(op string, err error) error { + if err == nil || err == io.EOF { + return err + } + if err == poll.ErrFileClosing { + err = ErrClosed + } else if checkWrapErr && errors.Is(err, poll.ErrFileClosing) { + panic("unexpected error wrapping poll.ErrFileClosing: " + err.Error()) + } + return &PathError{Op: op, Path: f.name, Err: err} +} + +// TempDir returns the default directory to use for temporary files. +// +// On Unix systems, it returns $TMPDIR if non-empty, else /tmp. +// On Windows, it uses GetTempPath, returning the first non-empty +// value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. +// On Plan 9, it returns /tmp. +// +// The directory is neither guaranteed to exist nor have accessible +// permissions. +func TempDir() string { + return tempDir() +} + +// UserCacheDir returns the default root directory to use for user-specific +// cached data. Users should create their own application-specific subdirectory +// within this one and use that. +// +// On Unix systems, it returns $XDG_CACHE_HOME as specified by +// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if +// non-empty, else $HOME/.cache. +// On Darwin, it returns $HOME/Library/Caches. +// On Windows, it returns %LocalAppData%. +// On Plan 9, it returns $home/lib/cache. +// +// If the location cannot be determined (for example, $HOME is not defined), +// then it will return an error. +func UserCacheDir() (string, error) { + var dir string + + switch runtime.GOOS { + case "windows": + dir = Getenv("LocalAppData") + if dir == "" { + return "", errors.New("%LocalAppData% is not defined") + } + + case "darwin", "ios": + dir = Getenv("HOME") + if dir == "" { + return "", errors.New("$HOME is not defined") + } + dir += "/Library/Caches" + + case "plan9": + dir = Getenv("home") + if dir == "" { + return "", errors.New("$home is not defined") + } + dir += "/lib/cache" + + default: // Unix + dir = Getenv("XDG_CACHE_HOME") + if dir == "" { + dir = Getenv("HOME") + if dir == "" { + return "", errors.New("neither $XDG_CACHE_HOME nor $HOME are defined") + } + dir += "/.cache" + } + } + + return dir, nil +} + +// UserConfigDir returns the default root directory to use for user-specific +// configuration data. Users should create their own application-specific +// subdirectory within this one and use that. +// +// On Unix systems, it returns $XDG_CONFIG_HOME as specified by +// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if +// non-empty, else $HOME/.config. +// On Darwin, it returns $HOME/Library/Application Support. +// On Windows, it returns %AppData%. +// On Plan 9, it returns $home/lib. +// +// If the location cannot be determined (for example, $HOME is not defined), +// then it will return an error. +func UserConfigDir() (string, error) { + var dir string + + switch runtime.GOOS { + case "windows": + dir = Getenv("AppData") + if dir == "" { + return "", errors.New("%AppData% is not defined") + } + + case "darwin", "ios": + dir = Getenv("HOME") + if dir == "" { + return "", errors.New("$HOME is not defined") + } + dir += "/Library/Application Support" + + case "plan9": + dir = Getenv("home") + if dir == "" { + return "", errors.New("$home is not defined") + } + dir += "/lib" + + default: // Unix + dir = Getenv("XDG_CONFIG_HOME") + if dir == "" { + dir = Getenv("HOME") + if dir == "" { + return "", errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined") + } + dir += "/.config" + } + } + + return dir, nil +} + +// UserHomeDir returns the current user's home directory. +// +// On Unix, including macOS, it returns the $HOME environment variable. +// On Windows, it returns %USERPROFILE%. +// On Plan 9, it returns the $home environment variable. +// +// If the expected variable is not set in the environment, UserHomeDir +// returns either a platform-specific default value or a non-nil error. +func UserHomeDir() (string, error) { + env, enverr := "HOME", "$HOME" + switch runtime.GOOS { + case "windows": + env, enverr = "USERPROFILE", "%userprofile%" + case "plan9": + env, enverr = "home", "$home" + } + if v := Getenv(env); v != "" { + return v, nil + } + // On some geese the home directory is not always defined. + switch runtime.GOOS { + case "android": + return "/sdcard", nil + case "ios": + return "/", nil + } + return "", errors.New(enverr + " is not defined") +} + +// Chmod changes the mode of the named file to mode. +// If the file is a symbolic link, it changes the mode of the link's target. +// If there is an error, it will be of type *PathError. +// +// A different subset of the mode bits are used, depending on the +// operating system. +// +// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and +// ModeSticky are used. +// +// On Windows, only the 0200 bit (owner writable) of mode is used; it +// controls whether the file's read-only attribute is set or cleared. +// The other bits are currently unused. For compatibility with Go 1.12 +// and earlier, use a non-zero mode. Use mode 0400 for a read-only +// file and 0600 for a readable+writable file. +// +// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive, +// and ModeTemporary are used. +func Chmod(name string, mode FileMode) error { return chmod(name, mode) } + +// Chmod changes the mode of the file to mode. +// If there is an error, it will be of type *PathError. +func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) } + +// SetDeadline sets the read and write deadlines for a File. +// It is equivalent to calling both SetReadDeadline and SetWriteDeadline. +// +// Only some kinds of files support setting a deadline. Calls to SetDeadline +// for files that do not support deadlines will return ErrNoDeadline. +// On most systems ordinary files do not support deadlines, but pipes do. +// +// A deadline is an absolute time after which I/O operations fail with an +// error instead of blocking. The deadline applies to all future and pending +// I/O, not just the immediately following call to Read or Write. +// After a deadline has been exceeded, the connection can be refreshed +// by setting a deadline in the future. +// +// If the deadline is exceeded a call to Read or Write or to other I/O +// methods will return an error that wraps ErrDeadlineExceeded. +// This can be tested using errors.Is(err, os.ErrDeadlineExceeded). +// That error implements the Timeout method, and calling the Timeout +// method will return true, but there are other possible errors for which +// the Timeout will return true even if the deadline has not been exceeded. +// +// An idle timeout can be implemented by repeatedly extending +// the deadline after successful Read or Write calls. +// +// A zero value for t means I/O operations will not time out. +func (f *File) SetDeadline(t time.Time) error { + return f.setDeadline(t) +} + +// SetReadDeadline sets the deadline for future Read calls and any +// currently-blocked Read call. +// A zero value for t means Read will not time out. +// Not all files support setting deadlines; see SetDeadline. +func (f *File) SetReadDeadline(t time.Time) error { + return f.setReadDeadline(t) +} + +// SetWriteDeadline sets the deadline for any future Write calls and any +// currently-blocked Write call. +// Even if Write times out, it may return n > 0, indicating that +// some of the data was successfully written. +// A zero value for t means Write will not time out. +// Not all files support setting deadlines; see SetDeadline. +func (f *File) SetWriteDeadline(t time.Time) error { + return f.setWriteDeadline(t) +} + +// SyscallConn returns a raw file. +// This implements the syscall.Conn interface. +func (f *File) SyscallConn() (syscall.RawConn, error) { + if err := f.checkValid("SyscallConn"); err != nil { + return nil, err + } + return newRawConn(f) +} + +// 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. Additionally, the root of the fs.FS returned for a relative path, +// DirFS("prefix"), will be affected by later calls to Chdir. DirFS is therefore not +// a general substitute for a chroot-style security mechanism when the directory tree +// contains arbitrary content. +// +// The directory dir must not be "". +// +// The result implements [io/fs.StatFS], [io/fs.ReadFileFS] and +// [io/fs.ReadDirFS]. +func DirFS(dir string) fs.FS { + return dirFS(dir) +} + +type dirFS string + +func (dir dirFS) Open(name string) (fs.File, error) { + fullname, err := dir.join(name) + if err != nil { + return nil, &PathError{Op: "open", Path: name, Err: err} + } + f, err := Open(fullname) + if err != nil { + // DirFS takes a string appropriate for GOOS, + // while the name argument here is always slash separated. + // dir.join will have mixed the two; undo that for + // error reporting. + err.(*PathError).Path = name + return nil, err + } + return f, nil +} + +// The ReadFile method calls the [ReadFile] function for the file +// with the given name in the directory. The function provides +// robust handling for small files and special file systems. +// Through this method, dirFS implements [io/fs.ReadFileFS]. +func (dir dirFS) ReadFile(name string) ([]byte, error) { + fullname, err := dir.join(name) + if err != nil { + return nil, &PathError{Op: "readfile", Path: name, Err: err} + } + b, err := ReadFile(fullname) + if err != nil { + if e, ok := err.(*PathError); ok { + // See comment in dirFS.Open. + e.Path = name + } + return nil, err + } + return b, nil +} + +// ReadDir reads the named directory, returning all its directory entries sorted +// by filename. Through this method, dirFS implements [io/fs.ReadDirFS]. +func (dir dirFS) ReadDir(name string) ([]DirEntry, error) { + fullname, err := dir.join(name) + if err != nil { + return nil, &PathError{Op: "readdir", Path: name, Err: err} + } + entries, err := ReadDir(fullname) + if err != nil { + if e, ok := err.(*PathError); ok { + // See comment in dirFS.Open. + e.Path = name + } + return nil, err + } + return entries, nil +} + +func (dir dirFS) Stat(name string) (fs.FileInfo, error) { + fullname, err := dir.join(name) + if err != nil { + return nil, &PathError{Op: "stat", Path: name, Err: err} + } + f, err := Stat(fullname) + if err != nil { + // See comment in dirFS.Open. + err.(*PathError).Path = name + return nil, err + } + return f, nil +} + +// join returns the path for name in dir. +func (dir dirFS) join(name string) (string, error) { + if dir == "" { + return "", errors.New("os: DirFS with empty root") + } + if !fs.ValidPath(name) { + return "", ErrInvalid + } + name, err := safefilepath.FromFS(name) + if err != nil { + return "", ErrInvalid + } + if IsPathSeparator(dir[len(dir)-1]) { + return string(dir) + name, nil + } + return string(dir) + string(PathSeparator) + name, 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 { + 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 + } + + if len(data) >= cap(data) { + d := append(data[:cap(data)], 0) + data = d[:len(data)] + } + } +} + +// 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. +// Since WriteFile requires multiple system calls to complete, a failure mid-operation +// can leave the file in a partially written state. +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 +} diff --git a/contrib/go/_std_1.22/src/os/file_open_unix.go b/contrib/go/_std_1.22/src/os/file_open_unix.go new file mode 100644 index 0000000000..a3336eac81 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/file_open_unix.go @@ -0,0 +1,17 @@ +// Copyright 2023 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. + +//go:build unix || (js && wasm) + +package os + +import ( + "internal/poll" + "syscall" +) + +func open(path string, flag int, perm uint32) (int, poll.SysFile, error) { + fd, err := syscall.Open(path, flag, perm) + return fd, poll.SysFile{}, err +} diff --git a/contrib/go/_std_1.22/src/os/file_posix.go b/contrib/go/_std_1.22/src/os/file_posix.go new file mode 100644 index 0000000000..5692657753 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/file_posix.go @@ -0,0 +1,256 @@ +// 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. + +//go:build unix || (js && wasm) || wasip1 || windows + +package os + +import ( + "runtime" + "syscall" + "time" +) + +// Close closes the File, rendering it unusable for I/O. +// On files that support SetDeadline, any pending I/O operations will +// be canceled and return immediately with an ErrClosed error. +// Close will return an error if it has already been called. +func (f *File) Close() error { + if f == nil { + return ErrInvalid + } + return f.file.close() +} + +// read reads up to len(b) bytes from the File. +// It returns the number of bytes read and an error, if any. +func (f *File) read(b []byte) (n int, err error) { + n, err = f.pfd.Read(b) + runtime.KeepAlive(f) + return n, err +} + +// pread reads len(b) bytes from the File starting at byte offset off. +// It returns the number of bytes read and the error, if any. +// EOF is signaled by a zero count with err set to nil. +func (f *File) pread(b []byte, off int64) (n int, err error) { + n, err = f.pfd.Pread(b, off) + runtime.KeepAlive(f) + return n, err +} + +// write writes len(b) bytes to the File. +// It returns the number of bytes written and an error, if any. +func (f *File) write(b []byte) (n int, err error) { + n, err = f.pfd.Write(b) + runtime.KeepAlive(f) + return n, err +} + +// pwrite writes len(b) bytes to the File starting at byte offset off. +// It returns the number of bytes written and an error, if any. +func (f *File) pwrite(b []byte, off int64) (n int, err error) { + n, err = f.pfd.Pwrite(b, off) + runtime.KeepAlive(f) + return n, err +} + +// syscallMode returns the syscall-specific mode bits from Go's portable mode bits. +func syscallMode(i FileMode) (o uint32) { + o |= uint32(i.Perm()) + if i&ModeSetuid != 0 { + o |= syscall.S_ISUID + } + if i&ModeSetgid != 0 { + o |= syscall.S_ISGID + } + if i&ModeSticky != 0 { + o |= syscall.S_ISVTX + } + // No mapping for Go's ModeTemporary (plan9 only). + return +} + +// See docs in file.go:Chmod. +func chmod(name string, mode FileMode) error { + longName := fixLongPath(name) + e := ignoringEINTR(func() error { + return syscall.Chmod(longName, syscallMode(mode)) + }) + if e != nil { + return &PathError{Op: "chmod", Path: name, Err: e} + } + return nil +} + +// See docs in file.go:(*File).Chmod. +func (f *File) chmod(mode FileMode) error { + if err := f.checkValid("chmod"); err != nil { + return err + } + if e := f.pfd.Fchmod(syscallMode(mode)); e != nil { + return f.wrapErr("chmod", e) + } + return nil +} + +// Chown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link's target. +// A uid or gid of -1 means to not change that value. +// If there is an error, it will be of type *PathError. +// +// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or +// EPLAN9 error, wrapped in *PathError. +func Chown(name string, uid, gid int) error { + e := ignoringEINTR(func() error { + return syscall.Chown(name, uid, gid) + }) + if e != nil { + return &PathError{Op: "chown", Path: name, Err: e} + } + return nil +} + +// Lchown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link itself. +// If there is an error, it will be of type *PathError. +// +// On Windows, it always returns the syscall.EWINDOWS error, wrapped +// in *PathError. +func Lchown(name string, uid, gid int) error { + e := ignoringEINTR(func() error { + return syscall.Lchown(name, uid, gid) + }) + if e != nil { + return &PathError{Op: "lchown", Path: name, Err: e} + } + return nil +} + +// Chown changes the numeric uid and gid of the named file. +// If there is an error, it will be of type *PathError. +// +// On Windows, it always returns the syscall.EWINDOWS error, wrapped +// in *PathError. +func (f *File) Chown(uid, gid int) error { + if err := f.checkValid("chown"); err != nil { + return err + } + if e := f.pfd.Fchown(uid, gid); e != nil { + return f.wrapErr("chown", e) + } + return nil +} + +// Truncate changes the size of the file. +// It does not change the I/O offset. +// If there is an error, it will be of type *PathError. +func (f *File) Truncate(size int64) error { + if err := f.checkValid("truncate"); err != nil { + return err + } + if e := f.pfd.Ftruncate(size); e != nil { + return f.wrapErr("truncate", e) + } + return nil +} + +// Sync commits the current contents of the file to stable storage. +// Typically, this means flushing the file system's in-memory copy +// of recently written data to disk. +func (f *File) Sync() error { + if err := f.checkValid("sync"); err != nil { + return err + } + if e := f.pfd.Fsync(); e != nil { + return f.wrapErr("sync", e) + } + return nil +} + +// Chtimes changes the access and modification times of the named +// file, similar to the Unix utime() or utimes() functions. +// A zero time.Time value will leave the corresponding file time unchanged. +// +// The underlying filesystem may truncate or round the values to a +// less precise time unit. +// If there is an error, it will be of type *PathError. +func Chtimes(name string, atime time.Time, mtime time.Time) error { + var utimes [2]syscall.Timespec + set := func(i int, t time.Time) { + if t.IsZero() { + utimes[i] = syscall.Timespec{Sec: _UTIME_OMIT, Nsec: _UTIME_OMIT} + } else { + utimes[i] = syscall.NsecToTimespec(t.UnixNano()) + } + } + set(0, atime) + set(1, mtime) + if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil { + return &PathError{Op: "chtimes", Path: name, Err: e} + } + return nil +} + +// Chdir changes the current working directory to the file, +// which must be a directory. +// If there is an error, it will be of type *PathError. +func (f *File) Chdir() error { + if err := f.checkValid("chdir"); err != nil { + return err + } + if e := f.pfd.Fchdir(); e != nil { + return f.wrapErr("chdir", e) + } + return nil +} + +// setDeadline sets the read and write deadline. +func (f *File) setDeadline(t time.Time) error { + if err := f.checkValid("SetDeadline"); err != nil { + return err + } + return f.pfd.SetDeadline(t) +} + +// setReadDeadline sets the read deadline. +func (f *File) setReadDeadline(t time.Time) error { + if err := f.checkValid("SetReadDeadline"); err != nil { + return err + } + return f.pfd.SetReadDeadline(t) +} + +// setWriteDeadline sets the write deadline. +func (f *File) setWriteDeadline(t time.Time) error { + if err := f.checkValid("SetWriteDeadline"); err != nil { + return err + } + return f.pfd.SetWriteDeadline(t) +} + +// checkValid checks whether f is valid for use. +// If not, it returns an appropriate error, perhaps incorporating the operation name op. +func (f *File) checkValid(op string) error { + if f == nil { + return ErrInvalid + } + return nil +} + +// ignoringEINTR makes a function call and repeats it if it returns an +// EINTR error. This appears to be required even though we install all +// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. +// Also #20400 and #36644 are issues in which a signal handler is +// installed without setting SA_RESTART. None of these are the common case, +// but there are enough of them that it seems that we can't avoid +// an EINTR loop. +func ignoringEINTR(fn func() error) error { + for { + err := fn() + if err != syscall.EINTR { + return err + } + } +} diff --git a/contrib/go/_std_1.22/src/os/file_unix.go b/contrib/go/_std_1.22/src/os/file_unix.go new file mode 100644 index 0000000000..a527b23e4f --- /dev/null +++ b/contrib/go/_std_1.22/src/os/file_unix.go @@ -0,0 +1,495 @@ +// 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. + +//go:build unix || (js && wasm) || wasip1 + +package os + +import ( + "internal/poll" + "internal/syscall/unix" + "io/fs" + "runtime" + "syscall" + _ "unsafe" // for go:linkname +) + +const _UTIME_OMIT = unix.UTIME_OMIT + +// fixLongPath is a noop on non-Windows platforms. +func fixLongPath(path string) string { + return path +} + +func rename(oldname, newname string) error { + fi, err := Lstat(newname) + if err == nil && fi.IsDir() { + // There are two independent errors this function can return: + // one for a bad oldname, and one for a bad newname. + // At this point we've determined the newname is bad. + // But just in case oldname is also bad, prioritize returning + // the oldname error because that's what we did historically. + // However, if the old name and new name are not the same, yet + // they refer to the same file, it implies a case-only + // rename on a case-insensitive filesystem, which is ok. + if ofi, err := Lstat(oldname); err != nil { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return &LinkError{"rename", oldname, newname, err} + } else if newname == oldname || !SameFile(fi, ofi) { + return &LinkError{"rename", oldname, newname, syscall.EEXIST} + } + } + err = ignoringEINTR(func() error { + return syscall.Rename(oldname, newname) + }) + if err != nil { + return &LinkError{"rename", oldname, newname, err} + } + return nil +} + +// file is the real representation of *File. +// The extra level of indirection ensures that no clients of os +// can overwrite this data, which could cause the finalizer +// to close the wrong file descriptor. +type file struct { + pfd poll.FD + name string + dirinfo *dirInfo // nil unless directory being read + nonblock bool // whether we set nonblocking mode + stdoutOrErr bool // whether this is stdout or stderr + appendMode bool // whether file is opened for appending +} + +// Fd returns the integer Unix file descriptor referencing the open file. +// If f is closed, the file descriptor becomes invalid. +// If f is garbage collected, a finalizer may close the file descriptor, +// making it invalid; see runtime.SetFinalizer for more information on when +// a finalizer might be run. On Unix systems this will cause the SetDeadline +// methods to stop working. +// Because file descriptors can be reused, the returned file descriptor may +// only be closed through the Close method of f, or by its finalizer during +// garbage collection. Otherwise, during garbage collection the finalizer +// may close an unrelated file descriptor with the same (reused) number. +// +// As an alternative, see the f.SyscallConn method. +func (f *File) Fd() uintptr { + if f == nil { + return ^(uintptr(0)) + } + + // If we put the file descriptor into nonblocking mode, + // then set it to blocking mode before we return it, + // because historically we have always returned a descriptor + // opened in blocking mode. The File will continue to work, + // but any blocking operation will tie up a thread. + if f.nonblock { + f.pfd.SetBlocking() + } + + return uintptr(f.pfd.Sysfd) +} + +// NewFile returns a new File with the given file descriptor and +// name. The returned value will be nil if fd is not a valid file +// descriptor. On Unix systems, if the file descriptor is in +// non-blocking mode, NewFile will attempt to return a pollable File +// (one for which the SetDeadline methods work). +// +// After passing it to NewFile, fd may become invalid under the same +// conditions described in the comments of the Fd method, and the same +// constraints apply. +func NewFile(fd uintptr, name string) *File { + fdi := int(fd) + if fdi < 0 { + return nil + } + + kind := kindNewFile + appendMode := false + if flags, err := unix.Fcntl(fdi, syscall.F_GETFL, 0); err == nil { + if unix.HasNonblockFlag(flags) { + kind = kindNonBlock + } + appendMode = flags&syscall.O_APPEND != 0 + } + f := newFile(fdi, name, kind) + f.appendMode = appendMode + return f +} + +// net_newUnixFile is a hidden entry point called by net.conn.File. +// This is used so that a nonblocking network connection will become +// blocking if code calls the Fd method. We don't want that for direct +// calls to NewFile: passing a nonblocking descriptor to NewFile should +// remain nonblocking if you get it back using Fd. But for net.conn.File +// the call to NewFile is hidden from the user. Historically in that case +// the Fd method has returned a blocking descriptor, and we want to +// retain that behavior because existing code expects it and depends on it. +// +//go:linkname net_newUnixFile net.newUnixFile +func net_newUnixFile(fd int, name string) *File { + if fd < 0 { + panic("invalid FD") + } + + f := newFile(fd, name, kindNonBlock) + f.nonblock = true // tell Fd to return blocking descriptor + return f +} + +// newFileKind describes the kind of file to newFile. +type newFileKind int + +const ( + // kindNewFile means that the descriptor was passed to us via NewFile. + kindNewFile newFileKind = iota + // kindOpenFile means that the descriptor was opened using + // Open, Create, or OpenFile (without O_NONBLOCK). + kindOpenFile + // kindPipe means that the descriptor was opened using Pipe. + kindPipe + // kindNonBlock means that the descriptor is already in + // non-blocking mode. + kindNonBlock + // kindNoPoll means that we should not put the descriptor into + // non-blocking mode, because we know it is not a pipe or FIFO. + // Used by openFdAt for directories. + kindNoPoll +) + +// newFile is like NewFile, but if called from OpenFile or Pipe +// (as passed in the kind parameter) it tries to add the file to +// the runtime poller. +func newFile(fd int, name string, kind newFileKind) *File { + f := &File{&file{ + pfd: poll.FD{ + Sysfd: fd, + IsStream: true, + ZeroReadIsEOF: true, + }, + name: name, + stdoutOrErr: fd == 1 || fd == 2, + }} + + pollable := kind == kindOpenFile || kind == kindPipe || kind == kindNonBlock + + // If the caller passed a non-blocking filedes (kindNonBlock), + // we assume they know what they are doing so we allow it to be + // used with kqueue. + if kind == kindOpenFile { + switch runtime.GOOS { + case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd": + var st syscall.Stat_t + err := ignoringEINTR(func() error { + return syscall.Fstat(fd, &st) + }) + typ := st.Mode & syscall.S_IFMT + // Don't try to use kqueue with regular files on *BSDs. + // On FreeBSD a regular file is always + // reported as ready for writing. + // On Dragonfly, NetBSD and OpenBSD the fd is signaled + // only once as ready (both read and write). + // Issue 19093. + // Also don't add directories to the netpoller. + if err == nil && (typ == syscall.S_IFREG || typ == syscall.S_IFDIR) { + pollable = false + } + + // In addition to the behavior described above for regular files, + // on Darwin, kqueue does not work properly with fifos: + // closing the last writer does not cause a kqueue event + // for any readers. See issue #24164. + if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && typ == syscall.S_IFIFO { + pollable = false + } + } + } + + clearNonBlock := false + if pollable { + if kind == kindNonBlock { + // The descriptor is already in non-blocking mode. + // We only set f.nonblock if we put the file into + // non-blocking mode. + } else if err := syscall.SetNonblock(fd, true); err == nil { + f.nonblock = true + clearNonBlock = true + } else { + pollable = false + } + } + + // An error here indicates a failure to register + // with the netpoll system. That can happen for + // a file descriptor that is not supported by + // epoll/kqueue; for example, disk files on + // Linux systems. We assume that any real error + // will show up in later I/O. + // We do restore the blocking behavior if it was set by us. + if pollErr := f.pfd.Init("file", pollable); pollErr != nil && clearNonBlock { + if err := syscall.SetNonblock(fd, false); err == nil { + f.nonblock = false + } + } + + runtime.SetFinalizer(f.file, (*file).close) + return f +} + +func sigpipe() // implemented in package runtime + +// epipecheck raises SIGPIPE if we get an EPIPE error on standard +// output or standard error. See the SIGPIPE docs in os/signal, and +// issue 11845. +func epipecheck(file *File, e error) { + if e == syscall.EPIPE && file.stdoutOrErr { + sigpipe() + } +} + +// DevNull is the name of the operating system's “null device.” +// On Unix-like systems, it is "/dev/null"; on Windows, "NUL". +const DevNull = "/dev/null" + +// openFileNolog is the Unix implementation of OpenFile. +// Changes here should be reflected in openFdAt, if relevant. +func openFileNolog(name string, flag int, perm FileMode) (*File, error) { + setSticky := false + if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 { + if _, err := Stat(name); IsNotExist(err) { + setSticky = true + } + } + + var r int + var s poll.SysFile + for { + var e error + r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) + if e == nil { + break + } + + // We have to check EINTR here, per issues 11180 and 39237. + if e == syscall.EINTR { + continue + } + + return nil, &PathError{Op: "open", Path: name, Err: e} + } + + // open(2) itself won't handle the sticky bit on *BSD and Solaris + if setSticky { + setStickyBit(name) + } + + // There's a race here with fork/exec, which we are + // content to live with. See ../syscall/exec_unix.go. + if !supportsCloseOnExec { + syscall.CloseOnExec(r) + } + + kind := kindOpenFile + if unix.HasNonblockFlag(flag) { + kind = kindNonBlock + } + + f := newFile(r, name, kind) + f.pfd.SysFile = s + return f, nil +} + +func (file *file) close() error { + if file == nil { + return syscall.EINVAL + } + if file.dirinfo != nil { + file.dirinfo.close() + file.dirinfo = nil + } + var err error + if e := file.pfd.Close(); e != nil { + if e == poll.ErrFileClosing { + e = ErrClosed + } + err = &PathError{Op: "close", Path: file.name, Err: e} + } + + // no need for a finalizer anymore + runtime.SetFinalizer(file, nil) + return err +} + +// seek sets the offset for the next Read or Write on file to offset, interpreted +// according to whence: 0 means relative to the origin of the file, 1 means +// relative to the current offset, and 2 means relative to the end. +// It returns the new offset and an error, if any. +func (f *File) seek(offset int64, whence int) (ret int64, err error) { + if f.dirinfo != nil { + // Free cached dirinfo, so we allocate a new one if we + // access this file as a directory again. See #35767 and #37161. + f.dirinfo.close() + f.dirinfo = nil + } + ret, err = f.pfd.Seek(offset, whence) + runtime.KeepAlive(f) + return ret, err +} + +// Truncate changes the size of the named file. +// If the file is a symbolic link, it changes the size of the link's target. +// If there is an error, it will be of type *PathError. +func Truncate(name string, size int64) error { + e := ignoringEINTR(func() error { + return syscall.Truncate(name, size) + }) + if e != nil { + return &PathError{Op: "truncate", Path: name, Err: e} + } + return nil +} + +// Remove removes the named file or (empty) directory. +// If there is an error, it will be of type *PathError. +func Remove(name string) error { + // System call interface forces us to know + // whether name is a file or directory. + // Try both: it is cheaper on average than + // doing a Stat plus the right one. + e := ignoringEINTR(func() error { + return syscall.Unlink(name) + }) + if e == nil { + return nil + } + e1 := ignoringEINTR(func() error { + return syscall.Rmdir(name) + }) + if e1 == nil { + return nil + } + + // Both failed: figure out which error to return. + // OS X and Linux differ on whether unlink(dir) + // returns EISDIR, so can't use that. However, + // both agree that rmdir(file) returns ENOTDIR, + // so we can use that to decide which error is real. + // Rmdir might also return ENOTDIR if given a bad + // file path, like /etc/passwd/foo, but in that case, + // both errors will be ENOTDIR, so it's okay to + // use the error from unlink. + if e1 != syscall.ENOTDIR { + e = e1 + } + return &PathError{Op: "remove", Path: name, Err: e} +} + +func tempDir() string { + dir := Getenv("TMPDIR") + if dir == "" { + if runtime.GOOS == "android" { + dir = "/data/local/tmp" + } else { + dir = "/tmp" + } + } + return dir +} + +// Link creates newname as a hard link to the oldname file. +// If there is an error, it will be of type *LinkError. +func Link(oldname, newname string) error { + e := ignoringEINTR(func() error { + return syscall.Link(oldname, newname) + }) + if e != nil { + return &LinkError{"link", oldname, newname, e} + } + return nil +} + +// Symlink creates newname as a symbolic link to oldname. +// On Windows, a symlink to a non-existent oldname creates a file symlink; +// if oldname is later created as a directory the symlink will not work. +// If there is an error, it will be of type *LinkError. +func Symlink(oldname, newname string) error { + e := ignoringEINTR(func() error { + return syscall.Symlink(oldname, newname) + }) + if e != nil { + return &LinkError{"symlink", oldname, newname, e} + } + return nil +} + +func readlink(name string) (string, error) { + for len := 128; ; len *= 2 { + b := make([]byte, len) + var ( + n int + e error + ) + for { + n, e = fixCount(syscall.Readlink(name, b)) + if e != syscall.EINTR { + break + } + } + // buffer too small + if (runtime.GOOS == "aix" || runtime.GOOS == "wasip1") && e == syscall.ERANGE { + continue + } + if e != nil { + return "", &PathError{Op: "readlink", Path: name, Err: e} + } + if n < len { + return string(b[0:n]), nil + } + } +} + +type unixDirent struct { + parent string + name string + typ FileMode + info FileInfo +} + +func (d *unixDirent) Name() string { return d.name } +func (d *unixDirent) IsDir() bool { return d.typ.IsDir() } +func (d *unixDirent) Type() FileMode { return d.typ } + +func (d *unixDirent) Info() (FileInfo, error) { + if d.info != nil { + return d.info, nil + } + return lstat(d.parent + "/" + d.name) +} + +func (d *unixDirent) String() string { + return fs.FormatDirEntry(d) +} + +func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) { + ude := &unixDirent{ + parent: parent, + name: name, + typ: typ, + } + if typ != ^FileMode(0) && !testingForceReadDirLstat { + return ude, nil + } + + info, err := lstat(parent + "/" + name) + if err != nil { + return nil, err + } + + ude.typ = info.Mode().Type() + ude.info = info + return ude, nil +} diff --git a/contrib/go/_std_1.22/src/os/file_windows.go b/contrib/go/_std_1.22/src/os/file_windows.go new file mode 100644 index 0000000000..8b04ed6e47 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/file_windows.go @@ -0,0 +1,447 @@ +// 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. + +package os + +import ( + "errors" + "internal/poll" + "internal/syscall/windows" + "runtime" + "sync" + "syscall" + "unsafe" +) + +// This matches the value in syscall/syscall_windows.go. +const _UTIME_OMIT = -1 + +// file is the real representation of *File. +// The extra level of indirection ensures that no clients of os +// can overwrite this data, which could cause the finalizer +// to close the wrong file descriptor. +type file struct { + pfd poll.FD + name string + dirinfo *dirInfo // nil unless directory being read + appendMode bool // whether file is opened for appending +} + +// Fd returns the Windows handle referencing the open file. +// If f is closed, the file descriptor becomes invalid. +// If f is garbage collected, a finalizer may close the file descriptor, +// making it invalid; see runtime.SetFinalizer for more information on when +// a finalizer might be run. On Unix systems this will cause the SetDeadline +// methods to stop working. +func (file *File) Fd() uintptr { + if file == nil { + return uintptr(syscall.InvalidHandle) + } + return uintptr(file.pfd.Sysfd) +} + +// newFile returns a new File with the given file handle and name. +// Unlike NewFile, it does not check that h is syscall.InvalidHandle. +func newFile(h syscall.Handle, name string, kind string) *File { + if kind == "file" { + var m uint32 + if syscall.GetConsoleMode(h, &m) == nil { + kind = "console" + } + if t, err := syscall.GetFileType(h); err == nil && t == syscall.FILE_TYPE_PIPE { + kind = "pipe" + } + } + + f := &File{&file{ + pfd: poll.FD{ + Sysfd: h, + IsStream: true, + ZeroReadIsEOF: true, + }, + name: name, + }} + runtime.SetFinalizer(f.file, (*file).close) + + // Ignore initialization errors. + // Assume any problems will show up in later I/O. + f.pfd.Init(kind, false) + + return f +} + +// newConsoleFile creates new File that will be used as console. +func newConsoleFile(h syscall.Handle, name string) *File { + return newFile(h, name, "console") +} + +// NewFile returns a new File with the given file descriptor and +// name. The returned value will be nil if fd is not a valid file +// descriptor. +func NewFile(fd uintptr, name string) *File { + h := syscall.Handle(fd) + if h == syscall.InvalidHandle { + return nil + } + return newFile(h, name, "file") +} + +func epipecheck(file *File, e error) { +} + +// DevNull is the name of the operating system's “null device.” +// On Unix-like systems, it is "/dev/null"; on Windows, "NUL". +const DevNull = "NUL" + +// openFileNolog is the Windows implementation of OpenFile. +func openFileNolog(name string, flag int, perm FileMode) (*File, error) { + if name == "" { + return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } + path := fixLongPath(name) + r, e := syscall.Open(path, flag|syscall.O_CLOEXEC, syscallMode(perm)) + if e != nil { + // We should return EISDIR when we are trying to open a directory with write access. + if e == syscall.ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) { + pathp, e1 := syscall.UTF16PtrFromString(path) + if e1 == nil { + var fa syscall.Win32FileAttributeData + e1 = syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) + if e1 == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + e = syscall.EISDIR + } + } + } + return nil, &PathError{Op: "open", Path: name, Err: e} + } + f, e := newFile(r, name, "file"), nil + if e != nil { + return nil, &PathError{Op: "open", Path: name, Err: e} + } + return f, nil +} + +func (file *file) close() error { + if file == nil { + return syscall.EINVAL + } + if file.dirinfo != nil { + file.dirinfo.close() + file.dirinfo = nil + } + var err error + if e := file.pfd.Close(); e != nil { + if e == poll.ErrFileClosing { + e = ErrClosed + } + err = &PathError{Op: "close", Path: file.name, Err: e} + } + + // no need for a finalizer anymore + runtime.SetFinalizer(file, nil) + return err +} + +// seek sets the offset for the next Read or Write on file to offset, interpreted +// according to whence: 0 means relative to the origin of the file, 1 means +// relative to the current offset, and 2 means relative to the end. +// It returns the new offset and an error, if any. +func (f *File) seek(offset int64, whence int) (ret int64, err error) { + if f.dirinfo != nil { + // Free cached dirinfo, so we allocate a new one if we + // access this file as a directory again. See #35767 and #37161. + f.dirinfo.close() + f.dirinfo = nil + } + ret, err = f.pfd.Seek(offset, whence) + runtime.KeepAlive(f) + return ret, err +} + +// Truncate changes the size of the named file. +// If the file is a symbolic link, it changes the size of the link's target. +func Truncate(name string, size int64) error { + f, e := OpenFile(name, O_WRONLY, 0666) + if e != nil { + return e + } + defer f.Close() + e1 := f.Truncate(size) + if e1 != nil { + return e1 + } + return nil +} + +// Remove removes the named file or directory. +// If there is an error, it will be of type *PathError. +func Remove(name string) error { + p, e := syscall.UTF16PtrFromString(fixLongPath(name)) + if e != nil { + return &PathError{Op: "remove", Path: name, Err: e} + } + + // Go file interface forces us to know whether + // name is a file or directory. Try both. + e = syscall.DeleteFile(p) + if e == nil { + return nil + } + e1 := syscall.RemoveDirectory(p) + if e1 == nil { + return nil + } + + // Both failed: figure out which error to return. + if e1 != e { + a, e2 := syscall.GetFileAttributes(p) + if e2 != nil { + e = e2 + } else { + if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + e = e1 + } else if a&syscall.FILE_ATTRIBUTE_READONLY != 0 { + if e1 = syscall.SetFileAttributes(p, a&^syscall.FILE_ATTRIBUTE_READONLY); e1 == nil { + if e = syscall.DeleteFile(p); e == nil { + return nil + } + } + } + } + } + return &PathError{Op: "remove", Path: name, Err: e} +} + +func rename(oldname, newname string) error { + e := windows.Rename(fixLongPath(oldname), fixLongPath(newname)) + if e != nil { + return &LinkError{"rename", oldname, newname, e} + } + return nil +} + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. The Windows handles underlying +// the returned files are marked as inheritable by child processes. +func Pipe() (r *File, w *File, err error) { + var p [2]syscall.Handle + e := syscall.Pipe(p[:]) + if e != nil { + return nil, nil, NewSyscallError("pipe", e) + } + return newFile(p[0], "|0", "pipe"), newFile(p[1], "|1", "pipe"), nil +} + +var ( + useGetTempPath2Once sync.Once + useGetTempPath2 bool +) + +func tempDir() string { + useGetTempPath2Once.Do(func() { + useGetTempPath2 = (windows.ErrorLoadingGetTempPath2() == nil) + }) + getTempPath := syscall.GetTempPath + if useGetTempPath2 { + getTempPath = windows.GetTempPath2 + } + n := uint32(syscall.MAX_PATH) + for { + b := make([]uint16, n) + n, _ = getTempPath(uint32(len(b)), &b[0]) + if n > uint32(len(b)) { + continue + } + if n == 3 && b[1] == ':' && b[2] == '\\' { + // Do nothing for path, like C:\. + } else if n > 0 && b[n-1] == '\\' { + // Otherwise remove terminating \. + n-- + } + return syscall.UTF16ToString(b[:n]) + } +} + +// Link creates newname as a hard link to the oldname file. +// If there is an error, it will be of type *LinkError. +func Link(oldname, newname string) error { + n, err := syscall.UTF16PtrFromString(fixLongPath(newname)) + if err != nil { + return &LinkError{"link", oldname, newname, err} + } + o, err := syscall.UTF16PtrFromString(fixLongPath(oldname)) + if err != nil { + return &LinkError{"link", oldname, newname, err} + } + err = syscall.CreateHardLink(n, o, 0) + if err != nil { + return &LinkError{"link", oldname, newname, err} + } + return nil +} + +// Symlink creates newname as a symbolic link to oldname. +// On Windows, a symlink to a non-existent oldname creates a file symlink; +// if oldname is later created as a directory the symlink will not work. +// If there is an error, it will be of type *LinkError. +func Symlink(oldname, newname string) error { + // '/' does not work in link's content + oldname = fromSlash(oldname) + + // need the exact location of the oldname when it's relative to determine if it's a directory + destpath := oldname + if v := volumeName(oldname); v == "" { + if len(oldname) > 0 && IsPathSeparator(oldname[0]) { + // oldname is relative to the volume containing newname. + if v = volumeName(newname); v != "" { + // Prepend the volume explicitly, because it may be different from the + // volume of the current working directory. + destpath = v + oldname + } + } else { + // oldname is relative to newname. + destpath = dirname(newname) + `\` + oldname + } + } + + fi, err := Stat(destpath) + isdir := err == nil && fi.IsDir() + + n, err := syscall.UTF16PtrFromString(fixLongPath(newname)) + if err != nil { + return &LinkError{"symlink", oldname, newname, err} + } + o, err := syscall.UTF16PtrFromString(fixLongPath(oldname)) + if err != nil { + return &LinkError{"symlink", oldname, newname, err} + } + + var flags uint32 = windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + if isdir { + flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY + } + err = syscall.CreateSymbolicLink(n, o, flags) + if err != nil { + // the unprivileged create flag is unsupported + // below Windows 10 (1703, v10.0.14972). retry without it. + flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + err = syscall.CreateSymbolicLink(n, o, flags) + if err != nil { + return &LinkError{"symlink", oldname, newname, err} + } + } + return nil +} + +// openSymlink calls CreateFile Windows API with FILE_FLAG_OPEN_REPARSE_POINT +// parameter, so that Windows does not follow symlink, if path is a symlink. +// openSymlink returns opened file handle. +func openSymlink(path string) (syscall.Handle, error) { + p, err := syscall.UTF16PtrFromString(path) + if err != nil { + return 0, err + } + attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) + // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. + // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted + attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT + h, err := syscall.CreateFile(p, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0) + if err != nil { + return 0, err + } + return h, nil +} + +// normaliseLinkPath converts absolute paths returned by +// DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, ...) +// into paths acceptable by all Windows APIs. +// For example, it converts +// +// \??\C:\foo\bar into C:\foo\bar +// \??\UNC\foo\bar into \\foo\bar +// \??\Volume{abc}\ into C:\ +func normaliseLinkPath(path string) (string, error) { + if len(path) < 4 || path[:4] != `\??\` { + // unexpected path, return it as is + return path, nil + } + // we have path that start with \??\ + s := path[4:] + switch { + case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar + return s, nil + case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar + return `\\` + s[4:], nil + } + + // handle paths, like \??\Volume{abc}\... + + h, err := openSymlink(path) + if err != nil { + return "", err + } + defer syscall.CloseHandle(h) + + buf := make([]uint16, 100) + for { + n, err := windows.GetFinalPathNameByHandle(h, &buf[0], uint32(len(buf)), windows.VOLUME_NAME_DOS) + if err != nil { + return "", err + } + if n < uint32(len(buf)) { + break + } + buf = make([]uint16, n) + } + s = syscall.UTF16ToString(buf) + if len(s) > 4 && s[:4] == `\\?\` { + s = s[4:] + if len(s) > 3 && s[:3] == `UNC` { + // return path like \\server\share\... + return `\` + s[3:], nil + } + return s, nil + } + return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s) +} + +func readReparseLink(path string) (string, error) { + h, err := openSymlink(path) + if err != nil { + return "", err + } + defer syscall.CloseHandle(h) + + rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) + var bytesReturned uint32 + err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) + if err != nil { + return "", err + } + + rdb := (*windows.REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0])) + switch rdb.ReparseTag { + case syscall.IO_REPARSE_TAG_SYMLINK: + rb := (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME)) + s := rb.Path() + if rb.Flags&windows.SYMLINK_FLAG_RELATIVE != 0 { + return s, nil + } + return normaliseLinkPath(s) + case windows.IO_REPARSE_TAG_MOUNT_POINT: + return normaliseLinkPath((*windows.MountPointReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME)).Path()) + default: + // the path is not a symlink or junction but another type of reparse + // point + return "", syscall.ENOENT + } +} + +func readlink(name string) (string, error) { + s, err := readReparseLink(fixLongPath(name)) + if err != nil { + return "", &PathError{Op: "readlink", Path: name, Err: err} + } + return s, nil +} diff --git a/contrib/go/_std_1.22/src/os/getwd.go b/contrib/go/_std_1.22/src/os/getwd.go new file mode 100644 index 0000000000..90604cf2f4 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/getwd.go @@ -0,0 +1,126 @@ +// 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. + +package os + +import ( + "runtime" + "sync" + "syscall" +) + +var getwdCache struct { + sync.Mutex + dir string +} + +// Getwd returns a rooted path name corresponding to the +// current directory. If the current directory can be +// reached via multiple paths (due to symbolic links), +// Getwd may return any one of them. +func Getwd() (dir string, err error) { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return syscall.Getwd() + } + + // Clumsy but widespread kludge: + // if $PWD is set and matches ".", use it. + dot, err := statNolog(".") + if err != nil { + return "", err + } + dir = Getenv("PWD") + if len(dir) > 0 && dir[0] == '/' { + d, err := statNolog(dir) + if err == nil && SameFile(dot, d) { + return dir, nil + } + } + + // If the operating system provides a Getwd call, use it. + // Otherwise, we're trying to find our way back to ".". + if syscall.ImplementsGetwd { + var ( + s string + e error + ) + for { + s, e = syscall.Getwd() + if e != syscall.EINTR { + break + } + } + return s, NewSyscallError("getwd", e) + } + + // Apply same kludge but to cached dir instead of $PWD. + getwdCache.Lock() + dir = getwdCache.dir + getwdCache.Unlock() + if len(dir) > 0 { + d, err := statNolog(dir) + if err == nil && SameFile(dot, d) { + return dir, nil + } + } + + // Root is a special case because it has no parent + // and ends in a slash. + root, err := statNolog("/") + if err != nil { + // Can't stat root - no hope. + return "", err + } + if SameFile(root, dot) { + return "/", nil + } + + // General algorithm: find name in parent + // and then find name of parent. Each iteration + // adds /name to the beginning of dir. + dir = "" + for parent := ".."; ; parent = "../" + parent { + if len(parent) >= 1024 { // Sanity check + return "", syscall.ENAMETOOLONG + } + fd, err := openFileNolog(parent, O_RDONLY, 0) + if err != nil { + return "", err + } + + for { + names, err := fd.Readdirnames(100) + if err != nil { + fd.Close() + return "", err + } + for _, name := range names { + d, _ := lstatNolog(parent + "/" + name) + if SameFile(d, dot) { + dir = "/" + name + dir + goto Found + } + } + } + + Found: + pd, err := fd.Stat() + fd.Close() + if err != nil { + return "", err + } + if SameFile(pd, root) { + break + } + // Set up for next round. + dot = pd + } + + // Save answer as hint to avoid the expensive path next time. + getwdCache.Lock() + getwdCache.dir = dir + getwdCache.Unlock() + + return dir, nil +} diff --git a/contrib/go/_std_1.22/src/os/path.go b/contrib/go/_std_1.22/src/os/path.go new file mode 100644 index 0000000000..6ac4cbe20f --- /dev/null +++ b/contrib/go/_std_1.22/src/os/path.go @@ -0,0 +1,85 @@ +// 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. + +package os + +import ( + "syscall" +) + +// MkdirAll creates a directory named path, +// along with any necessary parents, and returns nil, +// or else returns an error. +// The permission bits perm (before umask) are used for all +// directories that MkdirAll creates. +// If path is already a directory, MkdirAll does nothing +// and returns nil. +func MkdirAll(path string, perm FileMode) error { + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} + } + + // Slow path: make sure parent exists and then call Mkdir for path. + + // Extract the parent folder from path by first removing any trailing + // path separator and then scanning backward until finding a path + // separator or reaching the beginning of the string. + i := len(path) - 1 + for i >= 0 && IsPathSeparator(path[i]) { + i-- + } + for i >= 0 && !IsPathSeparator(path[i]) { + i-- + } + if i < 0 { + i = 0 + } + + // If there is a parent directory, and it is not the volume name, + // recurse to ensure parent directory exists. + if parent := path[:i]; len(parent) > len(volumeName(path)) { + err = MkdirAll(parent, perm) + if err != nil { + return err + } + } + + // Parent now exists; invoke Mkdir and use its result. + err = Mkdir(path, perm) + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +} + +// RemoveAll removes path and any children it contains. +// It removes everything it can but returns the first error +// it encounters. If the path does not exist, RemoveAll +// returns nil (no error). +// If there is an error, it will be of type *PathError. +func RemoveAll(path string) error { + return removeAll(path) +} + +// endsWithDot reports whether the final component of path is ".". +func endsWithDot(path string) bool { + if path == "." { + return true + } + if len(path) >= 2 && path[len(path)-1] == '.' && IsPathSeparator(path[len(path)-2]) { + return true + } + return false +} diff --git a/contrib/go/_std_1.22/src/os/path_unix.go b/contrib/go/_std_1.22/src/os/path_unix.go new file mode 100644 index 0000000000..1c80fa91f8 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/path_unix.go @@ -0,0 +1,75 @@ +// Copyright 2011 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. + +//go:build unix || (js && wasm) || wasip1 + +package os + +const ( + PathSeparator = '/' // OS-specific path separator + PathListSeparator = ':' // OS-specific path list separator +) + +// IsPathSeparator reports whether c is a directory separator character. +func IsPathSeparator(c uint8) bool { + return PathSeparator == c +} + +// basename removes trailing slashes and the leading directory name from path name. +func basename(name string) string { + i := len(name) - 1 + // Remove trailing slashes + for ; i > 0 && name[i] == '/'; i-- { + name = name[:i] + } + // Remove leading directory name + for i--; i >= 0; i-- { + if name[i] == '/' { + name = name[i+1:] + break + } + } + + return name +} + +// splitPath returns the base name and parent directory. +func splitPath(path string) (string, string) { + // if no better parent is found, the path is relative from "here" + dirname := "." + + // Remove all but one leading slash. + for len(path) > 1 && path[0] == '/' && path[1] == '/' { + path = path[1:] + } + + i := len(path) - 1 + + // Remove trailing slashes. + for ; i > 0 && path[i] == '/'; i-- { + path = path[:i] + } + + // if no slashes in path, base is path + basename := path + + // Remove leading directory path + for i--; i >= 0; i-- { + if path[i] == '/' { + if i == 0 { + dirname = path[:1] + } else { + dirname = path[:i] + } + basename = path[i+1:] + break + } + } + + return dirname, basename +} + +func volumeName(p string) string { + return "" +} diff --git a/contrib/go/_std_1.22/src/os/path_windows.go b/contrib/go/_std_1.22/src/os/path_windows.go new file mode 100644 index 0000000000..0522025148 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/path_windows.go @@ -0,0 +1,216 @@ +// Copyright 2011 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. + +package os + +const ( + PathSeparator = '\\' // OS-specific path separator + PathListSeparator = ';' // OS-specific path list separator +) + +// IsPathSeparator reports whether c is a directory separator character. +func IsPathSeparator(c uint8) bool { + // NOTE: Windows accepts / as path separator. + return c == '\\' || c == '/' +} + +// basename removes trailing slashes and the leading +// directory name and drive letter from path name. +func basename(name string) string { + // Remove drive letter + if len(name) == 2 && name[1] == ':' { + name = "." + } else if len(name) > 2 && name[1] == ':' { + name = name[2:] + } + i := len(name) - 1 + // Remove trailing slashes + for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- { + name = name[:i] + } + // Remove leading directory name + for i--; i >= 0; i-- { + if name[i] == '/' || name[i] == '\\' { + name = name[i+1:] + break + } + } + return name +} + +func isAbs(path string) (b bool) { + v := volumeName(path) + if v == "" { + return false + } + path = path[len(v):] + if path == "" { + return false + } + return IsPathSeparator(path[0]) +} + +func volumeName(path string) (v string) { + if len(path) < 2 { + return "" + } + // with drive letter + c := path[0] + if path[1] == ':' && + ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || + 'A' <= c && c <= 'Z') { + return path[:2] + } + // is it UNC + if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) && + !IsPathSeparator(path[2]) && path[2] != '.' { + // first, leading `\\` and next shouldn't be `\`. its server name. + for n := 3; n < l-1; n++ { + // second, next '\' shouldn't be repeated. + if IsPathSeparator(path[n]) { + n++ + // third, following something characters. its share name. + if !IsPathSeparator(path[n]) { + if path[n] == '.' { + break + } + for ; n < l; n++ { + if IsPathSeparator(path[n]) { + break + } + } + return path[:n] + } + break + } + } + } + return "" +} + +func fromSlash(path string) string { + // Replace each '/' with '\\' if present + var pathbuf []byte + var lastSlash int + for i, b := range path { + if b == '/' { + if pathbuf == nil { + pathbuf = make([]byte, len(path)) + } + copy(pathbuf[lastSlash:], path[lastSlash:i]) + pathbuf[i] = '\\' + lastSlash = i + 1 + } + } + if pathbuf == nil { + return path + } + + copy(pathbuf[lastSlash:], path[lastSlash:]) + return string(pathbuf) +} + +func dirname(path string) string { + vol := volumeName(path) + i := len(path) - 1 + for i >= len(vol) && !IsPathSeparator(path[i]) { + i-- + } + dir := path[len(vol) : i+1] + last := len(dir) - 1 + if last > 0 && IsPathSeparator(dir[last]) { + dir = dir[:last] + } + if dir == "" { + dir = "." + } + return vol + dir +} + +// This is set via go:linkname on runtime.canUseLongPaths, and is true when the OS +// supports opting into proper long path handling without the need for fixups. +var canUseLongPaths bool + +// fixLongPath returns the extended-length (\\?\-prefixed) form of +// path when needed, in order to avoid the default 260 character file +// path limit imposed by Windows. If path is not easily converted to +// the extended-length form (for example, if path is a relative path +// or contains .. elements), or is short enough, fixLongPath returns +// path unmodified. +// +// See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation +func fixLongPath(path string) string { + if canUseLongPaths { + return path + } + // Do nothing (and don't allocate) if the path is "short". + // Empirically (at least on the Windows Server 2013 builder), + // the kernel is arbitrarily okay with < 248 bytes. That + // matches what the docs above say: + // "When using an API to create a directory, the specified + // path cannot be so long that you cannot append an 8.3 file + // name (that is, the directory name cannot exceed MAX_PATH + // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. + // + // The MSDN docs appear to say that a normal path that is 248 bytes long + // will work; empirically the path must be less then 248 bytes long. + if len(path) < 248 { + // Don't fix. (This is how Go 1.7 and earlier worked, + // not automatically generating the \\?\ form) + return path + } + + // The extended form begins with \\?\, as in + // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. + // The extended form disables evaluation of . and .. path + // elements and disables the interpretation of / as equivalent + // to \. The conversion here rewrites / to \ and elides + // . elements as well as trailing or duplicate separators. For + // simplicity it avoids the conversion entirely for relative + // paths or paths containing .. elements. For now, + // \\server\share paths are not converted to + // \\?\UNC\server\share paths because the rules for doing so + // are less well-specified. + if len(path) >= 2 && path[:2] == `\\` { + // Don't canonicalize UNC paths. + return path + } + if !isAbs(path) { + // Relative path + return path + } + + const prefix = `\\?` + + pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) + copy(pathbuf, prefix) + n := len(path) + r, w := 0, len(prefix) + for r < n { + switch { + case IsPathSeparator(path[r]): + // empty block + r++ + case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])): + // /./ + r++ + case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])): + // /../ is currently unhandled + return path + default: + pathbuf[w] = '\\' + w++ + for ; r < n && !IsPathSeparator(path[r]); r++ { + pathbuf[w] = path[r] + w++ + } + } + } + // A drive's root directory needs a trailing \ + if w == len(`\\?\c:`) { + pathbuf[w] = '\\' + w++ + } + return string(pathbuf[:w]) +} diff --git a/contrib/go/_std_1.22/src/os/pipe2_unix.go b/contrib/go/_std_1.22/src/os/pipe2_unix.go new file mode 100644 index 0000000000..2d293fdb4d --- /dev/null +++ b/contrib/go/_std_1.22/src/os/pipe2_unix.go @@ -0,0 +1,22 @@ +// Copyright 2017 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. + +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC) + if e != nil { + return nil, nil, NewSyscallError("pipe2", e) + } + + return newFile(p[0], "|0", kindPipe), newFile(p[1], "|1", kindPipe), nil +} diff --git a/contrib/go/_std_1.22/src/os/pipe_unix.go b/contrib/go/_std_1.22/src/os/pipe_unix.go new file mode 100644 index 0000000000..2eb11a04cb --- /dev/null +++ b/contrib/go/_std_1.22/src/os/pipe_unix.go @@ -0,0 +1,28 @@ +// 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. + +//go:build aix || darwin + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if e != nil { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return newFile(p[0], "|0", kindPipe), newFile(p[1], "|1", kindPipe), nil +} diff --git a/contrib/go/_std_1.22/src/os/proc.go b/contrib/go/_std_1.22/src/os/proc.go new file mode 100644 index 0000000000..3aae5680ee --- /dev/null +++ b/contrib/go/_std_1.22/src/os/proc.go @@ -0,0 +1,80 @@ +// 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. + +// Process etc. + +package os + +import ( + "internal/testlog" + "runtime" + "syscall" +) + +// Args hold the command-line arguments, starting with the program name. +var Args []string + +func init() { + if runtime.GOOS == "windows" { + // Initialized in exec_windows.go. + return + } + Args = runtime_args() +} + +func runtime_args() []string // in package runtime + +// Getuid returns the numeric user id of the caller. +// +// On Windows, it returns -1. +func Getuid() int { return syscall.Getuid() } + +// Geteuid returns the numeric effective user id of the caller. +// +// On Windows, it returns -1. +func Geteuid() int { return syscall.Geteuid() } + +// Getgid returns the numeric group id of the caller. +// +// On Windows, it returns -1. +func Getgid() int { return syscall.Getgid() } + +// Getegid returns the numeric effective group id of the caller. +// +// On Windows, it returns -1. +func Getegid() int { return syscall.Getegid() } + +// Getgroups returns a list of the numeric ids of groups that the caller belongs to. +// +// On Windows, it returns syscall.EWINDOWS. See the os/user package +// for a possible alternative. +func Getgroups() ([]int, error) { + gids, e := syscall.Getgroups() + return gids, NewSyscallError("getgroups", e) +} + +// Exit causes the current program to exit with the given status code. +// Conventionally, code zero indicates success, non-zero an error. +// The program terminates immediately; deferred functions are not run. +// +// For portability, the status code should be in the range [0, 125]. +func Exit(code int) { + if code == 0 && testlog.PanicOnExit0() { + // We were told to panic on calls to os.Exit(0). + // This is used to fail tests that make an early + // unexpected call to os.Exit(0). + panic("unexpected call to os.Exit(0) during test") + } + + // Inform the runtime that os.Exit is being called. If -race is + // enabled, this will give race detector a chance to fail the + // program (racy programs do not have the right to finish + // successfully). If coverage is enabled, then this call will + // enable us to write out a coverage data file. + runtime_beforeExit(code) + + syscall.Exit(code) +} + +func runtime_beforeExit(exitCode int) // implemented in runtime diff --git a/contrib/go/_std_1.22/src/os/rawconn.go b/contrib/go/_std_1.22/src/os/rawconn.go new file mode 100644 index 0000000000..14a495d9c0 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/rawconn.go @@ -0,0 +1,47 @@ +// Copyright 2018 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. + +//go:build !plan9 + +package os + +import ( + "runtime" +) + +// rawConn implements syscall.RawConn. +type rawConn struct { + file *File +} + +func (c *rawConn) Control(f func(uintptr)) error { + if err := c.file.checkValid("SyscallConn.Control"); err != nil { + return err + } + err := c.file.pfd.RawControl(f) + runtime.KeepAlive(c.file) + return err +} + +func (c *rawConn) Read(f func(uintptr) bool) error { + if err := c.file.checkValid("SyscallConn.Read"); err != nil { + return err + } + err := c.file.pfd.RawRead(f) + runtime.KeepAlive(c.file) + return err +} + +func (c *rawConn) Write(f func(uintptr) bool) error { + if err := c.file.checkValid("SyscallConn.Write"); err != nil { + return err + } + err := c.file.pfd.RawWrite(f) + runtime.KeepAlive(c.file) + return err +} + +func newRawConn(file *File) (*rawConn, error) { + return &rawConn{file: file}, nil +} diff --git a/contrib/go/_std_1.22/src/os/removeall_at.go b/contrib/go/_std_1.22/src/os/removeall_at.go new file mode 100644 index 0000000000..8ea5df4117 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/removeall_at.go @@ -0,0 +1,199 @@ +// Copyright 2018 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. + +//go:build unix + +package os + +import ( + "internal/syscall/unix" + "io" + "syscall" +) + +func removeAll(path string) error { + if path == "" { + // fail silently to retain compatibility with previous behavior + // of RemoveAll. See issue 28830. + return nil + } + + // The rmdir system call does not permit removing ".", + // so we don't permit it either. + if endsWithDot(path) { + return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL} + } + + // Simple case: if Remove works, we're done. + err := Remove(path) + if err == nil || IsNotExist(err) { + return nil + } + + // RemoveAll recurses by deleting the path base from + // its parent directory + parentDir, base := splitPath(path) + + parent, err := Open(parentDir) + if IsNotExist(err) { + // If parent does not exist, base cannot exist. Fail silently + return nil + } + if err != nil { + return err + } + defer parent.Close() + + if err := removeAllFrom(parent, base); err != nil { + if pathErr, ok := err.(*PathError); ok { + pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path + err = pathErr + } + return err + } + return nil +} + +func removeAllFrom(parent *File, base string) error { + parentFd := int(parent.Fd()) + // Simple case: if Unlink (aka remove) works, we're done. + err := ignoringEINTR(func() error { + return unix.Unlinkat(parentFd, base, 0) + }) + if err == nil || IsNotExist(err) { + return nil + } + + // EISDIR means that we have a directory, and we need to + // remove its contents. + // EPERM or EACCES means that we don't have write permission on + // the parent directory, but this entry might still be a directory + // whose contents need to be removed. + // Otherwise just return the error. + if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES { + return &PathError{Op: "unlinkat", Path: base, Err: err} + } + + // Is this a directory we need to recurse into? + var statInfo syscall.Stat_t + statErr := ignoringEINTR(func() error { + return unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW) + }) + if statErr != nil { + if IsNotExist(statErr) { + return nil + } + return &PathError{Op: "fstatat", Path: base, Err: statErr} + } + if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR { + // Not a directory; return the error from the unix.Unlinkat. + return &PathError{Op: "unlinkat", Path: base, Err: err} + } + + // Remove the directory's entries. + var recurseErr error + for { + const reqSize = 1024 + var respSize int + + // Open the directory to recurse into + file, err := openFdAt(parentFd, base) + if err != nil { + if IsNotExist(err) { + return nil + } + recurseErr = &PathError{Op: "openfdat", Path: base, Err: err} + break + } + + for { + numErr := 0 + + names, readErr := file.Readdirnames(reqSize) + // Errors other than EOF should stop us from continuing. + if readErr != nil && readErr != io.EOF { + file.Close() + if IsNotExist(readErr) { + return nil + } + return &PathError{Op: "readdirnames", Path: base, Err: readErr} + } + + respSize = len(names) + for _, name := range names { + err := removeAllFrom(file, name) + if err != nil { + if pathErr, ok := err.(*PathError); ok { + pathErr.Path = base + string(PathSeparator) + pathErr.Path + } + numErr++ + if recurseErr == nil { + recurseErr = err + } + } + } + + // If we can delete any entry, break to start new iteration. + // Otherwise, we discard current names, get next entries and try deleting them. + if numErr != reqSize { + break + } + } + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + file.Close() + + // Finish when the end of the directory is reached + if respSize < reqSize { + break + } + } + + // Remove the directory itself. + unlinkError := ignoringEINTR(func() error { + return unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR) + }) + if unlinkError == nil || IsNotExist(unlinkError) { + return nil + } + + if recurseErr != nil { + return recurseErr + } + return &PathError{Op: "unlinkat", Path: base, Err: unlinkError} +} + +// openFdAt opens path relative to the directory in fd. +// Other than that this should act like openFileNolog. +// This acts like openFileNolog rather than OpenFile because +// we are going to (try to) remove the file. +// The contents of this file are not relevant for test caching. +func openFdAt(dirfd int, name string) (*File, error) { + var r int + for { + var e error + r, e = unix.Openat(dirfd, name, O_RDONLY|syscall.O_CLOEXEC, 0) + if e == nil { + break + } + + // See comment in openFileNolog. + if e == syscall.EINTR { + continue + } + + return nil, e + } + + if !supportsCloseOnExec { + syscall.CloseOnExec(r) + } + + // We use kindNoPoll because we know that this is a directory. + return newFile(r, name, kindNoPoll), nil +} diff --git a/contrib/go/_std_1.22/src/os/removeall_noat.go b/contrib/go/_std_1.22/src/os/removeall_noat.go new file mode 100644 index 0000000000..2b8a7727f4 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/removeall_noat.go @@ -0,0 +1,142 @@ +// Copyright 2018 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. + +//go:build !unix + +package os + +import ( + "io" + "runtime" + "syscall" +) + +func removeAll(path string) error { + if path == "" { + // fail silently to retain compatibility with previous behavior + // of RemoveAll. See issue 28830. + return nil + } + + // The rmdir system call permits removing "." on Plan 9, + // so we don't permit it to remain consistent with the + // "at" implementation of RemoveAll. + if endsWithDot(path) { + return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL} + } + + // Simple case: if Remove works, we're done. + err := Remove(path) + if err == nil || IsNotExist(err) { + return nil + } + + // Otherwise, is this a directory we need to recurse into? + dir, serr := Lstat(path) + if serr != nil { + if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { + return nil + } + return serr + } + if !dir.IsDir() { + // Not a directory; return the error from Remove. + return err + } + + // Remove contents & return first error. + err = nil + for { + fd, err := Open(path) + if err != nil { + if IsNotExist(err) { + // Already deleted by someone else. + return nil + } + return err + } + + const reqSize = 1024 + var names []string + var readErr error + + for { + numErr := 0 + names, readErr = fd.Readdirnames(reqSize) + + for _, name := range names { + err1 := RemoveAll(path + string(PathSeparator) + name) + if err == nil { + err = err1 + } + if err1 != nil { + numErr++ + } + } + + // If we can delete any entry, break to start new iteration. + // Otherwise, we discard current names, get next entries and try deleting them. + if numErr != reqSize { + break + } + } + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + fd.Close() + + if readErr == io.EOF { + break + } + // If Readdirnames returned an error, use it. + if err == nil { + err = readErr + } + if len(names) == 0 { + break + } + + // We don't want to re-open unnecessarily, so if we + // got fewer than request names from Readdirnames, try + // simply removing the directory now. If that + // succeeds, we are done. + if len(names) < reqSize { + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + + if err != nil { + // We got some error removing the + // directory contents, and since we + // read fewer names than we requested + // there probably aren't more files to + // remove. Don't loop around to read + // the directory again. We'll probably + // just get the same error. + return err + } + } + } + + // Remove directory. + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + if runtime.GOOS == "windows" && IsPermission(err1) { + if fs, err := Stat(path); err == nil { + if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil { + err1 = Remove(path) + } + } + } + if err == nil { + err = err1 + } + return err +} diff --git a/contrib/go/_std_1.22/src/os/signal/doc.go b/contrib/go/_std_1.22/src/os/signal/doc.go new file mode 100644 index 0000000000..a2a7525ef0 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/signal/doc.go @@ -0,0 +1,232 @@ +// Copyright 2015 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. + +/* +Package signal implements access to incoming signals. + +Signals are primarily used on Unix-like systems. For the use of this +package on Windows and Plan 9, see below. + +# Types of signals + +The signals SIGKILL and SIGSTOP may not be caught by a program, and +therefore cannot be affected by this package. + +Synchronous signals are signals triggered by errors in program +execution: SIGBUS, SIGFPE, and SIGSEGV. These are only considered +synchronous when caused by program execution, not when sent using +[os.Process.Kill] or the kill program or some similar mechanism. In +general, except as discussed below, Go programs will convert a +synchronous signal into a run-time panic. + +The remaining signals are asynchronous signals. They are not +triggered by program errors, but are instead sent from the kernel or +from some other program. + +Of the asynchronous signals, the SIGHUP signal is sent when a program +loses its controlling terminal. The SIGINT signal is sent when the +user at the controlling terminal presses the interrupt character, +which by default is ^C (Control-C). The SIGQUIT signal is sent when +the user at the controlling terminal presses the quit character, which +by default is ^\ (Control-Backslash). In general you can cause a +program to simply exit by pressing ^C, and you can cause it to exit +with a stack dump by pressing ^\. + +# Default behavior of signals in Go programs + +By default, a synchronous signal is converted into a run-time panic. A +SIGHUP, SIGINT, or SIGTERM signal causes the program to exit. A +SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGSTKFLT, SIGEMT, or SIGSYS signal +causes the program to exit with a stack dump. A SIGTSTP, SIGTTIN, or +SIGTTOU signal gets the system default behavior (these signals are +used by the shell for job control). The SIGPROF signal is handled +directly by the Go runtime to implement runtime.CPUProfile. Other +signals will be caught but no action will be taken. + +If the Go program is started with either SIGHUP or SIGINT ignored +(signal handler set to SIG_IGN), they will remain ignored. + +If the Go program is started with a non-empty signal mask, that will +generally be honored. However, some signals are explicitly unblocked: +the synchronous signals, SIGILL, SIGTRAP, SIGSTKFLT, SIGCHLD, SIGPROF, +and, on Linux, signals 32 (SIGCANCEL) and 33 (SIGSETXID) +(SIGCANCEL and SIGSETXID are used internally by glibc). Subprocesses +started by [os.Exec], or by [os/exec], will inherit the +modified signal mask. + +# Changing the behavior of signals in Go programs + +The functions in this package allow a program to change the way Go +programs handle signals. + +Notify disables the default behavior for a given set of asynchronous +signals and instead delivers them over one or more registered +channels. Specifically, it applies to the signals SIGHUP, SIGINT, +SIGQUIT, SIGABRT, and SIGTERM. It also applies to the job control +signals SIGTSTP, SIGTTIN, and SIGTTOU, in which case the system +default behavior does not occur. It also applies to some signals that +otherwise cause no action: SIGUSR1, SIGUSR2, SIGPIPE, SIGALRM, +SIGCHLD, SIGCONT, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGWINCH, +SIGIO, SIGPWR, SIGSYS, SIGINFO, SIGTHR, SIGWAITING, SIGLWP, SIGFREEZE, +SIGTHAW, SIGLOST, SIGXRES, SIGJVM1, SIGJVM2, and any real time signals +used on the system. Note that not all of these signals are available +on all systems. + +If the program was started with SIGHUP or SIGINT ignored, and Notify +is called for either signal, a signal handler will be installed for +that signal and it will no longer be ignored. If, later, Reset or +Ignore is called for that signal, or Stop is called on all channels +passed to Notify for that signal, the signal will once again be +ignored. Reset will restore the system default behavior for the +signal, while Ignore will cause the system to ignore the signal +entirely. + +If the program is started with a non-empty signal mask, some signals +will be explicitly unblocked as described above. If Notify is called +for a blocked signal, it will be unblocked. If, later, Reset is +called for that signal, or Stop is called on all channels passed to +Notify for that signal, the signal will once again be blocked. + +# SIGPIPE + +When a Go program writes to a broken pipe, the kernel will raise a +SIGPIPE signal. + +If the program has not called Notify to receive SIGPIPE signals, then +the behavior depends on the file descriptor number. A write to a +broken pipe on file descriptors 1 or 2 (standard output or standard +error) will cause the program to exit with a SIGPIPE signal. A write +to a broken pipe on some other file descriptor will take no action on +the SIGPIPE signal, and the write will fail with an EPIPE error. + +If the program has called Notify to receive SIGPIPE signals, the file +descriptor number does not matter. The SIGPIPE signal will be +delivered to the Notify channel, and the write will fail with an EPIPE +error. + +This means that, by default, command line programs will behave like +typical Unix command line programs, while other programs will not +crash with SIGPIPE when writing to a closed network connection. + +# Go programs that use cgo or SWIG + +In a Go program that includes non-Go code, typically C/C++ code +accessed using cgo or SWIG, Go's startup code normally runs first. It +configures the signal handlers as expected by the Go runtime, before +the non-Go startup code runs. If the non-Go startup code wishes to +install its own signal handlers, it must take certain steps to keep Go +working well. This section documents those steps and the overall +effect changes to signal handler settings by the non-Go code can have +on Go programs. In rare cases, the non-Go code may run before the Go +code, in which case the next section also applies. + +If the non-Go code called by the Go program does not change any signal +handlers or masks, then the behavior is the same as for a pure Go +program. + +If the non-Go code installs any signal handlers, it must use the +SA_ONSTACK flag with sigaction. Failing to do so is likely to cause +the program to crash if the signal is received. Go programs routinely +run with a limited stack, and therefore set up an alternate signal +stack. + +If the non-Go code installs a signal handler for any of the +synchronous signals (SIGBUS, SIGFPE, SIGSEGV), then it should record +the existing Go signal handler. If those signals occur while +executing Go code, it should invoke the Go signal handler (whether the +signal occurs while executing Go code can be determined by looking at +the PC passed to the signal handler). Otherwise some Go run-time +panics will not occur as expected. + +If the non-Go code installs a signal handler for any of the +asynchronous signals, it may invoke the Go signal handler or not as it +chooses. Naturally, if it does not invoke the Go signal handler, the +Go behavior described above will not occur. This can be an issue with +the SIGPROF signal in particular. + +The non-Go code should not change the signal mask on any threads +created by the Go runtime. If the non-Go code starts new threads of +its own, it may set the signal mask as it pleases. + +If the non-Go code starts a new thread, changes the signal mask, and +then invokes a Go function in that thread, the Go runtime will +automatically unblock certain signals: the synchronous signals, +SIGILL, SIGTRAP, SIGSTKFLT, SIGCHLD, SIGPROF, SIGCANCEL, and +SIGSETXID. When the Go function returns, the non-Go signal mask will +be restored. + +If the Go signal handler is invoked on a non-Go thread not running Go +code, the handler generally forwards the signal to the non-Go code, as +follows. If the signal is SIGPROF, the Go handler does +nothing. Otherwise, the Go handler removes itself, unblocks the +signal, and raises it again, to invoke any non-Go handler or default +system handler. If the program does not exit, the Go handler then +reinstalls itself and continues execution of the program. + +If a SIGPIPE signal is received, the Go program will invoke the +special handling described above if the SIGPIPE is received on a Go +thread. If the SIGPIPE is received on a non-Go thread the signal will +be forwarded to the non-Go handler, if any; if there is none the +default system handler will cause the program to terminate. + +# Non-Go programs that call Go code + +When Go code is built with options like -buildmode=c-shared, it will +be run as part of an existing non-Go program. The non-Go code may +have already installed signal handlers when the Go code starts (that +may also happen in unusual cases when using cgo or SWIG; in that case, +the discussion here applies). For -buildmode=c-archive the Go runtime +will initialize signals at global constructor time. For +-buildmode=c-shared the Go runtime will initialize signals when the +shared library is loaded. + +If the Go runtime sees an existing signal handler for the SIGCANCEL or +SIGSETXID signals (which are used only on Linux), it will turn on +the SA_ONSTACK flag and otherwise keep the signal handler. + +For the synchronous signals and SIGPIPE, the Go runtime will install a +signal handler. It will save any existing signal handler. If a +synchronous signal arrives while executing non-Go code, the Go runtime +will invoke the existing signal handler instead of the Go signal +handler. + +Go code built with -buildmode=c-archive or -buildmode=c-shared will +not install any other signal handlers by default. If there is an +existing signal handler, the Go runtime will turn on the SA_ONSTACK +flag and otherwise keep the signal handler. If Notify is called for an +asynchronous signal, a Go signal handler will be installed for that +signal. If, later, Reset is called for that signal, the original +handling for that signal will be reinstalled, restoring the non-Go +signal handler if any. + +Go code built without -buildmode=c-archive or -buildmode=c-shared will +install a signal handler for the asynchronous signals listed above, +and save any existing signal handler. If a signal is delivered to a +non-Go thread, it will act as described above, except that if there is +an existing non-Go signal handler, that handler will be installed +before raising the signal. + +# Windows + +On Windows a ^C (Control-C) or ^BREAK (Control-Break) normally cause +the program to exit. If Notify is called for [os.Interrupt], ^C or ^BREAK +will cause [os.Interrupt] to be sent on the channel, and the program will +not exit. If Reset is called, or Stop is called on all channels passed +to Notify, then the default behavior will be restored. + +Additionally, if Notify is called, and Windows sends CTRL_CLOSE_EVENT, +CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT to the process, Notify will +return syscall.SIGTERM. Unlike Control-C and Control-Break, Notify does +not change process behavior when either CTRL_CLOSE_EVENT, +CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT is received - the process will +still get terminated unless it exits. But receiving syscall.SIGTERM will +give the process an opportunity to clean up before termination. + +# Plan 9 + +On Plan 9, signals have type syscall.Note, which is a string. Calling +Notify with a syscall.Note will cause that value to be sent on the +channel when that string is posted as a note. +*/ +package signal diff --git a/contrib/go/_std_1.22/src/os/signal/sig.s b/contrib/go/_std_1.22/src/os/signal/sig.s new file mode 100644 index 0000000000..12833a8934 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/signal/sig.s @@ -0,0 +1,8 @@ +// Copyright 2012 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. + +// The runtime package uses //go:linkname to push a few functions into this +// package but we still need a .s file so the Go tool does not pass -complete +// to the go tool compile so the latter does not complain about Go functions +// with no bodies. diff --git a/contrib/go/_std_1.22/src/os/signal/signal.go b/contrib/go/_std_1.22/src/os/signal/signal.go new file mode 100644 index 0000000000..4250a7e0de --- /dev/null +++ b/contrib/go/_std_1.22/src/os/signal/signal.go @@ -0,0 +1,334 @@ +// Copyright 2012 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. + +package signal + +import ( + "context" + "os" + "sync" +) + +var handlers struct { + sync.Mutex + // Map a channel to the signals that should be sent to it. + m map[chan<- os.Signal]*handler + // Map a signal to the number of channels receiving it. + ref [numSig]int64 + // Map channels to signals while the channel is being stopped. + // Not a map because entries live here only very briefly. + // We need a separate container because we need m to correspond to ref + // at all times, and we also need to keep track of the *handler + // value for a channel being stopped. See the Stop function. + stopping []stopping +} + +type stopping struct { + c chan<- os.Signal + h *handler +} + +type handler struct { + mask [(numSig + 31) / 32]uint32 +} + +func (h *handler) want(sig int) bool { + return (h.mask[sig/32]>>uint(sig&31))&1 != 0 +} + +func (h *handler) set(sig int) { + h.mask[sig/32] |= 1 << uint(sig&31) +} + +func (h *handler) clear(sig int) { + h.mask[sig/32] &^= 1 << uint(sig&31) +} + +// Stop relaying the signals, sigs, to any channels previously registered to +// receive them and either reset the signal handlers to their original values +// (action=disableSignal) or ignore the signals (action=ignoreSignal). +func cancel(sigs []os.Signal, action func(int)) { + handlers.Lock() + defer handlers.Unlock() + + remove := func(n int) { + var zerohandler handler + + for c, h := range handlers.m { + if h.want(n) { + handlers.ref[n]-- + h.clear(n) + if h.mask == zerohandler.mask { + delete(handlers.m, c) + } + } + } + + action(n) + } + + if len(sigs) == 0 { + for n := 0; n < numSig; n++ { + remove(n) + } + } else { + for _, s := range sigs { + remove(signum(s)) + } + } +} + +// Ignore causes the provided signals to be ignored. If they are received by +// the program, nothing will happen. Ignore undoes the effect of any prior +// calls to Notify for the provided signals. +// If no signals are provided, all incoming signals will be ignored. +func Ignore(sig ...os.Signal) { + cancel(sig, ignoreSignal) +} + +// Ignored reports whether sig is currently ignored. +func Ignored(sig os.Signal) bool { + sn := signum(sig) + return sn >= 0 && signalIgnored(sn) +} + +var ( + // watchSignalLoopOnce guards calling the conditionally + // initialized watchSignalLoop. If watchSignalLoop is non-nil, + // it will be run in a goroutine lazily once Notify is invoked. + // See Issue 21576. + watchSignalLoopOnce sync.Once + watchSignalLoop func() +) + +// Notify causes package signal to relay incoming signals to c. +// If no signals are provided, all incoming signals will be relayed to c. +// Otherwise, just the provided signals will. +// +// Package signal will not block sending to c: the caller must ensure +// that c has sufficient buffer space to keep up with the expected +// signal rate. For a channel used for notification of just one signal value, +// a buffer of size 1 is sufficient. +// +// It is allowed to call Notify multiple times with the same channel: +// each call expands the set of signals sent to that channel. +// The only way to remove signals from the set is to call Stop. +// +// It is allowed to call Notify multiple times with different channels +// and the same signals: each channel receives copies of incoming +// signals independently. +func Notify(c chan<- os.Signal, sig ...os.Signal) { + if c == nil { + panic("os/signal: Notify using nil channel") + } + + handlers.Lock() + defer handlers.Unlock() + + h := handlers.m[c] + if h == nil { + if handlers.m == nil { + handlers.m = make(map[chan<- os.Signal]*handler) + } + h = new(handler) + handlers.m[c] = h + } + + add := func(n int) { + if n < 0 { + return + } + if !h.want(n) { + h.set(n) + if handlers.ref[n] == 0 { + enableSignal(n) + + // The runtime requires that we enable a + // signal before starting the watcher. + watchSignalLoopOnce.Do(func() { + if watchSignalLoop != nil { + go watchSignalLoop() + } + }) + } + handlers.ref[n]++ + } + } + + if len(sig) == 0 { + for n := 0; n < numSig; n++ { + add(n) + } + } else { + for _, s := range sig { + add(signum(s)) + } + } +} + +// Reset undoes the effect of any prior calls to Notify for the provided +// signals. +// If no signals are provided, all signal handlers will be reset. +func Reset(sig ...os.Signal) { + cancel(sig, disableSignal) +} + +// Stop causes package signal to stop relaying incoming signals to c. +// It undoes the effect of all prior calls to Notify using c. +// When Stop returns, it is guaranteed that c will receive no more signals. +func Stop(c chan<- os.Signal) { + handlers.Lock() + + h := handlers.m[c] + if h == nil { + handlers.Unlock() + return + } + delete(handlers.m, c) + + for n := 0; n < numSig; n++ { + if h.want(n) { + handlers.ref[n]-- + if handlers.ref[n] == 0 { + disableSignal(n) + } + } + } + + // Signals will no longer be delivered to the channel. + // We want to avoid a race for a signal such as SIGINT: + // it should be either delivered to the channel, + // or the program should take the default action (that is, exit). + // To avoid the possibility that the signal is delivered, + // and the signal handler invoked, and then Stop deregisters + // the channel before the process function below has a chance + // to send it on the channel, put the channel on a list of + // channels being stopped and wait for signal delivery to + // quiesce before fully removing it. + + handlers.stopping = append(handlers.stopping, stopping{c, h}) + + handlers.Unlock() + + signalWaitUntilIdle() + + handlers.Lock() + + for i, s := range handlers.stopping { + if s.c == c { + handlers.stopping = append(handlers.stopping[:i], handlers.stopping[i+1:]...) + break + } + } + + handlers.Unlock() +} + +// Wait until there are no more signals waiting to be delivered. +// Defined by the runtime package. +func signalWaitUntilIdle() + +func process(sig os.Signal) { + n := signum(sig) + if n < 0 { + return + } + + handlers.Lock() + defer handlers.Unlock() + + for c, h := range handlers.m { + if h.want(n) { + // send but do not block for it + select { + case c <- sig: + default: + } + } + } + + // Avoid the race mentioned in Stop. + for _, d := range handlers.stopping { + if d.h.want(n) { + select { + case d.c <- sig: + default: + } + } + } +} + +// NotifyContext returns a copy of the parent context that is marked done +// (its Done channel is closed) when one of the listed signals arrives, +// when the returned stop function is called, or when the parent context's +// Done channel is closed, whichever happens first. +// +// The stop function unregisters the signal behavior, which, like signal.Reset, +// may restore the default behavior for a given signal. For example, the default +// behavior of a Go program receiving os.Interrupt is to exit. Calling +// NotifyContext(parent, os.Interrupt) will change the behavior to cancel +// the returned context. Future interrupts received will not trigger the default +// (exit) behavior until the returned stop function is called. +// +// The stop function releases resources associated with it, so code should +// call stop as soon as the operations running in this Context complete and +// signals no longer need to be diverted to the context. +func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) { + ctx, cancel := context.WithCancel(parent) + c := &signalCtx{ + Context: ctx, + cancel: cancel, + signals: signals, + } + c.ch = make(chan os.Signal, 1) + Notify(c.ch, c.signals...) + if ctx.Err() == nil { + go func() { + select { + case <-c.ch: + c.cancel() + case <-c.Done(): + } + }() + } + return c, c.stop +} + +type signalCtx struct { + context.Context + + cancel context.CancelFunc + signals []os.Signal + ch chan os.Signal +} + +func (c *signalCtx) stop() { + c.cancel() + Stop(c.ch) +} + +type stringer interface { + String() string +} + +func (c *signalCtx) String() string { + var buf []byte + // We know that the type of c.Context is context.cancelCtx, and we know that the + // String method of cancelCtx returns a string that ends with ".WithCancel". + name := c.Context.(stringer).String() + name = name[:len(name)-len(".WithCancel")] + buf = append(buf, "signal.NotifyContext("+name...) + if len(c.signals) != 0 { + buf = append(buf, ", ["...) + for i, s := range c.signals { + buf = append(buf, s.String()...) + if i != len(c.signals)-1 { + buf = append(buf, ' ') + } + } + buf = append(buf, ']') + } + buf = append(buf, ')') + return string(buf) +} diff --git a/contrib/go/_std_1.22/src/os/signal/signal_unix.go b/contrib/go/_std_1.22/src/os/signal/signal_unix.go new file mode 100644 index 0000000000..21dfa41691 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/signal/signal_unix.go @@ -0,0 +1,62 @@ +// Copyright 2012 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. + +//go:build unix || (js && wasm) || wasip1 || windows + +package signal + +import ( + "os" + "syscall" +) + +// Defined by the runtime package. +func signal_disable(uint32) +func signal_enable(uint32) +func signal_ignore(uint32) +func signal_ignored(uint32) bool +func signal_recv() uint32 + +func loop() { + for { + process(syscall.Signal(signal_recv())) + } +} + +func init() { + watchSignalLoop = loop +} + +const ( + numSig = 65 // max across all systems +) + +func signum(sig os.Signal) int { + switch sig := sig.(type) { + case syscall.Signal: + i := int(sig) + if i < 0 || i >= numSig { + return -1 + } + return i + default: + return -1 + } +} + +func enableSignal(sig int) { + signal_enable(uint32(sig)) +} + +func disableSignal(sig int) { + signal_disable(uint32(sig)) +} + +func ignoreSignal(sig int) { + signal_ignore(uint32(sig)) +} + +func signalIgnored(sig int) bool { + return signal_ignored(uint32(sig)) +} diff --git a/contrib/go/_std_1.22/src/os/signal/ya.make b/contrib/go/_std_1.22/src/os/signal/ya.make new file mode 100644 index 0000000000..52b33175f6 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/signal/ya.make @@ -0,0 +1,10 @@ +GO_LIBRARY() +IF (TRUE) + SRCS( + doc.go + sig.s + signal.go + signal_unix.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/os/stat.go b/contrib/go/_std_1.22/src/os/stat.go new file mode 100644 index 0000000000..11d9efa457 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/stat.go @@ -0,0 +1,27 @@ +// Copyright 2017 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. + +package os + +import "internal/testlog" + +// Stat returns a FileInfo describing the named file. +// If there is an error, it will be of type *PathError. +func Stat(name string) (FileInfo, error) { + testlog.Stat(name) + return statNolog(name) +} + +// Lstat returns a FileInfo describing the named file. +// If the file is a symbolic link, the returned FileInfo +// describes the symbolic link. Lstat makes no attempt to follow the link. +// If there is an error, it will be of type *PathError. +// +// On Windows, if the file is a reparse point that is a surrogate for another +// named entity (such as a symbolic link or mounted folder), the returned +// FileInfo describes the reparse point, and makes no attempt to resolve it. +func Lstat(name string) (FileInfo, error) { + testlog.Stat(name) + return lstatNolog(name) +} diff --git a/contrib/go/_std_1.22/src/os/stat_darwin.go b/contrib/go/_std_1.22/src/os/stat_darwin.go new file mode 100644 index 0000000000..b92ffd4a0a --- /dev/null +++ b/contrib/go/_std_1.22/src/os/stat_darwin.go @@ -0,0 +1,47 @@ +// 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. + +package os + +import ( + "syscall" + "time" +) + +func fillFileStatFromSys(fs *fileStat, name string) { + fs.name = basename(name) + fs.size = fs.sys.Size + fs.modTime = time.Unix(fs.sys.Mtimespec.Unix()) + fs.mode = FileMode(fs.sys.Mode & 0777) + switch fs.sys.Mode & syscall.S_IFMT { + case syscall.S_IFBLK, syscall.S_IFWHT: + fs.mode |= ModeDevice + case syscall.S_IFCHR: + fs.mode |= ModeDevice | ModeCharDevice + case syscall.S_IFDIR: + fs.mode |= ModeDir + case syscall.S_IFIFO: + fs.mode |= ModeNamedPipe + case syscall.S_IFLNK: + fs.mode |= ModeSymlink + case syscall.S_IFREG: + // nothing to do + case syscall.S_IFSOCK: + fs.mode |= ModeSocket + } + if fs.sys.Mode&syscall.S_ISGID != 0 { + fs.mode |= ModeSetgid + } + if fs.sys.Mode&syscall.S_ISUID != 0 { + fs.mode |= ModeSetuid + } + if fs.sys.Mode&syscall.S_ISVTX != 0 { + fs.mode |= ModeSticky + } +} + +// For testing. +func atime(fi FileInfo) time.Time { + return time.Unix(fi.Sys().(*syscall.Stat_t).Atimespec.Unix()) +} diff --git a/contrib/go/_std_1.22/src/os/stat_linux.go b/contrib/go/_std_1.22/src/os/stat_linux.go new file mode 100644 index 0000000000..316c26c7ca --- /dev/null +++ b/contrib/go/_std_1.22/src/os/stat_linux.go @@ -0,0 +1,47 @@ +// 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. + +package os + +import ( + "syscall" + "time" +) + +func fillFileStatFromSys(fs *fileStat, name string) { + fs.name = basename(name) + fs.size = fs.sys.Size + fs.modTime = time.Unix(fs.sys.Mtim.Unix()) + fs.mode = FileMode(fs.sys.Mode & 0777) + switch fs.sys.Mode & syscall.S_IFMT { + case syscall.S_IFBLK: + fs.mode |= ModeDevice + case syscall.S_IFCHR: + fs.mode |= ModeDevice | ModeCharDevice + case syscall.S_IFDIR: + fs.mode |= ModeDir + case syscall.S_IFIFO: + fs.mode |= ModeNamedPipe + case syscall.S_IFLNK: + fs.mode |= ModeSymlink + case syscall.S_IFREG: + // nothing to do + case syscall.S_IFSOCK: + fs.mode |= ModeSocket + } + if fs.sys.Mode&syscall.S_ISGID != 0 { + fs.mode |= ModeSetgid + } + if fs.sys.Mode&syscall.S_ISUID != 0 { + fs.mode |= ModeSetuid + } + if fs.sys.Mode&syscall.S_ISVTX != 0 { + fs.mode |= ModeSticky + } +} + +// For testing. +func atime(fi FileInfo) time.Time { + return time.Unix(fi.Sys().(*syscall.Stat_t).Atim.Unix()) +} diff --git a/contrib/go/_std_1.22/src/os/stat_unix.go b/contrib/go/_std_1.22/src/os/stat_unix.go new file mode 100644 index 0000000000..431df33fae --- /dev/null +++ b/contrib/go/_std_1.22/src/os/stat_unix.go @@ -0,0 +1,52 @@ +// Copyright 2016 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. + +//go:build unix || (js && wasm) || wasip1 + +package os + +import ( + "syscall" +) + +// Stat returns the FileInfo structure describing file. +// If there is an error, it will be of type *PathError. +func (f *File) Stat() (FileInfo, error) { + if f == nil { + return nil, ErrInvalid + } + var fs fileStat + err := f.pfd.Fstat(&fs.sys) + if err != nil { + return nil, &PathError{Op: "stat", Path: f.name, Err: err} + } + fillFileStatFromSys(&fs, f.name) + return &fs, nil +} + +// statNolog stats a file with no test logging. +func statNolog(name string) (FileInfo, error) { + var fs fileStat + err := ignoringEINTR(func() error { + return syscall.Stat(name, &fs.sys) + }) + if err != nil { + return nil, &PathError{Op: "stat", Path: name, Err: err} + } + fillFileStatFromSys(&fs, name) + return &fs, nil +} + +// lstatNolog lstats a file with no test logging. +func lstatNolog(name string) (FileInfo, error) { + var fs fileStat + err := ignoringEINTR(func() error { + return syscall.Lstat(name, &fs.sys) + }) + if err != nil { + return nil, &PathError{Op: "lstat", Path: name, Err: err} + } + fillFileStatFromSys(&fs, name) + return &fs, nil +} diff --git a/contrib/go/_std_1.22/src/os/stat_windows.go b/contrib/go/_std_1.22/src/os/stat_windows.go new file mode 100644 index 0000000000..668255f74a --- /dev/null +++ b/contrib/go/_std_1.22/src/os/stat_windows.go @@ -0,0 +1,136 @@ +// 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. + +package os + +import ( + "internal/syscall/windows" + "syscall" + "unsafe" +) + +// Stat returns the FileInfo structure describing file. +// If there is an error, it will be of type *PathError. +func (file *File) Stat() (FileInfo, error) { + if file == nil { + return nil, ErrInvalid + } + return statHandle(file.name, file.pfd.Sysfd) +} + +// stat implements both Stat and Lstat of a file. +func stat(funcname, name string, followSurrogates bool) (FileInfo, error) { + if len(name) == 0 { + return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} + } + namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) + if err != nil { + return nil, &PathError{Op: funcname, Path: name, Err: err} + } + + // Try GetFileAttributesEx first, because it is faster than CreateFile. + // See https://golang.org/issues/19922#issuecomment-300031421 for details. + var fa syscall.Win32FileAttributeData + err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) + + // GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for + // files like c:\pagefile.sys. Use FindFirstFile for such files. + if err == windows.ERROR_SHARING_VIOLATION { + var fd syscall.Win32finddata + sh, err := syscall.FindFirstFile(namep, &fd) + if err != nil { + return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err} + } + syscall.FindClose(sh) + if fd.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + // Not a surrogate for another named entity. FindFirstFile is good enough. + fs := newFileStatFromWin32finddata(&fd) + if err := fs.saveInfoFromPath(name); err != nil { + return nil, err + } + return fs, nil + } + } + + if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + // Not a surrogate for another named entity, because it isn't any kind of reparse point. + // The information we got from GetFileAttributesEx is good enough for now. + fs := &fileStat{ + FileAttributes: fa.FileAttributes, + CreationTime: fa.CreationTime, + LastAccessTime: fa.LastAccessTime, + LastWriteTime: fa.LastWriteTime, + FileSizeHigh: fa.FileSizeHigh, + FileSizeLow: fa.FileSizeLow, + } + if err := fs.saveInfoFromPath(name); err != nil { + return nil, err + } + return fs, nil + } + + // Use CreateFile to determine whether the file is a name surrogate and, if so, + // save information about the link target. + // Set FILE_FLAG_BACKUP_SEMANTICS so that CreateFile will create the handle + // even if name refers to a directory. + h, err := syscall.CreateFile(namep, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) + if err != nil { + // Since CreateFile failed, we can't determine whether name refers to a + // name surrogate, or some other kind of reparse point. Since we can't return a + // FileInfo with a known-accurate Mode, we must return an error. + return nil, &PathError{Op: "CreateFile", Path: name, Err: err} + } + + fi, err := statHandle(name, h) + syscall.CloseHandle(h) + if err == nil && followSurrogates && fi.(*fileStat).isReparseTagNameSurrogate() { + // To obtain information about the link target, we reopen the file without + // FILE_FLAG_OPEN_REPARSE_POINT and examine the resulting handle. + // (See https://devblogs.microsoft.com/oldnewthing/20100212-00/?p=14963.) + h, err = syscall.CreateFile(namep, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) + if err != nil { + // name refers to a symlink, but we couldn't resolve the symlink target. + return nil, &PathError{Op: "CreateFile", Path: name, Err: err} + } + defer syscall.CloseHandle(h) + return statHandle(name, h) + } + return fi, err +} + +func statHandle(name string, h syscall.Handle) (FileInfo, error) { + ft, err := syscall.GetFileType(h) + if err != nil { + return nil, &PathError{Op: "GetFileType", Path: name, Err: err} + } + switch ft { + case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: + return &fileStat{name: basename(name), filetype: ft}, nil + } + fs, err := newFileStatFromGetFileInformationByHandle(name, h) + if err != nil { + return nil, err + } + fs.filetype = ft + return fs, err +} + +// statNolog implements Stat for Windows. +func statNolog(name string) (FileInfo, error) { + return stat("Stat", name, true) +} + +// lstatNolog implements Lstat for Windows. +func lstatNolog(name string) (FileInfo, error) { + followSurrogates := false + if name != "" && IsPathSeparator(name[len(name)-1]) { + // We try to implement POSIX semantics for Lstat path resolution + // (per https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/basedefs/V1_chap04.html#tag_04_12): + // symlinks before the last separator in the path must be resolved. Since + // the last separator in this case follows the last path element, we should + // follow symlinks in the last path element. + followSurrogates = true + } + return stat("Lstat", name, followSurrogates) +} diff --git a/contrib/go/_std_1.22/src/os/sticky_bsd.go b/contrib/go/_std_1.22/src/os/sticky_bsd.go new file mode 100644 index 0000000000..a6d9339505 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sticky_bsd.go @@ -0,0 +1,11 @@ +// Copyright 2014 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. + +//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || solaris || wasip1 + +package os + +// According to sticky(8), neither open(2) nor mkdir(2) will create +// a file with the sticky bit set. +const supportsCreateWithStickyBit = false diff --git a/contrib/go/_std_1.22/src/os/sticky_notbsd.go b/contrib/go/_std_1.22/src/os/sticky_notbsd.go new file mode 100644 index 0000000000..1d289b0fe3 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sticky_notbsd.go @@ -0,0 +1,9 @@ +// Copyright 2014 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. + +//go:build !aix && !darwin && !dragonfly && !freebsd && !js && !netbsd && !openbsd && !solaris && !wasip1 + +package os + +const supportsCreateWithStickyBit = true diff --git a/contrib/go/_std_1.22/src/os/sys.go b/contrib/go/_std_1.22/src/os/sys.go new file mode 100644 index 0000000000..28b0f6bab0 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sys.go @@ -0,0 +1,10 @@ +// Copyright 2012 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. + +package os + +// Hostname returns the host name reported by the kernel. +func Hostname() (name string, err error) { + return hostname() +} diff --git a/contrib/go/_std_1.22/src/os/sys_bsd.go b/contrib/go/_std_1.22/src/os/sys_bsd.go new file mode 100644 index 0000000000..63120fb9b4 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sys_bsd.go @@ -0,0 +1,17 @@ +// 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. + +//go:build darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || wasip1 + +package os + +import "syscall" + +func hostname() (name string, err error) { + name, err = syscall.Sysctl("kern.hostname") + if err != nil { + return "", NewSyscallError("sysctl kern.hostname", err) + } + return name, nil +} diff --git a/contrib/go/_std_1.22/src/os/sys_linux.go b/contrib/go/_std_1.22/src/os/sys_linux.go new file mode 100644 index 0000000000..36a8a24455 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sys_linux.go @@ -0,0 +1,53 @@ +// 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. + +package os + +import ( + "runtime" + "syscall" +) + +func hostname() (name string, err error) { + // Try uname first, as it's only one system call and reading + // from /proc is not allowed on Android. + var un syscall.Utsname + err = syscall.Uname(&un) + + var buf [512]byte // Enough for a DNS name. + for i, b := range un.Nodename[:] { + buf[i] = uint8(b) + if b == 0 { + name = string(buf[:i]) + break + } + } + // If we got a name and it's not potentially truncated + // (Nodename is 65 bytes), return it. + if err == nil && len(name) > 0 && len(name) < 64 { + return name, nil + } + if runtime.GOOS == "android" { + if name != "" { + return name, nil + } + return "localhost", nil + } + + f, err := Open("/proc/sys/kernel/hostname") + if err != nil { + return "", err + } + defer f.Close() + + n, err := f.Read(buf[:]) + if err != nil { + return "", err + } + + if n > 0 && buf[n-1] == '\n' { + n-- + } + return string(buf[:n]), nil +} diff --git a/contrib/go/_std_1.22/src/os/sys_unix.go b/contrib/go/_std_1.22/src/os/sys_unix.go new file mode 100644 index 0000000000..79005c2cbd --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sys_unix.go @@ -0,0 +1,14 @@ +// Copyright 2014 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. + +//go:build unix + +package os + +// supportsCloseOnExec reports whether the platform supports the +// O_CLOEXEC flag. +// On Darwin, the O_CLOEXEC flag was introduced in OS X 10.7 (Darwin 11.0.0). +// See https://support.apple.com/kb/HT1633. +// On FreeBSD, the O_CLOEXEC flag was introduced in version 8.3. +const supportsCloseOnExec = true diff --git a/contrib/go/_std_1.22/src/os/sys_windows.go b/contrib/go/_std_1.22/src/os/sys_windows.go new file mode 100644 index 0000000000..72ad90b924 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/sys_windows.go @@ -0,0 +1,33 @@ +// 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. + +package os + +import ( + "internal/syscall/windows" + "syscall" +) + +func hostname() (name string, err error) { + // Use PhysicalDnsHostname to uniquely identify host in a cluster + const format = windows.ComputerNamePhysicalDnsHostname + + n := uint32(64) + for { + b := make([]uint16, n) + err := windows.GetComputerNameEx(format, &b[0], &n) + if err == nil { + return syscall.UTF16ToString(b[:n]), nil + } + if err != syscall.ERROR_MORE_DATA { + return "", NewSyscallError("ComputerNameEx", err) + } + + // If we received an ERROR_MORE_DATA, but n doesn't get larger, + // something has gone wrong and we may be in an infinite loop + if n <= uint32(len(b)) { + return "", NewSyscallError("ComputerNameEx", err) + } + } +} diff --git a/contrib/go/_std_1.22/src/os/tempfile.go b/contrib/go/_std_1.22/src/os/tempfile.go new file mode 100644 index 0000000000..66c65e6c78 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/tempfile.go @@ -0,0 +1,121 @@ +// Copyright 2010 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. + +package os + +import ( + "errors" + "internal/bytealg" + "internal/itoa" + _ "unsafe" // for go:linkname +) + +// random number source provided by runtime. +// We generate random temporary file names so that there's a good +// chance the file doesn't exist yet - keeps the number of tries in +// TempFile to a minimum. +//go:linkname runtime_rand runtime.rand +func runtime_rand() uint64 + +func nextRandom() string { + return itoa.Uitoa(uint(uint32(runtime_rand()))) +} + +// CreateTemp creates a new temporary file in the directory dir, +// opens the file for reading and writing, and returns the resulting file. +// The filename is generated by taking pattern and adding a random string to the end. +// If pattern includes a "*", the random string replaces the last "*". +// If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. +// Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. +// The caller can use the file's Name method to find the pathname of the file. +// It is the caller's responsibility to remove the file when it is no longer needed. +func CreateTemp(dir, pattern string) (*File, error) { + if dir == "" { + dir = TempDir() + } + + prefix, suffix, err := prefixAndSuffix(pattern) + if err != nil { + return nil, &PathError{Op: "createtemp", Path: pattern, Err: err} + } + prefix = joinPath(dir, prefix) + + try := 0 + for { + name := prefix + nextRandom() + suffix + f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600) + if IsExist(err) { + if try++; try < 10000 { + continue + } + return nil, &PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: ErrExist} + } + return f, err + } +} + +var errPatternHasSeparator = errors.New("pattern contains path separator") + +// prefixAndSuffix splits pattern by the last wildcard "*", if applicable, +// returning prefix as the part before "*" and suffix as the part after "*". +func prefixAndSuffix(pattern string) (prefix, suffix string, err error) { + for i := 0; i < len(pattern); i++ { + if IsPathSeparator(pattern[i]) { + return "", "", errPatternHasSeparator + } + } + if pos := bytealg.LastIndexByteString(pattern, '*'); pos != -1 { + prefix, suffix = pattern[:pos], pattern[pos+1:] + } else { + prefix = pattern + } + return prefix, suffix, nil +} + +// MkdirTemp creates a new temporary directory in the directory dir +// and returns the pathname of the new directory. +// The new directory's name is generated by adding a random string to the end of pattern. +// If pattern includes a "*", the random string replaces the last "*" instead. +// If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. +// Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. +// It is the caller's responsibility to remove the directory when it is no longer needed. +func MkdirTemp(dir, pattern string) (string, error) { + if dir == "" { + dir = TempDir() + } + + prefix, suffix, err := prefixAndSuffix(pattern) + if err != nil { + return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err} + } + prefix = joinPath(dir, prefix) + + try := 0 + for { + name := prefix + nextRandom() + suffix + err := Mkdir(name, 0700) + if err == nil { + return name, nil + } + if IsExist(err) { + if try++; try < 10000 { + continue + } + return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} + } + if IsNotExist(err) { + if _, err := Stat(dir); IsNotExist(err) { + return "", err + } + } + return "", err + } +} + +func joinPath(dir, name string) string { + if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) { + return dir + name + } + return dir + string(PathSeparator) + name +} diff --git a/contrib/go/_std_1.22/src/os/types.go b/contrib/go/_std_1.22/src/os/types.go new file mode 100644 index 0000000000..d8edd98b68 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/types.go @@ -0,0 +1,74 @@ +// 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. + +package os + +import ( + "io/fs" + "syscall" +) + +// Getpagesize returns the underlying system's memory page size. +func Getpagesize() int { return syscall.Getpagesize() } + +// File represents an open file descriptor. +type File struct { + *file // os specific +} + +// A FileInfo describes a file and is returned by Stat and Lstat. +type FileInfo = fs.FileInfo + +// A FileMode represents a file's mode and permission bits. +// The bits have the same definition on all systems, so that +// information about files can be moved from one system +// to another portably. Not all bits apply to all systems. +// The only required bit is ModeDir for directories. +type FileMode = fs.FileMode + +// 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 +) + +func (fs *fileStat) Name() string { return fs.name } +func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() } + +// SameFile reports whether fi1 and fi2 describe the same file. +// For example, on Unix this means that the device and inode fields +// of the two underlying structures are identical; on other systems +// the decision may be based on the path names. +// SameFile only applies to results returned by this package's Stat. +// It returns false in other cases. +func SameFile(fi1, fi2 FileInfo) bool { + fs1, ok1 := fi1.(*fileStat) + fs2, ok2 := fi2.(*fileStat) + if !ok1 || !ok2 { + return false + } + return sameFile(fs1, fs2) +} diff --git a/contrib/go/_std_1.22/src/os/types_unix.go b/contrib/go/_std_1.22/src/os/types_unix.go new file mode 100644 index 0000000000..1b90a5a141 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/types_unix.go @@ -0,0 +1,30 @@ +// 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. + +//go:build !windows && !plan9 + +package os + +import ( + "syscall" + "time" +) + +// A fileStat is the implementation of FileInfo returned by Stat and Lstat. +type fileStat struct { + name string + size int64 + mode FileMode + modTime time.Time + sys syscall.Stat_t +} + +func (fs *fileStat) Size() int64 { return fs.size } +func (fs *fileStat) Mode() FileMode { return fs.mode } +func (fs *fileStat) ModTime() time.Time { return fs.modTime } +func (fs *fileStat) Sys() any { return &fs.sys } + +func sameFile(fs1, fs2 *fileStat) bool { + return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino +} diff --git a/contrib/go/_std_1.22/src/os/types_windows.go b/contrib/go/_std_1.22/src/os/types_windows.go new file mode 100644 index 0000000000..6b9fef6c12 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/types_windows.go @@ -0,0 +1,306 @@ +// 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. + +package os + +import ( + "internal/syscall/windows" + "sync" + "syscall" + "time" + "unsafe" +) + +// A fileStat is the implementation of FileInfo returned by Stat and Lstat. +type fileStat struct { + name string + + // from ByHandleFileInformation, Win32FileAttributeData, Win32finddata, and GetFileInformationByHandleEx + FileAttributes uint32 + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + FileSizeHigh uint32 + FileSizeLow uint32 + + // from Win32finddata and GetFileInformationByHandleEx + ReparseTag uint32 + + // what syscall.GetFileType returns + filetype uint32 + + // used to implement SameFile + sync.Mutex + path string + vol uint32 + idxhi uint32 + idxlo uint32 + appendNameToPath bool +} + +// newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle +// to gather all required information about the file handle h. +func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (fs *fileStat, err error) { + var d syscall.ByHandleFileInformation + err = syscall.GetFileInformationByHandle(h, &d) + if err != nil { + return nil, &PathError{Op: "GetFileInformationByHandle", Path: path, Err: err} + } + + var ti windows.FILE_ATTRIBUTE_TAG_INFO + err = windows.GetFileInformationByHandleEx(h, windows.FileAttributeTagInfo, (*byte)(unsafe.Pointer(&ti)), uint32(unsafe.Sizeof(ti))) + if err != nil { + if errno, ok := err.(syscall.Errno); ok && errno == windows.ERROR_INVALID_PARAMETER { + // It appears calling GetFileInformationByHandleEx with + // FILE_ATTRIBUTE_TAG_INFO fails on FAT file system with + // ERROR_INVALID_PARAMETER. Clear ti.ReparseTag in that + // instance to indicate no symlinks are possible. + ti.ReparseTag = 0 + } else { + return nil, &PathError{Op: "GetFileInformationByHandleEx", Path: path, Err: err} + } + } + + return &fileStat{ + name: basename(path), + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + vol: d.VolumeSerialNumber, + idxhi: d.FileIndexHigh, + idxlo: d.FileIndexLow, + ReparseTag: ti.ReparseTag, + // fileStat.path is used by os.SameFile to decide if it needs + // to fetch vol, idxhi and idxlo. But these are already set, + // so set fileStat.path to "" to prevent os.SameFile doing it again. + }, nil +} + +// newFileStatFromFileIDBothDirInfo copies all required information +// from windows.FILE_ID_BOTH_DIR_INFO d into the newly created fileStat. +func newFileStatFromFileIDBothDirInfo(d *windows.FILE_ID_BOTH_DIR_INFO) *fileStat { + // The FILE_ID_BOTH_DIR_INFO MSDN documentations isn't completely correct. + // FileAttributes can contain any file attributes that is currently set on the file, + // not just the ones documented. + // EaSize contains the reparse tag if the file is a reparse point. + return &fileStat{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: uint32(d.EndOfFile >> 32), + FileSizeLow: uint32(d.EndOfFile), + ReparseTag: d.EaSize, + idxhi: uint32(d.FileID >> 32), + idxlo: uint32(d.FileID), + } +} + +// newFileStatFromFileFullDirInfo copies all required information +// from windows.FILE_FULL_DIR_INFO d into the newly created fileStat. +func newFileStatFromFileFullDirInfo(d *windows.FILE_FULL_DIR_INFO) *fileStat { + return &fileStat{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: uint32(d.EndOfFile >> 32), + FileSizeLow: uint32(d.EndOfFile), + ReparseTag: d.EaSize, + } +} + +// newFileStatFromWin32finddata copies all required information +// from syscall.Win32finddata d into the newly created fileStat. +func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat { + fs := &fileStat{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + } + if d.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { + // Per https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw: + // “If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT + // attribute, this member specifies the reparse point tag. Otherwise, this + // value is undefined and should not be used.” + fs.ReparseTag = d.Reserved0 + } + return fs +} + +// isReparseTagNameSurrogate determines whether a tag's associated +// reparse point is a surrogate for another named entity (for example, a mounted folder). +// +// See https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-isreparsetagnamesurrogate +// and https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags. +func (fs *fileStat) isReparseTagNameSurrogate() bool { + // True for IO_REPARSE_TAG_SYMLINK and IO_REPARSE_TAG_MOUNT_POINT. + return fs.ReparseTag&0x20000000 != 0 +} + +func (fs *fileStat) isSymlink() bool { + // As of https://go.dev/cl/86556, we treat MOUNT_POINT reparse points as + // symlinks because otherwise certain directory junction tests in the + // path/filepath package would fail. + // + // However, + // https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions + // seems to suggest that directory junctions should be treated like hard + // links, not symlinks. + // + // TODO(bcmills): Get more input from Microsoft on what the behavior ought to + // be for MOUNT_POINT reparse points. + + return fs.ReparseTag == syscall.IO_REPARSE_TAG_SYMLINK || + fs.ReparseTag == windows.IO_REPARSE_TAG_MOUNT_POINT +} + +func (fs *fileStat) Size() int64 { + return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow) +} + +func (fs *fileStat) Mode() (m FileMode) { + if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { + m |= 0444 + } else { + m |= 0666 + } + if fs.isSymlink() { + return m | ModeSymlink + } + if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + m |= ModeDir | 0111 + } + switch fs.filetype { + case syscall.FILE_TYPE_PIPE: + m |= ModeNamedPipe + case syscall.FILE_TYPE_CHAR: + m |= ModeDevice | ModeCharDevice + } + if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 && m&ModeType == 0 { + if fs.ReparseTag == windows.IO_REPARSE_TAG_DEDUP { + // If the Data Deduplication service is enabled on Windows Server, its + // Optimization job may convert regular files to IO_REPARSE_TAG_DEDUP + // whenever that job runs. + // + // However, DEDUP reparse points remain similar in most respects to + // regular files: they continue to support random-access reads and writes + // of persistent data, and they shouldn't add unexpected latency or + // unavailability in the way that a network filesystem might. + // + // Go programs may use ModeIrregular to filter out unusual files (such as + // raw device files on Linux, POSIX FIFO special files, and so on), so + // to avoid files changing unpredictably from regular to irregular we will + // consider DEDUP files to be close enough to regular to treat as such. + } else { + m |= ModeIrregular + } + } + return m +} + +func (fs *fileStat) ModTime() time.Time { + return time.Unix(0, fs.LastWriteTime.Nanoseconds()) +} + +// Sys returns syscall.Win32FileAttributeData for file fs. +func (fs *fileStat) Sys() any { + return &syscall.Win32FileAttributeData{ + FileAttributes: fs.FileAttributes, + CreationTime: fs.CreationTime, + LastAccessTime: fs.LastAccessTime, + LastWriteTime: fs.LastWriteTime, + FileSizeHigh: fs.FileSizeHigh, + FileSizeLow: fs.FileSizeLow, + } +} + +func (fs *fileStat) loadFileId() error { + fs.Lock() + defer fs.Unlock() + if fs.path == "" { + // already done + return nil + } + var path string + if fs.appendNameToPath { + path = fixLongPath(fs.path + `\` + fs.name) + } else { + path = fs.path + } + pathp, err := syscall.UTF16PtrFromString(path) + if err != nil { + return err + } + + // Per https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-points-and-file-operations, + // “Applications that use the CreateFile function should specify the + // FILE_FLAG_OPEN_REPARSE_POINT flag when opening the file if it is a reparse + // point.” + // + // And per https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew, + // “If the file is not a reparse point, then this flag is ignored.” + // + // So we set FILE_FLAG_OPEN_REPARSE_POINT unconditionally, since we want + // information about the reparse point itself. + // + // If the file is a symlink, the symlink target should have already been + // resolved when the fileStat was created, so we don't need to worry about + // resolving symlink reparse points again here. + attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS | syscall.FILE_FLAG_OPEN_REPARSE_POINT) + + h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0) + if err != nil { + return err + } + defer syscall.CloseHandle(h) + var i syscall.ByHandleFileInformation + err = syscall.GetFileInformationByHandle(h, &i) + if err != nil { + return err + } + fs.path = "" + fs.vol = i.VolumeSerialNumber + fs.idxhi = i.FileIndexHigh + fs.idxlo = i.FileIndexLow + return nil +} + +// saveInfoFromPath saves full path of the file to be used by os.SameFile later, +// and set name from path. +func (fs *fileStat) saveInfoFromPath(path string) error { + fs.path = path + if !isAbs(fs.path) { + var err error + fs.path, err = syscall.FullPath(fs.path) + if err != nil { + return &PathError{Op: "FullPath", Path: path, Err: err} + } + } + fs.name = basename(path) + return nil +} + +func sameFile(fs1, fs2 *fileStat) bool { + e := fs1.loadFileId() + if e != nil { + return false + } + e = fs2.loadFileId() + if e != nil { + return false + } + return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo +} + +// For testing. +func atime(fi FileInfo) time.Time { + return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) +} diff --git a/contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go b/contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go new file mode 100644 index 0000000000..59636954b2 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_listgroups_unix.go @@ -0,0 +1,57 @@ +// Copyright 2016 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. + +//go:build (cgo || darwin) && !osusergo && (darwin || dragonfly || freebsd || (linux && !android) || netbsd || openbsd || (solaris && !illumos)) + +package user + +import ( + "fmt" + "strconv" + "unsafe" +) + +const maxGroups = 2048 + +func listGroups(u *User) ([]string, error) { + ug, err := strconv.Atoi(u.Gid) + if err != nil { + return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) + } + userGID := _C_gid_t(ug) + nameC := make([]byte, len(u.Username)+1) + copy(nameC, u.Username) + + n := _C_int(256) + gidsC := make([]_C_gid_t, n) + rv := getGroupList((*_C_char)(unsafe.Pointer(&nameC[0])), userGID, &gidsC[0], &n) + if rv == -1 { + // Mac is the only Unix that does not set n properly when rv == -1, so + // we need to use different logic for Mac vs. the other OS's. + if err := groupRetry(u.Username, nameC, userGID, &gidsC, &n); err != nil { + return nil, err + } + } + gidsC = gidsC[:n] + gids := make([]string, 0, n) + for _, g := range gidsC[:n] { + gids = append(gids, strconv.Itoa(int(g))) + } + return gids, nil +} + +// groupRetry retries getGroupList with much larger size for n. The result is +// stored in gids. +func groupRetry(username string, name []byte, userGID _C_gid_t, gids *[]_C_gid_t, n *_C_int) error { + // More than initial buffer, but now n contains the correct size. + if *n > maxGroups { + return fmt.Errorf("user: %q is a member of more than %d groups", username, maxGroups) + } + *gids = make([]_C_gid_t, *n) + rv := getGroupList((*_C_char)(unsafe.Pointer(&name[0])), userGID, &(*gids)[0], n) + if rv == -1 { + return fmt.Errorf("user: list groups for %s failed", username) + } + return nil +} diff --git a/contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go b/contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go new file mode 100644 index 0000000000..4f78dcad23 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_lookup_cgo.go @@ -0,0 +1,112 @@ +// Copyright 2011 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. + +//go:build cgo && !osusergo && unix && !android && !darwin + +package user + +import ( + "syscall" +) + +/* +#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS +#cgo CFLAGS: -fno-stack-protector +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <stdlib.h> +#include <string.h> + +static struct passwd mygetpwuid_r(int uid, char *buf, size_t buflen, int *found, int *perr) { + struct passwd pwd; + struct passwd *result; + memset (&pwd, 0, sizeof(pwd)); + *perr = getpwuid_r(uid, &pwd, buf, buflen, &result); + *found = result != NULL; + return pwd; +} + +static struct passwd mygetpwnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) { + struct passwd pwd; + struct passwd *result; + memset(&pwd, 0, sizeof(pwd)); + *perr = getpwnam_r(name, &pwd, buf, buflen, &result); + *found = result != NULL; + return pwd; +} + +static struct group mygetgrgid_r(int gid, char *buf, size_t buflen, int *found, int *perr) { + struct group grp; + struct group *result; + memset(&grp, 0, sizeof(grp)); + *perr = getgrgid_r(gid, &grp, buf, buflen, &result); + *found = result != NULL; + return grp; +} + +static struct group mygetgrnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) { + struct group grp; + struct group *result; + memset(&grp, 0, sizeof(grp)); + *perr = getgrnam_r(name, &grp, buf, buflen, &result); + *found = result != NULL; + return grp; +} +*/ +import "C" + +type _C_char = C.char +type _C_int = C.int +type _C_gid_t = C.gid_t +type _C_uid_t = C.uid_t +type _C_size_t = C.size_t +type _C_struct_group = C.struct_group +type _C_struct_passwd = C.struct_passwd +type _C_long = C.long + +func _C_pw_uid(p *_C_struct_passwd) _C_uid_t { return p.pw_uid } +func _C_pw_uidp(p *_C_struct_passwd) *_C_uid_t { return &p.pw_uid } +func _C_pw_gid(p *_C_struct_passwd) _C_gid_t { return p.pw_gid } +func _C_pw_gidp(p *_C_struct_passwd) *_C_gid_t { return &p.pw_gid } +func _C_pw_name(p *_C_struct_passwd) *_C_char { return p.pw_name } +func _C_pw_gecos(p *_C_struct_passwd) *_C_char { return p.pw_gecos } +func _C_pw_dir(p *_C_struct_passwd) *_C_char { return p.pw_dir } + +func _C_gr_gid(g *_C_struct_group) _C_gid_t { return g.gr_gid } +func _C_gr_name(g *_C_struct_group) *_C_char { return g.gr_name } + +func _C_GoString(p *_C_char) string { return C.GoString(p) } + +func _C_getpwnam_r(name *_C_char, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var f, e _C_int + pwd = C.mygetpwnam_r(name, buf, size, &f, &e) + return pwd, f != 0, syscall.Errno(e) +} + +func _C_getpwuid_r(uid _C_uid_t, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var f, e _C_int + pwd = C.mygetpwuid_r(_C_int(uid), buf, size, &f, &e) + return pwd, f != 0, syscall.Errno(e) +} + +func _C_getgrnam_r(name *_C_char, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var f, e _C_int + grp = C.mygetgrnam_r(name, buf, size, &f, &e) + return grp, f != 0, syscall.Errno(e) +} + +func _C_getgrgid_r(gid _C_gid_t, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var f, e _C_int + grp = C.mygetgrgid_r(_C_int(gid), buf, size, &f, &e) + return grp, f != 0, syscall.Errno(e) +} + +const ( + _C__SC_GETPW_R_SIZE_MAX = C._SC_GETPW_R_SIZE_MAX + _C__SC_GETGR_R_SIZE_MAX = C._SC_GETGR_R_SIZE_MAX +) + +func _C_sysconf(key _C_int) _C_long { return C.sysconf(key) } diff --git a/contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go b/contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go new file mode 100644 index 0000000000..321df652be --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_lookup_syscall.go @@ -0,0 +1,65 @@ +// Copyright 2011 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. + +//go:build !osusergo && darwin + +package user + +import ( + "internal/syscall/unix" + "syscall" +) + +type _C_char = byte +type _C_int = int32 +type _C_gid_t = uint32 +type _C_uid_t = uint32 +type _C_size_t = uintptr +type _C_struct_group = unix.Group +type _C_struct_passwd = unix.Passwd +type _C_long = int64 + +func _C_pw_uid(p *_C_struct_passwd) _C_uid_t { return p.Uid } +func _C_pw_uidp(p *_C_struct_passwd) *_C_uid_t { return &p.Uid } +func _C_pw_gid(p *_C_struct_passwd) _C_gid_t { return p.Gid } +func _C_pw_gidp(p *_C_struct_passwd) *_C_gid_t { return &p.Gid } +func _C_pw_name(p *_C_struct_passwd) *_C_char { return p.Name } +func _C_pw_gecos(p *_C_struct_passwd) *_C_char { return p.Gecos } +func _C_pw_dir(p *_C_struct_passwd) *_C_char { return p.Dir } + +func _C_gr_gid(g *_C_struct_group) _C_gid_t { return g.Gid } +func _C_gr_name(g *_C_struct_group) *_C_char { return g.Name } + +func _C_GoString(p *_C_char) string { return unix.GoString(p) } + +func _C_getpwnam_r(name *_C_char, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var result *_C_struct_passwd + errno = unix.Getpwnam(name, &pwd, buf, size, &result) + return pwd, result != nil, errno +} + +func _C_getpwuid_r(uid _C_uid_t, buf *_C_char, size _C_size_t) (pwd _C_struct_passwd, found bool, errno syscall.Errno) { + var result *_C_struct_passwd + errno = unix.Getpwuid(uid, &pwd, buf, size, &result) + return pwd, result != nil, errno +} + +func _C_getgrnam_r(name *_C_char, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var result *_C_struct_group + errno = unix.Getgrnam(name, &grp, buf, size, &result) + return grp, result != nil, errno +} + +func _C_getgrgid_r(gid _C_gid_t, buf *_C_char, size _C_size_t) (grp _C_struct_group, found bool, errno syscall.Errno) { + var result *_C_struct_group + errno = unix.Getgrgid(gid, &grp, buf, size, &result) + return grp, result != nil, errno +} + +const ( + _C__SC_GETPW_R_SIZE_MAX = unix.SC_GETPW_R_SIZE_MAX + _C__SC_GETGR_R_SIZE_MAX = unix.SC_GETGR_R_SIZE_MAX +) + +func _C_sysconf(key _C_int) _C_long { return unix.Sysconf(key) } diff --git a/contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go b/contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go new file mode 100644 index 0000000000..402429ba4a --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/cgo_lookup_unix.go @@ -0,0 +1,200 @@ +// Copyright 2011 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. + +//go:build (cgo || darwin) && !osusergo && unix && !android + +package user + +import ( + "fmt" + "runtime" + "strconv" + "strings" + "syscall" + "unsafe" +) + +func current() (*User, error) { + return lookupUnixUid(syscall.Getuid()) +} + +func lookupUser(username string) (*User, error) { + var pwd _C_struct_passwd + var found bool + nameC := make([]byte, len(username)+1) + copy(nameC, username) + + err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + pwd, found, errno = _C_getpwnam_r((*_C_char)(unsafe.Pointer(&nameC[0])), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return errno + }) + if err != nil { + return nil, fmt.Errorf("user: lookup username %s: %v", username, err) + } + if !found { + return nil, UnknownUserError(username) + } + return buildUser(&pwd), err +} + +func lookupUserId(uid string) (*User, error) { + i, e := strconv.Atoi(uid) + if e != nil { + return nil, e + } + return lookupUnixUid(i) +} + +func lookupUnixUid(uid int) (*User, error) { + var pwd _C_struct_passwd + var found bool + + err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + pwd, found, errno = _C_getpwuid_r(_C_uid_t(uid), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return errno + }) + if err != nil { + return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err) + } + if !found { + return nil, UnknownUserIdError(uid) + } + return buildUser(&pwd), nil +} + +func buildUser(pwd *_C_struct_passwd) *User { + u := &User{ + Uid: strconv.FormatUint(uint64(_C_pw_uid(pwd)), 10), + Gid: strconv.FormatUint(uint64(_C_pw_gid(pwd)), 10), + Username: _C_GoString(_C_pw_name(pwd)), + Name: _C_GoString(_C_pw_gecos(pwd)), + HomeDir: _C_GoString(_C_pw_dir(pwd)), + } + // The pw_gecos field isn't quite standardized. Some docs + // say: "It is expected to be a comma separated list of + // personal data where the first item is the full name of the + // user." + u.Name, _, _ = strings.Cut(u.Name, ",") + return u +} + +func lookupGroup(groupname string) (*Group, error) { + var grp _C_struct_group + var found bool + + cname := make([]byte, len(groupname)+1) + copy(cname, groupname) + + err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + grp, found, errno = _C_getgrnam_r((*_C_char)(unsafe.Pointer(&cname[0])), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return errno + }) + if err != nil { + return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err) + } + if !found { + return nil, UnknownGroupError(groupname) + } + return buildGroup(&grp), nil +} + +func lookupGroupId(gid string) (*Group, error) { + i, e := strconv.Atoi(gid) + if e != nil { + return nil, e + } + return lookupUnixGid(i) +} + +func lookupUnixGid(gid int) (*Group, error) { + var grp _C_struct_group + var found bool + + err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno { + var errno syscall.Errno + grp, found, errno = _C_getgrgid_r(_C_gid_t(gid), + (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) + return syscall.Errno(errno) + }) + if err != nil { + return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err) + } + if !found { + return nil, UnknownGroupIdError(strconv.Itoa(gid)) + } + return buildGroup(&grp), nil +} + +func buildGroup(grp *_C_struct_group) *Group { + g := &Group{ + Gid: strconv.Itoa(int(_C_gr_gid(grp))), + Name: _C_GoString(_C_gr_name(grp)), + } + return g +} + +type bufferKind _C_int + +var ( + userBuffer = bufferKind(_C__SC_GETPW_R_SIZE_MAX) + groupBuffer = bufferKind(_C__SC_GETGR_R_SIZE_MAX) +) + +func (k bufferKind) initialSize() _C_size_t { + sz := _C_sysconf(_C_int(k)) + if sz == -1 { + // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. + // Additionally, not all Linux systems have it, either. For + // example, the musl libc returns -1. + return 1024 + } + if !isSizeReasonable(int64(sz)) { + // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. + return maxBufferSize + } + return _C_size_t(sz) +} + +// retryWithBuffer repeatedly calls f(), increasing the size of the +// buffer each time, until f succeeds, fails with a non-ERANGE error, +// or the buffer exceeds a reasonable limit. +func retryWithBuffer(kind bufferKind, f func([]byte) syscall.Errno) error { + buf := make([]byte, kind.initialSize()) + for { + errno := f(buf) + if errno == 0 { + return nil + } else if runtime.GOOS == "aix" && errno+1 == 0 { + // On AIX getpwuid_r appears to return -1, + // not ERANGE, on buffer overflow. + } else if errno != syscall.ERANGE { + return errno + } + newSize := len(buf) * 2 + if !isSizeReasonable(int64(newSize)) { + return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) + } + buf = make([]byte, newSize) + } +} + +const maxBufferSize = 1 << 20 + +func isSizeReasonable(sz int64) bool { + return sz > 0 && sz <= maxBufferSize +} + +// Because we can't use cgo in tests: +func structPasswdForNegativeTest() _C_struct_passwd { + sp := _C_struct_passwd{} + *_C_pw_uidp(&sp) = 1<<32 - 2 + *_C_pw_gidp(&sp) = 1<<32 - 3 + return sp +} diff --git a/contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go b/contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go new file mode 100644 index 0000000000..41b64fca93 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/getgrouplist_syscall.go @@ -0,0 +1,19 @@ +// Copyright 2016 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. + +//go:build !osusergo && darwin + +package user + +import ( + "internal/syscall/unix" +) + +func getGroupList(name *_C_char, userGID _C_gid_t, gids *_C_gid_t, n *_C_int) _C_int { + err := unix.Getgrouplist(name, userGID, gids, n) + if err != nil { + return -1 + } + return 0 +} diff --git a/contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go b/contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go new file mode 100644 index 0000000000..fb482d35ba --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/getgrouplist_unix.go @@ -0,0 +1,22 @@ +// Copyright 2016 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. + +//go:build cgo && !osusergo && (dragonfly || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) + +package user + +/* +#include <unistd.h> +#include <sys/types.h> +#include <grp.h> + +static int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { + return getgrouplist(user, group, groups, ngroups); +} +*/ +import "C" + +func getGroupList(name *_C_char, userGID _C_gid_t, gids *_C_gid_t, n *_C_int) _C_int { + return C.mygetgrouplist(name, userGID, gids, n) +} diff --git a/contrib/go/_std_1.22/src/os/user/lookup.go b/contrib/go/_std_1.22/src/os/user/lookup.go new file mode 100644 index 0000000000..ed33d0c7cd --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/lookup.go @@ -0,0 +1,70 @@ +// Copyright 2011 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. + +package user + +import "sync" + +const ( + userFile = "/etc/passwd" + groupFile = "/etc/group" +) + +var colon = []byte{':'} + +// Current returns the current user. +// +// The first call will cache the current user information. +// Subsequent calls will return the cached value and will not reflect +// changes to the current user. +func Current() (*User, error) { + cache.Do(func() { cache.u, cache.err = current() }) + if cache.err != nil { + return nil, cache.err + } + u := *cache.u // copy + return &u, nil +} + +// cache of the current user +var cache struct { + sync.Once + u *User + err error +} + +// Lookup looks up a user by username. If the user cannot be found, the +// returned error is of type UnknownUserError. +func Lookup(username string) (*User, error) { + if u, err := Current(); err == nil && u.Username == username { + return u, err + } + return lookupUser(username) +} + +// LookupId looks up a user by userid. If the user cannot be found, the +// returned error is of type UnknownUserIdError. +func LookupId(uid string) (*User, error) { + if u, err := Current(); err == nil && u.Uid == uid { + return u, err + } + return lookupUserId(uid) +} + +// LookupGroup looks up a group by name. If the group cannot be found, the +// returned error is of type UnknownGroupError. +func LookupGroup(name string) (*Group, error) { + return lookupGroup(name) +} + +// LookupGroupId looks up a group by groupid. If the group cannot be found, the +// returned error is of type UnknownGroupIdError. +func LookupGroupId(gid string) (*Group, error) { + return lookupGroupId(gid) +} + +// GroupIds returns the list of group IDs that the user is a member of. +func (u *User) GroupIds() ([]string, error) { + return listGroups(u) +} diff --git a/contrib/go/_std_1.22/src/os/user/lookup_windows.go b/contrib/go/_std_1.22/src/os/user/lookup_windows.go new file mode 100644 index 0000000000..a48fc89720 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/lookup_windows.go @@ -0,0 +1,392 @@ +// Copyright 2012 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. + +package user + +import ( + "fmt" + "internal/syscall/windows" + "internal/syscall/windows/registry" + "syscall" + "unsafe" +) + +func isDomainJoined() (bool, error) { + var domain *uint16 + var status uint32 + err := syscall.NetGetJoinInformation(nil, &domain, &status) + if err != nil { + return false, err + } + syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) + return status == syscall.NetSetupDomainName, nil +} + +func lookupFullNameDomain(domainAndUser string) (string, error) { + return syscall.TranslateAccountName(domainAndUser, + syscall.NameSamCompatible, syscall.NameDisplay, 50) +} + +func lookupFullNameServer(servername, username string) (string, error) { + s, e := syscall.UTF16PtrFromString(servername) + if e != nil { + return "", e + } + u, e := syscall.UTF16PtrFromString(username) + if e != nil { + return "", e + } + var p *byte + e = syscall.NetUserGetInfo(s, u, 10, &p) + if e != nil { + return "", e + } + defer syscall.NetApiBufferFree(p) + i := (*syscall.UserInfo10)(unsafe.Pointer(p)) + return windows.UTF16PtrToString(i.FullName), nil +} + +func lookupFullName(domain, username, domainAndUser string) (string, error) { + joined, err := isDomainJoined() + if err == nil && joined { + name, err := lookupFullNameDomain(domainAndUser) + if err == nil { + return name, nil + } + } + name, err := lookupFullNameServer(domain, username) + if err == nil { + return name, nil + } + // domain worked neither as a domain nor as a server + // could be domain server unavailable + // pretend username is fullname + return username, nil +} + +// getProfilesDirectory retrieves the path to the root directory +// where user profiles are stored. +func getProfilesDirectory() (string, error) { + n := uint32(100) + for { + b := make([]uint16, n) + e := windows.GetProfilesDirectory(&b[0], &n) + if e == nil { + return syscall.UTF16ToString(b), nil + } + if e != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", e + } + if n <= uint32(len(b)) { + return "", e + } + } +} + +// lookupUsernameAndDomain obtains the username and domain for usid. +func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) { + username, domain, t, e := usid.LookupAccount("") + if e != nil { + return "", "", e + } + if t != syscall.SidTypeUser { + return "", "", fmt.Errorf("user: should be user account type, not %d", t) + } + return username, domain, nil +} + +// findHomeDirInRegistry finds the user home path based on the uid. +func findHomeDirInRegistry(uid string) (dir string, e error) { + k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE) + if e != nil { + return "", e + } + defer k.Close() + dir, _, e = k.GetStringValue("ProfileImagePath") + if e != nil { + return "", e + } + return dir, nil +} + +// lookupGroupName accepts the name of a group and retrieves the group SID. +func lookupGroupName(groupname string) (string, error) { + sid, _, t, e := syscall.LookupSID("", groupname) + if e != nil { + return "", e + } + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/7b2aeb27-92fc-41f6-8437-deb65d950921#gt_0387e636-5654-4910-9519-1f8326cf5ec0 + // SidTypeAlias should also be treated as a group type next to SidTypeGroup + // and SidTypeWellKnownGroup: + // "alias object -> resource group: A group object..." + // + // Tests show that "Administrators" can be considered of type SidTypeAlias. + if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { + return "", fmt.Errorf("lookupGroupName: should be group account type, not %d", t) + } + return sid.String() +} + +// listGroupsForUsernameAndDomain accepts username and domain and retrieves +// a SID list of the local groups where this user is a member. +func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) { + // Check if both the domain name and user should be used. + var query string + joined, err := isDomainJoined() + if err == nil && joined && len(domain) != 0 { + query = domain + `\` + username + } else { + query = username + } + q, err := syscall.UTF16PtrFromString(query) + if err != nil { + return nil, err + } + var p0 *byte + var entriesRead, totalEntries uint32 + // https://learn.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netusergetlocalgroups + // NetUserGetLocalGroups() would return a list of LocalGroupUserInfo0 + // elements which hold the names of local groups where the user participates. + // The list does not follow any sorting order. + // + // If no groups can be found for this user, NetUserGetLocalGroups() should + // always return the SID of a single group called "None", which + // also happens to be the primary group for the local user. + err = windows.NetUserGetLocalGroups(nil, q, 0, windows.LG_INCLUDE_INDIRECT, &p0, windows.MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries) + if err != nil { + return nil, err + } + defer syscall.NetApiBufferFree(p0) + if entriesRead == 0 { + return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username) + } + entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead:entriesRead] + var sids []string + for _, entry := range entries { + if entry.Name == nil { + continue + } + sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name)) + if err != nil { + return nil, err + } + sids = append(sids, sid) + } + return sids, nil +} + +func newUser(uid, gid, dir, username, domain string) (*User, error) { + domainAndUser := domain + `\` + username + name, e := lookupFullName(domain, username, domainAndUser) + if e != nil { + return nil, e + } + u := &User{ + Uid: uid, + Gid: gid, + Username: domainAndUser, + Name: name, + HomeDir: dir, + } + return u, nil +} + +var ( + // unused variables (in this implementation) + // modified during test to exercise code paths in the cgo implementation. + userBuffer = 0 + groupBuffer = 0 +) + +func current() (*User, error) { + t, e := syscall.OpenCurrentProcessToken() + if e != nil { + return nil, e + } + defer t.Close() + u, e := t.GetTokenUser() + if e != nil { + return nil, e + } + pg, e := t.GetTokenPrimaryGroup() + if e != nil { + return nil, e + } + uid, e := u.User.Sid.String() + if e != nil { + return nil, e + } + gid, e := pg.PrimaryGroup.String() + if e != nil { + return nil, e + } + dir, e := t.GetUserProfileDirectory() + if e != nil { + return nil, e + } + username, domain, e := lookupUsernameAndDomain(u.User.Sid) + if e != nil { + return nil, e + } + return newUser(uid, gid, dir, username, domain) +} + +// lookupUserPrimaryGroup obtains the primary group SID for a user using this method: +// https://support.microsoft.com/en-us/help/297951/how-to-use-the-primarygroupid-attribute-to-find-the-primary-group-for +// The method follows this formula: domainRID + "-" + primaryGroupRID +func lookupUserPrimaryGroup(username, domain string) (string, error) { + // get the domain RID + sid, _, t, e := syscall.LookupSID("", domain) + if e != nil { + return "", e + } + if t != syscall.SidTypeDomain { + return "", fmt.Errorf("lookupUserPrimaryGroup: should be domain account type, not %d", t) + } + domainRID, e := sid.String() + if e != nil { + return "", e + } + // If the user has joined a domain use the RID of the default primary group + // called "Domain Users": + // https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems + // SID: S-1-5-21domain-513 + // + // The correct way to obtain the primary group of a domain user is + // probing the user primaryGroupID attribute in the server Active Directory: + // https://learn.microsoft.com/en-us/windows/win32/adschema/a-primarygroupid + // + // Note that the primary group of domain users should not be modified + // on Windows for performance reasons, even if it's possible to do that. + // The .NET Developer's Guide to Directory Services Programming - Page 409 + // https://books.google.bg/books?id=kGApqjobEfsC&lpg=PA410&ots=p7oo-eOQL7&dq=primary%20group%20RID&hl=bg&pg=PA409#v=onepage&q&f=false + joined, err := isDomainJoined() + if err == nil && joined { + return domainRID + "-513", nil + } + // For non-domain users call NetUserGetInfo() with level 4, which + // in this case would not have any network overhead. + // The primary group should not change from RID 513 here either + // but the group will be called "None" instead: + // https://www.adampalmer.me/iodigitalsec/2013/08/10/windows-null-session-enumeration/ + // "Group 'None' (RID: 513)" + u, e := syscall.UTF16PtrFromString(username) + if e != nil { + return "", e + } + d, e := syscall.UTF16PtrFromString(domain) + if e != nil { + return "", e + } + var p *byte + e = syscall.NetUserGetInfo(d, u, 4, &p) + if e != nil { + return "", e + } + defer syscall.NetApiBufferFree(p) + i := (*windows.UserInfo4)(unsafe.Pointer(p)) + return fmt.Sprintf("%s-%d", domainRID, i.PrimaryGroupID), nil +} + +func newUserFromSid(usid *syscall.SID) (*User, error) { + username, domain, e := lookupUsernameAndDomain(usid) + if e != nil { + return nil, e + } + gid, e := lookupUserPrimaryGroup(username, domain) + if e != nil { + return nil, e + } + uid, e := usid.String() + if e != nil { + return nil, e + } + // If this user has logged in at least once their home path should be stored + // in the registry under the specified SID. References: + // https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx + // https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles + // + // The registry is the most reliable way to find the home path as the user + // might have decided to move it outside of the default location, + // (e.g. C:\users). Reference: + // https://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-do-i-set-a-home-directory-outside-cusers-for-a/aed68262-1bf4-4a4d-93dc-7495193a440f + dir, e := findHomeDirInRegistry(uid) + if e != nil { + // If the home path does not exist in the registry, the user might + // have not logged in yet; fall back to using getProfilesDirectory(). + // Find the username based on a SID and append that to the result of + // getProfilesDirectory(). The domain is not relevant here. + dir, e = getProfilesDirectory() + if e != nil { + return nil, e + } + dir += `\` + username + } + return newUser(uid, gid, dir, username, domain) +} + +func lookupUser(username string) (*User, error) { + sid, _, t, e := syscall.LookupSID("", username) + if e != nil { + return nil, e + } + if t != syscall.SidTypeUser { + return nil, fmt.Errorf("user: should be user account type, not %d", t) + } + return newUserFromSid(sid) +} + +func lookupUserId(uid string) (*User, error) { + sid, e := syscall.StringToSid(uid) + if e != nil { + return nil, e + } + return newUserFromSid(sid) +} + +func lookupGroup(groupname string) (*Group, error) { + sid, err := lookupGroupName(groupname) + if err != nil { + return nil, err + } + return &Group{Name: groupname, Gid: sid}, nil +} + +func lookupGroupId(gid string) (*Group, error) { + sid, err := syscall.StringToSid(gid) + if err != nil { + return nil, err + } + groupname, _, t, err := sid.LookupAccount("") + if err != nil { + return nil, err + } + if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { + return nil, fmt.Errorf("lookupGroupId: should be group account type, not %d", t) + } + return &Group{Name: groupname, Gid: gid}, nil +} + +func listGroups(user *User) ([]string, error) { + sid, err := syscall.StringToSid(user.Uid) + if err != nil { + return nil, err + } + username, domain, err := lookupUsernameAndDomain(sid) + if err != nil { + return nil, err + } + sids, err := listGroupsForUsernameAndDomain(username, domain) + if err != nil { + return nil, err + } + // Add the primary group of the user to the list if it is not already there. + // This is done only to comply with the POSIX concept of a primary group. + for _, sid := range sids { + if sid == user.Gid { + return sids, nil + } + } + return append(sids, user.Gid), nil +} diff --git a/contrib/go/_std_1.22/src/os/user/user.go b/contrib/go/_std_1.22/src/os/user/user.go new file mode 100644 index 0000000000..0307d2ad6a --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/user.go @@ -0,0 +1,95 @@ +// Copyright 2011 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. + +/* +Package user allows user account lookups by name or id. + +For most Unix systems, this package has two internal implementations of +resolving user and group ids to names, and listing supplementary group IDs. +One is written in pure Go and parses /etc/passwd and /etc/group. The other +is cgo-based and relies on the standard C library (libc) routines such as +getpwuid_r, getgrnam_r, and getgrouplist. + +When cgo is available, and the required routines are implemented in libc +for a particular platform, cgo-based (libc-backed) code is used. +This can be overridden by using osusergo build tag, which enforces +the pure Go implementation. +*/ +package user + +import ( + "strconv" +) + +// These may be set to false in init() for a particular platform and/or +// build flags to let the tests know to skip tests of some features. +var ( + userImplemented = true + groupImplemented = true + groupListImplemented = true +) + +// User represents a user account. +type User struct { + // Uid is the user ID. + // On POSIX systems, this is a decimal number representing the uid. + // On Windows, this is a security identifier (SID) in a string format. + // On Plan 9, this is the contents of /dev/user. + Uid string + // Gid is the primary group ID. + // On POSIX systems, this is a decimal number representing the gid. + // On Windows, this is a SID in a string format. + // On Plan 9, this is the contents of /dev/user. + Gid string + // Username is the login name. + Username string + // Name is the user's real or display name. + // It might be blank. + // On POSIX systems, this is the first (or only) entry in the GECOS field + // list. + // On Windows, this is the user's display name. + // On Plan 9, this is the contents of /dev/user. + Name string + // HomeDir is the path to the user's home directory (if they have one). + HomeDir string +} + +// Group represents a grouping of users. +// +// On POSIX systems Gid contains a decimal number representing the group ID. +type Group struct { + Gid string // group ID + Name string // group name +} + +// UnknownUserIdError is returned by LookupId when a user cannot be found. +type UnknownUserIdError int + +func (e UnknownUserIdError) Error() string { + return "user: unknown userid " + strconv.Itoa(int(e)) +} + +// UnknownUserError is returned by Lookup when +// a user cannot be found. +type UnknownUserError string + +func (e UnknownUserError) Error() string { + return "user: unknown user " + string(e) +} + +// UnknownGroupIdError is returned by LookupGroupId when +// a group cannot be found. +type UnknownGroupIdError string + +func (e UnknownGroupIdError) Error() string { + return "group: unknown groupid " + string(e) +} + +// UnknownGroupError is returned by LookupGroup when +// a group cannot be found. +type UnknownGroupError string + +func (e UnknownGroupError) Error() string { + return "group: unknown group " + string(e) +} diff --git a/contrib/go/_std_1.22/src/os/user/ya.make b/contrib/go/_std_1.22/src/os/user/ya.make new file mode 100644 index 0000000000..3bf6d0640d --- /dev/null +++ b/contrib/go/_std_1.22/src/os/user/ya.make @@ -0,0 +1,40 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + cgo_listgroups_unix.go + cgo_lookup_syscall.go + cgo_lookup_unix.go + getgrouplist_syscall.go + lookup.go + user.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED) + SRCS( + cgo_listgroups_unix.go + cgo_lookup_unix.go + lookup.go + user.go + ) + +IF (CGO_ENABLED) + CGO_SRCS( + cgo_lookup_cgo.go + getgrouplist_unix.go + ) +ENDIF() +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + listgroups_unix.go + lookup.go + lookup_stubs.go + lookup_unix.go + user.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + lookup.go + lookup_windows.go + user.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/os/wait_unimp.go b/contrib/go/_std_1.22/src/os/wait_unimp.go new file mode 100644 index 0000000000..810e35da63 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/wait_unimp.go @@ -0,0 +1,21 @@ +// Copyright 2016 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. + +// aix, darwin, js/wasm, openbsd, solaris and wasip1/wasm don't implement +// waitid/wait6. + +//go:build aix || darwin || (js && wasm) || openbsd || solaris || wasip1 + +package os + +// blockUntilWaitable attempts to block until a call to p.Wait will +// succeed immediately, and reports whether it has done so. +// It does not actually call p.Wait. +// This version is used on systems that do not implement waitid, +// or where we have not implemented it yet. Note that this is racy: +// a call to Process.Signal can in an extremely unlikely case send a +// signal to the wrong process, see issue #13987. +func (p *Process) blockUntilWaitable() (bool, error) { + return false, nil +} diff --git a/contrib/go/_std_1.22/src/os/wait_waitid.go b/contrib/go/_std_1.22/src/os/wait_waitid.go new file mode 100644 index 0000000000..cd078f3522 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/wait_waitid.go @@ -0,0 +1,48 @@ +// Copyright 2016 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. + +// We used to use this code for Darwin, but according to issue #19314 +// waitid returns if the process is stopped, even when using WEXITED. + +//go:build linux + +package os + +import ( + "runtime" + "syscall" + "unsafe" +) + +const _P_PID = 1 + +// blockUntilWaitable attempts to block until a call to p.Wait will +// succeed immediately, and reports whether it has done so. +// It does not actually call p.Wait. +func (p *Process) blockUntilWaitable() (bool, error) { + // The waitid system call expects a pointer to a siginfo_t, + // which is 128 bytes on all Linux systems. + // On darwin/amd64, it requires 104 bytes. + // We don't care about the values it returns. + var siginfo [16]uint64 + psig := &siginfo[0] + var e syscall.Errno + for { + _, _, e = syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0) + if e != syscall.EINTR { + break + } + } + runtime.KeepAlive(p) + if e != 0 { + // waitid has been available since Linux 2.6.9, but + // reportedly is not available in Ubuntu on Windows. + // See issue 16610. + if e == syscall.ENOSYS { + return false, nil + } + return false, NewSyscallError("waitid", e) + } + return true, nil +} diff --git a/contrib/go/_std_1.22/src/os/ya.make b/contrib/go/_std_1.22/src/os/ya.make new file mode 100644 index 0000000000..8911bf552d --- /dev/null +++ b/contrib/go/_std_1.22/src/os/ya.make @@ -0,0 +1,113 @@ +GO_LIBRARY() +IF (OS_DARWIN AND ARCH_ARM64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_ARM64 AND NOT RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_DARWIN AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + dir.go + dir_darwin.go + endian_little.go + env.go + error.go + error_errno.go + error_posix.go + exec.go + exec_posix.go + exec_unix.go + executable.go + executable_darwin.go + file.go + file_open_unix.go + file_posix.go + file_unix.go + getwd.go + path.go + path_unix.go + pipe_unix.go + proc.go + rawconn.go + removeall_at.go + stat.go + stat_darwin.go + stat_unix.go + sticky_bsd.go + sys.go + sys_bsd.go + sys_unix.go + tempfile.go + types.go + types_unix.go + wait_unimp.go + zero_copy_stub.go + ) +ELSEIF (OS_LINUX AND ARCH_AARCH64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_AARCH64 AND NOT RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_LINUX AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + dir.go + dir_unix.go + dirent_linux.go + endian_little.go + env.go + error.go + error_errno.go + error_posix.go + exec.go + exec_posix.go + exec_unix.go + executable.go + executable_procfs.go + file.go + file_open_unix.go + file_posix.go + file_unix.go + getwd.go + path.go + path_unix.go + pipe2_unix.go + proc.go + rawconn.go + removeall_at.go + stat.go + stat_linux.go + stat_unix.go + sticky_notbsd.go + sys.go + sys_linux.go + sys_unix.go + tempfile.go + types.go + types_unix.go + wait_waitid.go + zero_copy_linux.go + ) +ELSEIF (OS_WINDOWS AND ARCH_X86_64 AND RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND RACE AND NOT CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND CGO_ENABLED OR OS_WINDOWS AND ARCH_X86_64 AND NOT RACE AND NOT CGO_ENABLED) + SRCS( + dir.go + dir_windows.go + endian_little.go + env.go + error.go + error_errno.go + error_posix.go + exec.go + exec_posix.go + exec_windows.go + executable.go + executable_windows.go + file.go + file_posix.go + file_windows.go + getwd.go + path.go + path_windows.go + proc.go + rawconn.go + removeall_noat.go + stat.go + stat_windows.go + sticky_notbsd.go + sys.go + sys_windows.go + tempfile.go + types.go + types_windows.go + zero_copy_stub.go + ) +ENDIF() +END() diff --git a/contrib/go/_std_1.22/src/os/zero_copy_linux.go b/contrib/go/_std_1.22/src/os/zero_copy_linux.go new file mode 100644 index 0000000000..7c45aefeee --- /dev/null +++ b/contrib/go/_std_1.22/src/os/zero_copy_linux.go @@ -0,0 +1,167 @@ +// Copyright 2020 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. + +package os + +import ( + "internal/poll" + "io" + "syscall" +) + +var ( + pollCopyFileRange = poll.CopyFileRange + pollSplice = poll.Splice + pollSendFile = poll.SendFile +) + +func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) { + pfd, network := getPollFDAndNetwork(w) + // TODO(panjf2000): same as File.spliceToFile. + if pfd == nil || !pfd.IsStream || !isUnixOrTCP(string(network)) { + return + } + + sc, err := f.SyscallConn() + if err != nil { + return + } + + rerr := sc.Read(func(fd uintptr) (done bool) { + written, err, handled = pollSendFile(pfd, int(fd), 1<<63-1) + return true + }) + + if err == nil { + err = rerr + } + + return written, handled, wrapSyscallError("sendfile", err) +} + +func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) { + // Neither copy_file_range(2) nor splice(2) supports destinations opened with + // O_APPEND, so don't bother to try zero-copy with these system calls. + // + // Visit https://man7.org/linux/man-pages/man2/copy_file_range.2.html#ERRORS and + // https://man7.org/linux/man-pages/man2/splice.2.html#ERRORS for details. + if f.appendMode { + return 0, false, nil + } + + written, handled, err = f.copyFileRange(r) + if handled { + return + } + return f.spliceToFile(r) +} + +func (f *File) spliceToFile(r io.Reader) (written int64, handled bool, err error) { + var ( + remain int64 + lr *io.LimitedReader + ) + if lr, r, remain = tryLimitedReader(r); remain <= 0 { + return 0, true, nil + } + + pfd, _ := getPollFDAndNetwork(r) + // TODO(panjf2000): run some tests to see if we should unlock the non-streams for splice. + // Streams benefit the most from the splice(2), non-streams are not even supported in old kernels + // where splice(2) will just return EINVAL; newer kernels support non-streams like UDP, but I really + // doubt that splice(2) could help non-streams, cuz they usually send small frames respectively + // and one splice call would result in one frame. + // splice(2) is suitable for large data but the generation of fragments defeats its edge here. + // Therefore, don't bother to try splice if the r is not a streaming descriptor. + if pfd == nil || !pfd.IsStream { + return + } + + var syscallName string + written, handled, syscallName, err = pollSplice(&f.pfd, pfd, remain) + + if lr != nil { + lr.N = remain - written + } + + return written, handled, wrapSyscallError(syscallName, err) +} + +func (f *File) copyFileRange(r io.Reader) (written int64, handled bool, err error) { + var ( + remain int64 + lr *io.LimitedReader + ) + if lr, r, remain = tryLimitedReader(r); remain <= 0 { + return 0, true, nil + } + + var src *File + switch v := r.(type) { + case *File: + src = v + case fileWithoutWriteTo: + src = v.File + default: + return 0, false, nil + } + + if src.checkValid("ReadFrom") != nil { + // Avoid returning the error as we report handled as false, + // leave further error handling as the responsibility of the caller. + return 0, false, nil + } + + written, handled, err = pollCopyFileRange(&f.pfd, &src.pfd, remain) + if lr != nil { + lr.N -= written + } + return written, handled, wrapSyscallError("copy_file_range", err) +} + +// getPollFDAndNetwork tries to get the poll.FD and network type from the given interface +// by expecting the underlying type of i to be the implementation of syscall.Conn +// that contains a *net.rawConn. +func getPollFDAndNetwork(i any) (*poll.FD, poll.String) { + sc, ok := i.(syscall.Conn) + if !ok { + return nil, "" + } + rc, err := sc.SyscallConn() + if err != nil { + return nil, "" + } + irc, ok := rc.(interface { + PollFD() *poll.FD + Network() poll.String + }) + if !ok { + return nil, "" + } + return irc.PollFD(), irc.Network() +} + +// tryLimitedReader tries to assert the io.Reader to io.LimitedReader, it returns the io.LimitedReader, +// the underlying io.Reader and the remaining amount of bytes if the assertion succeeds, +// otherwise it just returns the original io.Reader and the theoretical unlimited remaining amount of bytes. +func tryLimitedReader(r io.Reader) (*io.LimitedReader, io.Reader, int64) { + var remain int64 = 1<<63 - 1 // by default, copy until EOF + + lr, ok := r.(*io.LimitedReader) + if !ok { + return nil, r, remain + } + + remain = lr.N + return lr, lr.R, remain +} + +func isUnixOrTCP(network string) bool { + switch network { + case "tcp", "tcp4", "tcp6", "unix": + return true + default: + return false + } +} diff --git a/contrib/go/_std_1.22/src/os/zero_copy_stub.go b/contrib/go/_std_1.22/src/os/zero_copy_stub.go new file mode 100644 index 0000000000..9ec5808101 --- /dev/null +++ b/contrib/go/_std_1.22/src/os/zero_copy_stub.go @@ -0,0 +1,17 @@ +// Copyright 2020 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. + +//go:build !linux + +package os + +import "io" + +func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) { + return 0, false, nil +} + +func (f *File) readFrom(r io.Reader) (n int64, handled bool, err error) { + return 0, false, nil +} |