package os
import (
"errors"
"internal/syscall/unix"
"runtime"
"syscall"
"time"
)
type sysfdType = int
func openRootNolog(name string ) (*Root , error ) {
var fd int
err := ignoringEINTR (func () error {
var err error
fd , _, err = open (name , syscall .O_CLOEXEC , 0 )
return err
})
if err != nil {
return nil , &PathError {Op : "open" , Path : name , Err : err }
}
return newRoot (fd , name )
}
func newRoot(fd int , name string ) (*Root , error ) {
var fs fileStat
err := ignoringEINTR (func () error {
return syscall .Fstat (fd , &fs .sys )
})
fillFileStatFromSys (&fs , name )
if err == nil && !fs .IsDir () {
syscall .Close (fd )
return nil , &PathError {Op : "open" , Path : name , Err : errors .New ("not a directory" )}
}
if !supportsCloseOnExec {
syscall .CloseOnExec (fd )
}
r := &Root {&root {
fd : fd ,
name : name ,
}}
r .root .cleanup = runtime .AddCleanup (r , func (f *root ) { f .Close () }, r .root )
return r , nil
}
func openRootInRoot(r *Root , name string ) (*Root , error ) {
fd , err := doInRoot (r , name , nil , func (parent int , name string ) (fd int , err error ) {
ignoringEINTR (func () error {
fd , err = unix .Openat (parent , name , syscall .O_NOFOLLOW |syscall .O_CLOEXEC , 0 )
if isNoFollowErr (err ) {
err = checkSymlink (parent , name , err )
}
return err
})
return fd , err
})
if err != nil {
return nil , &PathError {Op : "openat" , Path : name , Err : err }
}
return newRoot (fd , name )
}
func rootOpenFileNolog(root *Root , name string , flag int , perm FileMode ) (*File , error ) {
fd , err := doInRoot (root , name , nil , func (parent int , name string ) (fd int , err error ) {
ignoringEINTR (func () error {
fd , err = unix .Openat (parent , name , syscall .O_NOFOLLOW |syscall .O_CLOEXEC |flag , uint32 (perm ))
if err != nil {
isCreateExcl := flag &(O_CREATE |O_EXCL ) == (O_CREATE | O_EXCL )
if !isCreateExcl && (isNoFollowErr (err ) || err == syscall .ENOTDIR ) {
err = checkSymlink (parent , name , err )
}
if isCreateExcl && err == syscall .ELOOP {
err = syscall .EEXIST
}
}
return err
})
return fd , err
})
if err != nil {
return nil , &PathError {Op : "openat" , Path : name , Err : err }
}
f := newFile (fd , joinPath (root .Name (), name ), kindOpenFile , unix .HasNonblockFlag (flag ))
return f , nil
}
func rootOpenDir(parent int , name string ) (int , error ) {
var (
fd int
err error
)
ignoringEINTR (func () error {
fd , err = unix .Openat (parent , name , syscall .O_NOFOLLOW |syscall .O_CLOEXEC |syscall .O_DIRECTORY , 0 )
if isNoFollowErr (err ) || err == syscall .ENOTDIR {
err = checkSymlink (parent , name , err )
} else if err == syscall .ENOTSUP || err == syscall .EOPNOTSUPP {
err = syscall .ENOTDIR
}
return err
})
return fd , err
}
func rootStat(r *Root , name string , lstat bool ) (FileInfo , error ) {
fi , err := doInRoot (r , name , nil , func (parent sysfdType , n string ) (FileInfo , error ) {
var fs fileStat
if err := unix .Fstatat (parent , n , &fs .sys , unix .AT_SYMLINK_NOFOLLOW ); err != nil {
return nil , err
}
fillFileStatFromSys (&fs , name )
if !lstat && fs .Mode ()&ModeSymlink != 0 {
return nil , checkSymlink (parent , n , syscall .ELOOP )
}
return &fs , nil
})
if err != nil {
return nil , &PathError {Op : "statat" , Path : name , Err : err }
}
return fi , nil
}
func rootSymlink(r *Root , oldname , newname string ) error {
_ , err := doInRoot (r , newname , nil , func (parent sysfdType , name string ) (struct {}, error ) {
return struct {}{}, symlinkat (oldname , parent , name )
})
if err != nil {
return &LinkError {"symlinkat" , oldname , newname , err }
}
return nil
}
func afterResolvingSymlink(parent int , name string , f func () error ) error {
if err := checkSymlink (parent , name , nil ); err != nil {
return err
}
return f ()
}
func chmodat(parent int , name string , mode FileMode ) error {
return afterResolvingSymlink (parent , name , func () error {
return ignoringEINTR (func () error {
return unix .Fchmodat (parent , name , syscallMode (mode ), unix .AT_SYMLINK_NOFOLLOW )
})
})
}
func chownat(parent int , name string , uid , gid int ) error {
return afterResolvingSymlink (parent , name , func () error {
return ignoringEINTR (func () error {
return unix .Fchownat (parent , name , uid , gid , unix .AT_SYMLINK_NOFOLLOW )
})
})
}
func lchownat(parent int , name string , uid , gid int ) error {
return ignoringEINTR (func () error {
return unix .Fchownat (parent , name , uid , gid , unix .AT_SYMLINK_NOFOLLOW )
})
}
func chtimesat(parent int , name string , atime time .Time , mtime time .Time ) error {
return afterResolvingSymlink (parent , name , func () error {
return ignoringEINTR (func () error {
utimes := chtimesUtimes (atime , mtime )
return unix .Utimensat (parent , name , &utimes , unix .AT_SYMLINK_NOFOLLOW )
})
})
}
func mkdirat(fd int , name string , perm FileMode ) error {
return ignoringEINTR (func () error {
return unix .Mkdirat (fd , name , syscallMode (perm ))
})
}
func removeat(fd int , name string ) error {
e := ignoringEINTR (func () error {
return unix .Unlinkat (fd , name , 0 )
})
if e == nil {
return nil
}
e1 := ignoringEINTR (func () error {
return unix .Unlinkat (fd , name , unix .AT_REMOVEDIR )
})
if e1 == nil {
return nil
}
if e1 != syscall .ENOTDIR {
return e1
}
return e
}
func removefileat(fd int , name string ) error {
return ignoringEINTR (func () error {
return unix .Unlinkat (fd , name , 0 )
})
}
func removedirat(fd int , name string ) error {
return ignoringEINTR (func () error {
return unix .Unlinkat (fd , name , unix .AT_REMOVEDIR )
})
}
func renameat(oldfd int , oldname string , newfd int , newname string ) error {
return unix .Renameat (oldfd , oldname , newfd , newname )
}
func linkat(oldfd int , oldname string , newfd int , newname string ) error {
return unix .Linkat (oldfd , oldname , newfd , newname , 0 )
}
func symlinkat(oldname string , newfd int , newname string ) error {
return unix .Symlinkat (oldname , newfd , newname )
}
func modeAt(parent int , name string ) (FileMode , error ) {
var fs fileStat
if err := unix .Fstatat (parent , name , &fs .sys , unix .AT_SYMLINK_NOFOLLOW ); err != nil {
return 0 , err
}
fillFileStatFromSys (&fs , name )
return fs .mode , nil
}
func checkSymlink(parent int , name string , origError error ) error {
link , err := readlinkat (parent , name )
if err != nil {
return origError
}
return errSymlink (link )
}
func readlinkat(fd int , name string ) (string , error ) {
for len := 128 ; ; len *= 2 {
b := make ([]byte , len )
var (
n int
e error
)
ignoringEINTR (func () error {
n , e = unix .Readlinkat (fd , name , b )
return e
})
if e == syscall .ERANGE {
continue
}
if e != nil {
return "" , e
}
n = max (n , 0 )
if n < len {
return string (b [0 :n ]), nil
}
}
}
The pages are generated with Golds v0.7.7-preview . (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 .