// 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 (js && wasm) || plan9 package os import ( "errors" "sync/atomic" ) // root implementation for platforms with no openat. // Currently plan9 and js. type root struct { name string closed atomic.Bool } // openRootNolog is OpenRoot. func openRootNolog(name string) (*Root, error) { r, err := newRoot(name) if err != nil { return nil, &PathError{Op: "open", Path: name, Err: err} } return r, nil } // openRootInRoot is Root.OpenRoot. func openRootInRoot(r *Root, name string) (*Root, error) { if err := checkPathEscapes(r, name); err != nil { return nil, &PathError{Op: "openat", Path: name, Err: err} } r, err := newRoot(joinPath(r.root.name, name)) if err != nil { return nil, &PathError{Op: "openat", Path: name, Err: err} } return r, nil } // newRoot returns a new Root. // If fd is not a directory, it closes it and returns an error. func newRoot(name string) (*Root, error) { fi, err := Stat(name) if err != nil { return nil, err.(*PathError).Err } if !fi.IsDir() { return nil, errors.New("not a directory") } return &Root{root{name: name}}, nil } func (r *root) Close() error { // For consistency with platforms where Root.Close closes a handle, // mark the Root as closed and return errors from future calls. r.closed.Store(true) return nil } func (r *root) Name() string { return r.name } // rootOpenFileNolog is Root.OpenFile. func rootOpenFileNolog(r *Root, name string, flag int, perm FileMode) (*File, error) { if err := checkPathEscapes(r, name); err != nil { return nil, &PathError{Op: "openat", Path: name, Err: err} } f, err := openFileNolog(joinPath(r.root.name, name), flag, perm) if err != nil { return nil, &PathError{Op: "openat", Path: name, Err: underlyingError(err)} } return f, nil } func rootStat(r *Root, name string, lstat bool) (FileInfo, error) { var fi FileInfo var err error if lstat { err = checkPathEscapesLstat(r, name) if err == nil { fi, err = Lstat(joinPath(r.root.name, name)) } } else { err = checkPathEscapes(r, name) if err == nil { fi, err = Stat(joinPath(r.root.name, name)) } } if err != nil { return nil, &PathError{Op: "statat", Path: name, Err: underlyingError(err)} } return fi, nil } func rootMkdir(r *Root, name string, perm FileMode) error { if err := checkPathEscapes(r, name); err != nil { return &PathError{Op: "mkdirat", Path: name, Err: err} } if err := Mkdir(joinPath(r.root.name, name), perm); err != nil { return &PathError{Op: "mkdirat", Path: name, Err: underlyingError(err)} } return nil } func rootRemove(r *Root, name string) error { if err := checkPathEscapesLstat(r, name); err != nil { return &PathError{Op: "removeat", Path: name, Err: err} } if err := Remove(joinPath(r.root.name, name)); err != nil { return &PathError{Op: "removeat", Path: name, Err: underlyingError(err)} } return nil }