summaryrefslogtreecommitdiffhomepage
path: root/pkg/p9/local_server
diff options
context:
space:
mode:
authorGoogler <noreply@google.com>2018-04-27 10:37:02 -0700
committerAdin Scannell <ascannell@google.com>2018-04-28 01:44:26 -0400
commitd02b74a5dcfed4bfc8f2f8e545bca4d2afabb296 (patch)
tree54f95eef73aee6bacbfc736fffc631be2605ed53 /pkg/p9/local_server
parentf70210e742919f40aa2f0934a22f1c9ba6dada62 (diff)
Check in gVisor.
PiperOrigin-RevId: 194583126 Change-Id: Ica1d8821a90f74e7e745962d71801c598c652463
Diffstat (limited to 'pkg/p9/local_server')
-rw-r--r--pkg/p9/local_server/BUILD14
-rw-r--r--pkg/p9/local_server/local_server.go347
2 files changed, 361 insertions, 0 deletions
diff --git a/pkg/p9/local_server/BUILD b/pkg/p9/local_server/BUILD
new file mode 100644
index 000000000..8229e6308
--- /dev/null
+++ b/pkg/p9/local_server/BUILD
@@ -0,0 +1,14 @@
+package(licenses = ["notice"]) # Apache 2.0
+
+load("@io_bazel_rules_go//go:def.bzl", "go_binary")
+
+go_binary(
+ name = "local_server",
+ srcs = ["local_server.go"],
+ deps = [
+ "//pkg/fd",
+ "//pkg/log",
+ "//pkg/p9",
+ "//pkg/unet",
+ ],
+)
diff --git a/pkg/p9/local_server/local_server.go b/pkg/p9/local_server/local_server.go
new file mode 100644
index 000000000..5b1e97711
--- /dev/null
+++ b/pkg/p9/local_server/local_server.go
@@ -0,0 +1,347 @@
+// Copyright 2018 Google Inc.
+//
+// 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.
+
+// Binary local_server provides a local 9P2000.L server for the p9 package.
+//
+// To use, first start the server:
+// local_server /tmp/my_bind_addr
+//
+// Then, connect using the Linux 9P filesystem:
+// mount -t 9p -o trans=unix,version=9P2000.L /tmp/my_bind_addr /mnt
+//
+// This package also serves as an examplar.
+package main
+
+import (
+ "os"
+ "path"
+ "syscall"
+
+ "gvisor.googlesource.com/gvisor/pkg/fd"
+ "gvisor.googlesource.com/gvisor/pkg/log"
+ "gvisor.googlesource.com/gvisor/pkg/p9"
+ "gvisor.googlesource.com/gvisor/pkg/unet"
+)
+
+// local wraps a local file.
+type local struct {
+ p9.DefaultWalkGetAttr
+
+ path string
+ file *os.File
+}
+
+// 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 {
+ log.Warningf("error stating %#v: %v", l, err)
+ return qid, nil, err
+ }
+
+ // Construct the QID type.
+ qid.Type = p9.ModeFromOS(fi.Mode()).QIDType()
+
+ // Save the path from the Ino.
+ qid.Path = fi.Sys().(*syscall.Stat_t).Ino
+ return qid, fi, nil
+}
+
+// Attach implements p9.Attacher.Attach.
+func (l *local) Attach(name string) (p9.File, error) {
+ return &local{path: path.Clean(name)}, 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}
+ 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
+}
+
+// StatFS implements p9.File.StatFS.
+//
+// Not implemented.
+func (l *local) StatFS() (p9.FSStat, error) {
+ return p9.FSStat{}, syscall.ENOSYS
+}
+
+// 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 := fi.Sys().(*syscall.Stat_t)
+ attr := p9.Attr{
+ Mode: p9.FileMode(stat.Mode),
+ UID: p9.UID(stat.Uid),
+ GID: p9.GID(stat.Gid),
+ NLink: stat.Nlink,
+ RDev: 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),
+ }
+ valid := p9.AttrMask{
+ Mode: true,
+ UID: true,
+ GID: true,
+ NLink: true,
+ RDev: true,
+ Size: true,
+ Blocks: true,
+ ATime: true,
+ MTime: true,
+ CTime: true,
+ }
+
+ return qid, valid, attr, nil
+}
+
+// SetAttr implements p9.File.SetAttr.
+//
+// Not implemented.
+func (l *local) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
+ return syscall.ENOSYS
+}
+
+// Remove implements p9.File.Remove.
+//
+// Not implemented.
+func (l *local) Remove() error {
+ return syscall.ENOSYS
+}
+
+// Rename implements p9.File.Rename.
+//
+// Not implemented.
+func (l *local) Rename(directory p9.File, name string) error {
+ return syscall.ENOSYS
+}
+
+// Close implements p9.File.Close.
+func (l *local) Close() error {
+ if l.file != nil {
+ return l.file.Close()
+ }
+ return nil
+}
+
+// Open implements p9.File.Open.
+func (l *local) Open(mode p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) {
+ qid, _, err := l.info()
+ if err != nil {
+ return nil, qid, 0, err
+ }
+
+ // Do the actual open.
+ f, err := os.OpenFile(l.path, int(mode), 0)
+ if err != nil {
+ return nil, qid, 0, err
+ }
+ l.file = f
+
+ // Note: we don't send the local file for this server.
+ return nil, qid, 4096, nil
+}
+
+// Read implements p9.File.Read.
+func (l *local) ReadAt(p []byte, offset uint64) (int, error) {
+ return l.file.ReadAt(p, int64(offset))
+}
+
+// Write implements p9.File.Write.
+func (l *local) WriteAt(p []byte, offset uint64) (int, error) {
+ return l.file.WriteAt(p, int64(offset))
+}
+
+// Create implements p9.File.Create.
+func (l *local) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (*fd.FD, p9.File, p9.QID, uint32, error) {
+ f, err := os.OpenFile(l.path, int(mode)|syscall.O_CREAT|syscall.O_EXCL, os.FileMode(permissions))
+ if err != nil {
+ return nil, nil, p9.QID{}, 0, err
+ }
+
+ l2 := &local{path: path.Join(l.path, name), file: f}
+ qid, _, err := l2.info()
+ if err != nil {
+ l2.Close()
+ return nil, nil, p9.QID{}, 0, err
+ }
+
+ return nil, l2, qid, 4096, 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 {
+ if err := os.Link(target.(*local).path, path.Join(l.path, newname)); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Mknod implements p9.File.Mknod.
+//
+// Not implemented.
+func (l *local) Mknod(name string, permissions p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
+ return p9.QID{}, syscall.ENOSYS
+}
+
+// RenameAt implements p9.File.RenameAt.
+//
+// Not implemented.
+func (l *local) RenameAt(oldname string, newdir p9.File, newname string) error {
+ return syscall.ENOSYS
+}
+
+// UnlinkAt implements p9.File.UnlinkAt.
+//
+// Not implemented.
+func (l *local) UnlinkAt(name string, flags uint32) error {
+ return syscall.ENOSYS
+}
+
+// Readdir implements p9.File.Readdir.
+func (l *local) Readdir(offset uint64, count uint32) ([]p9.Dirent, error) {
+ // We only do *all* dirents in single shot.
+ const maxDirentBuffer = 1024 * 1024
+ buf := make([]byte, maxDirentBuffer)
+ n, err := syscall.ReadDirent(int(l.file.Fd()), buf)
+ if err != nil {
+ // Return zero entries.
+ return nil, nil
+ }
+
+ // Parse the entries; note that we read up to offset+count here.
+ _, newCount, newNames := syscall.ParseDirent(buf[:n], int(offset)+int(count), nil)
+ var dirents []p9.Dirent
+ for i := int(offset); i >= 0 && i < newCount; i++ {
+ entry := local{path: path.Join(l.path, newNames[i])}
+ qid, _, err := entry.info()
+ if err != nil {
+ continue
+ }
+ dirents = append(dirents, p9.Dirent{
+ QID: qid,
+ Type: qid.Type,
+ Name: newNames[i],
+ Offset: uint64(i + 1),
+ })
+ }
+
+ return dirents, nil
+}
+
+// Readlink implements p9.File.Readlink.
+//
+// Not properly implemented.
+func (l *local) Readlink() (string, error) {
+ return os.Readlink(l.path)
+}
+
+// Flush implements p9.File.Flush.
+func (l *local) Flush() error {
+ return nil
+}
+
+// Connect implements p9.File.Connect.
+func (l *local) Connect(p9.ConnectFlags) (*fd.FD, error) {
+ return nil, syscall.ECONNREFUSED
+}
+
+func main() {
+ log.SetLevel(log.Debug)
+
+ if len(os.Args) != 2 {
+ log.Warningf("usage: %s <bind-addr>", os.Args[0])
+ os.Exit(1)
+ }
+
+ // Bind and listen on the socket.
+ serverSocket, err := unet.BindAndListen(os.Args[1], false)
+ if err != nil {
+ log.Warningf("err binding: %v", err)
+ os.Exit(1)
+ }
+
+ // Run the server.
+ s := p9.NewServer(&local{})
+ s.Serve(serverSocket)
+}
+
+var (
+ _ p9.File = &local{}
+)