package sysfs
import (
"io"
"io/fs"
"os"
"time"
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/fsapi"
"github.com/tetratelabs/wazero/sys"
)
func NewStdioFile (stdin bool , f fs .File ) (fsapi .File , error ) {
var mode fs .FileMode
if st , err := f .Stat (); err != nil {
return nil , err
} else {
mode = st .Mode ()
}
var flag experimentalsys .Oflag
if stdin {
flag = experimentalsys .O_RDONLY
} else {
flag = experimentalsys .O_WRONLY
}
var file fsapi .File
if of , ok := f .(*os .File ); ok {
file = newOsFile ("" , flag , 0 , of )
} else {
file = &fsFile {file : f }
}
return &stdioFile {File : file , st : sys .Stat_t {Mode : mode , Nlink : 1 }}, nil
}
func OpenFile (path string , flag experimentalsys .Oflag , perm fs .FileMode ) (*os .File , experimentalsys .Errno ) {
return openFile (path , flag , perm )
}
func OpenOSFile (path string , flag experimentalsys .Oflag , perm fs .FileMode ) (experimentalsys .File , experimentalsys .Errno ) {
f , errno := OpenFile (path , flag , perm )
if errno != 0 {
return nil , errno
}
return newOsFile (path , flag , perm , f ), 0
}
func OpenFSFile (fs fs .FS , path string , flag experimentalsys .Oflag , perm fs .FileMode ) (experimentalsys .File , experimentalsys .Errno ) {
if flag &experimentalsys .O_DIRECTORY != 0 && flag &(experimentalsys .O_WRONLY |experimentalsys .O_RDWR ) != 0 {
return nil , experimentalsys .EISDIR
}
f , err := fs .Open (path )
if errno := experimentalsys .UnwrapOSError (err ); errno != 0 {
return nil , errno
}
return &fsFile {fs : fs , name : path , file : f }, 0
}
type stdioFile struct {
fsapi .File
st sys .Stat_t
}
func (f *stdioFile ) SetAppend (bool ) experimentalsys .Errno {
return 0
}
func (f *stdioFile ) IsAppend () bool {
return true
}
func (f *stdioFile ) Stat () (sys .Stat_t , experimentalsys .Errno ) {
return f .st , 0
}
func (f *stdioFile ) Close () experimentalsys .Errno {
return 0
}
type fsFile struct {
experimentalsys .UnimplementedFile
fs fs .FS
name string
file fs .File
reopenDir bool
closed bool
cachedSt *cachedStat
}
type cachedStat struct {
dev uint64
ino sys .Inode
isDir bool
}
func (f *fsFile ) cachedStat () (dev uint64 , ino sys .Inode , isDir bool , errno experimentalsys .Errno ) {
if f .cachedSt == nil {
if _, errno = f .Stat (); errno != 0 {
return
}
}
return f .cachedSt .dev , f .cachedSt .ino , f .cachedSt .isDir , 0
}
func (f *fsFile ) Dev () (uint64 , experimentalsys .Errno ) {
dev , _ , _ , errno := f .cachedStat ()
return dev , errno
}
func (f *fsFile ) Ino () (sys .Inode , experimentalsys .Errno ) {
_ , ino , _ , errno := f .cachedStat ()
return ino , errno
}
func (f *fsFile ) IsDir () (bool , experimentalsys .Errno ) {
_ , _ , isDir , errno := f .cachedStat ()
return isDir , errno
}
func (f *fsFile ) IsAppend () bool {
return false
}
func (f *fsFile ) SetAppend (bool ) (errno experimentalsys .Errno ) {
return fileError (f , f .closed , experimentalsys .ENOSYS )
}
func (f *fsFile ) Stat () (sys .Stat_t , experimentalsys .Errno ) {
if f .closed {
return sys .Stat_t {}, experimentalsys .EBADF
}
st , errno := statFile (f .file )
switch errno {
case 0 :
f .cachedSt = &cachedStat {dev : st .Dev , ino : st .Ino , isDir : st .Mode &fs .ModeDir == fs .ModeDir }
case experimentalsys .EIO :
errno = experimentalsys .EBADF
}
return st , errno
}
func (f *fsFile ) Read (buf []byte ) (n int , errno experimentalsys .Errno ) {
if n , errno = read (f .file , buf ); errno != 0 {
errno = fileError (f , f .closed , errno )
}
return
}
func (f *fsFile ) Pread (buf []byte , off int64 ) (n int , errno experimentalsys .Errno ) {
if ra , ok := f .file .(io .ReaderAt ); ok {
if n , errno = pread (ra , buf , off ); errno != 0 {
errno = fileError (f , f .closed , errno )
}
return
}
if rs , ok := f .file .(io .ReadSeeker ); ok {
currentOffset , err := rs .Seek (0 , io .SeekCurrent )
if err != nil {
return 0 , fileError (f , f .closed , experimentalsys .UnwrapOSError (err ))
}
defer func () { _, _ = rs .Seek (currentOffset , io .SeekStart ) }()
if off != currentOffset {
if _, err = rs .Seek (off , io .SeekStart ); err != nil {
return 0 , fileError (f , f .closed , experimentalsys .UnwrapOSError (err ))
}
}
n , err = rs .Read (buf )
if errno = experimentalsys .UnwrapOSError (err ); errno != 0 {
errno = fileError (f , f .closed , errno )
}
} else {
errno = experimentalsys .ENOSYS
}
return
}
func (f *fsFile ) Seek (offset int64 , whence int ) (newOffset int64 , errno experimentalsys .Errno ) {
var isDir bool
if offset == 0 && whence == io .SeekStart {
if isDir , errno = f .IsDir (); errno == 0 && isDir {
f .reopenDir = true
return
}
}
if s , ok := f .file .(io .Seeker ); ok {
if newOffset , errno = seek (s , offset , whence ); errno != 0 {
errno = fileError (f , f .closed , errno )
}
} else {
errno = experimentalsys .ENOSYS
}
return
}
func (f *fsFile ) Readdir (n int ) (dirents []experimentalsys .Dirent , errno experimentalsys .Errno ) {
if f .closed {
errno = experimentalsys .EBADF
return
}
if f .reopenDir {
f .reopenDir = false
if errno = adjustReaddirErr (f , f .closed , f .rewindDir ()); errno != 0 {
return
}
}
if of , ok := f .file .(readdirFile ); ok {
if dirents , errno = readdir (of , "" , n ); errno != 0 {
errno = adjustReaddirErr (f , f .closed , errno )
}
return
}
if rdf , ok := f .file .(fs .ReadDirFile ); ok {
entries , e := rdf .ReadDir (n )
if errno = adjustReaddirErr (f , f .closed , e ); errno != 0 {
return
}
dirents = make ([]experimentalsys .Dirent , 0 , len (entries ))
for _ , e := range entries {
dirents = append (dirents , experimentalsys .Dirent {Name : e .Name (), Type : e .Type ()})
}
} else {
errno = experimentalsys .EBADF
}
return
}
func (f *fsFile ) Write (buf []byte ) (n int , errno experimentalsys .Errno ) {
if w , ok := f .file .(io .Writer ); ok {
if n , errno = write (w , buf ); errno != 0 {
errno = fileError (f , f .closed , errno )
}
} else {
errno = experimentalsys .ENOSYS
}
return
}
func (f *fsFile ) Pwrite (buf []byte , off int64 ) (n int , errno experimentalsys .Errno ) {
if wa , ok := f .file .(io .WriterAt ); ok {
if n , errno = pwrite (wa , buf , off ); errno != 0 {
errno = fileError (f , f .closed , errno )
}
} else {
errno = experimentalsys .ENOSYS
}
return
}
func (f *fsFile ) Close () experimentalsys .Errno {
if f .closed {
return 0
}
f .closed = true
return f .close ()
}
func (f *fsFile ) close () experimentalsys .Errno {
return experimentalsys .UnwrapOSError (f .file .Close ())
}
func (f *fsFile ) IsNonblock () bool {
return false
}
func (f *fsFile ) SetNonblock (bool ) experimentalsys .Errno {
return experimentalsys .ENOSYS
}
func (f *fsFile ) Poll (fsapi .Pflag , int32 ) (ready bool , errno experimentalsys .Errno ) {
return false , experimentalsys .ENOSYS
}
func dirError(f experimentalsys .File , isClosed bool , errno experimentalsys .Errno ) experimentalsys .Errno {
if vErrno := validate (f , isClosed , false , true ); vErrno != 0 {
return vErrno
}
return errno
}
func fileError(f experimentalsys .File , isClosed bool , errno experimentalsys .Errno ) experimentalsys .Errno {
if vErrno := validate (f , isClosed , true , false ); vErrno != 0 {
return vErrno
}
return errno
}
func validate(f experimentalsys .File , isClosed , wantFile , wantDir bool ) experimentalsys .Errno {
if isClosed {
return experimentalsys .EBADF
}
isDir , errno := f .IsDir ()
if errno != 0 {
return errno
}
if wantFile && isDir {
return experimentalsys .EISDIR
} else if wantDir && !isDir {
return experimentalsys .ENOTDIR
}
return 0
}
func read(r io .Reader , buf []byte ) (n int , errno experimentalsys .Errno ) {
if len (buf ) == 0 {
return 0 , 0
}
n , err := r .Read (buf )
return n , experimentalsys .UnwrapOSError (err )
}
func pread(ra io .ReaderAt , buf []byte , off int64 ) (n int , errno experimentalsys .Errno ) {
if len (buf ) == 0 {
return 0 , 0
}
n , err := ra .ReadAt (buf , off )
return n , experimentalsys .UnwrapOSError (err )
}
func seek(s io .Seeker , offset int64 , whence int ) (int64 , experimentalsys .Errno ) {
if uint (whence ) > io .SeekEnd {
return 0 , experimentalsys .EINVAL
}
newOffset , err := s .Seek (offset , whence )
return newOffset , experimentalsys .UnwrapOSError (err )
}
func (f *fsFile ) rewindDir () experimentalsys .Errno {
file , err := f .fs .Open (f .name )
if err != nil {
return experimentalsys .UnwrapOSError (err )
}
fi , err := file .Stat ()
if err != nil {
return experimentalsys .UnwrapOSError (err )
}
if !fi .IsDir () {
return experimentalsys .ENOTDIR
}
_ = f .file .Close ()
f .file = file
return 0
}
type readdirFile interface {
Readdir(n int ) ([]fs .FileInfo , error )
}
func readdir(f readdirFile , path string , n int ) (dirents []experimentalsys .Dirent , errno experimentalsys .Errno ) {
fis , e := f .Readdir (n )
if errno = experimentalsys .UnwrapOSError (e ); errno != 0 {
return
}
dirents = make ([]experimentalsys .Dirent , 0 , len (fis ))
var ino sys .Inode
for fi := range fis {
t := fis [fi ]
if ino , errno = inoFromFileInfo (path , t ); errno != 0 {
return
}
dirents = append (dirents , experimentalsys .Dirent {Name : t .Name (), Ino : ino , Type : t .Mode ().Type ()})
}
return
}
func write(w io .Writer , buf []byte ) (n int , errno experimentalsys .Errno ) {
if len (buf ) == 0 {
return 0 , 0
}
n , err := w .Write (buf )
return n , experimentalsys .UnwrapOSError (err )
}
func pwrite(w io .WriterAt , buf []byte , off int64 ) (n int , errno experimentalsys .Errno ) {
if len (buf ) == 0 {
return 0 , 0
}
n , err := w .WriteAt (buf , off )
return n , experimentalsys .UnwrapOSError (err )
}
func chtimes(path string , atim , mtim int64 ) (errno experimentalsys .Errno ) {
if atim == experimentalsys .UTIME_OMIT && mtim == experimentalsys .UTIME_OMIT {
return
}
var st sys .Stat_t
if atim == experimentalsys .UTIME_OMIT || mtim == experimentalsys .UTIME_OMIT {
if st , errno = stat (path ); errno != 0 {
return
}
}
var atime , mtime time .Time
if atim == experimentalsys .UTIME_OMIT {
atime = epochNanosToTime (st .Atim )
mtime = epochNanosToTime (mtim )
} else if mtim == experimentalsys .UTIME_OMIT {
atime = epochNanosToTime (atim )
mtime = epochNanosToTime (st .Mtim )
} else {
atime = epochNanosToTime (atim )
mtime = epochNanosToTime (mtim )
}
return experimentalsys .UnwrapOSError (os .Chtimes (path , atime , mtime ))
}
func epochNanosToTime(epochNanos int64 ) time .Time {
seconds := epochNanos / 1e9
nanos := epochNanos % 1e9
return time .Unix (seconds , nanos )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .