Initial addition of 9p code to Podman

This includes two new hidden commands: a 9p server,
`podman machine server9p`, and a 9p client,
`podman machine client9p` with `server9p` currently only
configured to run on Windows and serve 9p via HyperV vsock, and
`client9p` only configured to run on Linux. The server is run by
`podman machine start` and has the same lifespan as gvproxy
(waits for the gvproxy PID to die before shutting down). The
client is run inside the VM, also by `podman machine start`, and
mounts uses kernel 9p mount code to complete the mount. It's
unfortunately not possible to use mount directly without the
wrapper; we need to set up the vsock and pass it to mount as an
FD.

In theory this can be generalized so that the server can run
anywhere and over almost any transport, but I haven't done this
here as I don't think we have a usecase other than HyperV right
now.

[NO NEW TESTS NEEDED] This requires changes to Podman in the VM,
so we need to wait until a build with this lands in FCOS to test.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2023-09-11 13:41:15 -04:00
committed by Matt Heon
parent 2972f5941f
commit 642fa98976
93 changed files with 15119 additions and 29 deletions

202
vendor/github.com/hugelgupf/p9/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,281 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package localfs exposes the host's local file system as a p9.File.
package localfs
import (
"os"
"path"
"github.com/hugelgupf/p9/fsimpl/templatefs"
"github.com/hugelgupf/p9/internal"
"github.com/hugelgupf/p9/linux"
"github.com/hugelgupf/p9/p9"
)
type attacher struct {
root string
}
var (
_ p9.Attacher = &attacher{}
)
// RootAttacher attaches at the host file system's root.
func RootAttacher() p9.Attacher {
return &attacher{root: "/"}
}
// Attacher returns an attacher that exposes files under root.
func Attacher(root string) p9.Attacher {
if len(root) == 0 {
root = "/"
}
return &attacher{root: root}
}
// Attach implements p9.Attacher.Attach.
func (a *attacher) Attach() (p9.File, error) {
umask(0)
return &Local{path: a.root}, nil
}
// Local is a p9.File.
type Local struct {
p9.DefaultWalkGetAttr
templatefs.NoopFile
path string
file *os.File
}
var (
_ p9.File = &Local{}
)
// info constructs a QID for this file.
func (l *Local) info() (p9.QID, os.FileInfo, error) {
var (
qid p9.QID
fi os.FileInfo
err error
)
// Stat the file.
if l.file != nil {
fi, err = l.file.Stat()
} else {
fi, err = os.Lstat(l.path)
}
if err != nil {
return qid, nil, err
}
// Construct the QID type.
qid.Type = p9.ModeFromOS(fi.Mode()).QIDType()
// Save the path from the Ino.
ninePath, err := localToQid(l.path, fi)
if err != nil {
return qid, nil, err
}
qid.Path = ninePath
return qid, fi, nil
}
// Walk implements p9.File.Walk.
func (l *Local) Walk(names []string) ([]p9.QID, p9.File, error) {
var qids []p9.QID
last := &Local{path: l.path}
// A walk with no names is a copy of self.
if len(names) == 0 {
return nil, last, nil
}
for _, name := range names {
c := &Local{path: path.Join(last.path, name)}
qid, _, err := c.info()
if err != nil {
return nil, nil, err
}
qids = append(qids, qid)
last = c
}
return qids, last, nil
}
// FSync implements p9.File.FSync.
func (l *Local) FSync() error {
return l.file.Sync()
}
// GetAttr implements p9.File.GetAttr.
//
// Not fully implemented.
func (l *Local) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) {
qid, fi, err := l.info()
if err != nil {
return qid, p9.AttrMask{}, p9.Attr{}, err
}
stat := internal.InfoToStat(fi)
attr := &p9.Attr{
Mode: p9.FileMode(stat.Mode),
UID: p9.UID(stat.Uid),
GID: p9.GID(stat.Gid),
NLink: p9.NLink(stat.Nlink),
RDev: p9.Dev(stat.Rdev),
Size: uint64(stat.Size),
BlockSize: uint64(stat.Blksize),
Blocks: uint64(stat.Blocks),
ATimeSeconds: uint64(stat.Atim.Sec),
ATimeNanoSeconds: uint64(stat.Atim.Nsec),
MTimeSeconds: uint64(stat.Mtim.Sec),
MTimeNanoSeconds: uint64(stat.Mtim.Nsec),
CTimeSeconds: uint64(stat.Ctim.Sec),
CTimeNanoSeconds: uint64(stat.Ctim.Nsec),
}
return qid, req, *attr, nil
}
// Close implements p9.File.Close.
func (l *Local) Close() error {
if l.file != nil {
// We don't set l.file = nil, as Close is called by servers
// only in Clunk. Clunk should release the last (direct)
// reference to this file.
return l.file.Close()
}
return nil
}
// Open implements p9.File.Open.
func (l *Local) Open(mode p9.OpenFlags) (p9.QID, uint32, error) {
qid, _, err := l.info()
if err != nil {
return qid, 0, err
}
// Do the actual open.
f, err := os.OpenFile(l.path, int(mode), 0)
if err != nil {
return qid, 0, err
}
l.file = f
return qid, 0, nil
}
// ReadAt implements p9.File.ReadAt.
func (l *Local) ReadAt(p []byte, offset int64) (int, error) {
return l.file.ReadAt(p, offset)
}
// Lock implements p9.File.Lock.
func (l *Local) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
return l.lock(pid, locktype, flags, start, length, client)
}
// WriteAt implements p9.File.WriteAt.
func (l *Local) WriteAt(p []byte, offset int64) (int, error) {
return l.file.WriteAt(p, offset)
}
// Create implements p9.File.Create.
func (l *Local) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
newName := path.Join(l.path, name)
f, err := os.OpenFile(newName, int(mode)|os.O_CREATE|os.O_EXCL, os.FileMode(permissions))
if err != nil {
return nil, p9.QID{}, 0, err
}
l2 := &Local{path: newName, file: f}
qid, _, err := l2.info()
if err != nil {
l2.Close()
return nil, p9.QID{}, 0, err
}
return l2, qid, 0, nil
}
// Mkdir implements p9.File.Mkdir.
//
// Not properly implemented.
func (l *Local) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
if err := os.Mkdir(path.Join(l.path, name), os.FileMode(permissions)); err != nil {
return p9.QID{}, err
}
// Blank QID.
return p9.QID{}, nil
}
// Symlink implements p9.File.Symlink.
//
// Not properly implemented.
func (l *Local) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
if err := os.Symlink(oldname, path.Join(l.path, newname)); err != nil {
return p9.QID{}, err
}
// Blank QID.
return p9.QID{}, nil
}
// Link implements p9.File.Link.
//
// Not properly implemented.
func (l *Local) Link(target p9.File, newname string) error {
return os.Link(target.(*Local).path, path.Join(l.path, newname))
}
// RenameAt implements p9.File.RenameAt.
func (l *Local) RenameAt(oldName string, newDir p9.File, newName string) error {
oldPath := path.Join(l.path, oldName)
newPath := path.Join(newDir.(*Local).path, newName)
return os.Rename(oldPath, newPath)
}
// Readlink implements p9.File.Readlink.
//
// Not properly implemented.
func (l *Local) Readlink() (string, error) {
return os.Readlink(l.path)
}
// Renamed implements p9.File.Renamed.
func (l *Local) Renamed(parent p9.File, newName string) {
l.path = path.Join(parent.(*Local).path, newName)
}
// SetAttr implements p9.File.SetAttr.
func (l *Local) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
// When truncate(2) is called on Linux, Linux will try to set time & size. Fake it. Sorry.
supported := p9.SetAttrMask{Size: true, MTime: true, CTime: true}
if !valid.IsSubsetOf(supported) {
return linux.ENOSYS
}
if valid.Size {
// If more than one thing is ever implemented, we can't just
// return an error here.
return os.Truncate(l.path, int64(attr.Size))
}
return nil
}

View File

@ -0,0 +1,50 @@
package localfs
import (
"io"
"path"
"github.com/hugelgupf/p9/p9"
)
// Readdir implements p9.File.Readdir.
func (l *Local) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
var (
p9Ents = make([]p9.Dirent, 0)
cursor = uint64(0)
)
for len(p9Ents) < int(count) {
singleEnt, err := l.file.Readdirnames(1)
if err == io.EOF {
return p9Ents, nil
} else if err != nil {
return nil, err
}
// we consumed an entry
cursor++
// cursor \in (offset, offset+count)
if cursor < offset || cursor > offset+uint64(count) {
continue
}
name := singleEnt[0]
localEnt := Local{path: path.Join(l.path, name)}
qid, _, err := localEnt.info()
if err != nil {
return p9Ents, err
}
p9Ents = append(p9Ents, p9.Dirent{
QID: qid,
Type: qid.Type,
Name: name,
Offset: cursor,
})
}
return p9Ents, nil
}

View File

@ -0,0 +1,40 @@
//go:build !windows
// +build !windows
package localfs
import (
"os"
"syscall"
"github.com/hugelgupf/p9/p9"
"golang.org/x/sys/unix"
)
func umask(mask int) int {
return syscall.Umask(mask)
}
func localToQid(_ string, fi os.FileInfo) (uint64, error) {
return uint64(fi.Sys().(*syscall.Stat_t).Ino), nil
}
// lock implements p9.File.Lock.
func (l *Local) lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
switch locktype {
case p9.ReadLock, p9.WriteLock:
if err := unix.Flock(int(l.file.Fd()), unix.LOCK_EX); err != nil {
return p9.LockStatusError, nil
}
case p9.Unlock:
if err := unix.Flock(int(l.file.Fd()), unix.LOCK_EX); err != nil {
return p9.LockStatusError, nil
}
default:
return p9.LockStatusOK, unix.ENOSYS
}
return p9.LockStatusOK, nil
}

View File

@ -0,0 +1,50 @@
package localfs
import (
"os"
"github.com/hugelgupf/p9/linux"
"github.com/hugelgupf/p9/p9"
"golang.org/x/sys/windows"
)
func umask(_ int) int {
return 0
}
func localToQid(path string, info os.FileInfo) (uint64, error) {
pathPtr, err := windows.UTF16PtrFromString(path)
if err != nil {
return 0, err
}
var (
access uint32 // none; we only need metadata
sharemode uint32
createmode uint32 = windows.OPEN_EXISTING
attribute uint32 = windows.FILE_ATTRIBUTE_NORMAL
)
if info.IsDir() {
attribute = windows.FILE_FLAG_BACKUP_SEMANTICS
}
fd, err := windows.CreateFile(pathPtr, access, sharemode, nil, createmode, attribute, 0)
if err != nil {
return 0, err
}
fi := &windows.ByHandleFileInformation{}
if err = windows.GetFileInformationByHandle(fd, fi); err != nil {
return 0, err
}
x := uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow)
return x, nil
}
// lock implements p9.File.Lock.
// As in FreeBSD NFS locking, we just say "sure, we did it" without actually
// doing anything; this lock design makes even less sense on Windows than
// it does on Linux (pid? really? what were they thinking?)
func (l *Local) lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
return p9.LockStatusOK, linux.ENOSYS
}

View File

@ -0,0 +1,25 @@
//go:build unix && !solaris && !openbsd
package localfs
import (
"github.com/hugelgupf/p9/fsimpl/xattr"
"github.com/hugelgupf/p9/p9"
"golang.org/x/sys/unix"
)
func (l *Local) SetXattr(attr string, data []byte, flags p9.XattrFlags) error {
return unix.Setxattr(l.path, attr, data, int(flags))
}
func (l *Local) ListXattrs() ([]string, error) {
return xattr.List(l.path)
}
func (l *Local) GetXattr(attr string) ([]byte, error) {
return xattr.Get(l.path, attr)
}
func (l *Local) RemoveXattr(attr string) error {
return unix.Removexattr(l.path, attr)
}

View File

@ -0,0 +1,194 @@
package templatefs
import (
"github.com/hugelgupf/p9/linux"
"github.com/hugelgupf/p9/p9"
)
// NotSymlinkFile denies Readlink with EINVAL.
//
// EINVAL is returned by readlink(2) when the file is not a symlink.
type NotSymlinkFile struct{}
// Readlink implements p9.File.Readlink.
func (NotSymlinkFile) Readlink() (string, error) {
return "", linux.EINVAL
}
// NotDirectoryFile denies any directory operations with ENOTDIR.
//
// Those operations are Create, Mkdir, Symlink, Link, Mknod, RenameAt,
// UnlinkAt, and Readdir.
type NotDirectoryFile struct{}
// Create implements p9.File.Create.
func (NotDirectoryFile) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
return nil, p9.QID{}, 0, linux.ENOTDIR
}
// Mkdir implements p9.File.Mkdir.
func (NotDirectoryFile) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.ENOTDIR
}
// Symlink implements p9.File.Symlink.
func (NotDirectoryFile) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.ENOTDIR
}
// Link implements p9.File.Link.
func (NotDirectoryFile) Link(target p9.File, newname string) error {
return linux.ENOTDIR
}
// Mknod implements p9.File.Mknod.
func (NotDirectoryFile) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.ENOTDIR
}
// RenameAt implements p9.File.RenameAt.
func (NotDirectoryFile) RenameAt(oldname string, newdir p9.File, newname string) error {
return linux.ENOTDIR
}
// UnlinkAt implements p9.File.UnlinkAt.
func (NotDirectoryFile) UnlinkAt(name string, flags uint32) error {
return linux.ENOTDIR
}
// Readdir implements p9.File.Readdir.
func (NotDirectoryFile) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
return nil, linux.ENOTDIR
}
// ReadOnlyFile returns default denials for all methods except Open, ReadAt,
// Walk, Close, and GetAttr.
//
// Returns EROFS for most modifying operations, ENOTDIR for file creation ops
// or readdir, EINVAL for readlink, xattr and lock operations return ENOSYS.
//
// Does nothing for Renamed.
type ReadOnlyFile struct {
NotSymlinkFile
NotDirectoryFile
XattrUnimplemented
NoopRenamed
NotLockable
}
// FSync implements p9.File.FSync.
func (ReadOnlyFile) FSync() error {
return linux.EROFS
}
// SetAttr implements p9.File.SetAttr.
func (ReadOnlyFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
return linux.EROFS
}
// Remove implements p9.File.Remove.
func (ReadOnlyFile) Remove() error {
return linux.EROFS
}
// Rename implements p9.File.Rename.
func (ReadOnlyFile) Rename(directory p9.File, name string) error {
return linux.EROFS
}
// WriteAt implements p9.File.WriteAt.
func (ReadOnlyFile) WriteAt(p []byte, offset int64) (int, error) {
return 0, linux.EROFS
}
// Flush implements p9.File.Flush.
func (ReadOnlyFile) Flush() error {
return nil
}
// ReadOnlyDir implements default denials for all methods except Walk, Open,
// GetAttr, Readdir, Close.
//
// Creation operations return EROFS. Read/write operations return EISDIR.
// EINVAL for readlink. Renaming does nothing by default, xattr and locking are
// unimplemented.
type ReadOnlyDir struct {
NotSymlinkFile
IsDir
XattrUnimplemented
NoopRenamed
NotLockable
}
// Create implements p9.File.Create.
func (ReadOnlyDir) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
return nil, p9.QID{}, 0, linux.EROFS
}
// Mkdir implements p9.File.Mkdir.
func (ReadOnlyDir) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.EROFS
}
// Symlink implements p9.File.Symlink.
func (ReadOnlyDir) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.EROFS
}
// Link implements p9.File.Link.
func (ReadOnlyDir) Link(target p9.File, newname string) error {
return linux.EROFS
}
// Mknod implements p9.File.Mknod.
func (ReadOnlyDir) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.EROFS
}
// RenameAt implements p9.File.RenameAt.
func (ReadOnlyDir) RenameAt(oldname string, newdir p9.File, newname string) error {
return linux.EROFS
}
// UnlinkAt implements p9.File.UnlinkAt.
func (ReadOnlyDir) UnlinkAt(name string, flags uint32) error {
return linux.EROFS
}
// Readdir implements p9.File.Readdir.
func (ReadOnlyDir) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
return nil, linux.EROFS
}
// FSync implements p9.File.FSync.
func (ReadOnlyDir) FSync() error {
return linux.EROFS
}
// SetAttr implements p9.File.SetAttr.
func (ReadOnlyDir) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
return linux.EROFS
}
// Remove implements p9.File.Remove.
func (ReadOnlyDir) Remove() error {
return linux.EROFS
}
// Rename implements p9.File.Rename.
func (ReadOnlyDir) Rename(directory p9.File, name string) error {
return linux.EROFS
}
// IsDir returns EISDIR for ReadAt and WriteAt.
type IsDir struct{}
// WriteAt implements p9.File.WriteAt.
func (IsDir) WriteAt(p []byte, offset int64) (int, error) {
return 0, linux.EISDIR
}
// ReadAt implements p9.File.ReadAt.
func (IsDir) ReadAt(p []byte, offset int64) (int, error) {
return 0, linux.EISDIR
}

View File

@ -0,0 +1,193 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package templatefs provides template p9.Files.
//
// NoopFile can be used to leave some methods unimplemented in incomplete
// p9.File implementations.
//
// NilCloser, ReadOnlyFile, NotDirectoryFile, and NotSymlinkFile can be used as
// templates for commonly implemented file types. They are careful not to
// conflict with each others' methods, so they can be embedded together.
package templatefs
import (
"github.com/hugelgupf/p9/linux"
"github.com/hugelgupf/p9/p9"
)
// NilCloser returns nil for Close.
type NilCloser struct{}
// Close implements p9.File.Close.
func (NilCloser) Close() error {
return nil
}
// NilSyncer returns nil for FSync.
type NilSyncer struct{}
// FSync implements p9.File.FSync.
func (NilSyncer) FSync() error {
return nil
}
// NoopRenamed does nothing when the file is renamed.
type NoopRenamed struct{}
// Renamed implements p9.File.Renamed.
func (NoopRenamed) Renamed(parent p9.File, newName string) {}
// NotImplementedFile is a p9.File that returns ENOSYS for every listed method.
//
// Compatible with NoopRenamed, NilCloser, and NilSyncer.
type NotImplementedFile struct {
p9.DefaultWalkGetAttr
NotLockable
XattrUnimplemented
}
// NoopFile is a p9.File with every method unimplemented.
type NoopFile struct {
NotImplementedFile
NilCloser
NilSyncer
NoopRenamed
}
var (
_ p9.File = &NoopFile{}
)
// Walk implements p9.File.Walk.
func (NotImplementedFile) Walk(names []string) ([]p9.QID, p9.File, error) {
return nil, nil, linux.ENOSYS
}
// StatFS implements p9.File.StatFS.
//
// Not implemented.
func (NotImplementedFile) StatFS() (p9.FSStat, error) {
return p9.FSStat{}, linux.ENOSYS
}
// Open implements p9.File.Open.
func (NotImplementedFile) Open(mode p9.OpenFlags) (p9.QID, uint32, error) {
return p9.QID{}, 0, linux.ENOSYS
}
// ReadAt implements p9.File.ReadAt.
func (NotImplementedFile) ReadAt(p []byte, offset int64) (int, error) {
return 0, linux.ENOSYS
}
// GetAttr implements p9.File.GetAttr.
func (NotImplementedFile) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) {
return p9.QID{}, p9.AttrMask{}, p9.Attr{}, linux.ENOSYS
}
// SetAttr implements p9.File.SetAttr.
func (NotImplementedFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
return linux.ENOSYS
}
// Remove implements p9.File.Remove.
func (NotImplementedFile) Remove() error {
return linux.ENOSYS
}
// Rename implements p9.File.Rename.
func (NotImplementedFile) Rename(directory p9.File, name string) error {
return linux.ENOSYS
}
// WriteAt implements p9.File.WriteAt.
func (NotImplementedFile) WriteAt(p []byte, offset int64) (int, error) {
return 0, linux.ENOSYS
}
// Create implements p9.File.Create.
func (NotImplementedFile) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
return nil, p9.QID{}, 0, linux.ENOSYS
}
// Mkdir implements p9.File.Mkdir.
func (NotImplementedFile) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.ENOSYS
}
// Symlink implements p9.File.Symlink.
func (NotImplementedFile) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.ENOSYS
}
// Link implements p9.File.Link.
func (NotImplementedFile) Link(target p9.File, newname string) error {
return linux.ENOSYS
}
// Mknod implements p9.File.Mknod.
func (NotImplementedFile) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
return p9.QID{}, linux.ENOSYS
}
// RenameAt implements p9.File.RenameAt.
func (NotImplementedFile) RenameAt(oldname string, newdir p9.File, newname string) error {
return linux.ENOSYS
}
// UnlinkAt implements p9.File.UnlinkAt.
func (NotImplementedFile) UnlinkAt(name string, flags uint32) error {
return linux.ENOSYS
}
// Readdir implements p9.File.Readdir.
func (NotImplementedFile) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
return nil, linux.ENOSYS
}
// Readlink implements p9.File.Readlink.
func (NotImplementedFile) Readlink() (string, error) {
return "", linux.ENOSYS
}
// XattrUnimplemented implements Xattr methods returning ENOSYS.
type XattrUnimplemented struct{}
// SetXattr implements p9.File.SetXattr.
func (XattrUnimplemented) SetXattr(attr string, data []byte, flags p9.XattrFlags) error {
return linux.ENOSYS
}
// GetXattr implements p9.File.GetXattr.
func (XattrUnimplemented) GetXattr(attr string) ([]byte, error) {
return nil, linux.ENOSYS
}
// ListXattrs implements p9.File.ListXattrs.
func (XattrUnimplemented) ListXattrs() ([]string, error) {
return nil, linux.ENOSYS
}
// RemoveXattr implements p9.File.RemoveXattr.
func (XattrUnimplemented) RemoveXattr(attr string) error {
return linux.ENOSYS
}
type NotLockable struct{}
// Lock implements p9.File.Lock.
func (NotLockable) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
return p9.LockStatusOK, linux.ENOSYS
}

39
vendor/github.com/hugelgupf/p9/fsimpl/xattr/xattr.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
//go:build unix && !openbsd && !solaris
package xattr
import (
"io/fs"
"strings"
"golang.org/x/sys/unix"
)
func List(p string) ([]string, error) {
sz, err := unix.Listxattr(p, nil)
if err != nil {
return nil, &fs.PathError{Op: "listxattr-get-size", Path: p, Err: err}
}
b := make([]byte, sz)
sz, err = unix.Listxattr(p, b)
if err != nil {
return nil, &fs.PathError{Op: "listxattr", Path: p, Err: err}
}
return strings.Split(strings.Trim(string(b[:sz]), "\000"), "\000"), nil
}
func Get(p string, attr string) ([]byte, error) {
sz, err := unix.Getxattr(p, attr, nil)
if err != nil {
return nil, &fs.PathError{Op: "getxattr-get-size", Path: p, Err: err}
}
b := make([]byte, sz)
sz, err = unix.Getxattr(p, attr, b)
if err != nil {
return nil, &fs.PathError{Op: "getxattr", Path: p, Err: err}
}
return b[:sz], nil
}

View File

@ -0,0 +1,11 @@
package xattr
import "github.com/hugelgupf/p9/linux"
func List(p string) ([]string, error) {
return nil, linux.ENOSYS
}
func Get(p string, attr string) ([]byte, error) {
return nil, linux.ENOSYS
}

2
vendor/github.com/hugelgupf/p9/internal/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package sys abstracts operating system features for p9.
package internal

31
vendor/github.com/hugelgupf/p9/internal/stat_bsd.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
//go:build freebsd || darwin || netbsd
// +build freebsd darwin netbsd
package internal
import (
"os"
"syscall"
"golang.org/x/sys/unix"
)
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
func InfoToStat(fi os.FileInfo) *Stat_t {
nativeStat := fi.Sys().(*syscall.Stat_t)
return &Stat_t{
Dev: nativeStat.Dev,
Ino: nativeStat.Ino,
Nlink: nativeStat.Nlink,
Mode: nativeStat.Mode,
Uid: nativeStat.Uid,
Gid: nativeStat.Gid,
Rdev: nativeStat.Rdev,
Size: nativeStat.Size,
Blksize: nativeStat.Blksize,
Blocks: nativeStat.Blocks,
Atim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Atimespec)),
Mtim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Mtimespec)),
Ctim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Ctimespec)),
}
}

View File

@ -0,0 +1,28 @@
package internal
import (
"os"
"syscall"
"golang.org/x/sys/unix"
)
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
func InfoToStat(fi os.FileInfo) *Stat_t {
nativeStat := fi.Sys().(*syscall.Stat_t)
return &Stat_t{
Dev: nativeStat.Dev,
Ino: nativeStat.Ino,
Nlink: nativeStat.Nlink,
Mode: nativeStat.Mode,
Uid: nativeStat.Uid,
Gid: nativeStat.Gid,
Rdev: nativeStat.Rdev,
Size: nativeStat.Size,
Blksize: int32(nativeStat.Blksize),
Blocks: nativeStat.Blocks,
Atim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Atim)),
Mtim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Mtim)),
Ctim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Ctim)),
}
}

View File

@ -0,0 +1,31 @@
//go:build linux || dragonfly || solaris
// +build linux dragonfly solaris
package internal
import (
"os"
"syscall"
"golang.org/x/sys/unix"
)
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
func InfoToStat(fi os.FileInfo) *Stat_t {
nativeStat := fi.Sys().(*syscall.Stat_t)
return &Stat_t{
Dev: nativeStat.Dev,
Ino: nativeStat.Ino,
Nlink: nativeStat.Nlink,
Mode: nativeStat.Mode,
Uid: nativeStat.Uid,
Gid: nativeStat.Gid,
Rdev: nativeStat.Rdev,
Size: nativeStat.Size,
Blksize: nativeStat.Blksize,
Blocks: nativeStat.Blocks,
Atim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Atim)),
Mtim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Mtim)),
Ctim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Ctim)),
}
}

11
vendor/github.com/hugelgupf/p9/internal/stat_unix.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
//go:build !windows
// +build !windows
package internal
import (
"golang.org/x/sys/unix"
)
// Stat_t is the Linux Stat_t.
type Stat_t = unix.Stat_t

View File

@ -0,0 +1,74 @@
package internal
import (
"os"
)
// NOTE: taken from amd64 Linux
type Timespec struct {
Sec int64
Nsec int64
}
type Stat_t struct {
Dev uint64
Ino uint64
Nlink uint64
Mode uint32
Uid uint32
Gid uint32
Rdev uint64
Size int64
Blksize int64
Blocks int64
Atim Timespec
Mtim Timespec
Ctim Timespec
}
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
func InfoToStat(fi os.FileInfo) *Stat_t {
return &Stat_t{
Size: fi.Size(),
Mode: uint32(modeFromOS(fi.Mode())),
Mtim: Timespec{
Sec: fi.ModTime().Unix(),
Nsec: fi.ModTime().UnixNano(),
},
}
}
// TODO: copied from pkg p9
// we should probably migrate the OS methods from p9 into sys
const (
FileModeMask uint32 = 0170000
ModeSocket = 0140000
ModeSymlink = 0120000
ModeRegular = 0100000
ModeBlockDevice = 060000
ModeDirectory = 040000
ModeCharacterDevice = 020000
ModeNamedPipe = 010000
)
func modeFromOS(mode os.FileMode) uint32 {
m := uint32(mode.Perm())
switch {
case mode.IsDir():
m |= ModeDirectory
case mode&os.ModeSymlink != 0:
m |= ModeSymlink
case mode&os.ModeSocket != 0:
m |= ModeSocket
case mode&os.ModeNamedPipe != 0:
m |= ModeNamedPipe
case mode&os.ModeCharDevice != 0:
m |= ModeCharacterDevice
case mode&os.ModeDevice != 0:
m |= ModeBlockDevice
default:
m |= ModeRegular
}
return m
}

291
vendor/github.com/hugelgupf/p9/linux/errno.go generated vendored Normal file
View File

@ -0,0 +1,291 @@
// 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 linux
import "fmt"
// Errno is a Linux error number on every GOOS.
type Errno uintptr
func (e Errno) Error() string {
if 0 <= int(e) && int(e) < len(errorTable) {
s := errorTable[e]
if s != "" {
return s
}
}
return fmt.Sprintf("errno %d", int(e))
}
// numbers defined on Linux/amd64.
const (
E2BIG = Errno(0x7)
EACCES = Errno(0xd)
EADDRINUSE = Errno(0x62)
EADDRNOTAVAIL = Errno(0x63)
EADV = Errno(0x44)
EAFNOSUPPORT = Errno(0x61)
EAGAIN = Errno(0xb)
EALREADY = Errno(0x72)
EBADE = Errno(0x34)
EBADF = Errno(0x9)
EBADFD = Errno(0x4d)
EBADMSG = Errno(0x4a)
EBADR = Errno(0x35)
EBADRQC = Errno(0x38)
EBADSLT = Errno(0x39)
EBFONT = Errno(0x3b)
EBUSY = Errno(0x10)
ECANCELED = Errno(0x7d)
ECHILD = Errno(0xa)
ECHRNG = Errno(0x2c)
ECOMM = Errno(0x46)
ECONNABORTED = Errno(0x67)
ECONNREFUSED = Errno(0x6f)
ECONNRESET = Errno(0x68)
EDEADLK = Errno(0x23)
EDEADLOCK = Errno(0x23)
EDESTADDRREQ = Errno(0x59)
EDOM = Errno(0x21)
EDOTDOT = Errno(0x49)
EDQUOT = Errno(0x7a)
EEXIST = Errno(0x11)
EFAULT = Errno(0xe)
EFBIG = Errno(0x1b)
EHOSTDOWN = Errno(0x70)
EHOSTUNREACH = Errno(0x71)
EHWPOISON = Errno(0x85)
EIDRM = Errno(0x2b)
EILSEQ = Errno(0x54)
EINPROGRESS = Errno(0x73)
EINTR = Errno(0x4)
EINVAL = Errno(0x16)
EIO = Errno(0x5)
EISCONN = Errno(0x6a)
EISDIR = Errno(0x15)
EISNAM = Errno(0x78)
EKEYEXPIRED = Errno(0x7f)
EKEYREJECTED = Errno(0x81)
EKEYREVOKED = Errno(0x80)
EL2HLT = Errno(0x33)
EL2NSYNC = Errno(0x2d)
EL3HLT = Errno(0x2e)
EL3RST = Errno(0x2f)
ELIBACC = Errno(0x4f)
ELIBBAD = Errno(0x50)
ELIBEXEC = Errno(0x53)
ELIBMAX = Errno(0x52)
ELIBSCN = Errno(0x51)
ELNRNG = Errno(0x30)
ELOOP = Errno(0x28)
EMEDIUMTYPE = Errno(0x7c)
EMFILE = Errno(0x18)
EMLINK = Errno(0x1f)
EMSGSIZE = Errno(0x5a)
EMULTIHOP = Errno(0x48)
ENAMETOOLONG = Errno(0x24)
ENAVAIL = Errno(0x77)
ENETDOWN = Errno(0x64)
ENETRESET = Errno(0x66)
ENETUNREACH = Errno(0x65)
ENFILE = Errno(0x17)
ENOANO = Errno(0x37)
ENOBUFS = Errno(0x69)
ENOCSI = Errno(0x32)
ENODATA = Errno(0x3d)
ENODEV = Errno(0x13)
ENOENT = Errno(0x2)
ENOEXEC = Errno(0x8)
ENOKEY = Errno(0x7e)
ENOLCK = Errno(0x25)
ENOLINK = Errno(0x43)
ENOMEDIUM = Errno(0x7b)
ENOMEM = Errno(0xc)
ENOMSG = Errno(0x2a)
ENONET = Errno(0x40)
ENOPKG = Errno(0x41)
ENOPROTOOPT = Errno(0x5c)
ENOSPC = Errno(0x1c)
ENOSR = Errno(0x3f)
ENOSTR = Errno(0x3c)
ENOSYS = Errno(0x26)
ENOTBLK = Errno(0xf)
ENOTCONN = Errno(0x6b)
ENOTDIR = Errno(0x14)
ENOTEMPTY = Errno(0x27)
ENOTNAM = Errno(0x76)
ENOTRECOVERABLE = Errno(0x83)
ENOTSOCK = Errno(0x58)
ENOTSUP = Errno(0x5f)
ENOTTY = Errno(0x19)
ENOTUNIQ = Errno(0x4c)
ENXIO = Errno(0x6)
EOPNOTSUPP = Errno(0x5f)
EOVERFLOW = Errno(0x4b)
EOWNERDEAD = Errno(0x82)
EPERM = Errno(0x1)
EPFNOSUPPORT = Errno(0x60)
EPIPE = Errno(0x20)
EPROTO = Errno(0x47)
EPROTONOSUPPORT = Errno(0x5d)
EPROTOTYPE = Errno(0x5b)
ERANGE = Errno(0x22)
EREMCHG = Errno(0x4e)
EREMOTE = Errno(0x42)
EREMOTEIO = Errno(0x79)
ERESTART = Errno(0x55)
ERFKILL = Errno(0x84)
EROFS = Errno(0x1e)
ESHUTDOWN = Errno(0x6c)
ESOCKTNOSUPPORT = Errno(0x5e)
ESPIPE = Errno(0x1d)
ESRCH = Errno(0x3)
ESRMNT = Errno(0x45)
ESTALE = Errno(0x74)
ESTRPIPE = Errno(0x56)
ETIME = Errno(0x3e)
ETIMEDOUT = Errno(0x6e)
ETOOMANYREFS = Errno(0x6d)
ETXTBSY = Errno(0x1a)
EUCLEAN = Errno(0x75)
EUNATCH = Errno(0x31)
EUSERS = Errno(0x57)
EWOULDBLOCK = Errno(0xb)
EXDEV = Errno(0x12)
EXFULL = Errno(0x36)
)
var errorTable = [...]string{
1: "operation not permitted",
2: "no such file or directory",
3: "no such process",
4: "interrupted system call",
5: "input/output error",
6: "no such device or address",
7: "argument list too long",
8: "exec format error",
9: "bad file descriptor",
10: "no child processes",
11: "resource temporarily unavailable",
12: "cannot allocate memory",
13: "permission denied",
14: "bad address",
15: "block device required",
16: "device or resource busy",
17: "file exists",
18: "invalid cross-device link",
19: "no such device",
20: "not a directory",
21: "is a directory",
22: "invalid argument",
23: "too many open files in system",
24: "too many open files",
25: "inappropriate ioctl for device",
26: "text file busy",
27: "file too large",
28: "no space left on device",
29: "illegal seek",
30: "read-only file system",
31: "too many links",
32: "broken pipe",
33: "numerical argument out of domain",
34: "numerical result out of range",
35: "resource deadlock avoided",
36: "file name too long",
37: "no locks available",
38: "function not implemented",
39: "directory not empty",
40: "too many levels of symbolic links",
42: "no message of desired type",
43: "identifier removed",
44: "channel number out of range",
45: "level 2 not synchronized",
46: "level 3 halted",
47: "level 3 reset",
48: "link number out of range",
49: "protocol driver not attached",
50: "no CSI structure available",
51: "level 2 halted",
52: "invalid exchange",
53: "invalid request descriptor",
54: "exchange full",
55: "no anode",
56: "invalid request code",
57: "invalid slot",
59: "bad font file format",
60: "device not a stream",
61: "no data available",
62: "timer expired",
63: "out of streams resources",
64: "machine is not on the network",
65: "package not installed",
66: "object is remote",
67: "link has been severed",
68: "advertise error",
69: "srmount error",
70: "communication error on send",
71: "protocol error",
72: "multihop attempted",
73: "RFS specific error",
74: "bad message",
75: "value too large for defined data type",
76: "name not unique on network",
77: "file descriptor in bad state",
78: "remote address changed",
79: "can not access a needed shared library",
80: "accessing a corrupted shared library",
81: ".lib section in a.out corrupted",
82: "attempting to link in too many shared libraries",
83: "cannot exec a shared library directly",
84: "invalid or incomplete multibyte or wide character",
85: "interrupted system call should be restarted",
86: "streams pipe error",
87: "too many users",
88: "socket operation on non-socket",
89: "destination address required",
90: "message too long",
91: "protocol wrong type for socket",
92: "protocol not available",
93: "protocol not supported",
94: "socket type not supported",
95: "operation not supported",
96: "protocol family not supported",
97: "address family not supported by protocol",
98: "address already in use",
99: "cannot assign requested address",
100: "network is down",
101: "network is unreachable",
102: "network dropped connection on reset",
103: "software caused connection abort",
104: "connection reset by peer",
105: "no buffer space available",
106: "transport endpoint is already connected",
107: "transport endpoint is not connected",
108: "cannot send after transport endpoint shutdown",
109: "too many references: cannot splice",
110: "connection timed out",
111: "connection refused",
112: "host is down",
113: "no route to host",
114: "operation already in progress",
115: "operation now in progress",
116: "stale NFS file handle",
117: "structure needs cleaning",
118: "not a XENIX named type file",
119: "no XENIX semaphores available",
120: "is a named type file",
121: "remote I/O error",
122: "disk quota exceeded",
123: "no medium found",
124: "wrong medium type",
125: "operation canceled",
126: "required key not available",
127: "key has expired",
128: "key has been revoked",
129: "key was rejected by service",
130: "owner died",
131: "state not recoverable",
132: "operation not possible due to RF-kill",
}

38
vendor/github.com/hugelgupf/p9/linux/errors.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
package linux
import (
"errors"
"os"
)
// ExtractErrno extracts an [Errno] from an error, best effort.
//
// If the system-specific or Go-specific error cannot be mapped to anything, it
// will be logged and EIO will be returned.
func ExtractErrno(err error) Errno {
for _, pair := range []struct {
error
Errno
}{
{os.ErrNotExist, ENOENT},
{os.ErrExist, EEXIST},
{os.ErrPermission, EACCES},
{os.ErrInvalid, EINVAL},
} {
if errors.Is(err, pair.error) {
return pair.Errno
}
}
var errno Errno
if errors.As(err, &errno) {
return errno
}
if e := sysErrno(err); e != 0 {
return e
}
// Default case.
return EIO
}

17
vendor/github.com/hugelgupf/p9/linux/errors_linux.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
//go:build linux
// +build linux
package linux
import (
"errors"
"syscall"
)
func sysErrno(err error) Errno {
var systemErr syscall.Errno
if errors.As(err, &systemErr) {
return Errno(systemErr)
}
return 0
}

21
vendor/github.com/hugelgupf/p9/linux/errors_unix.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
//go:build !windows && !linux
// +build !windows,!linux
package linux
import "syscall"
func sysErrno(err error) Errno {
se, ok := err.(syscall.Errno)
if ok {
// POSIX-defined errors seem to match up to error number 34
// according to http://www.ioplex.com/~miallen/errcmpp.html.
//
// 9P2000.L expects Linux error codes, so after 34 we normalize.
if se <= 34 {
return Errno(se)
}
return 0
}
return 0
}

28
vendor/github.com/hugelgupf/p9/linux/errors_windows.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
//go:build windows
// +build windows
package linux
import (
"errors"
"syscall"
)
func sysErrno(err error) Errno {
for _, pair := range []struct {
error
Errno
}{
{syscall.ERROR_FILE_NOT_FOUND, ENOENT},
{syscall.ERROR_PATH_NOT_FOUND, ENOENT},
{syscall.ERROR_ACCESS_DENIED, EACCES},
{syscall.ERROR_FILE_EXISTS, EEXIST},
{syscall.ERROR_INSUFFICIENT_BUFFER, ENOMEM},
} {
if errors.Is(err, pair.error) {
return pair.Errno
}
}
// No clue what to do with others.
return 0
}

253
vendor/github.com/hugelgupf/p9/p9/buffer.go generated vendored Normal file
View File

@ -0,0 +1,253 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"encoding/binary"
)
// encoder is used for messages and 9P primitives.
type encoder interface {
// decode decodes from the given buffer. decode may be called more than once
// to reuse the instance. It must clear any previous state.
//
// This may not fail, exhaustion will be recorded in the buffer.
decode(b *buffer)
// encode encodes to the given buffer.
//
// This may not fail.
encode(b *buffer)
}
// order is the byte order used for encoding.
var order = binary.LittleEndian
// buffer is a slice that is consumed.
//
// This is passed to the encoder methods.
type buffer struct {
// data is the underlying data. This may grow during encode.
data []byte
// overflow indicates whether an overflow has occurred.
overflow bool
}
// append appends n bytes to the buffer and returns a slice pointing to the
// newly appended bytes.
func (b *buffer) append(n int) []byte {
b.data = append(b.data, make([]byte, n)...)
return b.data[len(b.data)-n:]
}
// consume consumes n bytes from the buffer.
func (b *buffer) consume(n int) ([]byte, bool) {
if !b.has(n) {
b.markOverrun()
return nil, false
}
rval := b.data[:n]
b.data = b.data[n:]
return rval, true
}
// has returns true if n bytes are available.
func (b *buffer) has(n int) bool {
return len(b.data) >= n
}
// markOverrun immediately marks this buffer as overrun.
//
// This is used by ReadString, since some invalid data implies the rest of the
// buffer is no longer valid either.
func (b *buffer) markOverrun() {
b.overflow = true
}
// isOverrun returns true if this buffer has run past the end.
func (b *buffer) isOverrun() bool {
return b.overflow
}
// Read8 reads a byte from the buffer.
func (b *buffer) Read8() uint8 {
v, ok := b.consume(1)
if !ok {
return 0
}
return uint8(v[0])
}
// Read16 reads a 16-bit value from the buffer.
func (b *buffer) Read16() uint16 {
v, ok := b.consume(2)
if !ok {
return 0
}
return order.Uint16(v)
}
// Read32 reads a 32-bit value from the buffer.
func (b *buffer) Read32() uint32 {
v, ok := b.consume(4)
if !ok {
return 0
}
return order.Uint32(v)
}
// Read64 reads a 64-bit value from the buffer.
func (b *buffer) Read64() uint64 {
v, ok := b.consume(8)
if !ok {
return 0
}
return order.Uint64(v)
}
// ReadQIDType reads a QIDType value.
func (b *buffer) ReadQIDType() QIDType {
return QIDType(b.Read8())
}
// ReadTag reads a Tag value.
func (b *buffer) ReadTag() tag {
return tag(b.Read16())
}
// ReadFID reads a FID value.
func (b *buffer) ReadFID() fid {
return fid(b.Read32())
}
// ReadUID reads a UID value.
func (b *buffer) ReadUID() UID {
return UID(b.Read32())
}
// ReadGID reads a GID value.
func (b *buffer) ReadGID() GID {
return GID(b.Read32())
}
// ReadPermissions reads a file mode value and applies the mask for permissions.
func (b *buffer) ReadPermissions() FileMode {
return b.ReadFileMode() & permissionsMask
}
// ReadFileMode reads a file mode value.
func (b *buffer) ReadFileMode() FileMode {
return FileMode(b.Read32())
}
// ReadOpenFlags reads an OpenFlags.
func (b *buffer) ReadOpenFlags() OpenFlags {
return OpenFlags(b.Read32())
}
// ReadMsgType writes a msgType.
func (b *buffer) ReadMsgType() msgType {
return msgType(b.Read8())
}
// ReadString deserializes a string.
func (b *buffer) ReadString() string {
l := b.Read16()
if !b.has(int(l)) {
// Mark the buffer as corrupted.
b.markOverrun()
return ""
}
bs := make([]byte, l)
for i := 0; i < int(l); i++ {
bs[i] = byte(b.Read8())
}
return string(bs)
}
// Write8 writes a byte to the buffer.
func (b *buffer) Write8(v uint8) {
b.append(1)[0] = byte(v)
}
// Write16 writes a 16-bit value to the buffer.
func (b *buffer) Write16(v uint16) {
order.PutUint16(b.append(2), v)
}
// Write32 writes a 32-bit value to the buffer.
func (b *buffer) Write32(v uint32) {
order.PutUint32(b.append(4), v)
}
// Write64 writes a 64-bit value to the buffer.
func (b *buffer) Write64(v uint64) {
order.PutUint64(b.append(8), v)
}
// WriteQIDType writes a QIDType value.
func (b *buffer) WriteQIDType(qidType QIDType) {
b.Write8(uint8(qidType))
}
// WriteTag writes a Tag value.
func (b *buffer) WriteTag(tag tag) {
b.Write16(uint16(tag))
}
// WriteFID writes a FID value.
func (b *buffer) WriteFID(fid fid) {
b.Write32(uint32(fid))
}
// WriteUID writes a UID value.
func (b *buffer) WriteUID(uid UID) {
b.Write32(uint32(uid))
}
// WriteGID writes a GID value.
func (b *buffer) WriteGID(gid GID) {
b.Write32(uint32(gid))
}
// WritePermissions applies a permissions mask and writes the FileMode.
func (b *buffer) WritePermissions(perm FileMode) {
b.WriteFileMode(perm & permissionsMask)
}
// WriteFileMode writes a FileMode.
func (b *buffer) WriteFileMode(mode FileMode) {
b.Write32(uint32(mode))
}
// WriteOpenFlags writes an OpenFlags.
func (b *buffer) WriteOpenFlags(flags OpenFlags) {
b.Write32(uint32(flags))
}
// WriteMsgType writes a MsgType.
func (b *buffer) WriteMsgType(t msgType) {
b.Write8(uint8(t))
}
// WriteString serializes the given string.
func (b *buffer) WriteString(s string) {
b.Write16(uint16(len(s)))
for i := 0; i < len(s); i++ {
b.Write8(byte(s[i]))
}
}

344
vendor/github.com/hugelgupf/p9/p9/client.go generated vendored Normal file
View File

@ -0,0 +1,344 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"errors"
"fmt"
"io"
"sync"
"github.com/hugelgupf/p9/linux"
"github.com/u-root/uio/ulog"
)
// ErrOutOfTags indicates no tags are available.
var ErrOutOfTags = errors.New("out of tags -- messages lost?")
// ErrOutOfFIDs indicates no more FIDs are available.
var ErrOutOfFIDs = errors.New("out of FIDs -- messages lost?")
// ErrUnexpectedTag indicates a response with an unexpected tag was received.
var ErrUnexpectedTag = errors.New("unexpected tag in response")
// ErrVersionsExhausted indicates that all versions to negotiate have been exhausted.
var ErrVersionsExhausted = errors.New("exhausted all versions to negotiate")
// ErrBadVersionString indicates that the version string is malformed or unsupported.
var ErrBadVersionString = errors.New("bad version string")
// ErrBadResponse indicates the response didn't match the request.
type ErrBadResponse struct {
Got msgType
Want msgType
}
// Error returns a highly descriptive error.
func (e *ErrBadResponse) Error() string {
return fmt.Sprintf("unexpected message type: got %v, want %v", e.Got, e.Want)
}
// response is the asynchronous return from recv.
//
// This is used in the pending map below.
type response struct {
r message
done chan error
}
var responsePool = sync.Pool{
New: func() interface{} {
return &response{
done: make(chan error, 1),
}
},
}
// Client is at least a 9P2000.L client.
type Client struct {
// conn is the connected conn.
conn io.ReadWriteCloser
// tagPool is the collection of available tags.
tagPool pool
// fidPool is the collection of available fids.
fidPool pool
// pending is the set of pending messages.
pending map[tag]*response
pendingMu sync.Mutex
// sendMu is the lock for sending a request.
sendMu sync.Mutex
// recvr is essentially a mutex for calling recv.
//
// Whoever writes to this channel is permitted to call recv. When
// finished calling recv, this channel should be emptied.
recvr chan bool
// messageSize is the maximum total size of a message.
messageSize uint32
// payloadSize is the maximum payload size of a read or write
// request. For large reads and writes this means that the
// read or write is broken up into buffer-size/payloadSize
// requests.
payloadSize uint32
// version is the agreed upon version X of 9P2000.L.Google.X.
// version 0 implies 9P2000.L.
version uint32
// log is the logger to write to, if specified.
log ulog.Logger
}
// ClientOpt enables optional client configuration.
type ClientOpt func(*Client) error
// WithMessageSize overrides the default message size.
func WithMessageSize(m uint32) ClientOpt {
return func(c *Client) error {
// Need at least one byte of payload.
if m <= msgDotLRegistry.largestFixedSize {
return &ErrMessageTooLarge{
size: m,
msize: msgDotLRegistry.largestFixedSize,
}
}
c.messageSize = m
return nil
}
}
// WithClientLogger overrides the default logger for the client.
func WithClientLogger(l ulog.Logger) ClientOpt {
return func(c *Client) error {
c.log = l
return nil
}
}
func roundDown(p uint32, align uint32) uint32 {
if p > align && p%align != 0 {
return p - p%align
}
return p
}
// NewClient creates a new client. It performs a Tversion exchange with
// the server to assert that messageSize is ok to use.
//
// You should not use the same conn for multiple clients.
func NewClient(conn io.ReadWriteCloser, o ...ClientOpt) (*Client, error) {
c := &Client{
conn: conn,
tagPool: pool{start: 1, limit: uint64(noTag)},
fidPool: pool{start: 1, limit: uint64(noFID)},
pending: make(map[tag]*response),
recvr: make(chan bool, 1),
messageSize: DefaultMessageSize,
log: ulog.Null,
// Request a high version by default.
version: highestSupportedVersion,
}
for _, opt := range o {
if err := opt(c); err != nil {
return nil, err
}
}
// Compute a payload size and round to 512 (normal block size)
// if it's larger than a single block.
c.payloadSize = roundDown(c.messageSize-msgDotLRegistry.largestFixedSize, 512)
// Agree upon a version.
requested := c.version
for {
rversion := rversion{}
err := c.sendRecv(&tversion{Version: versionString(version9P2000L, requested), MSize: c.messageSize}, &rversion)
// The server told us to try again with a lower version.
if errors.Is(err, linux.EAGAIN) {
if requested == lowestSupportedVersion {
return nil, ErrVersionsExhausted
}
requested--
continue
}
// We requested an impossible version or our other parameters were bogus.
if err != nil {
return nil, err
}
// Parse the version.
baseVersion, version, ok := parseVersion(rversion.Version)
if !ok {
// The server gave us a bad version. We return a generically worrisome error.
c.log.Printf("server returned bad version string %q", rversion.Version)
return nil, ErrBadVersionString
}
if baseVersion != version9P2000L {
c.log.Printf("server returned unsupported base version %q (version %q)", baseVersion, rversion.Version)
return nil, ErrBadVersionString
}
c.version = version
break
}
return c, nil
}
// handleOne handles a single incoming message.
//
// This should only be called with the token from recvr. Note that the received
// tag will automatically be cleared from pending.
func (c *Client) handleOne() {
t, r, err := recv(c.log, c.conn, c.messageSize, func(t tag, mt msgType) (message, error) {
c.pendingMu.Lock()
resp := c.pending[t]
c.pendingMu.Unlock()
// Not expecting this message?
if resp == nil {
c.log.Printf("client received unexpected tag %v, ignoring", t)
return nil, ErrUnexpectedTag
}
// Is it an error? We specifically allow this to
// go through, and then we deserialize below.
if mt == msgRlerror {
return &rlerror{}, nil
}
// Does it match expectations?
if mt != resp.r.typ() {
return nil, &ErrBadResponse{Got: mt, Want: resp.r.typ()}
}
// Return the response.
return resp.r, nil
})
if err != nil {
// No tag was extracted (probably a conn error).
//
// Likely catastrophic. Notify all waiters and clear pending.
c.pendingMu.Lock()
for _, resp := range c.pending {
resp.done <- err
}
c.pending = make(map[tag]*response)
c.pendingMu.Unlock()
} else {
// Process the tag.
//
// We know that is is contained in the map because our lookup function
// above must have succeeded (found the tag) to return nil err.
c.pendingMu.Lock()
resp := c.pending[t]
delete(c.pending, t)
c.pendingMu.Unlock()
resp.r = r
resp.done <- err
}
}
// waitAndRecv co-ordinates with other receivers to handle responses.
func (c *Client) waitAndRecv(done chan error) error {
for {
select {
case err := <-done:
return err
case c.recvr <- true:
select {
case err := <-done:
// It's possible that we got the token, despite
// done also being available. Check for that.
<-c.recvr
return err
default:
// Handle receiving one tag.
c.handleOne()
// Return the token.
<-c.recvr
}
}
}
}
// sendRecv performs a roundtrip message exchange.
//
// This is called by internal functions.
func (c *Client) sendRecv(tm message, rm message) error {
t, ok := c.tagPool.Get()
if !ok {
return ErrOutOfTags
}
defer c.tagPool.Put(t)
// Indicate we're expecting a response.
//
// Note that the tag will be cleared from pending
// automatically (see handleOne for details).
resp := responsePool.Get().(*response)
defer responsePool.Put(resp)
resp.r = rm
c.pendingMu.Lock()
c.pending[tag(t)] = resp
c.pendingMu.Unlock()
// Send the request over the wire.
c.sendMu.Lock()
err := send(c.log, c.conn, tag(t), tm)
c.sendMu.Unlock()
if err != nil {
return fmt.Errorf("send: %w", err)
}
// Co-ordinate with other receivers.
if err := c.waitAndRecv(resp.done); err != nil {
return fmt.Errorf("wait: %w", err)
}
// Is it an error message?
//
// For convenience, we transform these directly
// into errors. Handlers need not handle this case.
if rlerr, ok := resp.r.(*rlerror); ok {
return linux.Errno(rlerr.Error)
}
// At this point, we know it matches.
//
// Per recv call above, we will only allow a type
// match (and give our r) or an instance of Rlerror.
return nil
}
// Version returns the negotiated 9P2000.L.Google version number.
func (c *Client) Version() uint32 {
return c.version
}
// Close closes the underlying connection.
func (c *Client) Close() error {
return c.conn.Close()
}

568
vendor/github.com/hugelgupf/p9/p9/client_file.go generated vendored Normal file
View File

