// Copyright 2024 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 || linux || netbsd || openbsd || solaris || windows || wasip1package osimport ()// root implementation for platforms with a function to open a file// relative to a directory.type root struct { name string// refs is incremented while an operation is using fd. // closed is set when Close is called. // fd is closed when closed is true and refs is 0. mu sync.Mutex fd sysfdType refs int// number of active operations closed bool// set when closed}func ( *root) () error { .mu.Lock()defer .mu.Unlock()if !.closed && .refs == 0 {syscall.Close(.fd) } .closed = trueruntime.SetFinalizer(, nil) // no need for a finalizer any morereturnnil}func ( *root) () error { .mu.Lock()defer .mu.Unlock()if .closed {returnErrClosed } .refs++returnnil}func ( *root) () { .mu.Lock()defer .mu.Unlock()if .refs <= 0 {panic("bad Root refcount") } .refs--if .closed && .refs == 0 {syscall.Close(.fd) }}func ( *root) () string {return .name}func rootMkdir( *Root, string, FileMode) error { , := doInRoot(, , func( sysfdType, string) (struct{}, error) {returnstruct{}{}, mkdirat(, , ) })if != nil {return &PathError{Op: "mkdirat", Path: , Err: } }return}func rootRemove( *Root, string) error { , := doInRoot(, , func( sysfdType, string) (struct{}, error) {returnstruct{}{}, removeat(, ) })if != nil {return &PathError{Op: "removeat", Path: , Err: } }return}// doInRoot performs an operation on a path in a Root.//// It opens the directory containing the final element of the path,// and calls f with the directory FD and name of the final element.//// If the path refers to a symlink which should be followed,// then f must return errSymlink.// doInRoot will follow the symlink and call f again.func doInRoot[ any]( *Root, string, func( sysfdType, string) (, error)) ( , error) {if := .root.incref(); != nil {return , }defer .root.decref() , := splitPathInRoot(, nil, nil)if != nil {return , } := .root.fd := deferfunc() {if != {syscall.Close() } }()// When resolving .. path components, we restart path resolution from the root. // (We can't openat(dir, "..") to move up to the parent directory, // because dir may have moved since we opened it.) // To limit how many opens a malicious path can cause us to perform, we set // a limit on the total number of path steps and the total number of restarts // caused by .. components. If *both* limits are exceeded, we halt the operation.const = 255const = 8 := 0 := 0 := 0 := 0for { ++if > && > {return , syscall.ENAMETOOLONG }if [] == ".." {// Resolve one or more parent ("..") path components. // // Rewrite the original path, // removing the elements eliminated by ".." components, // and start over from the beginning. ++ := + 1for < len() && [] == ".." { ++ } := - if > {return , errPathEscapes } = slices.Delete(, -, ) = 0if != {syscall.Close() } = continue }if == len()-1 {// This is the last path element. // Call f to decide what to do with it. // If f returns errSymlink, this element is a symlink // which should be followed. , = (, [])if , := .(errSymlink); ! {return , } } else {varsysfdType , = rootOpenDir(, [])if == nil {if != {syscall.Close() } = } elseif , := .(errSymlink); ! {return , } }if , := .(errSymlink); { ++if > rootMaxSymlinks {return , syscall.ELOOP } , := splitPathInRoot(string(), [:], [+1:])if != nil {return , }iflen() < || !slices.Equal([:], [:]) {// Some component in the path which we have already traversed // has changed. We need to restart parsing from the root. = 0if != {syscall.Close() } = } = continue } ++ }}// errSymlink reports that a file being operated on is actually a symlink,// and the target of that symlink.type errSymlink stringfunc (errSymlink) () string { panic("errSymlink is not user-visible") }
The pages are generated with Goldsv0.7.3-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.