@ -0,0 +1,568 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"fmt"
"io"
"runtime"
"sync/atomic"
"github.com/hugelgupf/p9/linux"
)
// Attach attaches to a server.
//
// Note that authentication is not currently supported.
func (c *Client) Attach(name string) (File, error) {
id, ok := c.fidPool.Get()
if !ok {
return nil, ErrOutOfFIDs
}
rattach := rattach{}
if err := c.sendRecv(&tattach{fid: fid(id), Auth: tauth{AttachName: name, Authenticationfid: noFID, UID: NoUID}}, &rattach); err != nil {
c.fidPool.Put(id)
return nil, err
}
return c.newFile(fid(id)), nil
}
// newFile returns a new client file.
func (c *Client) newFile(fid fid) *clientFile {
cf := &clientFile{
client: c,
fid: fid,
}
// Make sure the file is closed.
runtime.SetFinalizer(cf, (*clientFile).Close)
return cf
}
// clientFile is provided to clients.
//
// This proxies all of the interfaces found in file.go.
type clientFile struct {
// client is the originating client.
client *Client
// fid is the fid for this file.
fid fid
// closed indicates whether this file has been closed.
closed uint32
}
// SetXattr implements p9.File.SetXattr.
func (c *clientFile) SetXattr(attr string, data []byte, flags XattrFlags) error {
return linux.ENOSYS
}
// RemoveXattr implements p9.File.RemoveXattr.
func (c *clientFile) RemoveXattr(attr string) error {
return linux.ENOSYS
}
// GetXattr implements p9.File.GetXattr.
func (c *clientFile) GetXattr(attr string) ([]byte, error) {
return nil, linux.ENOSYS
}
// ListXattrs implements p9.File.ListXattrs.
func (c *clientFile) ListXattrs() ([]string, error) {
return nil, linux.ENOSYS
}
// Walk implements File.Walk.
func (c *clientFile) Walk(names []string) ([]QID, File, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return nil, nil, linux.EBADF
}
id, ok := c.client.fidPool.Get()
if !ok {
return nil, nil, ErrOutOfFIDs
}
rwalk := rwalk{}
if err := c.client.sendRecv(&twalk{fid: c.fid, newFID: fid(id), Names: names}, &rwalk); err != nil {
c.client.fidPool.Put(id)
return nil, nil, err
}
// Return a new client file.
return rwalk.QIDs, c.client.newFile(fid(id)), nil
}
// WalkGetAttr implements File.WalkGetAttr.
func (c *clientFile) WalkGetAttr(components []string) ([]QID, File, AttrMask, Attr, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return nil, nil, AttrMask{}, Attr{}, linux.EBADF
}
if !versionSupportsTwalkgetattr(c.client.version) {
qids, file, err := c.Walk(components)
if err != nil {
return nil, nil, AttrMask{}, Attr{}, err
}
_, valid, attr, err := file.GetAttr(AttrMaskAll)
if err != nil {
file.Close()
return nil, nil, AttrMask{}, Attr{}, err
}
return qids, file, valid, attr, nil
}
id, ok := c.client.fidPool.Get()
if !ok {
return nil, nil, AttrMask{}, Attr{}, ErrOutOfFIDs
}
rwalkgetattr := rwalkgetattr{}
if err := c.client.sendRecv(&twalkgetattr{fid: c.fid, newFID: fid(id), Names: components}, &rwalkgetattr); err != nil {
c.client.fidPool.Put(id)
return nil, nil, AttrMask{}, Attr{}, err
}
// Return a new client file.
return rwalkgetattr.QIDs, c.client.newFile(fid(id)), rwalkgetattr.Valid, rwalkgetattr.Attr, nil
}
// StatFS implements File.StatFS.
func (c *clientFile) StatFS() (FSStat, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return FSStat{}, linux.EBADF
}
rstatfs := rstatfs{}
if err := c.client.sendRecv(&tstatfs{fid: c.fid}, &rstatfs); err != nil {
return FSStat{}, err
}
return rstatfs.FSStat, nil
}
// FSync implements File.FSync.
func (c *clientFile) FSync() error {
if atomic.LoadUint32(&c.closed) != 0 {
return linux.EBADF
}
return c.client.sendRecv(&tfsync{fid: c.fid}, &rfsync{})
}
// GetAttr implements File.GetAttr.
func (c *clientFile) GetAttr(req AttrMask) (QID, AttrMask, Attr, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return QID{}, AttrMask{}, Attr{}, linux.EBADF
}
rgetattr := rgetattr{}
if err := c.client.sendRecv(&tgetattr{fid: c.fid, AttrMask: req}, &rgetattr); err != nil {
return QID{}, AttrMask{}, Attr{}, err
}
return rgetattr.QID, rgetattr.Valid, rgetattr.Attr, nil
}
// SetAttr implements File.SetAttr.
func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error {
if atomic.LoadUint32(&c.closed) != 0 {
return linux.EBADF
}
return c.client.sendRecv(&tsetattr{fid: c.fid, Valid: valid, SetAttr: attr}, &rsetattr{})
}
// Lock implements File.Lock
func (c *clientFile) Lock(pid int, locktype LockType, flags LockFlags, start, length uint64, client string) (LockStatus, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return LockStatusError, linux.EBADF
}
r := rlock{}
err := c.client.sendRecv(&tlock{
Type: locktype,
Flags: flags,
Start: start,
Length: length,
PID: int32(pid),
Client: client,
}, &r)
return r.Status, err
}
// Remove implements File.Remove.
//
// N.B. This method is no longer part of the file interface and should be
// considered deprecated.
func (c *clientFile) Remove() error {
// Avoid double close.
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
return linux.EBADF
}
runtime.SetFinalizer(c, nil)
// Send the remove message.
if err := c.client.sendRecv(&tremove{fid: c.fid}, &rremove{}); err != nil {
return err
}
// "It is correct to consider remove to be a clunk with the side effect
// of removing the file if permissions allow."
// https://swtch.com/plan9port/man/man9/remove.html
// Return the fid to the pool.
c.client.fidPool.Put(uint64(c.fid))
return nil
}
// Close implements File.Close.
func (c *clientFile) Close() error {
// Avoid double close.
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
return linux.EBADF
}
runtime.SetFinalizer(c, nil)
// Send the close message.
if err := c.client.sendRecv(&tclunk{fid: c.fid}, &rclunk{}); err != nil {
// If an error occurred, we toss away the fid. This isn't ideal,
// but I'm not sure what else makes sense in this context.
return err
}
// Return the fid to the pool.
c.client.fidPool.Put(uint64(c.fid))
return nil
}
// Open implements File.Open.
func (c *clientFile) Open(flags OpenFlags) (QID, uint32, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return QID{}, 0, linux.EBADF
}
rlopen := rlopen{}
if err := c.client.sendRecv(&tlopen{fid: c.fid, Flags: flags}, &rlopen); err != nil {
return QID{}, 0, err
}
return rlopen.QID, rlopen.IoUnit, nil
}
// chunk applies fn to p in chunkSize-sized chunks until fn returns a partial result, p is
// exhausted, or an error is encountered (which may be io.EOF).
func chunk(chunkSize uint32, fn func([]byte, int64) (int, error), p []byte, offset int64) (int, error) {
// Some p9.Clients depend on executing fn on zero-byte buffers. Handle this
// as a special case (normally it is fine to short-circuit and return (0, nil)).
if len(p) == 0 {
return fn(p, offset)
}
// total is the cumulative bytes processed.
var total int
for {
var n int
var err error
// We're done, don't bother trying to do anything more.
if total == len(p) {
return total, nil
}
// Apply fn to a chunkSize-sized (or less) chunk of p.
if len(p) < total+int(chunkSize) {
n, err = fn(p[total:], offset)
} else {
n, err = fn(p[total:total+int(chunkSize)], offset)
}
total += n
offset += int64(n)
// Return whatever we have processed if we encounter an error. This error
// could be io.EOF.
if err != nil {
return total, err
}
// Did we get a partial result? If so, return it immediately.
if n < int(chunkSize) {
return total, nil
}
// If we received more bytes than we ever requested, this is a problem.
if total > len(p) {
panic(fmt.Sprintf("bytes completed (%d)) > requested (%d)", total, len(p)))
}
}
}
// ReadAt proxies File.ReadAt.
func (c *clientFile) ReadAt(p []byte, offset int64) (int, error) {
return chunk(c.client.payloadSize, c.readAt, p, offset)
}
func (c *clientFile) readAt(p []byte, offset int64) (int, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return 0, linux.EBADF
}
rread := rread{Data: p}
if err := c.client.sendRecv(&tread{fid: c.fid, Offset: uint64(offset), Count: uint32(len(p))}, &rread); err != nil {
return 0, err
}
// The message may have been truncated, or for some reason a new buffer
// allocated. This isn't the common path, but we make sure that if the
// payload has changed we copy it. See transport.go for more information.
if len(p) > 0 && len(rread.Data) > 0 && &rread.Data[0] != &p[0] {
copy(p, rread.Data)
}
// io.EOF is not an error that a p9 server can return. Use POSIX semantics to
// return io.EOF manually: zero bytes were returned and a non-zero buffer was used.
if len(rread.Data) == 0 && len(p) > 0 {
return 0, io.EOF
}
return len(rread.Data), nil
}
// WriteAt proxies File.WriteAt.
func (c *clientFile) WriteAt(p []byte, offset int64) (int, error) {
return chunk(c.client.payloadSize, c.writeAt, p, offset)
}
func (c *clientFile) writeAt(p []byte, offset int64) (int, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return 0, linux.EBADF
}
rwrite := rwrite{}
if err := c.client.sendRecv(&twrite{fid: c.fid, Offset: uint64(offset), Data: p}, &rwrite); err != nil {
return 0, err
}
return int(rwrite.Count), nil
}
// Rename implements File.Rename.
func (c *clientFile) Rename(dir File, name string) error {
if atomic.LoadUint32(&c.closed) != 0 {
return linux.EBADF
}
clientDir, ok := dir.(*clientFile)
if !ok {
return linux.EBADF
}
return c.client.sendRecv(&trename{fid: c.fid, Directory: clientDir.fid, Name: name}, &rrename{})
}
// Create implements File.Create.
func (c *clientFile) Create(name string, openFlags OpenFlags, permissions FileMode, uid UID, gid GID) (File, QID, uint32, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return nil, QID{}, 0, linux.EBADF
}
msg := tlcreate{
fid: c.fid,
Name: name,
OpenFlags: openFlags,
Permissions: permissions,
GID: NoGID,
}
if versionSupportsTucreation(c.client.version) {
msg.GID = gid
rucreate := rucreate{}
if err := c.client.sendRecv(&tucreate{tlcreate: msg, UID: uid}, &rucreate); err != nil {
return nil, QID{}, 0, err
}
return c, rucreate.QID, rucreate.IoUnit, nil
}
rlcreate := rlcreate{}
if err := c.client.sendRecv(&msg, &rlcreate); err != nil {
return nil, QID{}, 0, err
}
return c, rlcreate.QID, rlcreate.IoUnit, nil
}
// Mkdir implements File.Mkdir.
func (c *clientFile) Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return QID{}, linux.EBADF
}
msg := tmkdir{
Directory: c.fid,
Name: name,
Permissions: permissions,
GID: NoGID,
}
if versionSupportsTucreation(c.client.version) {
msg.GID = gid
rumkdir := rumkdir{}
if err := c.client.sendRecv(&tumkdir{tmkdir: msg, UID: uid}, &rumkdir); err != nil {
return QID{}, err
}
return rumkdir.QID, nil
}
rmkdir := rmkdir{}
if err := c.client.sendRecv(&msg, &rmkdir); err != nil {
return QID{}, err
}
return rmkdir.QID, nil
}
// Symlink implements File.Symlink.
func (c *clientFile) Symlink(oldname string, newname string, uid UID, gid GID) (QID, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return QID{}, linux.EBADF
}
msg := tsymlink{
Directory: c.fid,
Name: newname,
Target: oldname,
GID: NoGID,
}
if versionSupportsTucreation(c.client.version) {
msg.GID = gid
rusymlink := rusymlink{}
if err := c.client.sendRecv(&tusymlink{tsymlink: msg, UID: uid}, &rusymlink); err != nil {
return QID{}, err
}
return rusymlink.QID, nil
}
rsymlink := rsymlink{}
if err := c.client.sendRecv(&msg, &rsymlink); err != nil {
return QID{}, err
}
return rsymlink.QID, nil
}
// Link implements File.Link.
func (c *clientFile) Link(target File, newname string) error {
if atomic.LoadUint32(&c.closed) != 0 {
return linux.EBADF
}
targetFile, ok := target.(*clientFile)
if !ok {
return linux.EBADF
}
return c.client.sendRecv(&tlink{Directory: c.fid, Name: newname, Target: targetFile.fid}, &rlink{})
}
// Mknod implements File.Mknod.
func (c *clientFile) Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return QID{}, linux.EBADF
}
msg := tmknod{
Directory: c.fid,
Name: name,
Mode: mode,
Major: major,
Minor: minor,
GID: NoGID,
}
if versionSupportsTucreation(c.client.version) {
msg.GID = gid
rumknod := rumknod{}
if err := c.client.sendRecv(&tumknod{tmknod: msg, UID: uid}, &rumknod); err != nil {
return QID{}, err
}
return rumknod.QID, nil
}
rmknod := rmknod{}
if err := c.client.sendRecv(&msg, &rmknod); err != nil {
return QID{}, err
}
return rmknod.QID, nil
}
// RenameAt implements File.RenameAt.
func (c *clientFile) RenameAt(oldname string, newdir File, newname string) error {
if atomic.LoadUint32(&c.closed) != 0 {
return linux.EBADF
}
clientNewDir, ok := newdir.(*clientFile)
if !ok {
return linux.EBADF
}
return c.client.sendRecv(&trenameat{OldDirectory: c.fid, OldName: oldname, NewDirectory: clientNewDir.fid, NewName: newname}, &rrenameat{})
}
// UnlinkAt implements File.UnlinkAt.
func (c *clientFile) UnlinkAt(name string, flags uint32) error {
if atomic.LoadUint32(&c.closed) != 0 {
return linux.EBADF
}
return c.client.sendRecv(&tunlinkat{Directory: c.fid, Name: name, Flags: flags}, &runlinkat{})
}
// Readdir implements File.Readdir.
func (c *clientFile) Readdir(offset uint64, count uint32) (Dirents, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return nil, linux.EBADF
}
rreaddir := rreaddir{}
if err := c.client.sendRecv(&treaddir{Directory: c.fid, Offset: offset, Count: count}, &rreaddir); err != nil {
return nil, err
}
return rreaddir.Entries, nil
}
// Readlink implements File.Readlink.
func (c *clientFile) Readlink() (string, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return "", linux.EBADF
}
rreadlink := rreadlink{}
if err := c.client.sendRecv(&treadlink{fid: c.fid}, &rreadlink); err != nil {
return "", err
}
return rreadlink.Target, nil
}
// Renamed implements File.Renamed.
func (c *clientFile) Renamed(newDir File, newName string) {}

274
vendor/github.com/hugelgupf/p9/p9/file.go generated vendored Normal file
View File

@ -0,0 +1,274 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"github.com/hugelgupf/p9/linux"
)
// Attacher is provided by the server.
type Attacher interface {
// Attach returns a new File.
//
// The client-side attach will be translate to a series of walks from
// the file returned by this Attach call.
Attach() (File, error)
}
// File is a set of operations corresponding to a single node.
//
// Note that on the server side, the server logic places constraints on
// concurrent operations to make things easier. This may reduce the need for
// complex, error-prone locking and logic in the backend. These are documented
// for each method.
//
// There are three different types of guarantees provided:
//
// none: There is no concurrency guarantee. The method may be invoked
// concurrently with any other method on any other file.
//
// read: The method is guaranteed to be exclusive of any write or global
// operation that is mutating the state of the directory tree starting at this
// node. For example, this means creating new files, symlinks, directories or
// renaming a directory entry (or renaming in to this target), but the method
// may be called concurrently with other read methods.
//
// write: The method is guaranteed to be exclusive of any read, write or global
// operation that is mutating the state of the directory tree starting at this
// node, as described in read above. There may however, be other write
// operations executing concurrently on other components in the directory tree.
//
// global: The method is guaranteed to be exclusive of any read, write or
// global operation.
type File interface {
// Walk walks to the path components given in names.
//
// Walk returns QIDs in the same order that the names were passed in.
//
// An empty list of arguments should return a copy of the current file.
//
// On the server, Walk has a read concurrency guarantee.
Walk(names []string) ([]QID, File, error)
// WalkGetAttr walks to the next file and returns its maximal set of
// attributes.
//
// Server-side p9.Files may return linux.ENOSYS to indicate that Walk
// and GetAttr should be used separately to satisfy this request.
//
// On the server, WalkGetAttr has a read concurrency guarantee.
WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error)
// StatFS returns information about the file system associated with
// this file.
//
// On the server, StatFS has no concurrency guarantee.
StatFS() (FSStat, error)
// GetAttr returns attributes of this node.
//
// On the server, GetAttr has a read concurrency guarantee.
GetAttr(req AttrMask) (QID, AttrMask, Attr, error)
// SetAttr sets attributes on this node.
//
// On the server, SetAttr has a write concurrency guarantee.
SetAttr(valid SetAttrMask, attr SetAttr) error
// Close is called when all references are dropped on the server side,
// and Close should be called by the client to drop all references.
//
// For server-side implementations of Close, the error is ignored.
//
// Close must be called even when Open has not been called.
//
// On the server, Close has no concurrency guarantee.
Close() error
// Open must be called prior to using ReadAt, WriteAt, or Readdir. Once
// Open is called, some operations, such as Walk, will no longer work.
//
// On the client, Open should be called only once. The fd return is
// optional, and may be nil.
//
// On the server, Open has a read concurrency guarantee. Open is
// guaranteed to be called only once.
//
// N.B. The server must resolve any lazy paths when open is called.
// After this point, read and write may be called on files with no
// deletion check, so resolving in the data path is not viable.
Open(mode OpenFlags) (QID, uint32, error)
// ReadAt reads from this file. Open must be called first.
//
// This may return io.EOF in addition to linux.Errno values.
//
// On the server, ReadAt has a read concurrency guarantee. See Open for
// additional requirements regarding lazy path resolution.
ReadAt(p []byte, offset int64) (int, error)
// WriteAt writes to this file. Open must be called first.
//
// This may return io.EOF in addition to linux.Errno values.
//
// On the server, WriteAt has a read concurrency guarantee. See Open
// for additional requirements regarding lazy path resolution.
WriteAt(p []byte, offset int64) (int, error)
// SetXattr sets the extended attributes attr=data of the file.
//
// Flags are implementation-specific, but are
// generally Linux setxattr(2) flags.
SetXattr(attr string, data []byte, flags XattrFlags) error
// GetXattr fetches the extended attribute attr of the file.
GetXattr(attr string) ([]byte, error)
// ListXattrs lists the extended attribute names of the file.
ListXattrs() ([]string, error)
// RemoveXattr removes the extended attribute attr from the file.
RemoveXattr(attr string) error
// FSync syncs this node. Open must be called first.
//
// On the server, FSync has a read concurrency guarantee.
FSync() error
// Lock locks the file. The operation as defined in 9P2000.L is fairly
// ambitious, being a near-direct mapping to lockf(2)/fcntl(2)-style
// locking, but most implementations use flock(2).
//
// Arguments are defined by the 9P2000.L standard.
//
// Pid is the PID on the client. Locktype is one of read, write, or
// unlock (resp. 0, 1, or 2). Flags are to block (0), meaning wait; or
// reclaim (1), which is currently "reserved for future use." Start and
// length are the start of the region to use and the size. In many
// implementations, they are ignored and flock(2) is used. Client is an
// arbitrary string, also frequently unused. The Linux v9fs client
// happens to set the client name to the node name.
//
// The Linux v9fs client implements fcntl(F_SETLK) by calling lock
// without any flags set.
//
// The Linux v9fs client implements the fcntl(F_SETLKW) (blocking)
// lock request by calling lock with P9_LOCK_FLAGS_BLOCK set. If the
// response is P9_LOCK_BLOCKED, it retries the lock request in an
// interruptible loop until status is no longer P9_LOCK_BLOCKED.
//
// The Linux v9fs client translates BSD advisory locks (flock) to
// whole-file POSIX record locks. v9fs does not implement mandatory
// locks and will return ENOLCK if use is attempted.
//
// In the return values, a LockStatus corresponds to an Rlock, while
// returning an error corresponds to an Rlerror message. If any non-nil
// error is returned, an Rlerror message will be sent.
//
// The most commonly used return values are success and error (resp. 0
// and 2); blocked (1) and grace (3) are also possible.
Lock(pid int, locktype LockType, flags LockFlags, start, length uint64, client string) (LockStatus, error)
// Create creates a new regular file and opens it according to the
// flags given. This file is already Open.
//
// N.B. On the client, the returned file is a reference to the current
// file, which now represents the created file. This is not the case on
// the server. These semantics are very subtle and can easily lead to
// bugs, but are a consequence of the 9P create operation.
//
// On the server, Create has a write concurrency guarantee.
Create(name string, flags OpenFlags, permissions FileMode, uid UID, gid GID) (File, QID, uint32, error)
// Mkdir creates a subdirectory.
//
// On the server, Mkdir has a write concurrency guarantee.
Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error)
// Symlink makes a new symbolic link.
//
// On the server, Symlink has a write concurrency guarantee.
Symlink(oldName string, newName string, uid UID, gid GID) (QID, error)
// Link makes a new hard link.
//
// On the server, Link has a write concurrency guarantee.
Link(target File, newName string) error
// Mknod makes a new device node.
//
// On the server, Mknod has a write concurrency guarantee.
Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error)
// Rename renames the file.
//
// Rename will never be called on the server, and RenameAt will always
// be used instead.
Rename(newDir File, newName string) error
// RenameAt renames a given file to a new name in a potentially new
// directory.
//
// oldName must be a name relative to this file, which must be a
// directory. newName is a name relative to newDir.
//
// On the server, RenameAt has a global concurrency guarantee.
RenameAt(oldName string, newDir File, newName string) error
// UnlinkAt the given named file.
//
// name must be a file relative to this directory.
//
// Flags are implementation-specific (e.g. O_DIRECTORY), but are
// generally Linux unlinkat(2) flags.
//
// On the server, UnlinkAt has a write concurrency guarantee.
UnlinkAt(name string, flags uint32) error
// Readdir reads directory entries.
//
// offset is the entry offset, and count the number of entries to
// return.
//
// This may return io.EOF in addition to linux.Errno values.
//
// On the server, Readdir has a read concurrency guarantee.
Readdir(offset uint64, count uint32) (Dirents, error)
// Readlink reads the link target.
//
// On the server, Readlink has a read concurrency guarantee.
Readlink() (string, error)
// Renamed is called when this node is renamed.
//
// This may not fail. The file will hold a reference to its parent
// within the p9 package, and is therefore safe to use for the lifetime
// of this File (until Close is called).
//
// This method should not be called by clients, who should use the
// relevant Rename methods. (Although the method will be a no-op.)
//
// On the server, Renamed has a global concurrency guarantee.
Renamed(newDir File, newName string)
}
// DefaultWalkGetAttr implements File.WalkGetAttr to return ENOSYS for server-side Files.
type DefaultWalkGetAttr struct{}
// WalkGetAttr implements File.WalkGetAttr.
func (DefaultWalkGetAttr) WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error) {
return nil, nil, AttrMask{}, Attr{}, linux.ENOSYS
}

27
vendor/github.com/hugelgupf/p9/p9/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
//go:build gofuzz
// +build gofuzz
package p9
import (
"bytes"
"github.com/u-root/uio/ulog"
)
func Fuzz(data []byte) int {
buf := bytes.NewBuffer(data)
tag, msg, err := recv(ulog.Null, buf, DefaultMessageSize, msgDotLRegistry.get)
if err != nil {
if msg != nil {
panic("msg !=nil on error")
}
return 0
}
buf.Reset()
send(ulog.Null, buf, tag, msg)
if err != nil {
panic(err)
}
return 1
}

1390
vendor/github.com/hugelgupf/p9/p9/handlers.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

2348
vendor/github.com/hugelgupf/p9/p9/messages.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

1167
vendor/github.com/hugelgupf/p9/p9/p9.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

238
vendor/github.com/hugelgupf/p9/p9/path_tree.go generated vendored Normal file
View File

@ -0,0 +1,238 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"fmt"
"sync"
)
// pathNode is a single node in a path traversal.
//
// These are shared by all fidRefs that point to the same path.
//
// Lock ordering:
//
// opMu
// childMu
//
// Two different pathNodes may only be locked if Server.renameMu is held for
// write, in which case they can be acquired in any order.
type pathNode struct {
// opMu synchronizes high-level, sematic operations, such as the
// simultaneous creation and deletion of a file.
opMu sync.RWMutex
// deleted indicates that the backing file has been deleted. We stop many
// operations at the API level if they are incompatible with a file that has
// already been unlinked. deleted is protected by opMu. However, it may be
// changed without opMu if this node is deleted as part of an entire subtree
// on unlink. So deleted must only be accessed/mutated using atomics.
deleted uint32
// childMu protects the fields below.
childMu sync.RWMutex
// childNodes maps child path component names to their pathNode.
childNodes map[string]*pathNode
// childRefs maps child path component names to all of the their
// references.
childRefs map[string]map[*fidRef]struct{}
// childRefNames maps child references back to their path component
// name.
childRefNames map[*fidRef]string
}
func newPathNode() *pathNode {
return &pathNode{
childNodes: make(map[string]*pathNode),
childRefs: make(map[string]map[*fidRef]struct{}),
childRefNames: make(map[*fidRef]string),
}
}
// forEachChildRef calls fn for each child reference.
func (p *pathNode) forEachChildRef(fn func(ref *fidRef, name string)) {
p.childMu.RLock()
defer p.childMu.RUnlock()
for name, m := range p.childRefs {
for ref := range m {
fn(ref, name)
}
}
}
// forEachChildNode calls fn for each child pathNode.
func (p *pathNode) forEachChildNode(fn func(pn *pathNode)) {
p.childMu.RLock()
defer p.childMu.RUnlock()
for _, pn := range p.childNodes {
fn(pn)
}
}
// pathNodeFor returns the path node for the given name, or a new one.
func (p *pathNode) pathNodeFor(name string) *pathNode {
p.childMu.RLock()
// Fast path, node already exists.
if pn, ok := p.childNodes[name]; ok {
p.childMu.RUnlock()
return pn
}
p.childMu.RUnlock()
// Slow path, create a new pathNode for shared use.
p.childMu.Lock()
// Re-check after re-lock.
if pn, ok := p.childNodes[name]; ok {
p.childMu.Unlock()
return pn
}
pn := newPathNode()
p.childNodes[name] = pn
p.childMu.Unlock()
return pn
}
// nameFor returns the name for the given fidRef.
//
// Precondition: addChild is called for ref before nameFor.
func (p *pathNode) nameFor(ref *fidRef) string {
p.childMu.RLock()
n, ok := p.childRefNames[ref]
p.childMu.RUnlock()
if !ok {
// This should not happen, don't proceed.
panic(fmt.Sprintf("expected name for %+v, none found", ref))
}
return n
}
// addChildLocked adds a child reference to p.
//
// Precondition: As addChild, plus childMu is locked for write.
func (p *pathNode) addChildLocked(ref *fidRef, name string) {
if n, ok := p.childRefNames[ref]; ok {
// This should not happen, don't proceed.
panic(fmt.Sprintf("unexpected fidRef %+v with path %q, wanted %q", ref, n, name))
}
p.childRefNames[ref] = name
m, ok := p.childRefs[name]
if !ok {
m = make(map[*fidRef]struct{})
p.childRefs[name] = m
}
m[ref] = struct{}{}
}
// addChild adds a child reference to p.
//
// Precondition: ref may only be added once at a time.
func (p *pathNode) addChild(ref *fidRef, name string) {
p.childMu.Lock()
p.addChildLocked(ref, name)
p.childMu.Unlock()
}
// removeChild removes the given child.
//
// This applies only to an individual fidRef, which is not required to exist.
func (p *pathNode) removeChild(ref *fidRef) {
p.childMu.Lock()
// This ref may not exist anymore. This can occur, e.g., in unlink,
// where a removeWithName removes the ref, and then a DecRef on the ref
// attempts to remove again.
if name, ok := p.childRefNames[ref]; ok {
m, ok := p.childRefs[name]
if !ok {
// This should not happen, don't proceed.
p.childMu.Unlock()
panic(fmt.Sprintf("name %s missing from childfidRefs", name))
}
delete(m, ref)
if len(m) == 0 {
delete(p.childRefs, name)
}
}
delete(p.childRefNames, ref)
p.childMu.Unlock()
}
// addPathNodeFor adds an existing pathNode as the node for name.
//
// Preconditions: newName does not exist.
func (p *pathNode) addPathNodeFor(name string, pn *pathNode) {
p.childMu.Lock()
if opn, ok := p.childNodes[name]; ok {
p.childMu.Unlock()
panic(fmt.Sprintf("unexpected pathNode %+v with path %q", opn, name))
}
p.childNodes[name] = pn
p.childMu.Unlock()
}
// removeWithName removes all references with the given name.
//
// The provided function is executed after reference removal. The only method
// it may (transitively) call on this pathNode is addChildLocked.
//
// If a child pathNode for name exists, it is removed from this pathNode and
// returned by this function. Any operations on the removed tree must use this
// value.
func (p *pathNode) removeWithName(name string, fn func(ref *fidRef)) *pathNode {
p.childMu.Lock()
defer p.childMu.Unlock()
if m, ok := p.childRefs[name]; ok {
for ref := range m {
delete(m, ref)
delete(p.childRefNames, ref)
if fn == nil {
continue
}
// Attempt to hold a reference while calling fn() to
// prevent concurrent destruction of the child, which
// can lead to data races. If the child has already
// been destroyed, then we can skip the callback.
if ref.TryIncRef() {
fn(ref)
ref.DecRef()
}
}
}
// Return the original path node, if it exists.
origPathNode := p.childNodes[name]
delete(p.childNodes, name)
return origPathNode
}

65
vendor/github.com/hugelgupf/p9/p9/pool.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"sync"
)
// pool is a simple allocator.
//
// It is used for both tags and FIDs.
type pool struct {
mu sync.Mutex
// cache is the set of returned values.
cache []uint64
// start is the starting value (if needed).
start uint64
// limit is the upper limit.
limit uint64
}
// Get gets a value from the pool.
func (p *pool) Get() (uint64, bool) {
p.mu.Lock()
defer p.mu.Unlock()
// Anything cached?
if len(p.cache) > 0 {
v := p.cache[len(p.cache)-1]
p.cache = p.cache[:len(p.cache)-1]
return v, true
}
// Over the limit?
if p.start == p.limit {
return 0, false
}
// Generate a new value.
v := p.start
p.start++
return v, true
}
// Put returns a value to the pool.
func (p *pool) Put(v uint64) {
p.mu.Lock()
p.cache = append(p.cache, v)
p.mu.Unlock()
}

681
vendor/github.com/hugelgupf/p9/p9/server.go generated vendored Normal file
View File

@ -0,0 +1,681 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"context"
"errors"
"fmt"
"io"
"net"
"runtime/debug"
"strings"
"sync"
"sync/atomic"
"github.com/hugelgupf/p9/linux"
"github.com/u-root/uio/ulog"
)
// Server is a 9p2000.L server.
type Server struct {
// attacher provides the attach function.
attacher Attacher
// pathTree is the full set of paths opened on this server.
//
// These may be across different connections, but rename operations
// must be serialized globally for safely. There is a single pathTree
// for the entire server, and not per connection.
pathTree *pathNode
// renameMu is a global lock protecting rename operations. With this
// lock, we can be certain that any given rename operation can safely
// acquire two path nodes in any order, as all other concurrent
// operations acquire at most a single node.
renameMu sync.RWMutex
// log is a logger to log to, if specified.
log ulog.Logger
}
// ServerOpt is an optional config for a new server.
type ServerOpt func(s *Server)
// WithServerLogger overrides the default logger for the server.
func WithServerLogger(l ulog.Logger) ServerOpt {
return func(s *Server) {
s.log = l
}
}
// NewServer returns a new server.
func NewServer(attacher Attacher, o ...ServerOpt) *Server {
s := &Server{
attacher: attacher,
pathTree: newPathNode(),
log: ulog.Null,
}
for _, opt := range o {
opt(s)
}
return s
}
// connState is the state for a single connection.
type connState struct {
// server is the backing server.
server *Server
// fids is the set of active fids.
//
// This is used to find fids for files.
fidMu sync.Mutex
fids map[fid]*fidRef
// tags is the set of active tags.
//
// The given channel is closed when the
// tag is finished with processing.
tagMu sync.Mutex
tags map[tag]chan struct{}
// messageSize is the maximum message size. The server does not
// do automatic splitting of messages.
messageSize uint32
readBufPool sync.Pool
pristineZeros []byte
// baseVersion is the version of 9P protocol.
baseVersion baseVersion
// version is the agreed upon version X of 9P2000.L.Google.X.
// version 0 implies 9P2000.L.
version uint32
// pendingWg counts requests that are still being handled.
pendingWg sync.WaitGroup
// recvMu serializes receiving from t.
recvMu sync.Mutex
// recvIdle is the number of goroutines in handleRequests() attempting to
// lock recvMu so that they can receive from t. recvIdle is accessed
// using atomic memory operations.
recvIdle int32
// If recvShutdown is true, at least one goroutine has observed a
// connection error while receiving from t, and all goroutines in
// handleRequests() should exit immediately. recvShutdown is protected
// by recvMu.
recvShutdown bool
// sendMu serializes sending to r.
sendMu sync.Mutex
// t reads T messages and r write R messages
t io.ReadCloser
r io.WriteCloser
}
// xattrOp is the xattr related operations, walk or create.
type xattrOp int
const (
xattrNone = 0
xattrCreate = 1
xattrWalk = 2
)
type pendingXattr struct {
// the pending xattr-related operation
op xattrOp
// name is the attribute.
name string
// size of the attribute value, represents the
// length of the attribute value that is going to write to or read from a file.
size uint64
// flags associated with a txattrcreate message.
// generally Linux setxattr(2) flags.
flags XattrFlags
// saved up xattr operation value (for reads, listed / gotten buffer --
// ready for chunking; for writes, this is used to accumulate chunked
// values until a Tclunk actuates the operation)
buf []byte
}
// fidRef wraps a node and tracks references.
type fidRef struct {
// server is the associated server.
server *Server
// file is the associated File.
file File
// pendingXattr is the xattr-related operations that are going to be done
// in a tread or twrite request.
pendingXattr pendingXattr
// refs is an active refence count.
//
// The node above will be closed only when refs reaches zero.
refs int64
// opened indicates whether this has been opened already.
//
// This is updated in handlers.go.
//
// opened is protected by pathNode.opMu or renameMu (for write).
opened bool
// mode is the fidRef's mode from the walk. Only the type bits are
// valid, the permissions may change. This is used to sanity check
// operations on this element, and prevent walks across
// non-directories.
mode FileMode
// openFlags is the mode used in the open.
//
// This is updated in handlers.go.
//
// opened is protected by pathNode.opMu or renameMu (for write).
openFlags OpenFlags
// pathNode is the current pathNode for this fid.
pathNode *pathNode
// parent is the parent fidRef. We hold on to a parent reference to
// ensure that hooks, such as Renamed, can be executed safely by the
// server code.
//
// Note that parent cannot be changed without holding both the global
// rename lock and a writable lock on the associated pathNode for this
// fidRef. Holding either of these locks is sufficient to examine
// parent safely.
//
// The parent will be nil for root fidRefs, and non-nil otherwise. The
// method maybeParent can be used to return a cyclical reference, and
// isRoot should be used to check for root over looking at parent
// directly.
parent *fidRef
}
// IncRef increases the references on a fid.
func (f *fidRef) IncRef() {
atomic.AddInt64(&f.refs, 1)
}
// DecRef should be called when you're finished with a fid.
func (f *fidRef) DecRef() error {
if atomic.AddInt64(&f.refs, -1) == 0 {
var (
errs []error
err = f.file.Close()
)
if err != nil {
err = fmt.Errorf("file: %w", err)
errs = append(errs, err)
}
// Drop the parent reference.
//
// Since this fidRef is guaranteed to be non-discoverable when
// the references reach zero, we don't need to worry about
// clearing the parent.
if f.parent != nil {
// If we've been previously deleted, removing this
// ref is a no-op. That's expected.
f.parent.pathNode.removeChild(f)
if pErr := f.parent.DecRef(); pErr != nil {
pErr = fmt.Errorf("parent: %w", pErr)
errs = append(errs, pErr)
}
}
return errors.Join(errs...)
}
return nil
}
// TryIncRef returns true if a new reference is taken on the fid, and false if
// the fid has been destroyed.
func (f *fidRef) TryIncRef() bool {
for {
r := atomic.LoadInt64(&f.refs)
if r <= 0 {
return false
}
if atomic.CompareAndSwapInt64(&f.refs, r, r+1) {
return true
}
}
}
// isDeleted returns true if this fidRef has been deleted.
//
// Precondition: this must be called via safelyRead, safelyWrite or
// safelyGlobal.
func (f *fidRef) isDeleted() bool {
return atomic.LoadUint32(&f.pathNode.deleted) != 0
}
// isRoot indicates whether this is a root fid.
func (f *fidRef) isRoot() bool {
return f.parent == nil
}
// maybeParent returns a cyclic reference for roots, and the parent otherwise.
func (f *fidRef) maybeParent() *fidRef {
if f.parent != nil {
return f.parent
}
return f // Root has itself.
}
// notifyDelete marks all fidRefs as deleted.
//
// Precondition: this must be called via safelyWrite or safelyGlobal.
func notifyDelete(pn *pathNode) {
atomic.StoreUint32(&pn.deleted, 1)
// Call on all subtrees.
pn.forEachChildNode(func(pn *pathNode) {
notifyDelete(pn)
})
}
// markChildDeleted marks all children below the given name as deleted.
//
// Precondition: this must be called via safelyWrite or safelyGlobal.
func (f *fidRef) markChildDeleted(name string) {
if origPathNode := f.pathNode.removeWithName(name, nil); origPathNode != nil {
// Mark all children as deleted.
notifyDelete(origPathNode)
}
}
// notifyNameChange calls the relevant Renamed method on all nodes in the path,
// recursively. Note that this applies only for subtrees, as these
// notifications do not apply to the actual file whose name has changed.
//
// Precondition: this must be called via safelyGlobal.
func notifyNameChange(pn *pathNode) {
// Call on all local references.
pn.forEachChildRef(func(ref *fidRef, name string) {
ref.file.Renamed(ref.parent.file, name)
})
// Call on all subtrees.
pn.forEachChildNode(func(pn *pathNode) {
notifyNameChange(pn)
})
}
// renameChildTo renames the given child to the target.
//
// Precondition: this must be called via safelyGlobal.
func (f *fidRef) renameChildTo(oldName string, target *fidRef, newName string) {
target.markChildDeleted(newName)
origPathNode := f.pathNode.removeWithName(oldName, func(ref *fidRef) {
// N.B. DecRef can take f.pathNode's parent's childMu. This is
// allowed because renameMu is held for write via safelyGlobal.
ref.parent.DecRef() // Drop original reference.
ref.parent = target // Change parent.
ref.parent.IncRef() // Acquire new one.
if f.pathNode == target.pathNode {
target.pathNode.addChildLocked(ref, newName)
} else {
target.pathNode.addChild(ref, newName)
}
ref.file.Renamed(target.file, newName)
})
if origPathNode != nil {
// Replace the previous (now deleted) path node.
target.pathNode.addPathNodeFor(newName, origPathNode)
// Call Renamed on all children.
notifyNameChange(origPathNode)
}
}
// safelyRead executes the given operation with the local path node locked.
// This implies that paths will not change during the operation.
func (f *fidRef) safelyRead(fn func() error) (err error) {
f.server.renameMu.RLock()
defer f.server.renameMu.RUnlock()
f.pathNode.opMu.RLock()
defer f.pathNode.opMu.RUnlock()
return fn()
}
// safelyWrite executes the given operation with the local path node locked in
// a writable fashion. This implies some paths may change.
func (f *fidRef) safelyWrite(fn func() error) (err error) {
f.server.renameMu.RLock()
defer f.server.renameMu.RUnlock()
f.pathNode.opMu.Lock()
defer f.pathNode.opMu.Unlock()
return fn()
}
// safelyGlobal executes the given operation with the global path lock held.
func (f *fidRef) safelyGlobal(fn func() error) (err error) {
f.server.renameMu.Lock()
defer f.server.renameMu.Unlock()
return fn()
}
// Lookupfid finds the given fid.
//
// You should call fid.DecRef when you are finished using the fid.
func (cs *connState) LookupFID(fid fid) (*fidRef, bool) {
cs.fidMu.Lock()
defer cs.fidMu.Unlock()
fidRef, ok := cs.fids[fid]
if ok {
fidRef.IncRef()
return fidRef, true
}
return nil, false
}
// Insertfid installs the given fid.
//
// This fid starts with a reference count of one. If a fid exists in
// the slot already it is closed, per the specification.
func (cs *connState) InsertFID(fid fid, newRef *fidRef) {
cs.fidMu.Lock()
defer cs.fidMu.Unlock()
origRef, ok := cs.fids[fid]
if ok {
defer origRef.DecRef()
}
newRef.IncRef()
cs.fids[fid] = newRef
}
// Deletefid removes the given fid.
//
// This simply removes it from the map and drops a reference.
func (cs *connState) DeleteFID(fid fid) error {
cs.fidMu.Lock()
defer cs.fidMu.Unlock()
fidRef, ok := cs.fids[fid]
if !ok {
return linux.EBADF
}
delete(cs.fids, fid)
return fidRef.DecRef()
}
// StartTag starts handling the tag.
//
// False is returned if this tag is already active.
func (cs *connState) StartTag(t tag) bool {
cs.tagMu.Lock()
defer cs.tagMu.Unlock()
_, ok := cs.tags[t]
if ok {
return false
}
cs.tags[t] = make(chan struct{})
return true
}
// ClearTag finishes handling a tag.
func (cs *connState) ClearTag(t tag) {
cs.tagMu.Lock()
defer cs.tagMu.Unlock()
ch, ok := cs.tags[t]
if !ok {
// Should never happen.
panic("unused tag cleared")
}
delete(cs.tags, t)
// Notify.
close(ch)
}
// Waittag waits for a tag to finish.
func (cs *connState) WaitTag(t tag) {
cs.tagMu.Lock()
ch, ok := cs.tags[t]
cs.tagMu.Unlock()
if !ok {
return
}
// Wait for close.
<-ch
}
// handleRequest handles a single request.
//
// The recvDone channel is signaled when recv is done (with a error if
// necessary). The sendDone channel is signaled with the result of the send.
func (cs *connState) handleRequest() bool {
cs.pendingWg.Add(1)
defer cs.pendingWg.Done()
// Obtain the right to receive a message from cs.t.
atomic.AddInt32(&cs.recvIdle, 1)
cs.recvMu.Lock()
atomic.AddInt32(&cs.recvIdle, -1)
if cs.recvShutdown {
// Another goroutine already detected a connection problem; exit
// immediately.
cs.recvMu.Unlock()
return false
}
messageSize := atomic.LoadUint32(&cs.messageSize)
if messageSize == 0 {
// Default or not yet negotiated.
messageSize = maximumLength
}
// Receive a message.
tag, m, err := recv(cs.server.log, cs.t, messageSize, msgDotLRegistry.get)
if errSocket, ok := err.(ConnError); ok {
if errSocket.error != io.EOF {
// Connection problem; stop serving.
cs.server.log.Printf("p9.recv: %v", errSocket.error)
}
cs.recvShutdown = true
cs.recvMu.Unlock()
return false
}
// Ensure that another goroutine is available to receive from cs.t.
if atomic.LoadInt32(&cs.recvIdle) == 0 {
go cs.handleRequests() // S/R-SAFE: Irrelevant.
}
cs.recvMu.Unlock()
// Deal with other errors.
if err != nil && err != io.EOF {
// If it's not a connection error, but some other protocol error,
// we can send a response immediately.
cs.sendMu.Lock()
err := send(cs.server.log, cs.r, tag, newErr(err))
cs.sendMu.Unlock()
if err != nil {
cs.server.log.Printf("p9.send: %v", err)
}
return true
}
// Try to start the tag.
if !cs.StartTag(tag) {
cs.server.log.Printf("no valid tag [%05d]", tag)
// Nothing we can do at this point; client is bogus.
return true
}
// Handle the message.
r := cs.handle(m)
// Clear the tag before sending. That's because as soon as this
// hits the wire, the client can legally send another message
// with the same tag.
cs.ClearTag(tag)
// Send back the result.
cs.sendMu.Lock()
err = send(cs.server.log, cs.r, tag, r)
cs.sendMu.Unlock()
if err != nil {
cs.server.log.Printf("p9.send: %v", err)
}
msgDotLRegistry.put(m)
m = nil // 'm' should not be touched after this point.
return true
}
func (cs *connState) handle(m message) (r message) {
defer func() {
if r == nil {
// Don't allow a panic to propagate.
err := recover()
// Include a useful log message.
cs.server.log.Printf("panic in handler - %v: %s", err, debug.Stack())
// Wrap in an EFAULT error; we don't really have a
// better way to describe this kind of error. It will
// usually manifest as a result of the test framework.
r = newErr(linux.EFAULT)
}
}()
if handler, ok := m.(handler); ok {
// Call the message handler.
r = handler.handle(cs)
} else {
// Produce an ENOSYS error.
r = newErr(linux.ENOSYS)
}
return
}
func (cs *connState) handleRequests() {
for {
if !cs.handleRequest() {
return
}
}
}
func (cs *connState) stop() {
// Wait for completion of all inflight request goroutines.. If a
// request is stuck, something has the opportunity to kill us with
// SIGABRT to get a stack dump of the offending handler.
cs.pendingWg.Wait()
// Ensure the connection is closed.
cs.r.Close()
cs.t.Close()
for _, fidRef := range cs.fids {
// Drop final reference in the fid table. Note this should
// always close the file, since we've ensured that there are no
// handlers running via the wait for Pending => 0 below.
fidRef.DecRef()
}
}
// Handle handles a single connection.
func (s *Server) Handle(t io.ReadCloser, r io.WriteCloser) error {
cs := &connState{
server: s,
t: t,
r: r,
fids: make(map[fid]*fidRef),
tags: make(map[tag]chan struct{}),
}
defer cs.stop()
// Serve requests from t in the current goroutine; handleRequests()
// will create more goroutines as needed.
cs.handleRequests()
return nil
}
func isErrClosing(err error) bool {
return strings.Contains(err.Error(), "use of closed network connection")
}
// Serve handles requests from the bound socket.
//
// The passed serverSocket _must_ be created in packet mode.
func (s *Server) Serve(serverSocket net.Listener) error {
return s.ServeContext(nil, serverSocket)
}
var errAlreadyClosed = errors.New("already closed")
// ServeContext handles requests from the bound socket.
//
// The passed serverSocket _must_ be created in packet mode.
//
// When the context is done, the listener is closed and serve returns once
// every request has been handled.
func (s *Server) ServeContext(ctx context.Context, serverSocket net.Listener) error {
var wg sync.WaitGroup
defer wg.Wait()
var cancelCause context.CancelCauseFunc
if ctx != nil {
ctx, cancelCause = context.WithCancelCause(ctx)
wg.Add(1)
go func() {
defer wg.Done()
<-ctx.Done()
// Only close the server socket if it wasn't already closed.
if err := ctx.Err(); errors.Is(err, errAlreadyClosed) {
return
}
serverSocket.Close()
}()
}
for {
conn, err := serverSocket.Accept()
if err != nil {
if cancelCause != nil {
cancelCause(errAlreadyClosed)
}
if isErrClosing(err) {
return nil
}
// Something went wrong.
return err
}
wg.Add(1)
go func(conn net.Conn) { // S/R-SAFE: Irrelevant.
s.Handle(conn, conn)
wg.Done()
}(conn)
}
}

245
vendor/github.com/hugelgupf/p9/p9/transport.go generated vendored Normal file
View File

@ -0,0 +1,245 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"sync"
"github.com/hugelgupf/p9/vecnet"
"github.com/u-root/uio/ulog"
)
// ConnError is returned in cases of a connection issue.
//
// This may be treated differently than other errors.
type ConnError struct {
// error is the socket error.
error
}
func (e ConnError) Error() string {
return fmt.Sprintf("socket error: %v", e.error)
}
// Is reports whether any error in err's chain matches target.
func (e ConnError) Is(target error) bool { return target == e.error }
// ErrMessageTooLarge indicates the size was larger than reasonable.
type ErrMessageTooLarge struct {
size uint32
msize uint32
}
// Error returns a sensible error.
func (e *ErrMessageTooLarge) Error() string {
return fmt.Sprintf("message too large for fixed buffer: size is %d, limit is %d", e.size, e.msize)
}
// ErrNoValidMessage indicates no valid message could be decoded.
var ErrNoValidMessage = errors.New("buffer contained no valid message")
const (
// headerLength is the number of bytes required for a header.
headerLength uint32 = 7
// maximumLength is the largest possible message.
maximumLength uint32 = 4 * 1024 * 1024
// initialBufferLength is the initial data buffer we allocate.
initialBufferLength uint32 = 64
)
var dataPool = sync.Pool{
New: func() interface{} {
// These buffers are used for decoding without a payload.
// We need to return a pointer to avoid unnecessary allocations
// (see https://staticcheck.io/docs/checks#SA6002).
b := make([]byte, initialBufferLength)
return &b
},
}
// send sends the given message over the socket.
func send(l ulog.Logger, w io.Writer, tag tag, m message) error {
data := dataPool.Get().(*[]byte)
dataBuf := buffer{data: (*data)[:0]}
// Encode the message. The buffer will grow automatically.
m.encode(&dataBuf)
l.Printf("send [w %p] [Tag %06d] %s", w, tag, m)
// Get our vectors to send.
var hdr [headerLength]byte
vecs := make(net.Buffers, 0, 3)
vecs = append(vecs, hdr[:])
if len(dataBuf.data) > 0 {
vecs = append(vecs, dataBuf.data)
}
totalLength := headerLength + uint32(len(dataBuf.data))
// Is there a payload?
if payloader, ok := m.(payloader); ok {
p := payloader.Payload()
if len(p) > 0 {
vecs = append(vecs, p)
totalLength += uint32(len(p))
}
defer payloader.PayloadCleanup()
}
// Construct the header.
headerBuf := buffer{data: hdr[:0]}
headerBuf.Write32(totalLength)
headerBuf.WriteMsgType(m.typ())
headerBuf.WriteTag(tag)
if _, err := vecs.WriteTo(w); err != nil {
return ConnError{err}
}
// All set.
dataPool.Put(&dataBuf.data)
return nil
}
// lookupTagAndType looks up an existing message or creates a new one.
//
// This is called by recv after decoding the header. Any error returned will be
// propagating back to the caller. You may use messageByType directly as a
// lookupTagAndType function (by design).
type lookupTagAndType func(tag tag, t msgType) (message, error)
// recv decodes a message from the socket.
//
// This is done in two parts, and is thus not safe for multiple callers.
//
// On a socket error, the special error type ErrSocket is returned.
//
// The tag value NoTag will always be returned if err is non-nil.
func recv(l ulog.Logger, r io.Reader, msize uint32, lookup lookupTagAndType) (tag, message, error) {
// Read a header.
var hdr [headerLength]byte
if _, err := io.ReadAtLeast(r, hdr[:], int(headerLength)); err != nil {
return noTag, nil, ConnError{err}
}
// Decode the header.
headerBuf := buffer{data: hdr[:]}
size := headerBuf.Read32()
t := headerBuf.ReadMsgType()
tag := headerBuf.ReadTag()
if size < headerLength {
// The message is too small.
//
// See above: it's probably screwed.
return noTag, nil, ConnError{ErrNoValidMessage}
}
if size > maximumLength || size > msize {
// The message is too big.
return noTag, nil, ConnError{&ErrMessageTooLarge{size, msize}}
}
remaining := size - headerLength
// Find our message to decode.
m, err := lookup(tag, t)
if err != nil {
// Throw away the contents of this message.
if remaining > 0 {
_, _ = io.Copy(ioutil.Discard, io.LimitReader(r, int64(remaining)))
}
return tag, nil, err
}
// Not yet initialized.
var dataBuf buffer
var vecs vecnet.Buffers
appendBuffer := func(size int) *[]byte {
// Pull a data buffer from the pool.
datap := dataPool.Get().(*[]byte)
data := *datap
if size > len(data) {
// Create a larger data buffer.
data = make([]byte, size)
datap = &data
} else {
// Limit the data buffer.
data = data[:size]
}
dataBuf = buffer{data: data}
vecs = append(vecs, data)
return datap
}
// Read the rest of the payload.
//
// This requires some special care to ensure that the vectors all line
// up the way they should. We do this to minimize copying data around.
if payloader, ok := m.(payloader); ok {
fixedSize := payloader.FixedSize()
// Do we need more than there is?
if fixedSize > remaining {
// This is not a valid message.
if remaining > 0 {
_, _ = io.Copy(ioutil.Discard, io.LimitReader(r, int64(remaining)))
}
return noTag, nil, ErrNoValidMessage
}
if fixedSize != 0 {
datap := appendBuffer(int(fixedSize))
defer dataPool.Put(datap)
}
// Include the payload.
p := payloader.Payload()
if p == nil || len(p) != int(remaining-fixedSize) {
p = make([]byte, remaining-fixedSize)
payloader.SetPayload(p)
}
if len(p) > 0 {
vecs = append(vecs, p)
}
} else if remaining != 0 {
datap := appendBuffer(int(remaining))
defer dataPool.Put(datap)
}
if len(vecs) > 0 {
if _, err := vecs.ReadFrom(r); err != nil {
return noTag, nil, ConnError{err}
}
}
// Decode the message data.
m.decode(&dataBuf)
if dataBuf.isOverrun() {
// No need to drain the socket.
return noTag, nil, ErrNoValidMessage
}
l.Printf("recv [r %p] [Tag %06d] %s", r, tag, m)
// All set.
return tag, m, nil
}

134
vendor/github.com/hugelgupf/p9/p9/version.go generated vendored Normal file
View File

@ -0,0 +1,134 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"fmt"
"strconv"
"strings"
)
const (
// highestSupportedVersion is the highest supported version X in a
// version string of the format 9P2000.L.Google.X.
//
// Clients are expected to start requesting this version number and
// to continuously decrement it until a Tversion request succeeds.
highestSupportedVersion uint32 = 7
// lowestSupportedVersion is the lowest supported version X in a
// version string of the format 9P2000.L.Google.X.
//
// Clients are free to send a Tversion request at a version below this
// value but are expected to encounter an Rlerror in response.
lowestSupportedVersion uint32 = 0
)
type baseVersion string
const (
undetermined baseVersion = ""
version9P2000 baseVersion = "9P2000"
version9P2000U baseVersion = "9P2000.u"
version9P2000L baseVersion = "9P2000.L"
)
// HighestVersionString returns the highest possible version string that a client
// may request or a server may support.
func HighestVersionString() string {
return versionString(version9P2000L, highestSupportedVersion)
}
// parseVersion parses a Tversion version string into a numeric version number
// if the version string is supported by p9. Otherwise returns (0, false).
//
// From Tversion(9P): "Version strings are defined such that, if the client string
// contains one or more period characters, the initial substring up to but not
// including any single period in the version string defines a version of the protocol."
//
// p9 intentionally diverges from this and always requires that the version string
// start with 9P2000.L to express that it is always compatible with 9P2000.L. The
// only supported versions extensions are of the format 9p2000.L.Google.X where X
// is an ever increasing version counter.
//
// Version 9P2000.L.Google.0 implies 9P2000.L.
//
// New versions must always be a strict superset of 9P2000.L. A version increase must
// define a predicate representing the feature extension introduced by that version. The
// predicate must be commented and should take the format:
//
// // VersionSupportsX returns true if version v supports X and must be checked when ...
//
// func VersionSupportsX(v int32) bool {
// ...
//
// )
func parseVersion(str string) (baseVersion, uint32, bool) {
switch str {
case "9P2000.L":
return version9P2000L, 0, true
case "9P2000.u":
return version9P2000U, 0, true
case "9P2000":
return version9P2000, 0, true
default:
substr := strings.Split(str, ".")
if len(substr) != 4 {
return "", 0, false
}
if substr[0] != "9P2000" || substr[1] != "L" || substr[2] != "Google" || len(substr[3]) == 0 {
return "", 0, false
}
version, err := strconv.ParseUint(substr[3], 10, 32)
if err != nil {
return "", 0, false
}
return version9P2000L, uint32(version), true
}
}
// versionString formats a p9 version number into a Tversion version string.
func versionString(baseVersion baseVersion, version uint32) string {
// Special case the base version so that clients expecting this string
// instead of the 9P2000.L.Google.0 equivalent get it. This is important
// for backwards compatibility with legacy servers that check for exactly
// the baseVersion and allow nothing else.
if version == 0 {
return string(baseVersion)
}
return fmt.Sprintf("9P2000.L.Google.%d", version)
}
// versionSupportsTwalkgetattr returns true if version v supports the
// Twalkgetattr message. This predicate must be checked by clients before
// attempting to make a Twalkgetattr request.
func versionSupportsTwalkgetattr(v uint32) bool {
return v >= 2
}
// versionSupportsTucreation returns true if version v supports the Tucreation
// messages (Tucreate, Tusymlink, Tumkdir, Tumknod). This predicate must be
// checked by clients before attempting to make a Tucreation request.
// If Tucreation messages are not supported, their non-UID supporting
// counterparts (Tlcreate, Tsymlink, Tmkdir, Tmknod) should be used.
func versionSupportsTucreation(v uint32) bool {
return v >= 3
}
// VersionSupportsMultiUser returns true if version v supports multi-user fake
// directory permissions and ID values.
func VersionSupportsMultiUser(v uint32) bool {
return v >= 6
}

22
vendor/github.com/hugelgupf/p9/vecnet/iov32_linux.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build 386 || mips || arm || mipsle
// +build 386 mips arm mipsle
package vecnet
func iovlen(i int) uint32 {
return uint32(i)
}

22
vendor/github.com/hugelgupf/p9/vecnet/iov_linux.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !386 && !arm && !mips && !mipsle
// +build !386,!arm,!mips,!mipsle
package vecnet
func iovlen(i int) uint64 {
return uint64(i)
}

54
vendor/github.com/hugelgupf/p9/vecnet/vecnet.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package vecnet provides access to recvmsg syscalls on net.Conns.
package vecnet
import (
"io"
"net"
"syscall"
)
// Buffers points to zero or more buffers to read into.
//
// On connections that support it, ReadFrom is optimized into the batch read
// operation recvmsg.
type Buffers net.Buffers
// ReadFrom reads into the pre-allocated bufs. Returns bytes read.
//
// ReadFrom keeps reading until all bufs are filled or EOF is received.
//
// The pre-allocatted space used by ReadFrom is based upon slice lengths.
func (bufs Buffers) ReadFrom(r io.Reader) (int64, error) {
if conn, ok := r.(syscall.Conn); ok && readFromBuffers != nil {
return readFromBuffers(bufs, conn)
}
var total int64
for _, buf := range bufs {
for filled := 0; filled < len(buf); {
n, err := r.Read(buf[filled:])
total += int64(n)
filled += n
if (n == 0 && err == nil) || err == io.EOF {
return total, io.EOF
} else if err != nil {
return total, err
}
}
}
return total, nil
}

112
vendor/github.com/hugelgupf/p9/vecnet/vecnet_linux.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !386
// +build !386
package vecnet
import (
"io"
"runtime"
"syscall"
"unsafe"
)
var readFromBuffers = readFromBuffersLinux
func readFromBuffersLinux(bufs Buffers, conn syscall.Conn) (int64, error) {
rc, err := conn.SyscallConn()
if err != nil {
return 0, err
}
length := int64(0)
for _, buf := range bufs {
length += int64(len(buf))
}
for n := int64(0); n < length; {
cur, err := recvmsg(bufs, rc)
if err != nil && (cur == 0 || err != io.EOF) {
return n, err
}
n += int64(cur)
// Consume iovecs to retry.
for consumed := 0; consumed < cur; {
if len(bufs[0]) <= cur-consumed {
consumed += len(bufs[0])
bufs = bufs[1:]
} else {
bufs[0] = bufs[0][cur-consumed:]
break
}
}
}
return length, nil
}
// buildIovec builds an iovec slice from the given []byte slice.
//
// iovecs is used as an initial slice, to avoid excessive allocations.
func buildIovec(bufs Buffers, iovecs []syscall.Iovec) ([]syscall.Iovec, int) {
var length int
for _, buf := range bufs {
if l := len(buf); l > 0 {
iovecs = append(iovecs, syscall.Iovec{
Base: &buf[0],
Len: iovlen(l),
})
length += l
}
}
return iovecs, length
}
func recvmsg(bufs Buffers, rc syscall.RawConn) (int, error) {
iovecs, length := buildIovec(bufs, make([]syscall.Iovec, 0, 2))
var msg syscall.Msghdr
if len(iovecs) != 0 {
msg.Iov = &iovecs[0]
msg.Iovlen = iovlen(len(iovecs))
}
// n is the bytes received.
var n uintptr
var e syscall.Errno
err := rc.Read(func(fd uintptr) bool {
n, _, e = syscall.Syscall(syscall.SYS_RECVMSG, fd, uintptr(unsafe.Pointer(&msg)), syscall.MSG_DONTWAIT)
// Return false if EINTR, EAGAIN, or EWOULDBLOCK to retry.
return !(e == syscall.EINTR || e == syscall.EAGAIN || e == syscall.EWOULDBLOCK)
})
runtime.KeepAlive(iovecs)
if err != nil {
return 0, err
}
if e != 0 {
return 0, e
}
// The other end is closed by returning a 0 length read with no error.
if n == 0 {
return 0, io.EOF
}
if int(n) > length {
return length, io.ErrShortBuffer
}
return int(n), nil
}

24
vendor/github.com/hugelgupf/p9/vecnet/vecnet_other.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !linux || (linux && 386)
// +build !linux linux,386
package vecnet
import (
"syscall"
)
var readFromBuffers func(bufs Buffers, conn syscall.Conn) (int64, error)