Added 9p server as well as minikube mount command

pull/1066/head
Aaron Prindle 2017-02-03 17:36:06 -08:00
parent 45262e853e
commit 60a5910f88
53 changed files with 8315 additions and 1 deletions

76
cmd/minikube/cmd/mount.go Normal file
View File

@ -0,0 +1,76 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 cmd
import (
"fmt"
"os"
"sync"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/third_party/go9p/p/srv/examples/ufs"
)
// mountCmd represents the mount command
var mountCmd = &cobra.Command{
Use: "mount [flags] MOUNT_DIRECTORY(ex:\"/home\")",
Short: "Mounts the specified directory into minikube.",
Long: `Mounts the specified directory into minikube.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
errText := `Please specify a driver name and a directory to be mounted:
\tminikube mount MOUNT_DIRECTORY(ex:"/home")
`
fmt.Fprintln(os.Stderr, errText)
os.Exit(1)
}
var debugVal int
if glog.V(1) {
debugVal = 1 // ufs.StartServer takes int debug param
}
mountDir := args[0]
api, err := machine.NewAPIClient(clientType)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %s\n", err)
os.Exit(1)
}
defer api.Close()
fmt.Printf("Mounting %s into /mount-9p on the minikubeVM\n", mountDir)
fmt.Println("This daemon process needs to stay alive for the mount to still be accessible...")
var wg sync.WaitGroup
wg.Add(1)
go func() {
ufs.StartServer(constants.DefaultUfsAddress, debugVal, mountDir)
wg.Done()
}()
err = cluster.Mount9pHost(api)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
wg.Wait()
},
}
func init() {
RootCmd.AddCommand(mountCmd)
}

View File

@ -722,6 +722,33 @@ _minikube_logs()
noun_aliases=()
}
_minikube_mount()
{
last_command="minikube_mount"
commands=()
flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
flags_completion=()
flags+=("--alsologtostderr")
flags+=("--log_backtrace_at=")
flags+=("--log_dir=")
flags+=("--logtostderr")
flags+=("--show-libmachine-logs")
flags+=("--stderrthreshold=")
flags+=("--use-vendored-driver")
flags+=("--v=")
two_word_flags+=("-v")
flags+=("--vmodule=")
must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}
_minikube_service_list()
{
last_command="minikube_service_list"
@ -973,6 +1000,7 @@ _minikube()
commands+=("get-k8s-versions")
commands+=("ip")
commands+=("logs")
commands+=("mount")
commands+=("service")
commands+=("ssh")
commands+=("start")

View File

@ -31,6 +31,7 @@ Minikube is a CLI tool that provisions and manages single-node Kubernetes cluste
* [minikube get-k8s-versions](minikube_get-k8s-versions.md) - Gets the list of available kubernetes versions available for minikube.
* [minikube ip](minikube_ip.md) - Retrieve the IP address of the running cluster.
* [minikube logs](minikube_logs.md) - Gets the logs of the running localkube instance, used for debugging minikube, not user code.
* [minikube mount](minikube_mount.md) - Mounts the specified directory into minikube.
* [minikube service](minikube_service.md) - Gets the kubernetes URL(s) for the specified service in your local cluster
* [minikube ssh](minikube_ssh.md) - Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'
* [minikube start](minikube_start.md) - Starts a local kubernetes cluster.

30
docs/minikube_mount.md Normal file
View File

@ -0,0 +1,30 @@
## minikube mount
Mounts the specified directory into minikube.
### Synopsis
Mounts the specified directory into minikube.
```
minikube mount [flags] MOUNT_DIRECTORY(ex:"/home")
```
### Options inherited from parent commands
```
--alsologtostderr log to standard error as well as files
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory (default "")
--logtostderr log to standard error instead of files
--show-libmachine-logs Deprecated: To enable libmachine logs, set --v=3 or higher
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
--use-vendored-driver Use the vendored in drivers instead of RPC
-v, --v Level log level for V logs
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
### SEE ALSO
* [minikube](minikube.md) - Minikube is a tool for managing local Kubernetes clusters.

View File

@ -381,6 +381,23 @@ func GetHostLogs(api libmachine.API) (string, error) {
return s, nil
}
// Mount9pHost runs the mount command from the 9p client on the VM to the 9p server on the host
func Mount9pHost(api libmachine.API) error {
host, err := CheckIfApiExistsAndLoad(api)
if err != nil {
return errors.Wrap(err, "Error checking that api exists and loading it")
}
ip, err := getVMHostIP(host)
if err != nil {
return errors.Wrap(err, "Error getting the host IP address to use from within the VM")
}
_, err = host.RunSSHCommand(GetMount9pCommand(ip))
if err != nil {
return err
}
return nil
}
func CheckIfApiExistsAndLoad(api libmachine.API) (*host.Host, error) {
exists, err := api.Exists(constants.MachineName)
if err != nil {

View File

@ -17,8 +17,12 @@ limitations under the License.
package cluster
import (
"errors"
"net"
"github.com/docker/machine/drivers/vmwarefusion"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/host"
"k8s.io/minikube/pkg/minikube/constants"
)
@ -67,3 +71,14 @@ func createXhyveHost(config MachineConfig) *xhyveDriver {
Virtio9pFolder: "/Users",
}
}
func getVMHostIP(host *host.Host) (net.IP, error) {
switch host.DriverName {
case "virtualbox":
return net.ParseIP("10.0.2.2"), nil
case "xhyve":
return net.ParseIP("10.0.2.2"), nil
default:
return []byte{}, errors.New("Error, attempted to get host ip address for unsupported driver")
}
}

View File

@ -17,11 +17,14 @@ limitations under the License.
package cluster
import (
"errors"
"fmt"
"net"
"path/filepath"
kvm "github.com/dhiltgen/docker-machine-kvm"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/host"
"k8s.io/minikube/pkg/minikube/constants"
)
@ -43,3 +46,14 @@ func createKVMHost(config MachineConfig) *kvm.Driver {
IOMode: "threads",
}
}
func getVMHostIP(host *host.Host) (net.IP, error) {
switch host.DriverName {
case "virtualbox":
return net.ParseIP("10.0.2.2"), nil
case "kvm":
return net.ParseIP("10.0.2.2"), nil
default:
return []byte{}, errors.New("Error, attempted to get host ip address for unsupported driver")
}
}

View File

@ -17,8 +17,15 @@ limitations under the License.
package cluster
import (
"fmt"
"net"
"regexp"
"strings"
"github.com/docker/machine/drivers/hyperv"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/host"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/constants"
)
@ -32,3 +39,41 @@ func createHypervHost(config MachineConfig) drivers.Driver {
d.SSHUser = "docker"
return d
}
func getVMHostIP(host *host.Host) (net.IP, error) {
switch host.DriverName {
case "virtualbox":
return net.ParseIP("10.0.2.2"), nil
case "hyperv":
re := regexp.MustCompile("\"VSwitch\": \"(.*?)\",")
hypervVirtualSwitch := re.FindStringSubmatch(string(host.RawDriver))[1]
ip, err := getWindowsHostIpFromHyperV(hypervVirtualSwitch)
if err != nil {
return []byte{}, errors.Wrap(err, "Error getting 9p mount command")
}
return ip, nil
default:
return []byte{}, errors.New("Error, attempted to get host ip address for unsupported driver")
}
}
func getWindowsHostIpFromHyperV(hypervVirtualSwitch string) (net.IP, error) {
virtualSwitchTemplate := "vEthernet (%s)"
i, _ := net.InterfaceByName(fmt.Sprintf(virtualSwitchTemplate, hypervVirtualSwitch))
addrs, _ := i.Addrs()
for _, a := range addrs {
switch a.(type) {
case *net.IPNet:
ip := a.String()
if strings.Contains(ip, ".") {
vmIP := net.ParseIP(strings.Split(ip, "/")[0])
if vmIP.String() == "" {
return nil, errors.Errorf("Error finding IPV4 address for virtual switch %s", hypervVirtualSwitch)
}
return vmIP, nil
}
}
}
return nil, errors.Errorf("Error finding IPV4 address for virtual switch %s", hypervVirtualSwitch)
}

View File

@ -20,8 +20,8 @@ import (
"bytes"
gflag "flag"
"fmt"
"net"
"strings"
"text/template"
"k8s.io/minikube/pkg/minikube/constants"
@ -193,3 +193,10 @@ else
fi
fi
`, constants.LocalkubePIDPath)
func GetMount9pCommand(ip net.IP) string {
return fmt.Sprintf(`
sudo mkdir /mount-9p;
sudo mount -t 9p -o trans=tcp -o port=5640 %s /mount-9p;
sudo chmod 775 /mount-9p;`, ip)
}

View File

@ -118,3 +118,8 @@ const (
LocalkubeRunning = "active"
LocalkubeStopped = "inactive"
)
const (
DefaultUfsAddress = ":5640"
DefaultUfsDebugLvl = 0
)

13
third_party/go9p/AUTHORS vendored Normal file
View File

@ -0,0 +1,13 @@
# This is the official list of Go9p authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# Please keep the list sorted.
Andrey Mirtchovski <mirtchovski@gmail.com>
Latchesar Ionkov <lionkov@gmail.com>
Roger Peppe <rogpeppe@gmail.com>

18
third_party/go9p/CONTRIBUTORS vendored Normal file
View File

@ -0,0 +1,18 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the Go9p repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# The submission process automatically checks to make sure
# that people submitting code are listed in this file (by email address).
# XXX more bumph here?
# Names should be added to this file like so:
# Name <email address>
# Please keep the list sorted.
Andrey Mirtchovski <mirtchovski@gmail.com>
Latchesar Ionkov <lionkov@gmail.com>
Akshat Kumar <seed@mail.nanosouffle.net>
Roger Peppe <rogpeppe@gmail.com>

27
third_party/go9p/LICENSE vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2012 The Go9p Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* The names of Go9p's contributors may not be used to endorse
or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

481
third_party/go9p/p/clnt/clnt.go vendored Executable file
View File

@ -0,0 +1,481 @@
// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The clnt package provides definitions and functions used to implement
// a 9P2000 file client.
package clnt
import (
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"log"
"net"
"sync"
)
// Debug flags
const (
DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr
DbgPrintPackets // print the raw packets on stderr
DbgLogFcalls // keep the last N 9P messages (can be accessed over http)
DbgLogPackets // keep the last N 9P messages (can be accessed over http)
)
type StatsOps interface {
statsRegister()
statsUnregister()
}
// The Clnt type represents a 9P2000 client. The client is connected to
// a 9P2000 file server and its methods can be used to access and manipulate
// the files exported by the server.
type Clnt struct {
sync.Mutex
Debuglevel int // =0 don't print anything, >0 print Fcalls, >1 print raw packets
Msize uint32 // Maximum size of the 9P messages
Dotu bool // If true, 9P2000.u protocol is spoken
Root *Fid // Fid that points to the rood directory
Id string // Used when printing debug messages
Log *p.Logger
conn net.Conn
tagpool *pool
fidpool *pool
reqout chan *Req
done chan bool
reqfirst *Req
reqlast *Req
err error
reqchan chan *Req
tchan chan *p.Fcall
next, prev *Clnt
}
// A Fid type represents a file on the server. Fids are used for the
// low level methods that correspond directly to the 9P2000 message requests
type Fid struct {
sync.Mutex
Clnt *Clnt // Client the fid belongs to
Iounit uint32
p.Qid // The Qid description for the file
Mode uint8 // Open mode (one of p.O* values) (if file is open)
Fid uint32 // Fid number
p.User // The user the fid belongs to
walked bool // true if the fid points to a walked file on the server
}
// The file is similar to the Fid, but is used in the high-level client
// interface.
type File struct {
fid *Fid
offset uint64
}
type pool struct {
sync.Mutex
need int
nchan chan uint32
maxid uint32
imap []byte
}
type Req struct {
sync.Mutex
Clnt *Clnt
Tc *p.Fcall
Rc *p.Fcall
Err error
Done chan *Req
tag uint16
prev, next *Req
fid *Fid
}
type ClntList struct {
sync.Mutex
clntList, clntLast *Clnt
}
var clnts *ClntList
var DefaultDebuglevel int
var DefaultLogger *p.Logger
func (clnt *Clnt) Rpcnb(r *Req) error {
var tag uint16
if r.Tc.Type == p.Tversion {
tag = p.NOTAG
} else {
tag = r.tag
}
p.SetTag(r.Tc, tag)
clnt.Lock()
if clnt.err != nil {
clnt.Unlock()
return clnt.err
}
if clnt.reqlast != nil {
clnt.reqlast.next = r
} else {
clnt.reqfirst = r
}
r.prev = clnt.reqlast
clnt.reqlast = r
clnt.Unlock()
clnt.reqout <- r
return nil
}
func (clnt *Clnt) Rpc(tc *p.Fcall) (rc *p.Fcall, err error) {
r := clnt.ReqAlloc()
r.Tc = tc
r.Done = make(chan *Req)
err = clnt.Rpcnb(r)
if err != nil {
return
}
<-r.Done
rc = r.Rc
err = r.Err
clnt.ReqFree(r)
return
}
func (clnt *Clnt) recv() {
var err error
err = nil
buf := make([]byte, clnt.Msize*8)
pos := 0
for {
if len(buf) < int(clnt.Msize) {
b := make([]byte, clnt.Msize*8)
copy(b, buf[0:pos])
buf = b
b = nil
}
n, oerr := clnt.conn.Read(buf[pos:])
if oerr != nil || n == 0 {
err = &p.Error{oerr.Error(), p.EIO}
clnt.Lock()
clnt.err = err
clnt.Unlock()
goto closed
}
pos += n
for pos > 4 {
sz, _ := p.Gint32(buf)
if pos < int(sz) {
if len(buf) < int(sz) {
b := make([]byte, clnt.Msize*8)
copy(b, buf[0:pos])
buf = b
b = nil
}
break
}
fc, err, fcsize := p.Unpack(buf, clnt.Dotu)
clnt.Lock()
if err != nil {
clnt.err = err
clnt.conn.Close()
clnt.Unlock()
goto closed
}
if clnt.Debuglevel > 0 {
clnt.logFcall(fc)
if clnt.Debuglevel&DbgPrintPackets != 0 {
log.Println("}-}", clnt.Id, fmt.Sprint(fc.Pkt))
}
if clnt.Debuglevel&DbgPrintFcalls != 0 {
log.Println("}}}", clnt.Id, fc.String())
}
}
var r *Req = nil
for r = clnt.reqfirst; r != nil; r = r.next {
if r.Tc.Tag == fc.Tag {
break
}
}
if r == nil {
clnt.err = &p.Error{"unexpected response", p.EINVAL}
clnt.conn.Close()
clnt.Unlock()
goto closed
}
r.Rc = fc
if r.prev != nil {
r.prev.next = r.next
} else {
clnt.reqfirst = r.next
}
if r.next != nil {
r.next.prev = r.prev
} else {
clnt.reqlast = r.prev
}
clnt.Unlock()
if r.Tc.Type != r.Rc.Type-1 {
if r.Rc.Type != p.Rerror {
r.Err = &p.Error{"invalid response", p.EINVAL}
log.Println(fmt.Sprintf("TTT %v", r.Tc))
log.Println(fmt.Sprintf("RRR %v", r.Rc))
} else {
if r.Err == nil {
r.Err = &p.Error{r.Rc.Error, r.Rc.Errornum}
}
}
}
if r.Done != nil {
r.Done <- r
}
pos -= fcsize
buf = buf[fcsize:]
}
}
closed:
clnt.done <- true
/* send error to all pending requests */
clnt.Lock()
r := clnt.reqfirst
clnt.reqfirst = nil
clnt.reqlast = nil
if err == nil {
err = clnt.err
}
clnt.Unlock()
for ; r != nil; r = r.next {
r.Err = err
if r.Done != nil {
r.Done <- r
}
}
clnts.Lock()
if clnt.prev != nil {
clnt.prev.next = clnt.next
} else {
clnts.clntList = clnt.next
}
if clnt.next != nil {
clnt.next.prev = clnt.prev
} else {
clnts.clntLast = clnt.prev
}
clnts.Unlock()
if sop, ok := (interface{}(clnt)).(StatsOps); ok {
sop.statsUnregister()
}
}
func (clnt *Clnt) send() {
for {
select {
case <-clnt.done:
return
case req := <-clnt.reqout:
if clnt.Debuglevel > 0 {
clnt.logFcall(req.Tc)
if clnt.Debuglevel&DbgPrintPackets != 0 {
log.Println("{-{", clnt.Id, fmt.Sprint(req.Tc.Pkt))
}
if clnt.Debuglevel&DbgPrintFcalls != 0 {
log.Println("{{{", clnt.Id, req.Tc.String())
}
}
for buf := req.Tc.Pkt; len(buf) > 0; {
n, err := clnt.conn.Write(buf)
if err != nil {
/* just close the socket, will get signal on clnt.done */
clnt.conn.Close()
break
}
buf = buf[n:]
}
}
}
}
// Creates and initializes a new Clnt object. Doesn't send any data
// on the wire.
func NewClnt(c net.Conn, msize uint32, dotu bool) *Clnt {
clnt := new(Clnt)
clnt.conn = c
clnt.Msize = msize
clnt.Dotu = dotu
clnt.Debuglevel = DefaultDebuglevel
clnt.Log = DefaultLogger
clnt.Id = c.RemoteAddr().String() + ":"
clnt.tagpool = newPool(uint32(p.NOTAG))
clnt.fidpool = newPool(p.NOFID)
clnt.reqout = make(chan *Req)
clnt.done = make(chan bool)
clnt.reqchan = make(chan *Req, 16)
clnt.tchan = make(chan *p.Fcall, 16)
go clnt.recv()
go clnt.send()
clnts.Lock()
if clnts.clntLast != nil {
clnts.clntLast.next = clnt
} else {
clnts.clntList = clnt
}
clnt.prev = clnts.clntLast
clnts.clntLast = clnt
clnts.Unlock()
if sop, ok := (interface{}(clnt)).(StatsOps); ok {
sop.statsRegister()
}
return clnt
}
// Establishes a new socket connection to the 9P server and creates
// a client object for it. Negotiates the dialect and msize for the
// connection. Returns a Clnt object, or Error.
func Connect(c net.Conn, msize uint32, dotu bool) (*Clnt, error) {
clnt := NewClnt(c, msize, dotu)
ver := "9P2000"
if clnt.Dotu {
ver = "9P2000.u"
}
tc := p.NewFcall(clnt.Msize)
err := p.PackTversion(tc, clnt.Msize, ver)
if err != nil {
return nil, err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return nil, err
}
if rc.Msize < clnt.Msize {
clnt.Msize = rc.Msize
}
clnt.Dotu = rc.Version == "9P2000.u" && clnt.Dotu
return clnt, nil
}
// Creates a new Fid object for the client
func (clnt *Clnt) FidAlloc() *Fid {
fid := new(Fid)
fid.Fid = clnt.fidpool.getId()
fid.Clnt = clnt
return fid
}
func (clnt *Clnt) NewFcall() *p.Fcall {
select {
case tc := <-clnt.tchan:
return tc
default:
}
return p.NewFcall(clnt.Msize)
}
func (clnt *Clnt) FreeFcall(fc *p.Fcall) {
if fc != nil && len(fc.Buf) >= int(clnt.Msize) {
select {
case clnt.tchan <- fc:
break
default:
}
}
}
func (clnt *Clnt) ReqAlloc() *Req {
var req *Req
select {
case req = <-clnt.reqchan:
break
default:
req = new(Req)
req.Clnt = clnt
req.tag = uint16(clnt.tagpool.getId())
}
return req
}
func (clnt *Clnt) ReqFree(req *Req) {
clnt.FreeFcall(req.Tc)
req.Tc = nil
req.Rc = nil
req.Err = nil
req.Done = nil
req.next = nil
req.prev = nil
select {
case clnt.reqchan <- req:
break
default:
clnt.tagpool.putId(uint32(req.tag))
}
}
func NewFile(f *Fid, offset uint64) *File {
return &File{f, offset}
}
func (f *File) Fid() *Fid {
return f.fid
}
func (clnt *Clnt) logFcall(fc *p.Fcall) {
if clnt.Debuglevel&DbgLogPackets != 0 {
pkt := make([]byte, len(fc.Pkt))
copy(pkt, fc.Pkt)
clnt.Log.Log(pkt, clnt, DbgLogPackets)
}
if clnt.Debuglevel&DbgLogFcalls != 0 {
f := new(p.Fcall)
*f = *fc
f.Pkt = nil
clnt.Log.Log(f, clnt, DbgLogFcalls)
}
}
func init() {
clnts = new(ClntList)
if sop, ok := (interface{}(clnts)).(StatsOps); ok {
sop.statsRegister()
}
}

32
third_party/go9p/p/clnt/close.go vendored Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2009 The Go9p 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 clnt
import "k8s.io/minikube/third_party/go9p/p"
// Clunks a fid. Returns nil if successful.
func (clnt *Clnt) Clunk(fid *Fid) (err error) {
err = nil
if fid.walked {
tc := clnt.NewFcall()
err := p.PackTclunk(tc, fid.Fid)
if err != nil {
return err
}
_, err = clnt.Rpc(tc)
}
clnt.fidpool.putId(fid.Fid)
fid.walked = false
fid.Fid = p.NOFID
return
}
// Closes a file. Returns nil if successful.
func (file *File) Close() error {
// Should we cancel all pending requests for the File
return file.fid.Clnt.Clunk(file.fid)
}

View File

@ -0,0 +1,489 @@
package main
// An interactive client for 9P servers.
import (
"bufio"
"flag"
"fmt"
"io"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/clnt"
"os"
"path"
"strings"
)
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
var ouser = flag.String("user", "", "user to connect as")
var cmdfile = flag.String("file", "", "read commands from file")
var prompt = flag.String("prompt", "9p> ", "prompt for interactive client")
var debug = flag.Bool("d", false, "enable debugging (fcalls)")
var debugall = flag.Bool("D", false, "enable debugging (raw packets)")
var cwd = "/"
var cfid *clnt.Fid
type Cmd struct {
fun func(c *clnt.Clnt, s []string)
help string
}
var cmds map[string]*Cmd
func init() {
cmds = make(map[string]*Cmd)
cmds["write"] = &Cmd{cmdwrite, "write file string [...]\t«write the unmodified string to file, create file if necessary»"}
cmds["echo"] = &Cmd{cmdecho, "echo file string [...]\t«echo string to file (newline appended)»"}
cmds["stat"] = &Cmd{cmdstat, "stat file [...]\t«stat file»"}
cmds["ls"] = &Cmd{cmdls, "ls [-l] file [...]\t«list contents of directory or file»"}
cmds["cd"] = &Cmd{cmdcd, "cd dir\t«change working directory»"}
cmds["cat"] = &Cmd{cmdcat, "cat file [...]\t«print the contents of file»"}
cmds["mkdir"] = &Cmd{cmdmkdir, "mkdir dir [...]\t«create dir on remote server»"}
cmds["get"] = &Cmd{cmdget, "get file [local]\t«get file from remote server»"}
cmds["put"] = &Cmd{cmdput, "put file [remote]\t«put file on the remote server as 'file'»"}
cmds["pwd"] = &Cmd{cmdpwd, "pwd\t«print working directory»"}
cmds["rm"] = &Cmd{cmdrm, "rm file [...]\t«remove file from remote server»"}
cmds["help"] = &Cmd{cmdhelp, "help [cmd]\t«print available commands or help on cmd»"}
cmds["quit"] = &Cmd{cmdquit, "quit\t«exit»"}
cmds["exit"] = &Cmd{cmdquit, "exit\t«quit»"}
}
// normalize user-supplied path. path starting with '/' is left untouched, otherwise is considered
// local from cwd
func normpath(s string) string {
if len(s) > 0 {
if s[0] == '/' {
return path.Clean(s)
}
return path.Clean(cwd + "/" + s)
}
return "/"
}
func b(mode uint32, s uint8) string {
var bits = []string{"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"}
return bits[(mode>>s)&7]
}
// Convert file mode bits to string representation
func modetostr(mode uint32) string {
d := "-"
if mode&p.DMDIR != 0 {
d = "d"
} else if mode&p.DMAPPEND != 0 {
d = "a"
}
return fmt.Sprintf("%s%s%s%s", d, b(mode, 6), b(mode, 3), b(mode, 0))
}
// Write the string s to remote file f. Create f if it doesn't exist
func writeone(c *clnt.Clnt, f, s string) {
fname := normpath(f)
file, oserr := c.FCreate(fname, 0666, p.OWRITE)
if oserr != nil {
file, oserr = c.FOpen(fname, p.OWRITE|p.OTRUNC)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", fname, oserr)
return
}
}
defer file.Close()
m, oserr := file.Write([]byte(s))
if oserr != nil {
fmt.Fprintf(os.Stderr, "error writing to %s: %v\n", fname, oserr)
return
}
if m != len(s) {
fmt.Fprintf(os.Stderr, "short write %s\n", fname)
return
}
}
// Write s[1:] (with appended spaces) to the file s[0]
func cmdwrite(c *clnt.Clnt, s []string) {
fname := normpath(s[0])
str := strings.Join(s[1:], " ")
writeone(c, fname, str)
}
// Echo (append newline) s[1:] to s[0]
func cmdecho(c *clnt.Clnt, s []string) {
fname := normpath(s[0])
str := strings.Join(s[1:], " ") + "\n"
writeone(c, fname, str)
}
// Stat the remote file f
func statone(c *clnt.Clnt, f string) {
fname := normpath(f)
stat, oserr := c.FStat(fname)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error in stat %s: %v\n", fname, oserr)
return
}
fmt.Fprintf(os.Stdout, "%s\n", stat)
}
func cmdstat(c *clnt.Clnt, s []string) {
for _, f := range s {
statone(c, normpath(f))
}
}
func dirtostr(d *p.Dir) string {
return fmt.Sprintf("%s %s %s %-8d\t\t%s", modetostr(d.Mode), d.Uid, d.Gid, d.Length, d.Name)
}
func lsone(c *clnt.Clnt, s string, long bool) {
st, oserr := c.FStat(normpath(s))
if oserr != nil {
fmt.Fprintf(os.Stderr, "error stat: %v\n", oserr)
return
}
if st.Mode&p.DMDIR != 0 {
file, oserr := c.FOpen(s, p.OREAD)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error opening dir: %s\n", oserr)
return
}
defer file.Close()
for {
d, oserr := file.Readdir(0)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error reading dir: %v\n", oserr)
}
if d == nil || len(d) == 0 {
break
}
for _, dir := range d {
if long {
fmt.Fprintf(os.Stdout, "%s\n", dirtostr(dir))
} else {
os.Stdout.WriteString(dir.Name + "\n")
}
}
}
} else {
fmt.Fprintf(os.Stdout, "%s\n", dirtostr(st))
}
}
func cmdls(c *clnt.Clnt, s []string) {
long := false
if len(s) > 0 && s[0] == "-l" {
long = true
s = s[1:]
}
if len(s) == 0 {
lsone(c, cwd, long)
} else {
for _, d := range s {
lsone(c, cwd+d, long)
}
}
}
func walkone(c *clnt.Clnt, s string, fileok bool) {
ncwd := normpath(s)
fid, err := c.FWalk(ncwd)
defer c.Clunk(fid)
if err != nil {
fmt.Fprintf(os.Stderr, "walk error: %s\n", err)
return
}
if fileok != true && (fid.Type&p.QTDIR == 0) {
fmt.Fprintf(os.Stderr, "can't cd to file [%s]\n", ncwd)
return
}
cwd = ncwd
}
func cmdcd(c *clnt.Clnt, s []string) {
if s != nil {
walkone(c, strings.Join(s, "/"), false)
}
}
// Print the contents of f
func cmdcat(c *clnt.Clnt, s []string) {
buf := make([]byte, 8192)
Outer:
for _, f := range s {
fname := normpath(f)
file, oserr := c.FOpen(fname, p.OREAD)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", f, oserr)
continue Outer
}
defer file.Close()
for {
n, oserr := file.Read(buf)
if oserr != nil && oserr != io.EOF {
fmt.Fprintf(os.Stderr, "error reading %s: %v\n", f, oserr)
}
if n == 0 {
break
}
os.Stdout.Write(buf[0:n])
}
}
}
// Create a single directory on remote server
func mkone(c *clnt.Clnt, s string) {
fname := normpath(s)
file, oserr := c.FCreate(fname, 0777|p.DMDIR, p.OWRITE)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error creating directory %s: %v\n", fname, oserr)
return
}
file.Close()
}
// Create directories on remote server
func cmdmkdir(c *clnt.Clnt, s []string) {
for _, f := range s {
mkone(c, f)
}
}
// Copy a remote file to local filesystem
func cmdget(c *clnt.Clnt, s []string) {
var from, to string
switch len(s) {
case 1:
from = normpath(s[0])
_, to = path.Split(s[0])
case 2:
from, to = normpath(s[0]), s[1]
default:
fmt.Fprintf(os.Stderr, "from arguments; usage: get from to\n")
}
tofile, err := os.Create(to)
if err != nil {
fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err)
return
}
defer tofile.Close()
file, ferr := c.FOpen(from, p.OREAD)
if ferr != nil {
fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err)
return
}
defer file.Close()
buf := make([]byte, 8192)
for {
n, oserr := file.Read(buf)
if oserr != nil {
fmt.Fprintf(os.Stderr, "error reading %s: %s\n", from, oserr)
return
}
if n == 0 {
break
}
m, err := tofile.Write(buf[0:n])
if err != nil {
fmt.Fprintf(os.Stderr, "error writing %s: %s\n", to, err)
return
}
if m != n {
fmt.Fprintf(os.Stderr, "short write %s\n", to)
return
}
}
}
// Copy a local file to remote server
func cmdput(c *clnt.Clnt, s []string) {
var from, to string
switch len(s) {
case 1:
_, to = path.Split(s[0])
to = normpath(to)
from = s[0]
case 2:
from, to = s[0], normpath(s[1])
default:
fmt.Fprintf(os.Stderr, "incorrect arguments; usage: put local [remote]\n")
}
fromfile, err := os.Open(from)
if err != nil {
fmt.Fprintf(os.Stderr, "error opening %s for reading: %s\n", from, err)
return
}
defer fromfile.Close()
file, ferr := c.FOpen(to, p.OWRITE|p.OTRUNC)
if ferr != nil {
file, ferr = c.FCreate(to, 0666, p.OWRITE)
if ferr != nil {
fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err)
return
}
}
defer file.Close()
buf := make([]byte, 8192)
for {
n, oserr := fromfile.Read(buf)
if oserr != nil && oserr != io.EOF {
fmt.Fprintf(os.Stderr, "error reading %s: %s\n", from, oserr)
return
}
if n == 0 {
break
}
m, oserr := file.Write(buf[0:n])
if oserr != nil {
fmt.Fprintf(os.Stderr, "error writing %s: %v\n", to, oserr)
return
}
if m != n {
fmt.Fprintf(os.Stderr, "short write %s\n", to)
return
}
}
}
func cmdpwd(c *clnt.Clnt, s []string) { fmt.Fprintf(os.Stdout, cwd+"\n") }
// Remove f from remote server
func rmone(c *clnt.Clnt, f string) {
fname := normpath(f)
err := c.FRemove(fname)
if err != nil {
fmt.Fprintf(os.Stderr, "error in stat %s", err)
return
}
}
// Remove one or more files from the server
func cmdrm(c *clnt.Clnt, s []string) {
for _, f := range s {
rmone(c, normpath(f))
}
}
// Print available commands
func cmdhelp(c *clnt.Clnt, s []string) {
cmdstr := ""
if len(s) > 0 {
for _, h := range s {
v, ok := cmds[h]
if ok {
cmdstr = cmdstr + v.help + "\n"
} else {
cmdstr = cmdstr + "unknown command: " + h + "\n"
}
}
} else {
cmdstr = "available commands: "
for k := range cmds {
cmdstr = cmdstr + " " + k
}
cmdstr = cmdstr + "\n"
}
fmt.Fprintf(os.Stdout, "%s", cmdstr)
}
func cmdquit(c *clnt.Clnt, s []string) { os.Exit(0) }
func cmd(c *clnt.Clnt, cmd string) {
ncmd := strings.Fields(cmd)
if len(ncmd) <= 0 {
return
}
v, ok := cmds[ncmd[0]]
if ok == false {
fmt.Fprintf(os.Stderr, "unknown command: %s\n", ncmd[0])
return
}
v.fun(c, ncmd[1:])
return
}
func interactive(c *clnt.Clnt) {
reader := bufio.NewReaderSize(os.Stdin, 8192)
for {
fmt.Print(*prompt)
line, err := reader.ReadSlice('\n')
if err != nil {
fmt.Fprintf(os.Stderr, "exiting...\n")
break
}
str := strings.TrimSpace(string(line))
// TODO: handle larger input lines by doubling buffer
in := strings.Split(str, "\n")
for i := range in {
if len(in[i]) > 0 {
cmd(c, in[i])
}
}
}
}
func main() {
var user p.User
var err error
var c *clnt.Clnt
var file *clnt.File
flag.Parse()
if *ouser == "" {
user = p.OsUsers.Uid2User(os.Geteuid())
} else {
user = p.OsUsers.Uname2User(*ouser)
}
naddr := *addr
if strings.LastIndex(naddr, ":") == -1 {
naddr = naddr + ":5640"
}
c, err = clnt.Mount("tcp", naddr, "", user)
if err != nil {
fmt.Fprintf(os.Stderr, "error mounting %s: %s\n", naddr, err)
os.Exit(1)
}
if *debug {
c.Debuglevel = 1
}
if *debugall {
c.Debuglevel = 2
}
walkone(c, "/", false)
if file != nil {
//process(c)
fmt.Sprint(os.Stderr, "file reading unimplemented\n")
} else if flag.NArg() > 0 {
flags := flag.Args()
for _, uc := range flags {
cmd(c, uc)
}
} else {
interactive(c)
}
return
}

View File

@ -0,0 +1,59 @@
package main
import (
"flag"
"io"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/clnt"
"log"
"os"
)
var debuglevel = flag.Int("d", 0, "debuglevel")
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
func main() {
var user p.User
var err error
var c *clnt.Clnt
var file *clnt.File
var d []*p.Dir
flag.Parse()
user = p.OsUsers.Uid2User(os.Geteuid())
clnt.DefaultDebuglevel = *debuglevel
c, err = clnt.Mount("tcp", *addr, "", user)
if err != nil {
log.Fatal(err)
}
lsarg := "/"
if flag.NArg() == 1 {
lsarg = flag.Arg(0)
} else if flag.NArg() > 1 {
log.Fatal("error: only one argument expected")
}
file, err = c.FOpen(lsarg, p.OREAD)
if err != nil {
log.Fatal(err)
}
for {
d, err = file.Readdir(0)
if d == nil || len(d) == 0 || err != nil {
break
}
for i := 0; i < len(d); i++ {
os.Stdout.WriteString(d[i].Name + "\n")
}
}
file.Close()
if err != nil && err != io.EOF {
log.Fatal(err)
}
return
}

View File

@ -0,0 +1,61 @@
package main
import (
"flag"
"io"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/clnt"
"log"
"os"
)
var debuglevel = flag.Int("d", 0, "debuglevel")
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
func main() {
var n int
var user p.User
var err error
var c *clnt.Clnt
var file *clnt.File
var buf []byte
flag.Parse()
user = p.OsUsers.Uid2User(os.Geteuid())
clnt.DefaultDebuglevel = *debuglevel
c, err = clnt.Mount("tcp", *addr, "", user)
if err != nil {
goto error
}
if flag.NArg() != 1 {
log.Println("invalid arguments")
return
}
file, err = c.FOpen(flag.Arg(0), p.OREAD)
if err != nil {
goto error
}
buf = make([]byte, 8192)
for {
n, err = file.Read(buf)
if n == 0 {
break
}
os.Stdout.Write(buf[0:n])
}
file.Close()
if err != nil && err != io.EOF {
goto error
}
return
error:
log.Println("Error", err)
}

View File

@ -0,0 +1,101 @@
package main
import (
"flag"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/clnt"
"log"
"os"
"strings"
)
var debuglevel = flag.Int("d", 0, "debuglevel")
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
func main() {
var user p.User
var ba [][]byte
var nreqs int
var rchan chan *clnt.Req
var tag *clnt.Tag
var fid *clnt.Fid
var wnames []string
flag.Parse()
user = p.OsUsers.Uid2User(os.Geteuid())
clnt.DefaultDebuglevel = *debuglevel
c, err := clnt.Mount("tcp", *addr, "", user)
if err != nil {
goto error
}
if flag.NArg() != 1 {
log.Println("invalid arguments")
return
}
ba = make([][]byte, 100)
for i := 0; i < len(ba); i++ {
ba[i] = make([]byte, 8192)
}
nreqs = 0
rchan = make(chan *clnt.Req)
tag = c.TagAlloc(rchan)
// walk the file
wnames = strings.Split(flag.Arg(0), "/")
for wnames[0] == "" {
wnames = wnames[1:]
}
fid = c.FidAlloc()
for root := c.Root; len(wnames) > 0; root = fid {
n := len(wnames)
if n > 8 {
n = 8
}
err = tag.Walk(root, fid, wnames[0:n])
if err != nil {
goto error
}
nreqs++
wnames = wnames[n:]
}
err = tag.Open(fid, p.OREAD)
if err != nil {
goto error
}
for i := 0; i < len(ba); i++ {
err = tag.Read(fid, uint64(i*8192), 8192)
if err != nil {
goto error
}
nreqs++
}
err = tag.Clunk(fid)
// now start reading...
for nreqs > 0 {
r := <-rchan
if r.Tc.Type == p.Tread {
i := r.Tc.Offset / 8192
copy(ba[i], r.Rc.Data)
ba[i] = ba[i][0:r.Rc.Count]
}
nreqs--
}
for i := 0; i < len(ba); i++ {
os.Stdout.Write(ba[i])
}
return
error:
log.Println("error: ", err)
}

View File

@ -0,0 +1,74 @@
// Connects to a server over TLS and lists the specified directory
package main
import (
"crypto/rand"
"crypto/tls"
"flag"
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/clnt"
"log"
"os"
)
var debuglevel = flag.Int("d", 0, "debuglevel")
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
func main() {
var user p.User
var file *clnt.File
flag.Parse()
user = p.OsUsers.Uid2User(os.Geteuid())
clnt.DefaultDebuglevel = *debuglevel
c, oerr := tls.Dial("tcp", *addr, &tls.Config{
Rand: rand.Reader,
InsecureSkipVerify: true,
})
if oerr != nil {
log.Println("can't dial", oerr)
return
}
clnt, err := clnt.MountConn(c, "", user)
if err != nil {
goto error
}
if flag.NArg() != 1 {
log.Println("invalid arguments")
return
}
file, oerr = clnt.FOpen(flag.Arg(0), p.OREAD)
if oerr != nil {
goto oerror
}
for {
d, oerr := file.Readdir(0)
if oerr != nil {
goto oerror
}
if d == nil || len(d) == 0 {
break
}
for i := 0; i < len(d); i++ {
os.Stdout.WriteString(d[i].Name + "\n")
}
}
file.Close()
return
error:
log.Println(fmt.Sprintf("Error: %s", err))
return
oerror:
log.Println("Error", oerr)
}

View File

@ -0,0 +1,71 @@
package main
import (
"flag"
"io"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/clnt"
"log"
"os"
)
var debuglevel = flag.Int("d", 0, "debuglevel")
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
func main() {
var n, m int
var user p.User
var err error
var c *clnt.Clnt
var file *clnt.File
var buf []byte
flag.Parse()
user = p.OsUsers.Uid2User(os.Geteuid())
clnt.DefaultDebuglevel = *debuglevel
c, err = clnt.Mount("tcp", *addr, "", user)
if err != nil {
goto error
}
if flag.NArg() != 1 {
log.Println("invalid arguments")
return
}
file, err = c.FOpen(flag.Arg(0), p.OWRITE|p.OTRUNC)
if err != nil {
file, err = c.FCreate(flag.Arg(0), 0666, p.OWRITE)
if err != nil {
goto error
}
}
buf = make([]byte, 8192)
for {
n, err = os.Stdin.Read(buf)
if err != nil && err != io.EOF {
goto error
}
if n == 0 {
break
}
m, err = file.Write(buf[0:n])
if err != nil {
goto error
}
if m != n {
err = &p.Error{"short write", 0}
goto error
}
}
file.Close()
return
error:
log.Println("Error", err)
}

95
third_party/go9p/p/clnt/mount.go vendored Normal file
View File

@ -0,0 +1,95 @@
// Copyright 2009 The Go9p 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 clnt
import (
"k8s.io/minikube/third_party/go9p/p"
"net"
)
// Creates an authentication fid for the specified user. Returns the fid, if
// successful, or an Error.
func (clnt *Clnt) Auth(user p.User, aname string) (*Fid, error) {
fid := clnt.FidAlloc()
tc := clnt.NewFcall()
err := p.PackTauth(tc, fid.Fid, user.Name(), aname, uint32(user.Id()), clnt.Dotu)
if err != nil {
return nil, err
}
_, err = clnt.Rpc(tc)
if err != nil {
return nil, err
}
fid.Iounit = clnt.Msize - p.IOHDRSZ
fid.User = user
fid.walked = true
return fid, nil
}
// Creates a fid for the specified user that points to the root
// of the file server's file tree. Returns a Fid pointing to the root,
// if successful, or an Error.
func (clnt *Clnt) Attach(afid *Fid, user p.User, aname string) (*Fid, error) {
var afno uint32
if afid != nil {
afno = afid.Fid
} else {
afno = p.NOFID
}
fid := clnt.FidAlloc()
tc := clnt.NewFcall()
err := p.PackTattach(tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), clnt.Dotu)
if err != nil {
return nil, err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return nil, err
}
fid.Qid = rc.Qid
fid.User = user
fid.walked = true
return fid, nil
}
// Connects to a file server and attaches to it as the specified user.
func Mount(ntype, addr, aname string, user p.User) (*Clnt, error) {
c, e := net.Dial(ntype, addr)
if e != nil {
return nil, &p.Error{e.Error(), p.EIO}
}
return MountConn(c, aname, user)
}
func MountConn(c net.Conn, aname string, user p.User) (*Clnt, error) {
clnt, err := Connect(c, 8192+p.IOHDRSZ, true)
if err != nil {
return nil, err
}
fid, err := clnt.Attach(nil, user, aname)
if err != nil {
clnt.Unmount()
return nil, err
}
clnt.Root = fid
return clnt, nil
}
// Closes the connection to the file sever.
func (clnt *Clnt) Unmount() {
clnt.Lock()
clnt.err = &p.Error{"connection closed", p.EIO}
clnt.conn.Close()
clnt.Unlock()
}

98
third_party/go9p/p/clnt/open.go vendored Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2009 The Go9p 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 clnt
import (
"k8s.io/minikube/third_party/go9p/p"
"strings"
)
// Opens the file associated with the fid. Returns nil if
// the operation is successful.
func (clnt *Clnt) Open(fid *Fid, mode uint8) error {
tc := clnt.NewFcall()
err := p.PackTopen(tc, fid.Fid, mode)
if err != nil {
return err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return err
}
fid.Qid = rc.Qid
fid.Iounit = rc.Iounit
if fid.Iounit == 0 || fid.Iounit > clnt.Msize-p.IOHDRSZ {
fid.Iounit = clnt.Msize - p.IOHDRSZ
}
fid.Mode = mode
return nil
}
// Creates a file in the directory associated with the fid. Returns nil
// if the operation is successful.
func (clnt *Clnt) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error {
tc := clnt.NewFcall()
err := p.PackTcreate(tc, fid.Fid, name, perm, mode, ext, clnt.Dotu)
if err != nil {
return err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return err
}
fid.Qid = rc.Qid
fid.Iounit = rc.Iounit
if fid.Iounit == 0 || fid.Iounit > clnt.Msize-p.IOHDRSZ {
fid.Iounit = clnt.Msize - p.IOHDRSZ
}
fid.Mode = mode
return nil
}
// Creates and opens a named file.
// Returns the file if the operation is successful, or an Error.
func (clnt *Clnt) FCreate(path string, perm uint32, mode uint8) (*File, error) {
n := strings.LastIndex(path, "/")
if n < 0 {
n = 0
}
fid, err := clnt.FWalk(path[0:n])
if err != nil {
return nil, err
}
if path[n] == '/' {
n++
}
err = clnt.Create(fid, path[n:], perm, mode, "")
if err != nil {
clnt.Clunk(fid)
return nil, err
}
return &File{fid, 0}, nil
}
// Opens a named file. Returns the opened file, or an Error.
func (clnt *Clnt) FOpen(path string, mode uint8) (*File, error) {
fid, err := clnt.FWalk(path)
if err != nil {
return nil, err
}
err = clnt.Open(fid, mode)
if err != nil {
clnt.Clunk(fid)
return nil, err
}
return &File{fid, 0}, nil
}

97
third_party/go9p/p/clnt/pool.go vendored Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2009 The Go9p 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 clnt
var m2id = [...]uint8{
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 6,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 7,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 6,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 0,
}
func newPool(maxid uint32) *pool {
p := new(pool)
p.maxid = maxid
p.nchan = make(chan uint32)
return p
}
func (p *pool) getId() uint32 {
var n uint32 = 0
var ret uint32
p.Lock()
for n = 0; n < uint32(len(p.imap)); n++ {
if p.imap[n] != 0xFF {
break
}
}
if int(n) >= len(p.imap) {
m := uint32(len(p.imap) + 32)
if uint32(m*8) > p.maxid {
m = p.maxid/8 + 1
}
b := make([]byte, m)
copy(b, p.imap)
p.imap = b
}
if n >= uint32(len(p.imap)) {
p.need++
p.Unlock()
ret = <-p.nchan
} else {
ret = uint32(m2id[p.imap[n]])
p.imap[n] |= 1 << ret
ret += n * 8
p.Unlock()
}
return ret
}
func (p *pool) putId(id uint32) {
p.Lock()
if p.need > 0 {
p.nchan <- id
p.need--
p.Unlock()
return
}
p.imap[id/8] &= ^(1 << (id % 8))
p.Unlock()
}

124
third_party/go9p/p/clnt/read.go vendored Normal file
View File

@ -0,0 +1,124 @@
// Copyright 2009 The Go9p 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 clnt
import (
"io"
"k8s.io/minikube/third_party/go9p/p"
)
// Reads count bytes starting from offset from the file associated with the fid.
// Returns a slice with the data read, if the operation was successful, or an
// Error.
func (clnt *Clnt) Read(fid *Fid, offset uint64, count uint32) ([]byte, error) {
if count > fid.Iounit {
count = fid.Iounit
}
tc := clnt.NewFcall()
err := p.PackTread(tc, fid.Fid, offset, count)
if err != nil {
return nil, err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return nil, err
}
return rc.Data, nil
}
// Reads up to len(buf) bytes from the File. Returns the number
// of bytes read, or an Error.
func (file *File) Read(buf []byte) (int, error) {
n, err := file.ReadAt(buf, int64(file.offset))
if err == nil {
file.offset += uint64(n)
}
return n, err
}
// Reads up to len(buf) bytes from the file starting from offset.
// Returns the number of bytes read, or an Error.
func (file *File) ReadAt(buf []byte, offset int64) (int, error) {
b, err := file.fid.Clnt.Read(file.fid, uint64(offset), uint32(len(buf)))
if err != nil {
return 0, err
}
if len(b) == 0 {
return 0, io.EOF
}
copy(buf, b)
return len(b), nil
}
// Reads exactly len(buf) bytes from the File starting from offset.
// Returns the number of bytes read (could be less than len(buf) if
// end-of-file is reached), or an Error.
func (file *File) Readn(buf []byte, offset uint64) (int, error) {
ret := 0
for len(buf) > 0 {
n, err := file.ReadAt(buf, int64(offset))
if err != nil {
return 0, err
}
if n == 0 {
break
}
buf = buf[n:]
offset += uint64(n)
ret += n
}
return ret, nil
}
// Reads the content of the directory associated with the File.
// Returns an array of maximum num entries (if num is 0, returns
// all entries from the directory). If the operation fails, returns
// an Error.
func (file *File) Readdir(num int) ([]*p.Dir, error) {
buf := make([]byte, file.fid.Clnt.Msize-p.IOHDRSZ)
dirs := make([]*p.Dir, 32)
pos := 0
for {
n, err := file.Read(buf)
if err != nil && err != io.EOF {
return nil, err
}
if n == 0 {
break
}
for b := buf[0:n]; len(b) > 0; {
d, perr := p.UnpackDir(b, file.fid.Clnt.Dotu)
if perr != nil {
return nil, perr
}
b = b[d.Size+2:]
if pos >= len(dirs) {
s := make([]*p.Dir, len(dirs)+32)
copy(s, dirs)
dirs = s
}
dirs[pos] = d
pos++
if num != 0 && pos >= num {
break
}
}
}
return dirs[0:pos], nil
}

35
third_party/go9p/p/clnt/remove.go vendored Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2009 The Go9p 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 clnt
import "k8s.io/minikube/third_party/go9p/p"
// Removes the file associated with the Fid. Returns nil if the
// operation is successful.
func (clnt *Clnt) Remove(fid *Fid) error {
tc := clnt.NewFcall()
err := p.PackTremove(tc, fid.Fid)
if err != nil {
return err
}
_, err = clnt.Rpc(tc)
clnt.fidpool.putId(fid.Fid)
fid.Fid = p.NOFID
return err
}
// Removes the named file. Returns nil if the operation is successful.
func (clnt *Clnt) FRemove(path string) error {
var err error
fid, err := clnt.FWalk(path)
if err != nil {
return err
}
err = clnt.Remove(fid)
return err
}

63
third_party/go9p/p/clnt/seek.go vendored Normal file
View File

@ -0,0 +1,63 @@
// Copyright 2009 The Go9p 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 clnt
import (
"k8s.io/minikube/third_party/go9p/p"
)
var Eisdir = &p.Error{"file is a directory", p.EIO}
var Enegoff = &p.Error{"negative i/o offset", p.EIO}
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end. Seek returns the new offset and an error, if
// any.
//
// Seeking to a negative offset is an error, and results in Enegoff.
// Seeking to 0 in a directory is only valid if whence is 0. Seek returns
// Eisdir otherwise.
func (f *File) Seek(offset int64, whence int) (int64, error) {
var off int64
switch whence {
case 0:
// origin
off = offset
if f.fid.Qid.Type&p.QTDIR > 0 && off != 0 {
return 0, Eisdir
}
case 1:
// current
if f.fid.Qid.Type&p.QTDIR > 0 {
return 0, Eisdir
}
off = offset + int64(f.offset)
case 2:
// end
if f.fid.Qid.Type&p.QTDIR > 0 {
return 0, Eisdir
}
dir, err := f.fid.Clnt.Stat(f.fid)
if err != nil {
return 0, &p.Error{"stat error in seek: " + err.Error(), p.EIO}
}
off = int64(dir.Length) + offset
default:
return 0, &p.Error{"bad whence in seek", p.EIO}
}
if off < 0 {
return 0, Enegoff
}
f.offset = uint64(off)
return off, nil
}

47
third_party/go9p/p/clnt/stat.go vendored Normal file
View File

@ -0,0 +1,47 @@
// Copyright 2009 The Go9p 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 clnt
import "k8s.io/minikube/third_party/go9p/p"
// Returns the metadata for the file associated with the Fid, or an Error.
func (clnt *Clnt) Stat(fid *Fid) (*p.Dir, error) {
tc := clnt.NewFcall()
err := p.PackTstat(tc, fid.Fid)
if err != nil {
return nil, err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return nil, err
}
return &rc.Dir, nil
}
// Returns the metadata for a named file, or an Error.
func (clnt *Clnt) FStat(path string) (*p.Dir, error) {
fid, err := clnt.FWalk(path)
if err != nil {
return nil, err
}
d, err := clnt.Stat(fid)
clnt.Clunk(fid)
return d, err
}
// Modifies the data of the file associated with the Fid, or an Error.
func (clnt *Clnt) Wstat(fid *Fid, dir *p.Dir) error {
tc := clnt.NewFcall()
err := p.PackTwstat(tc, fid.Fid, dir, clnt.Dotu)
if err != nil {
return err
}
_, err = clnt.Rpc(tc)
return err
}

58
third_party/go9p/p/clnt/stats_http.go vendored Normal file
View File

@ -0,0 +1,58 @@
// +build httpstats
package clnt
import (
"fmt"
"io"
"k8s.io/minikube/third_party/go9p/p"
"net/http"
)
func (clnt *Clnt) ServeHTTP(c http.ResponseWriter, r *http.Request) {
io.WriteString(c, fmt.Sprintf("<html><body><h1>Client %s</h1>", clnt.Id))
defer io.WriteString(c, "</body></html>")
// fcalls
if clnt.Debuglevel&DbgLogFcalls != 0 {
fs := clnt.Log.Filter(clnt, DbgLogFcalls)
io.WriteString(c, fmt.Sprintf("<h2>Last %d 9P messages</h2>", len(fs)))
for _, l := range fs {
fc := l.Data.(*p.Fcall)
if fc.Type != 0 {
io.WriteString(c, fmt.Sprintf("<br>%s", fc))
}
}
}
}
func clntServeHTTP(c http.ResponseWriter, r *http.Request) {
io.WriteString(c, fmt.Sprintf("<html><body>"))
defer io.WriteString(c, "</body></html>")
clnts.Lock()
if clnts.clntList == nil {
io.WriteString(c, "no clients")
}
for clnt := clnts.clntList; clnt != nil; clnt = clnt.next {
io.WriteString(c, fmt.Sprintf("<a href='/go9p/clnt/%s'>%s</a><br>", clnt.Id, clnt.Id))
}
clnts.Unlock()
}
func (clnt *Clnt) statsRegister() {
http.Handle("/go9p/clnt/"+clnt.Id, clnt)
}
func (clnt *Clnt) statsUnregister() {
http.Handle("/go9p/clnt/"+clnt.Id, nil)
}
func (c *ClntList) statsRegister() {
http.HandleFunc("/go9p/clnt", clntServeHTTP)
}
func (c *ClntList) statsUnregister() {
http.HandleFunc("/go9p/clnt", nil)
}

237
third_party/go9p/p/clnt/tag.go vendored Normal file
View File

@ -0,0 +1,237 @@
// Copyright 2009 The Go9p 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 clnt
import "k8s.io/minikube/third_party/go9p/p"
type Tag struct {
clnt *Clnt
tag uint16
reqchan chan *Req
respchan chan *Req
donechan chan bool
}
func (clnt *Clnt) TagAlloc(reqchan chan *Req) *Tag {
tag := new(Tag)
tag.clnt = clnt
tag.tag = uint16(clnt.tagpool.getId())
tag.reqchan = reqchan
tag.respchan = make(chan *Req, 16)
tag.donechan = make(chan bool)
go tag.reqproc()
return tag
}
func (clnt *Clnt) TagFree(tag *Tag) {
tag.donechan <- true
clnt.tagpool.putId(uint32(tag.tag))
}
func (tag *Tag) reqAlloc() *Req {
r := new(Req)
r.tag = tag.tag
r.Clnt = tag.clnt
r.Done = tag.respchan
r.Tc = tag.clnt.NewFcall()
return r
}
func (tag *Tag) ReqFree(r *Req) {
tag.clnt.FreeFcall(r.Tc)
}
func (tag *Tag) reqproc() {
for {
select {
case <-tag.donechan:
return
case r := <-tag.respchan:
rc := r.Rc
fid := r.fid
err := r.Rc.Type == p.Rerror
switch r.Tc.Type {
case p.Tauth:
if err {
fid.User = nil
}
case p.Tattach:
if !err {
fid.Qid = rc.Qid
} else {
fid.User = nil
}
case p.Twalk:
if !err {
fid.walked = true
if len(rc.Wqid) > 0 {
fid.Qid = rc.Wqid[len(rc.Wqid)-1]
}
} else {
fid.User = nil
}
case p.Topen:
case p.Tcreate:
if !err {
fid.Iounit = rc.Iounit
fid.Qid = rc.Qid
} else {
fid.Mode = 0
}
case p.Tclunk:
case p.Tremove:
tag.clnt.fidpool.putId(fid.Fid)
}
tag.reqchan <- r
}
}
}
func (tag *Tag) Auth(afid *Fid, user p.User, aname string) error {
req := tag.reqAlloc()
req.fid = afid
err := p.PackTauth(req.Tc, afid.Fid, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu)
if err != nil {
return err
}
afid.User = user
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Attach(fid, afid *Fid, user p.User, aname string) error {
var afno uint32
if afid != nil {
afno = afid.Fid
} else {
afno = p.NOFID
}
req := tag.reqAlloc()
req.fid = fid
err := p.PackTattach(req.Tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu)
if err != nil {
return err
}
fid.User = user
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Walk(fid *Fid, newfid *Fid, wnames []string) error {
req := tag.reqAlloc()
req.fid = newfid
if len(wnames) == 0 {
newfid.Qid = fid.Qid
}
err := p.PackTwalk(req.Tc, fid.Fid, newfid.Fid, wnames)
if err != nil {
return err
}
newfid.User = fid.User
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Open(fid *Fid, mode uint8) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTopen(req.Tc, fid.Fid, mode)
if err != nil {
return err
}
fid.Mode = mode
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTcreate(req.Tc, fid.Fid, name, perm, mode, ext, tag.clnt.Dotu)
if err != nil {
return err
}
fid.Mode = mode
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Read(fid *Fid, offset uint64, count uint32) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTread(req.Tc, fid.Fid, offset, count)
if err != nil {
return err
}
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Write(fid *Fid, data []byte, offset uint64) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTwrite(req.Tc, fid.Fid, offset, uint32(len(data)), data)
if err != nil {
return err
}
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Clunk(fid *Fid) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTclunk(req.Tc, fid.Fid)
if err != nil {
return err
}
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Remove(fid *Fid) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTremove(req.Tc, fid.Fid)
if err != nil {
return err
}
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Stat(fid *Fid) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTstat(req.Tc, fid.Fid)
if err != nil {
return err
}
return tag.clnt.Rpcnb(req)
}
func (tag *Tag) Wstat(fid *Fid, dir *p.Dir) error {
req := tag.reqAlloc()
req.fid = fid
err := p.PackTwstat(req.Tc, fid.Fid, dir, tag.clnt.Dotu)
if err != nil {
return err
}
return tag.clnt.Rpcnb(req)
}

104
third_party/go9p/p/clnt/walk.go vendored Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2009 The Go9p 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 clnt
import (
"k8s.io/minikube/third_party/go9p/p"
"strings"
)
// Starting from the file associated with fid, walks all wnames in
// sequence and associates the resulting file with newfid. If no wnames
// were walked successfully, an Error is returned. Otherwise a slice with a
// Qid for each walked name is returned.
func (clnt *Clnt) Walk(fid *Fid, newfid *Fid, wnames []string) ([]p.Qid, error) {
tc := clnt.NewFcall()
err := p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames)
if err != nil {
return nil, err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return nil, err
}
newfid.walked = true
return rc.Wqid, nil
}
// Walks to a named file. Returns a Fid associated with the file,
// or an Error.
func (clnt *Clnt) FWalk(path string) (*Fid, error) {
var err error = nil
var i, m int
for i = 0; i < len(path); i++ {
if path[i] != '/' {
break
}
}
if i > 0 {
path = path[i:]
}
wnames := strings.Split(path, "/")
newfid := clnt.FidAlloc()
fid := clnt.Root
newfid.User = fid.User
/* get rid of the empty names */
for i, m = 0, 0; i < len(wnames); i++ {
if wnames[i] != "" {
wnames[m] = wnames[i]
m++
}
}
wnames = wnames[0:m]
for {
n := len(wnames)
if n > 16 {
n = 16
}
tc := clnt.NewFcall()
err = p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames[0:n])
if err != nil {
goto error
}
var rc *p.Fcall
rc, err = clnt.Rpc(tc)
if err != nil {
goto error
}
newfid.walked = true
if len(rc.Wqid) != n {
err = &p.Error{"file not found", p.ENOENT}
goto error
}
if len(rc.Wqid) > 0 {
newfid.Qid = rc.Wqid[len(rc.Wqid)-1]
} else {
newfid.Qid = fid.Qid
}
wnames = wnames[n:]
fid = newfid
if len(wnames) == 0 {
break
}
}
return newfid, nil
error:
clnt.Clunk(newfid)
return nil, err
}

68
third_party/go9p/p/clnt/write.go vendored Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2009 The Go9p 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 clnt
import "k8s.io/minikube/third_party/go9p/p"
// Write up to len(data) bytes starting from offset. Returns the
// number of bytes written, or an Error.
func (clnt *Clnt) Write(fid *Fid, data []byte, offset uint64) (int, error) {
if uint32(len(data)) > fid.Iounit {
data = data[0:fid.Iounit]
}
tc := clnt.NewFcall()
err := p.PackTwrite(tc, fid.Fid, offset, uint32(len(data)), data)
if err != nil {
return 0, err
}
rc, err := clnt.Rpc(tc)
if err != nil {
return 0, err
}
return int(rc.Count), nil
}
// Writes up to len(buf) bytes to a file. Returns the number of
// bytes written, or an Error.
func (file *File) Write(buf []byte) (int, error) {
n, err := file.WriteAt(buf, int64(file.offset))
if err == nil {
file.offset += uint64(n)
}
return n, err
}
// Writes up to len(buf) bytes starting from offset. Returns the number
// of bytes written, or an Error.
func (file *File) WriteAt(buf []byte, offset int64) (int, error) {
return file.fid.Clnt.Write(file.fid, buf, uint64(offset))
}
// Writes exactly len(buf) bytes starting from offset. Returns the number of
// bytes written. If Error is returned the number of bytes can be less
// than len(buf).
func (file *File) Writen(buf []byte, offset uint64) (int, error) {
ret := 0
for len(buf) > 0 {
n, err := file.WriteAt(buf, int64(offset))
if err != nil {
return ret, err
}
if n == 0 {
break
}
buf = buf[n:]
offset += uint64(n)
ret += n
}
return ret, nil
}

161
third_party/go9p/p/fmt.go vendored Normal file
View File

@ -0,0 +1,161 @@
// Copyright 2009 The Go9p 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 p
import "fmt"
func permToString(perm uint32) string {
ret := ""
if perm&DMDIR != 0 {
ret += "d"
}
if perm&DMAPPEND != 0 {
ret += "a"
}
if perm&DMAUTH != 0 {
ret += "A"
}
if perm&DMEXCL != 0 {
ret += "l"
}
if perm&DMTMP != 0 {
ret += "t"
}
if perm&DMDEVICE != 0 {
ret += "D"
}
if perm&DMSOCKET != 0 {
ret += "S"
}
if perm&DMNAMEDPIPE != 0 {
ret += "P"
}
if perm&DMSYMLINK != 0 {
ret += "L"
}
ret += fmt.Sprintf("%o", perm&0777)
return ret
}
func (qid *Qid) String() string {
b := ""
if qid.Type&QTDIR != 0 {
b += "d"
}
if qid.Type&QTAPPEND != 0 {
b += "a"
}
if qid.Type&QTAUTH != 0 {
b += "A"
}
if qid.Type&QTEXCL != 0 {
b += "l"
}
if qid.Type&QTTMP != 0 {
b += "t"
}
if qid.Type&QTSYMLINK != 0 {
b += "L"
}
return fmt.Sprintf("(%x %x '%s')", qid.Path, qid.Version, b)
}
func (d *Dir) String() string {
ret := fmt.Sprintf("'%s' '%s' '%s' '%s' q ", d.Name, d.Uid, d.Gid, d.Muid)
ret += d.Qid.String() + " m " + permToString(d.Mode)
ret += fmt.Sprintf(" at %d mt %d l %d t %d d %d", d.Atime, d.Mtime,
d.Length, d.Type, d.Dev)
/* dotu ? */
ret += " ext " + d.Ext
return ret
}
func (fc *Fcall) String() string {
ret := ""
switch fc.Type {
default:
ret = fmt.Sprintf("invalid call: %d", fc.Type)
case Tversion:
ret = fmt.Sprintf("Tversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
case Rversion:
ret = fmt.Sprintf("Rversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
case Tauth:
ret = fmt.Sprintf("Tauth tag %d afid %d uname '%s' nuname %d aname '%s'",
fc.Tag, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
case Rauth:
ret = fmt.Sprintf("Rauth tag %d aqid %v", fc.Tag, &fc.Qid)
case Rattach:
ret = fmt.Sprintf("Rattach tag %d aqid %v", fc.Tag, &fc.Qid)
case Tattach:
ret = fmt.Sprintf("Tattach tag %d fid %d afid %d uname '%s' nuname %d aname '%s'",
fc.Tag, fc.Fid, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
case Tflush:
ret = fmt.Sprintf("Tflush tag %d oldtag %d", fc.Tag, fc.Oldtag)
case Rerror:
ret = fmt.Sprintf("Rerror tag %d ename '%s' ecode %d", fc.Tag, fc.Error, fc.Errornum)
case Twalk:
ret = fmt.Sprintf("Twalk tag %d fid %d newfid %d ", fc.Tag, fc.Fid, fc.Newfid)
for i := 0; i < len(fc.Wname); i++ {
ret += fmt.Sprintf("%d:'%s' ", i, fc.Wname[i])
}
case Rwalk:
ret = fmt.Sprintf("Rwalk tag %d ", fc.Tag)
for i := 0; i < len(fc.Wqid); i++ {
ret += fmt.Sprintf("%v ", &fc.Wqid[i])
}
case Topen:
ret = fmt.Sprintf("Topen tag %d fid %d mode %x", fc.Tag, fc.Fid, fc.Mode)
case Ropen:
ret = fmt.Sprintf("Ropen tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
case Rcreate:
ret = fmt.Sprintf("Rcreate tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
case Tcreate:
ret = fmt.Sprintf("Tcreate tag %d fid %d name '%s' perm ", fc.Tag, fc.Fid, fc.Name)
ret += permToString(fc.Perm)
ret += fmt.Sprintf(" mode %x ", fc.Mode)
case Tread:
ret = fmt.Sprintf("Tread tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
case Rread:
ret = fmt.Sprintf("Rread tag %d count %d", fc.Tag, fc.Count)
case Twrite:
ret = fmt.Sprintf("Twrite tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
case Rwrite:
ret = fmt.Sprintf("Rwrite tag %d count %d", fc.Tag, fc.Count)
case Tclunk:
ret = fmt.Sprintf("Tclunk tag %d fid %d", fc.Tag, fc.Fid)
case Rclunk:
ret = fmt.Sprintf("Rclunk tag %d", fc.Tag)
case Tremove:
ret = fmt.Sprintf("Tremove tag %d fid %d", fc.Tag, fc.Fid)
case Tstat:
ret = fmt.Sprintf("Tstat tag %d fid %d", fc.Tag, fc.Fid)
case Rstat:
ret = fmt.Sprintf("Rstat tag %d st (%v)", fc.Tag, &fc.Dir)
case Twstat:
ret = fmt.Sprintf("Twstat tag %d fid %d st (%v)", fc.Tag, fc.Fid, &fc.Dir)
case Rflush:
ret = fmt.Sprintf("Rflush tag %d", fc.Tag)
case Rremove:
ret = fmt.Sprintf("Rremove tag %d", fc.Tag)
case Rwstat:
ret = fmt.Sprintf("Rwstat tag %d", fc.Tag)
}
return ret
}

115
third_party/go9p/p/log.go vendored Normal file
View File

@ -0,0 +1,115 @@
package p
type Log struct {
Data interface{}
Owner interface{}
Type int
}
type Logger struct {
items []*Log
idx int
logchan chan *Log
fltchan chan *flt
rszchan chan int
}
type flt struct {
owner interface{}
itype int
fltchan chan []*Log
}
func NewLogger(sz int) *Logger {
if sz == 0 {
return nil
}
l := new(Logger)
l.items = make([]*Log, sz)
l.logchan = make(chan *Log, 16)
l.fltchan = make(chan *flt)
l.rszchan = make(chan int)
go l.doLog()
return l
}
func (l *Logger) Resize(sz int) {
if sz == 0 {
return
}
l.rszchan <- sz
}
func (l *Logger) Log(data, owner interface{}, itype int) {
l.logchan <- &Log{data, owner, itype}
}
func (l *Logger) Filter(owner interface{}, itype int) []*Log {
c := make(chan []*Log)
l.fltchan <- &flt{owner, itype, c}
return <-c
}
func (l *Logger) doLog() {
for {
select {
case it := <-l.logchan:
if l.idx >= len(l.items) {
l.idx = 0
}
l.items[l.idx] = it
l.idx++
case sz := <-l.rszchan:
it := make([]*Log, sz)
for i, j := l.idx, 0; j < len(it); j++ {
if i >= len(l.items) {
i = 0
}
it[j] = l.items[i]
i++
if i == l.idx {
break
}
}
l.items = it
l.idx = 0
case flt := <-l.fltchan:
n := 0
// we don't care about the order while counting
for _, it := range l.items {
if it == nil {
continue
}
if (flt.owner == nil || it.Owner == flt.owner) &&
(flt.itype == 0 || it.Type == flt.itype) {
n++
}
}
its := make([]*Log, n)
for i, m := l.idx, 0; m < len(its); i++ {
if i >= len(l.items) {
i = 0
}
it := l.items[i]
if it != nil && (flt.owner == nil || it.Owner == flt.owner) &&
(flt.itype == 0 || it.Type == flt.itype) {
its[m] = it
m++
}
}
flt.fltchan <- its
}
}
}

97
third_party/go9p/p/osusers.go vendored Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2009 The Go9p 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 p
import (
"os/user"
"strconv"
"sync"
)
var once sync.Once
type osUser struct {
*user.User
uid int
gid int
}
type osUsers struct {
groups map[int]*osGroup
sync.Mutex
}
// Simple Users implementation that defers to os/user and fakes
// looking up groups by gid only.
var OsUsers *osUsers
func (u *osUser) Name() string { return u.Username }
func (u *osUser) Id() int { return u.uid }
func (u *osUser) Groups() []Group { return []Group{OsUsers.Gid2Group(u.gid)} }
func (u *osUser) IsMember(g Group) bool { return u.gid == g.Id() }
type osGroup struct {
gid int
}
func (g *osGroup) Name() string { return "" }
func (g *osGroup) Id() int { return g.gid }
func (g *osGroup) Members() []User { return nil }
func initOsusers() {
OsUsers = new(osUsers)
OsUsers.groups = make(map[int]*osGroup)
}
func newUser(u *user.User) *osUser {
uid, uerr := strconv.Atoi(u.Uid)
gid, gerr := strconv.Atoi(u.Gid)
if uerr != nil || gerr != nil {
/* non-numeric uid/gid => unsupported system */
return nil
}
return &osUser{u, uid, gid}
}
func (up *osUsers) Uid2User(uid int) User {
u, err := user.LookupId(strconv.Itoa(uid))
if err != nil {
return nil
}
return newUser(u)
}
func (up *osUsers) Uname2User(uname string) User {
u, err := user.Lookup(uname)
if err != nil {
return nil
}
return newUser(u)
}
func (up *osUsers) Gid2Group(gid int) Group {
once.Do(initOsusers)
OsUsers.Lock()
group, present := OsUsers.groups[gid]
if present {
OsUsers.Unlock()
return group
}
group = new(osGroup)
group.gid = gid
OsUsers.groups[gid] = group
OsUsers.Unlock()
return group
}
func (up *osUsers) Gname2Group(gname string) Group {
return nil
}

529
third_party/go9p/p/p9.go vendored Normal file
View File

@ -0,0 +1,529 @@
// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The p9 package provides the definitions and functions used to implement
// the 9P2000 protocol.
package p
import (
"fmt"
)
// 9P2000 message types
const (
Tversion = 100 + iota
Rversion
Tauth
Rauth
Tattach
Rattach
Terror
Rerror
Tflush
Rflush
Twalk
Rwalk
Topen
Ropen
Tcreate
Rcreate
Tread
Rread
Twrite
Rwrite
Tclunk
Rclunk
Tremove
Rremove
Tstat
Rstat
Twstat
Rwstat
Tlast
)
const (
MSIZE = 8192 + IOHDRSZ // default message size (8192+IOHdrSz)
IOHDRSZ = 24 // the non-data size of the Twrite messages
PORT = 564 // default port for 9P file servers
)
// Qid types
const (
QTDIR = 0x80 // directories
QTAPPEND = 0x40 // append only files
QTEXCL = 0x20 // exclusive use files
QTMOUNT = 0x10 // mounted channel
QTAUTH = 0x08 // authentication file
QTTMP = 0x04 // non-backed-up file
QTSYMLINK = 0x02 // symbolic link (Unix, 9P2000.u)
QTLINK = 0x01 // hard link (Unix, 9P2000.u)
QTFILE = 0x00
)
// Flags for the mode field in Topen and Tcreate messages
const (
OREAD = 0 // open read-only
OWRITE = 1 // open write-only
ORDWR = 2 // open read-write
OEXEC = 3 // execute (== read but check execute permission)
OTRUNC = 16 // or'ed in (except for exec), truncate file first
OCEXEC = 32 // or'ed in, close on exec
ORCLOSE = 64 // or'ed in, remove on close
)
// File modes
const (
DMDIR = 0x80000000 // mode bit for directories
DMAPPEND = 0x40000000 // mode bit for append only files
DMEXCL = 0x20000000 // mode bit for exclusive use files
DMMOUNT = 0x10000000 // mode bit for mounted channel
DMAUTH = 0x08000000 // mode bit for authentication file
DMTMP = 0x04000000 // mode bit for non-backed-up file
DMSYMLINK = 0x02000000 // mode bit for symbolic link (Unix, 9P2000.u)
DMLINK = 0x01000000 // mode bit for hard link (Unix, 9P2000.u)
DMDEVICE = 0x00800000 // mode bit for device file (Unix, 9P2000.u)
DMNAMEDPIPE = 0x00200000 // mode bit for named pipe (Unix, 9P2000.u)
DMSOCKET = 0x00100000 // mode bit for socket (Unix, 9P2000.u)
DMSETUID = 0x00080000 // mode bit for setuid (Unix, 9P2000.u)
DMSETGID = 0x00040000 // mode bit for setgid (Unix, 9P2000.u)
DMREAD = 0x4 // mode bit for read permission
DMWRITE = 0x2 // mode bit for write permission
DMEXEC = 0x1 // mode bit for execute permission
)
const (
NOTAG uint16 = 0xFFFF // no tag specified
NOFID uint32 = 0xFFFFFFFF // no fid specified
NOUID uint32 = 0xFFFFFFFF // no uid specified
)
// Error values
const (
EPERM = 1
ENOENT = 2
EIO = 5
EEXIST = 17
ENOTDIR = 20
EINVAL = 22
)
// Error represents a 9P2000 (and 9P2000.u) error
type Error struct {
Err string // textual representation of the error
Errornum uint32 // numeric representation of the error (9P2000.u)
}
// File identifier
type Qid struct {
Type uint8 // type of the file (high 8 bits of the mode)
Version uint32 // version number for the path
Path uint64 // server's unique identification of the file
}
// Dir describes a file
type Dir struct {
Size uint16 // size-2 of the Dir on the wire
Type uint16
Dev uint32
Qid // file's Qid
Mode uint32 // permissions and flags
Atime uint32 // last access time in seconds
Mtime uint32 // last modified time in seconds
Length uint64 // file length in bytes
Name string // file name
Uid string // owner name
Gid string // group name
Muid string // name of the last user that modified the file
/* 9P2000.u extension */
Ext string // special file's descriptor
Uidnum uint32 // owner ID
Gidnum uint32 // group ID
Muidnum uint32 // ID of the last user that modified the file
}
// Fcall represents a 9P2000 message
type Fcall struct {
Size uint32 // size of the message
Type uint8 // message type
Fid uint32 // file identifier
Tag uint16 // message tag
Msize uint32 // maximum message size (used by Tversion, Rversion)
Version string // protocol version (used by Tversion, Rversion)
Oldtag uint16 // tag of the message to flush (used by Tflush)
Error string // error (used by Rerror)
Qid // file Qid (used by Rauth, Rattach, Ropen, Rcreate)
Iounit uint32 // maximum bytes read without breaking in multiple messages (used by Ropen, Rcreate)
Afid uint32 // authentication fid (used by Tauth, Tattach)
Uname string // user name (used by Tauth, Tattach)
Aname string // attach name (used by Tauth, Tattach)
Perm uint32 // file permission (mode) (used by Tcreate)
Name string // file name (used by Tcreate)
Mode uint8 // open mode (used by Topen, Tcreate)
Newfid uint32 // the fid that represents the file walked to (used by Twalk)
Wname []string // list of names to walk (used by Twalk)
Wqid []Qid // list of Qids for the walked files (used by Rwalk)
Offset uint64 // offset in the file to read/write from/to (used by Tread, Twrite)
Count uint32 // number of bytes read/written (used by Tread, Rread, Twrite, Rwrite)
Data []uint8 // data read/to-write (used by Rread, Twrite)
Dir // file description (used by Rstat, Twstat)
/* 9P2000.u extensions */
Errornum uint32 // error code, 9P2000.u only (used by Rerror)
Ext string // special file description, 9P2000.u only (used by Tcreate)
Unamenum uint32 // user ID, 9P2000.u only (used by Tauth, Tattach)
Pkt []uint8 // raw packet data
Buf []uint8 // buffer to put the raw data in
}
// Interface for accessing users and groups
type Users interface {
Uid2User(uid int) User
Uname2User(uname string) User
Gid2Group(gid int) Group
Gname2Group(gname string) Group
}
// Represents a user
type User interface {
Name() string // user name
Id() int // user id
Groups() []Group // groups the user belongs to (can return nil)
IsMember(g Group) bool // returns true if the user is member of the specified group
}
// Represents a group of users
type Group interface {
Name() string // group name
Id() int // group id
Members() []User // list of members that belong to the group (can return nil)
}
// minimum size of a 9P2000 message for a type
var minFcsize = [...]uint32{
6, /* Tversion msize[4] version[s] */
6, /* Rversion msize[4] version[s] */
8, /* Tauth fid[4] uname[s] aname[s] */
13, /* Rauth aqid[13] */
12, /* Tattach fid[4] afid[4] uname[s] aname[s] */
13, /* Rattach qid[13] */
0, /* Terror */
2, /* Rerror ename[s] (ecode[4]) */
2, /* Tflush oldtag[2] */
0, /* Rflush */
10, /* Twalk fid[4] newfid[4] nwname[2] */
2, /* Rwalk nwqid[2] */
5, /* Topen fid[4] mode[1] */
17, /* Ropen qid[13] iounit[4] */
11, /* Tcreate fid[4] name[s] perm[4] mode[1] */
17, /* Rcreate qid[13] iounit[4] */
16, /* Tread fid[4] offset[8] count[4] */
4, /* Rread count[4] */
16, /* Twrite fid[4] offset[8] count[4] */
4, /* Rwrite count[4] */
4, /* Tclunk fid[4] */
0, /* Rclunk */
4, /* Tremove fid[4] */
0, /* Rremove */
4, /* Tstat fid[4] */
4, /* Rstat stat[n] */
8, /* Twstat fid[4] stat[n] */
0, /* Rwstat */
20, /* Tbread fileid[8] offset[8] count[4] */
4, /* Rbread count[4] */
20, /* Tbwrite fileid[8] offset[8] count[4] */
4, /* Rbwrite count[4] */
16, /* Tbtrunc fileid[8] offset[8] */
0, /* Rbtrunc */
}
// minimum size of a 9P2000.u message for a type
var minFcusize = [...]uint32{
6, /* Tversion msize[4] version[s] */
6, /* Rversion msize[4] version[s] */
12, /* Tauth fid[4] uname[s] aname[s] */
13, /* Rauth aqid[13] */
16, /* Tattach fid[4] afid[4] uname[s] aname[s] */
13, /* Rattach qid[13] */
0, /* Terror */
6, /* Rerror ename[s] (ecode[4]) */
2, /* Tflush oldtag[2] */
0, /* Rflush */
10, /* Twalk fid[4] newfid[4] nwname[2] */
2, /* Rwalk nwqid[2] */
5, /* Topen fid[4] mode[1] */
17, /* Ropen qid[13] iounit[4] */
13, /* Tcreate fid[4] name[s] perm[4] mode[1] */
17, /* Rcreate qid[13] iounit[4] */
16, /* Tread fid[4] offset[8] count[4] */
4, /* Rread count[4] */
16, /* Twrite fid[4] offset[8] count[4] */
4, /* Rwrite count[4] */
4, /* Tclunk fid[4] */
0, /* Rclunk */
4, /* Tremove fid[4] */
0, /* Rremove */
4, /* Tstat fid[4] */
4, /* Rstat stat[n] */
8, /* Twstat fid[4] stat[n] */
20, /* Tbread fileid[8] offset[8] count[4] */
4, /* Rbread count[4] */
20, /* Tbwrite fileid[8] offset[8] count[4] */
4, /* Rbwrite count[4] */
16, /* Tbtrunc fileid[8] offset[8] */
0, /* Rbtrunc */
}
func gint8(buf []byte) (uint8, []byte) { return buf[0], buf[1:] }
func gint16(buf []byte) (uint16, []byte) {
return uint16(buf[0]) | (uint16(buf[1]) << 8), buf[2:]
}
func gint32(buf []byte) (uint32, []byte) {
return uint32(buf[0]) | (uint32(buf[1]) << 8) | (uint32(buf[2]) << 16) |
(uint32(buf[3]) << 24),
buf[4:]
}
func Gint32(buf []byte) (uint32, []byte) { return gint32(buf) }
func gint64(buf []byte) (uint64, []byte) {
return uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) |
(uint64(buf[3]) << 24) | (uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) |
(uint64(buf[6]) << 48) | (uint64(buf[7]) << 56),
buf[8:]
}
func gstr(buf []byte) (string, []byte) {
var n uint16
if buf == nil {
return "", nil
}
n, buf = gint16(buf)
if int(n) > len(buf) {
return "", nil
}
return string(buf[0:n]), buf[n:]
}
func gqid(buf []byte, qid *Qid) []byte {
qid.Type, buf = gint8(buf)
qid.Version, buf = gint32(buf)
qid.Path, buf = gint64(buf)
return buf
}
func gstat(buf []byte, d *Dir, dotu bool) []byte {
d.Size, buf = gint16(buf)
d.Type, buf = gint16(buf)
d.Dev, buf = gint32(buf)
buf = gqid(buf, &d.Qid)
d.Mode, buf = gint32(buf)
d.Atime, buf = gint32(buf)
d.Mtime, buf = gint32(buf)
d.Length, buf = gint64(buf)
d.Name, buf = gstr(buf)
if buf == nil {
return nil
}
d.Uid, buf = gstr(buf)
if buf == nil {
return nil
}
d.Gid, buf = gstr(buf)
if buf == nil {
return nil
}
d.Muid, buf = gstr(buf)
if buf == nil {
return nil
}
if dotu {
d.Ext, buf = gstr(buf)
if buf == nil {
return nil
}
d.Uidnum, buf = gint32(buf)
d.Gidnum, buf = gint32(buf)
d.Muidnum, buf = gint32(buf)
} else {
d.Uidnum = NOUID
d.Gidnum = NOUID
d.Muidnum = NOUID
}
return buf
}
func pint8(val uint8, buf []byte) []byte {
buf[0] = val
return buf[1:]
}
func pint16(val uint16, buf []byte) []byte {
buf[0] = uint8(val)
buf[1] = uint8(val >> 8)
return buf[2:]
}
func pint32(val uint32, buf []byte) []byte {
buf[0] = uint8(val)
buf[1] = uint8(val >> 8)
buf[2] = uint8(val >> 16)
buf[3] = uint8(val >> 24)
return buf[4:]
}
func pint64(val uint64, buf []byte) []byte {
buf[0] = uint8(val)
buf[1] = uint8(val >> 8)
buf[2] = uint8(val >> 16)
buf[3] = uint8(val >> 24)
buf[4] = uint8(val >> 32)
buf[5] = uint8(val >> 40)
buf[6] = uint8(val >> 48)
buf[7] = uint8(val >> 56)
return buf[8:]
}
func pstr(val string, buf []byte) []byte {
n := uint16(len(val))
buf = pint16(n, buf)
b := []byte(val)
copy(buf, b)
return buf[n:]
}
func pqid(val *Qid, buf []byte) []byte {
buf = pint8(val.Type, buf)
buf = pint32(val.Version, buf)
buf = pint64(val.Path, buf)
return buf
}
func statsz(d *Dir, dotu bool) int {
sz := 2 + 2 + 4 + 13 + 4 + 4 + 4 + 8 + 2 + 2 + 2 + 2 + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
if dotu {
sz += 2 + 4 + 4 + 4 + len(d.Ext)
}
return sz
}
func pstat(d *Dir, buf []byte, dotu bool) []byte {
sz := statsz(d, dotu)
buf = pint16(uint16(sz-2), buf)
buf = pint16(d.Type, buf)
buf = pint32(d.Dev, buf)
buf = pqid(&d.Qid, buf)
buf = pint32(d.Mode, buf)
buf = pint32(d.Atime, buf)
buf = pint32(d.Mtime, buf)
buf = pint64(d.Length, buf)
buf = pstr(d.Name, buf)
buf = pstr(d.Uid, buf)
buf = pstr(d.Gid, buf)
buf = pstr(d.Muid, buf)
if dotu {
buf = pstr(d.Ext, buf)
buf = pint32(d.Uidnum, buf)
buf = pint32(d.Gidnum, buf)
buf = pint32(d.Muidnum, buf)
}
return buf
}
// Converts a Dir value to its on-the-wire representation and writes it to
// the buf. Returns the number of bytes written, 0 if there is not enough space.
func PackDir(d *Dir, buf []byte, dotu bool) int {
sz := statsz(d, dotu)
if sz > len(buf) {
return 0
}
buf = pstat(d, buf, dotu)
return sz
}
// Converts the on-the-wire representation of a stat to Stat value.
// Returns an error if the conversion is impossible, otherwise
// a pointer to a Stat value.
func UnpackDir(buf []byte, dotu bool) (d *Dir, err error) {
sz := 2 + 2 + 4 + 13 + 4 + /* size[2] type[2] dev[4] qid[13] mode[4] */
4 + 4 + 8 + /* atime[4] mtime[4] length[8] */
2 + 2 + 2 + 2 /* name[s] uid[s] gid[s] muid[s] */
if dotu {
sz += 2 + 4 + 4 + 4 /* extension[s] n_uid[4] n_gid[4] n_muid[4] */
}
if len(buf) < sz {
goto szerror
}
d = new(Dir)
buf = gstat(buf, d, dotu)
if buf == nil {
goto szerror
}
return d, nil
szerror:
return nil, &Error{"short buffer", EINVAL}
}
// Allocates a new Fcall.
func NewFcall(sz uint32) *Fcall {
fc := new(Fcall)
fc.Buf = make([]byte, sz)
return fc
}
// Sets the tag of a Fcall.
func SetTag(fc *Fcall, tag uint16) {
fc.Tag = tag
pint16(tag, fc.Pkt[5:])
}
func packCommon(fc *Fcall, size int, id uint8) ([]byte, error) {
size += 4 + 1 + 2 /* size[4] id[1] tag[2] */
if len(fc.Buf) < int(size) {
return nil, &Error{"buffer too small", EINVAL}
}
fc.Size = uint32(size)
fc.Type = id
fc.Tag = NOTAG
p := fc.Buf
p = pint32(uint32(size), p)
p = pint8(id, p)
p = pint16(NOTAG, p)
fc.Pkt = fc.Buf[0:size]
return p, nil
}
func (err *Error) Error() string {
if err != nil {
return fmt.Sprintf("%s: %d", err.Err, err.Errornum)
}
return ""
}

219
third_party/go9p/p/packr.go vendored Normal file
View File

@ -0,0 +1,219 @@
// Copyright 2009 The Go9p 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 p
// Create a Rversion message in the specified Fcall.
func PackRversion(fc *Fcall, msize uint32, version string) error {
size := 4 + 2 + len(version) /* msize[4] version[s] */
p, err := packCommon(fc, size, Rversion)
if err != nil {
return err
}
fc.Msize = msize
fc.Version = version
p = pint32(msize, p)
p = pstr(version, p)
return nil
}
// Create a Rauth message in the specified Fcall.
func PackRauth(fc *Fcall, aqid *Qid) error {
size := 13 /* aqid[13] */
p, err := packCommon(fc, size, Rauth)
if err != nil {
return err
}
fc.Qid = *aqid
p = pqid(aqid, p)
return nil
}
// Create a Rerror message in the specified Fcall. If dotu is true,
// the function will create a 9P2000.u message. If false, nerror is
// ignored.
func PackRerror(fc *Fcall, error string, errornum uint32, dotu bool) error {
size := 2 + len(error) /* ename[s] */
if dotu {
size += 4 /* ecode[4] */
}
p, err := packCommon(fc, size, Rerror)
if err != nil {
return err
}
fc.Error = error
p = pstr(error, p)
if dotu {
fc.Errornum = errornum
p = pint32(errornum, p)
}
return nil
}
// Create a Rflush message in the specified Fcall.
func PackRflush(fc *Fcall) error {
_, err := packCommon(fc, 0, Rflush)
return err
}
// Create a Rattach message in the specified Fcall.
func PackRattach(fc *Fcall, aqid *Qid) error {
size := 13 /* aqid[13] */
p, err := packCommon(fc, size, Rattach)
if err != nil {
return err
}
fc.Qid = *aqid
p = pqid(aqid, p)
return nil
}
// Create a Rwalk message in the specified Fcall.
func PackRwalk(fc *Fcall, wqids []Qid) error {
nwqid := len(wqids)
size := 2 + nwqid*13 /* nwqid[2] nwname*wqid[13] */
p, err := packCommon(fc, size, Rwalk)
if err != nil {
return err
}
p = pint16(uint16(nwqid), p)
fc.Wqid = make([]Qid, nwqid)
for i := 0; i < nwqid; i++ {
fc.Wqid[i] = wqids[i]
p = pqid(&wqids[i], p)
}
return nil
}
// Create a Ropen message in the specified Fcall.
func PackRopen(fc *Fcall, qid *Qid, iounit uint32) error {
size := 13 + 4 /* qid[13] iounit[4] */
p, err := packCommon(fc, size, Ropen)
if err != nil {
return err
}
fc.Qid = *qid
fc.Iounit = iounit
p = pqid(qid, p)
p = pint32(iounit, p)
return nil
}
// Create a Rcreate message in the specified Fcall.
func PackRcreate(fc *Fcall, qid *Qid, iounit uint32) error {
size := 13 + 4 /* qid[13] iounit[4] */
p, err := packCommon(fc, size, Rcreate)
if err != nil {
return err
}
fc.Qid = *qid
fc.Iounit = iounit
p = pqid(qid, p)
p = pint32(iounit, p)
return nil
}
// Initializes the specified Fcall value to contain Rread message.
// The user should copy the returned data to the slice pointed by
// fc.Data and call SetRreadCount to update the data size to the
// actual value.
func InitRread(fc *Fcall, count uint32) error {
size := int(4 + count) /* count[4] data[count] */
p, err := packCommon(fc, size, Rread)
if err != nil {
return err
}
fc.Count = count
fc.Data = p[4 : fc.Count+4]
p = pint32(count, p)
return nil
}
// Updates the size of the data returned by Rread. Expects that
// the Fcall value is already initialized by InitRread.
func SetRreadCount(fc *Fcall, count uint32) {
/* we need to update both the packet size as well as the data count */
size := 4 + 1 + 2 + 4 + count /* size[4] id[1] tag[2] count[4] data[count] */
pint32(size, fc.Pkt)
pint32(count, fc.Pkt[7:])
fc.Size = size
fc.Count = count
fc.Pkt = fc.Pkt[0:size]
fc.Data = fc.Data[0:count]
fc.Size = size
}
// Create a Rread message in the specified Fcall.
func PackRread(fc *Fcall, data []byte) error {
count := uint32(len(data))
err := InitRread(fc, count)
if err != nil {
return err
}
copy(fc.Data, data)
return nil
}
// Create a Rwrite message in the specified Fcall.
func PackRwrite(fc *Fcall, count uint32) error {
p, err := packCommon(fc, 4, Rwrite) /* count[4] */
if err != nil {
return err
}
fc.Count = count
p = pint32(count, p)
return nil
}
// Create a Rclunk message in the specified Fcall.
func PackRclunk(fc *Fcall) error {
_, err := packCommon(fc, 0, Rclunk)
return err
}
// Create a Rremove message in the specified Fcall.
func PackRremove(fc *Fcall) error {
_, err := packCommon(fc, 0, Rremove)
return err
}
// Create a Rstat message in the specified Fcall. If dotu is true, the
// function will create a 9P2000.u stat representation that includes
// st.Nuid, st.Ngid, st.Nmuid and st.Ext. Otherwise these values will be
// ignored.
func PackRstat(fc *Fcall, d *Dir, dotu bool) error {
stsz := statsz(d, dotu)
size := 2 + stsz /* stat[n] */
p, err := packCommon(fc, size, Rstat)
if err != nil {
return err
}
p = pint16(uint16(stsz), p)
p = pstat(d, p, dotu)
fc.Dir = *d
return nil
}
// Create a Rwstat message in the specified Fcall.
func PackRwstat(fc *Fcall) error {
_, err := packCommon(fc, 0, Rwstat)
return err
}

255
third_party/go9p/p/packt.go vendored Normal file
View File

@ -0,0 +1,255 @@
// Copyright 2009 The Go9p 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 p
// Create a Tversion message in the specified Fcall.
func PackTversion(fc *Fcall, msize uint32, version string) error {
size := 4 + 2 + len(version) /* msize[4] version[s] */
p, err := packCommon(fc, size, Tversion)
if err != nil {
return err
}
fc.Msize = msize
fc.Version = version
p = pint32(msize, p)
p = pstr(version, p)
return nil
}
// Create a Tauth message in the specified Fcall.
func PackTauth(fc *Fcall, fid uint32, uname string, aname string, unamenum uint32, dotu bool) error {
size := 4 + 2 + 2 + len(uname) + len(aname) /* fid[4] uname[s] aname[s] */
if dotu {
size += 4 /* n_uname[4] */
}
p, err := packCommon(fc, size, Tauth)
if err != nil {
return err
}
fc.Fid = fid
fc.Uname = uname
fc.Aname = aname
p = pint32(fid, p)
p = pstr(uname, p)
p = pstr(aname, p)
if dotu {
fc.Unamenum = unamenum
p = pint32(unamenum, p)
}
return nil
}
// Create a Tflush message in the specified Fcall.
func PackTflush(fc *Fcall, oldtag uint16) error {
p, err := packCommon(fc, 2, Tflush)
if err != nil {
return err
}
fc.Oldtag = oldtag
p = pint16(oldtag, p)
return nil
}
// Create a Tattach message in the specified Fcall. If dotu is true,
// the function will create 9P2000.u including the nuname value, otherwise
// nuname is ignored.
func PackTattach(fc *Fcall, fid uint32, afid uint32, uname string, aname string, unamenum uint32, dotu bool) error {
size := 4 + 4 + 2 + len(uname) + 2 + len(aname) /* fid[4] afid[4] uname[s] aname[s] */
if dotu {
size += 4
}
p, err := packCommon(fc, size, Tattach)
if err != nil {
return err
}
fc.Fid = fid
fc.Afid = afid
fc.Uname = uname
fc.Aname = aname
p = pint32(fid, p)
p = pint32(afid, p)
p = pstr(uname, p)
p = pstr(aname, p)
if dotu {
fc.Unamenum = unamenum
p = pint32(unamenum, p)
}
return nil
}
// Create a Twalk message in the specified Fcall.
func PackTwalk(fc *Fcall, fid uint32, newfid uint32, wnames []string) error {
nwname := len(wnames)
size := 4 + 4 + 2 + nwname*2 /* fid[4] newfid[4] nwname[2] nwname*wname[s] */
for i := 0; i < nwname; i++ {
size += len(wnames[i])
}
p, err := packCommon(fc, size, Twalk)
if err != nil {
return err
}
fc.Fid = fid
fc.Newfid = newfid
p = pint32(fid, p)
p = pint32(newfid, p)
p = pint16(uint16(nwname), p)
fc.Wname = make([]string, nwname)
for i := 0; i < nwname; i++ {
fc.Wname[i] = wnames[i]
p = pstr(wnames[i], p)
}
return nil
}
// Create a Topen message in the specified Fcall.
func PackTopen(fc *Fcall, fid uint32, mode uint8) error {
size := 4 + 1 /* fid[4] mode[1] */
p, err := packCommon(fc, size, Topen)
if err != nil {
return err
}
fc.Fid = fid
fc.Mode = mode
p = pint32(fid, p)
p = pint8(mode, p)
return nil
}
// Create a Tcreate message in the specified Fcall. If dotu is true,
// the function will create a 9P2000.u message that includes ext.
// Otherwise the ext value is ignored.
func PackTcreate(fc *Fcall, fid uint32, name string, perm uint32, mode uint8, ext string, dotu bool) error {
size := 4 + 2 + len(name) + 4 + 1 /* fid[4] name[s] perm[4] mode[1] */
if dotu {
size += 2 + len(ext)
}
p, err := packCommon(fc, size, Tcreate)
if err != nil {
return err
}
fc.Fid = fid
fc.Name = name
fc.Perm = perm
fc.Mode = mode
p = pint32(fid, p)
p = pstr(name, p)
p = pint32(perm, p)
p = pint8(mode, p)
if dotu {
fc.Ext = ext
p = pstr(ext, p)
}
return nil
}
// Create a Tread message in the specified Fcall.
func PackTread(fc *Fcall, fid uint32, offset uint64, count uint32) error {
size := 4 + 8 + 4 /* fid[4] offset[8] count[4] */
p, err := packCommon(fc, size, Tread)
if err != nil {
return err
}
fc.Fid = fid
fc.Offset = offset
fc.Count = count
p = pint32(fid, p)
p = pint64(offset, p)
p = pint32(count, p)
return nil
}
// Create a Twrite message in the specified Fcall.
func PackTwrite(fc *Fcall, fid uint32, offset uint64, count uint32, data []byte) error {
c := len(data)
size := 4 + 8 + 4 + c /* fid[4] offset[8] count[4] data[count] */
p, err := packCommon(fc, size, Twrite)
if err != nil {
return err
}
fc.Fid = fid
fc.Offset = offset
fc.Count = count
p = pint32(fid, p)
p = pint64(offset, p)
p = pint32(count, p)
fc.Data = p
copy(fc.Data, data)
return nil
}
// Create a Tclunk message in the specified Fcall.
func PackTclunk(fc *Fcall, fid uint32) error {
p, err := packCommon(fc, 4, Tclunk) /* fid[4] */
if err != nil {
return err
}
fc.Fid = fid
p = pint32(fid, p)
return nil
}
// Create a Tremove message in the specified Fcall.
func PackTremove(fc *Fcall, fid uint32) error {
p, err := packCommon(fc, 4, Tremove) /* fid[4] */
if err != nil {
return err
}
fc.Fid = fid
p = pint32(fid, p)
return nil
}
// Create a Tstat message in the specified Fcall.
func PackTstat(fc *Fcall, fid uint32) error {
p, err := packCommon(fc, 4, Tstat) /* fid[4] */
if err != nil {
return err
}
fc.Fid = fid
p = pint32(fid, p)
return nil
}
// Create a Twstat message in the specified Fcall. If dotu is true
// the function will create 9P2000.u message, otherwise the 9P2000.u
// specific fields from the Stat value will be ignored.
func PackTwstat(fc *Fcall, fid uint32, d *Dir, dotu bool) error {
stsz := statsz(d, dotu)
size := 4 + 2 + stsz /* fid[4] stat[n] */
p, err := packCommon(fc, size, Twstat)
if err != nil {
return err
}
fc.Fid = fid
fc.Dir = *d
p = pint32(fid, p)
p = pint16(uint16(stsz), p)
p = pstat(d, p, dotu)
return nil
}

257
third_party/go9p/p/srv/conn.go vendored Normal file
View File

@ -0,0 +1,257 @@
// Copyright 2009 The Go9p 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 srv
import (
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"log"
"net"
)
func (srv *Srv) NewConn(c net.Conn) {
conn := new(Conn)
conn.Srv = srv
conn.Msize = srv.Msize
conn.Dotu = srv.Dotu
conn.Debuglevel = srv.Debuglevel
conn.conn = c
conn.fidpool = make(map[uint32]*Fid)
conn.reqs = make(map[uint16]*Req)
conn.reqout = make(chan *Req, srv.Maxpend)
conn.done = make(chan bool)
conn.rchan = make(chan *p.Fcall, 64)
srv.Lock()
if srv.conns == nil {
srv.conns = make(map[*Conn]*Conn)
}
srv.conns[conn] = conn
srv.Unlock()
conn.Id = c.RemoteAddr().String()
if op, ok := (conn.Srv.ops).(ConnOps); ok {
op.ConnOpened(conn)
}
if sop, ok := (interface{}(conn)).(StatsOps); ok {
sop.statsRegister()
}
go conn.recv()
go conn.send()
}
func (conn *Conn) close() {
conn.done <- true
conn.Srv.Lock()
delete(conn.Srv.conns, conn)
conn.Srv.Unlock()
if sop, ok := (interface{}(conn)).(StatsOps); ok {
sop.statsUnregister()
}
if op, ok := (conn.Srv.ops).(ConnOps); ok {
op.ConnClosed(conn)
}
/* call FidDestroy for all remaining fids */
if op, ok := (conn.Srv.ops).(FidOps); ok {
for _, fid := range conn.fidpool {
op.FidDestroy(fid)
}
}
}
func (conn *Conn) recv() {
var err error
var n int
buf := make([]byte, conn.Msize*8)
pos := 0
for {
if len(buf) < int(conn.Msize) {
b := make([]byte, conn.Msize*8)
copy(b, buf[0:pos])
buf = b
b = nil
}
n, err = conn.conn.Read(buf[pos:])
if err != nil || n == 0 {
conn.close()
return
}
pos += n
for pos > 4 {
sz, _ := p.Gint32(buf)
if sz > conn.Msize {
log.Println("bad client connection: ", conn.conn.RemoteAddr())
conn.conn.Close()
conn.close()
return
}
if pos < int(sz) {
if len(buf) < int(sz) {
b := make([]byte, conn.Msize*8)
copy(b, buf[0:pos])
buf = b
b = nil
}
break
}
fc, err, fcsize := p.Unpack(buf, conn.Dotu)
if err != nil {
log.Println(fmt.Sprintf("invalid packet : %v %v", err, buf))
conn.conn.Close()
conn.close()
return
}
tag := fc.Tag
req := new(Req)
select {
case req.Rc = <-conn.rchan:
break
default:
req.Rc = p.NewFcall(conn.Msize)
}
req.Conn = conn
req.Tc = fc
// req.Rc = rc
if conn.Debuglevel > 0 {
conn.logFcall(req.Tc)
if conn.Debuglevel&DbgPrintPackets != 0 {
log.Println(">->", conn.Id, fmt.Sprint(req.Tc.Pkt))
}
if conn.Debuglevel&DbgPrintFcalls != 0 {
log.Println(">>>", conn.Id, req.Tc.String())
}
}
conn.Lock()
conn.nreqs++
conn.tsz += uint64(fc.Size)
conn.npend++
if conn.npend > conn.maxpend {
conn.maxpend = conn.npend
}
req.next = conn.reqs[tag]
conn.reqs[tag] = req
process := req.next == nil
if req.next != nil {
req.next.prev = req
}
conn.Unlock()
if process {
go req.process()
}
buf = buf[fcsize:]
pos -= fcsize
}
}
panic("unreached")
}
func (conn *Conn) send() {
for {
select {
case <-conn.done:
return
case req := <-conn.reqout:
p.SetTag(req.Rc, req.Tc.Tag)
conn.Lock()
conn.rsz += uint64(req.Rc.Size)
conn.npend--
conn.Unlock()
if conn.Debuglevel > 0 {
conn.logFcall(req.Rc)
if conn.Debuglevel&DbgPrintPackets != 0 {
log.Println("<-<", conn.Id, fmt.Sprint(req.Rc.Pkt))
}
if conn.Debuglevel&DbgPrintFcalls != 0 {
log.Println("<<<", conn.Id, req.Rc.String())
}
}
for buf := req.Rc.Pkt; len(buf) > 0; {
n, err := conn.conn.Write(buf)
if err != nil {
/* just close the socket, will get signal on conn.done */
log.Println("error while writing")
conn.conn.Close()
break
}
buf = buf[n:]
}
select {
case conn.rchan <- req.Rc:
break
default:
}
}
}
panic("unreached")
}
func (conn *Conn) RemoteAddr() net.Addr {
return conn.conn.RemoteAddr()
}
func (conn *Conn) LocalAddr() net.Addr {
return conn.conn.LocalAddr()
}
func (conn *Conn) logFcall(fc *p.Fcall) {
if conn.Debuglevel&DbgLogPackets != 0 {
pkt := make([]byte, len(fc.Pkt))
copy(pkt, fc.Pkt)
conn.Srv.Log.Log(pkt, conn, DbgLogPackets)
}
if conn.Debuglevel&DbgLogFcalls != 0 {
f := new(p.Fcall)
*f = *fc
f.Pkt = nil
conn.Srv.Log.Log(f, conn, DbgLogFcalls)
}
}
func (srv *Srv) StartNetListener(ntype, addr string) error {
l, err := net.Listen(ntype, addr)
if err != nil {
return &p.Error{err.Error(), p.EIO}
}
return srv.StartListener(l)
}
// Start listening on the specified network and address for incoming
// connections. Once a connection is established, create a new Conn
// value, read messages from the socket, send them to the specified
// server, and send back responses received from the server.
func (srv *Srv) StartListener(l net.Listener) error {
for {
c, err := l.Accept()
if err != nil {
return &p.Error{err.Error(), p.EIO}
}
srv.NewConn(c)
}
return nil
}

View File

@ -0,0 +1,147 @@
// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// A synthetic filesystem emulating a persistent cloning interface
// from Plan 9. Reading the /clone file creates new entries in the filesystem
// each containing unique information/data. Clone files remember what it written
// to them. Removing a clone file does what is expected.
package main
import (
"flag"
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
"log"
"os"
"strconv"
"time"
)
type ClFile struct {
srv.File
created string
id int
data []byte
}
type Clone struct {
srv.File
clones int
}
var addr = flag.String("addr", ":5640", "network address")
var debug = flag.Bool("d", false, "print debug messages")
var root *srv.File
func (cl *ClFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
var b []byte
if len(cl.data) == 0 {
str := strconv.Itoa(cl.id) + " created on:" + cl.created
b = []byte(str)
} else {
b = cl.data
}
n := len(b)
if offset >= uint64(n) {
return 0, nil
}
b = b[int(offset):n]
n -= int(offset)
if len(buf) < n {
n = len(buf)
}
copy(buf[offset:int(offset)+n], b[offset:])
return n, nil
}
func (cl *ClFile) Write(fid *srv.FFid, data []byte, offset uint64) (int, error) {
n := uint64(len(cl.data))
nlen := offset + uint64(len(data))
if nlen > n {
ndata := make([]byte, nlen)
copy(ndata, cl.data[0:n])
cl.data = ndata
}
copy(cl.data[offset:], data[offset:])
return len(data), nil
}
func (cl *ClFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
return nil
}
func (cl *ClFile) Remove(fid *srv.FFid) error {
return nil
}
func (cl *Clone) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
// we only allow a single read from us, change the offset and we're done
if offset > uint64(0) {
return 0, nil
}
cl.clones += 1
ncl := new(ClFile)
ncl.id = cl.clones
ncl.created = time.Now().String()
name := strconv.Itoa(ncl.id)
err := ncl.Add(root, name, p.OsUsers.Uid2User(os.Geteuid()), nil, 0666, ncl)
if err != nil {
return 0, &p.Error{"can not create file", 0}
}
b := []byte(name)
if len(buf) < len(b) {
// cleanup
ncl.File.Remove()
return 0, &p.Error{"not enough buffer space for result", 0}
}
copy(buf, b)
return len(b), nil
}
func main() {
var err error
var cl *Clone
var s *srv.Fsrv
flag.Parse()
user := p.OsUsers.Uid2User(os.Geteuid())
root = new(srv.File)
err = root.Add(nil, "/", user, nil, p.DMDIR|0777, nil)
if err != nil {
goto error
}
cl = new(Clone)
err = cl.Add(root, "clone", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, cl)
if err != nil {
goto error
}
s = srv.NewFileSrv(root)
s.Dotu = true
if *debug {
s.Debuglevel = 1
}
s.Start(s)
err = s.StartNetListener("tcp", *addr)
if err != nil {
goto error
}
return
error:
log.Println(fmt.Sprintf("Error: %s", err))
}

View File

@ -0,0 +1,251 @@
// Copyright 2009 The Go9p 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 main
import (
"flag"
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
"log"
"os"
)
type Ramfs struct {
srv *srv.Fsrv
user p.User
group p.Group
blksz int
blkchan chan []byte
zero []byte // blksz array of zeroes
}
type RFile struct {
srv.File
data [][]byte
}
var addr = flag.String("addr", ":5640", "network address")
var debug = flag.Int("d", 0, "debuglevel")
var blksize = flag.Int("b", 8192, "block size")
var logsz = flag.Int("l", 2048, "log size")
var rsrv Ramfs
func (f *RFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
f.Lock()
defer f.Unlock()
if offset > f.Length {
return 0, nil
}
count := uint32(len(buf))
if offset+uint64(count) > f.Length {
count = uint32(f.Length - offset)
}
for n, off, b := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz), buf[0:count]; len(b) > 0; n++ {
m := rsrv.blksz - int(off)
if m > len(b) {
m = len(b)
}
blk := rsrv.zero
if len(f.data[n]) != 0 {
blk = f.data[n]
}
// log.Stderr("read block", n, "off", off, "len", m, "l", len(blk), "ll", len(b))
copy(b, blk[off:off+uint64(m)])
b = b[m:]
off = 0
}
return int(count), nil
}
func (f *RFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
f.Lock()
defer f.Unlock()
// make sure the data array is big enough
sz := offset + uint64(len(buf))
if f.Length < sz {
f.expand(sz)
}
count := 0
for n, off := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz); len(buf) > 0; n++ {
blk := f.data[n]
if len(blk) == 0 {
select {
case blk = <-rsrv.blkchan:
break
default:
blk = make([]byte, rsrv.blksz)
}
// if off>0 {
copy(blk, rsrv.zero /*[0:off]*/)
// }
f.data[n] = blk
}
m := copy(blk[off:], buf)
buf = buf[m:]
count += m
off = 0
}
return count, nil
}
func (f *RFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) {
ff := new(RFile)
err := ff.Add(&f.File, name, rsrv.user, rsrv.group, perm, ff)
return &ff.File, err
}
func (f *RFile) Remove(fid *srv.FFid) error {
f.trunc(0)
return nil
}
func (f *RFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
var uid, gid uint32
f.Lock()
defer f.Unlock()
up := rsrv.srv.Upool
uid = dir.Uidnum
gid = dir.Gidnum
if uid == p.NOUID && dir.Uid != "" {
user := up.Uname2User(dir.Uid)
if user == nil {
return srv.Enouser
}
f.Uidnum = uint32(user.Id())
}
if gid == p.NOUID && dir.Gid != "" {
group := up.Gname2Group(dir.Gid)
if group == nil {
return srv.Enouser
}
f.Gidnum = uint32(group.Id())
}
if dir.Mode != 0xFFFFFFFF {
f.Mode = (f.Mode &^ 0777) | (dir.Mode & 0777)
}
if dir.Name != "" {
if err := f.Rename(dir.Name); err != nil {
return err
}
}
if dir.Length != 0xFFFFFFFFFFFFFFFF {
f.trunc(dir.Length)
}
return nil
}
// called with f locked
func (f *RFile) trunc(sz uint64) {
if f.Length == sz {
return
}
if f.Length > sz {
f.shrink(sz)
} else {
f.expand(sz)
}
}
// called with f lock held
func (f *RFile) shrink(sz uint64) {
blknum := sz / uint64(rsrv.blksz)
off := sz % uint64(rsrv.blksz)
if off > 0 {
if len(f.data[blknum]) > 0 {
copy(f.data[blknum][off:], rsrv.zero)
}
blknum++
}
for i := blknum; i < uint64(len(f.data)); i++ {
if len(f.data[i]) == 0 {
continue
}
select {
case rsrv.blkchan <- f.data[i]:
break
default:
}
}
f.data = f.data[0:blknum]
f.Length = sz
}
func (f *RFile) expand(sz uint64) {
blknum := sz / uint64(rsrv.blksz)
if sz%uint64(rsrv.blksz) != 0 {
blknum++
}
data := make([][]byte, blknum)
if f.data != nil {
copy(data, f.data)
}
f.data = data
f.Length = sz
}
func main() {
var err error
var l *p.Logger
flag.Parse()
rsrv.user = p.OsUsers.Uid2User(os.Geteuid())
rsrv.group = p.OsUsers.Gid2Group(os.Getegid())
rsrv.blksz = *blksize
rsrv.blkchan = make(chan []byte, 2048)
rsrv.zero = make([]byte, rsrv.blksz)
root := new(RFile)
err = root.Add(nil, "/", rsrv.user, nil, p.DMDIR|0777, root)
if err != nil {
goto error
}
l = p.NewLogger(*logsz)
rsrv.srv = srv.NewFileSrv(&root.File)
rsrv.srv.Dotu = true
rsrv.srv.Debuglevel = *debug
rsrv.srv.Start(rsrv.srv)
rsrv.srv.Id = "ramfs"
rsrv.srv.Log = l
err = rsrv.srv.StartNetListener("tcp", *addr)
if err != nil {
goto error
}
return
error:
log.Println(fmt.Sprintf("Error: %s", err))
}

View File

@ -0,0 +1,98 @@
// Copyright 2009 The Go9p 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 main
import (
"flag"
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
"log"
"os"
"time"
)
type Time struct {
srv.File
}
type InfTime struct {
srv.File
}
var addr = flag.String("addr", ":5640", "network address")
var debug = flag.Bool("d", false, "print debug messages")
var debugall = flag.Bool("D", false, "print packets as well as debug messages")
func (*InfTime) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
// push out time ignoring offset (infinite read)
t := time.Now().String() + "\n"
b := []byte(t)
ml := len(b)
if ml > len(buf) {
ml = len(buf)
}
copy(buf, b[0:ml])
return ml, nil
}
func (*Time) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
b := []byte(time.Now().String())
have := len(b)
off := int(offset)
if off >= have {
return 0, nil
}
return copy(buf, b[off:]), nil
}
func main() {
var err error
var tm *Time
var ntm *InfTime
var s *srv.Fsrv
flag.Parse()
user := p.OsUsers.Uid2User(os.Geteuid())
root := new(srv.File)
err = root.Add(nil, "/", user, nil, p.DMDIR|0555, nil)
if err != nil {
goto error
}
tm = new(Time)
err = tm.Add(root, "time", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, tm)
if err != nil {
goto error
}
ntm = new(InfTime)
err = ntm.Add(root, "inftime", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, ntm)
if err != nil {
goto error
}
s = srv.NewFileSrv(root)
s.Dotu = true
if *debug {
s.Debuglevel = 1
}
if *debugall {
s.Debuglevel = 2
}
s.Start(s)
err = s.StartNetListener("tcp", *addr)
if err != nil {
goto error
}
return
error:
log.Println(fmt.Sprintf("Error: %s", err))
}

View File

@ -0,0 +1,297 @@
// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Listen on SSL connection, can be used as an example with p/clnt/examples/tls.go
// Sample certificate was copied from the Go source code
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"encoding/hex"
"flag"
"fmt"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
"log"
"math/big"
"os"
)
type Ramfs struct {
srv *srv.Fsrv
user p.User
group p.Group
blksz int
blkchan chan []byte
zero []byte // blksz array of zeroes
}
type RFile struct {
srv.File
data [][]byte
}
var addr = flag.String("addr", ":5640", "network address")
var debug = flag.Int("d", 0, "debuglevel")
var blksize = flag.Int("b", 8192, "block size")
var logsz = flag.Int("l", 2048, "log size")
var rsrv Ramfs
func (f *RFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
f.Lock()
defer f.Unlock()
if offset > f.Length {
return 0, nil
}
count := uint32(len(buf))
if offset+uint64(count) > f.Length {
count = uint32(f.Length - offset)
}
for n, off, b := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz), buf[0:count]; len(b) > 0; n++ {
m := rsrv.blksz - int(off)
if m > len(b) {
m = len(b)
}
blk := rsrv.zero
if len(f.data[n]) != 0 {
blk = f.data[n]
}
// log.Stderr("read block", n, "off", off, "len", m, "l", len(blk), "ll", len(b))
copy(b, blk[off:off+uint64(m)])
b = b[m:]
off = 0
}
return int(count), nil
}
func (f *RFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
f.Lock()
defer f.Unlock()
// make sure the data array is big enough
sz := offset + uint64(len(buf))
if f.Length < sz {
f.expand(sz)
}
count := 0
for n, off := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz); len(buf) > 0; n++ {
blk := f.data[n]
if len(blk) == 0 {
select {
case blk = <-rsrv.blkchan:
break
default:
blk = make([]byte, rsrv.blksz)
}
// if off>0 {
copy(blk, rsrv.zero /*[0:off]*/)
// }
f.data[n] = blk
}
m := copy(blk[off:], buf)
buf = buf[m:]
count += m
off = 0
}
return count, nil
}
func (f *RFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) {
ff := new(RFile)
err := ff.Add(&f.File, name, rsrv.user, rsrv.group, perm, ff)
return &ff.File, err
}
func (f *RFile) Remove(fid *srv.FFid) error {
f.trunc(0)
return nil
}
func (f *RFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
var uid, gid uint32
f.Lock()
defer f.Unlock()
up := rsrv.srv.Upool
uid = dir.Uidnum
gid = dir.Gidnum
if uid == p.NOUID && dir.Uid != "" {
user := up.Uname2User(dir.Uid)
if user == nil {
return srv.Enouser
}
f.Uidnum = uint32(user.Id())
}
if gid == p.NOUID && dir.Gid != "" {
group := up.Gname2Group(dir.Gid)
if group == nil {
return srv.Enouser
}
f.Gidnum = uint32(group.Id())
}
if dir.Mode != 0xFFFFFFFF {
f.Mode = (f.Mode &^ 0777) | (dir.Mode & 0777)
}
if dir.Name != "" {
if err := f.Rename(dir.Name); err != nil {
return err
}
}
if dir.Length != 0xFFFFFFFFFFFFFFFF {
f.trunc(dir.Length)
}
return nil
}
// called with f locked
func (f *RFile) trunc(sz uint64) {
if f.Length == sz {
return
}
if f.Length > sz {
f.shrink(sz)
} else {
f.expand(sz)
}
}
// called with f lock held
func (f *RFile) shrink(sz uint64) {
blknum := sz / uint64(rsrv.blksz)
off := sz % uint64(rsrv.blksz)
if off > 0 {
if len(f.data[blknum]) > 0 {
copy(f.data[blknum][off:], rsrv.zero)
}
blknum++
}
for i := blknum; i < uint64(len(f.data)); i++ {
if len(f.data[i]) == 0 {
continue
}
select {
case rsrv.blkchan <- f.data[i]:
break
default:
}
}
f.data = f.data[0:blknum]
f.Length = sz
}
func (f *RFile) expand(sz uint64) {
blknum := sz / uint64(rsrv.blksz)
if sz%uint64(rsrv.blksz) != 0 {
blknum++
}
data := make([][]byte, blknum)
if f.data != nil {
copy(data, f.data)
}
f.data = data
f.Length = sz
}
func main() {
var err error
flag.Parse()
rsrv.user = p.OsUsers.Uid2User(os.Geteuid())
rsrv.group = p.OsUsers.Gid2Group(os.Getegid())
rsrv.blksz = *blksize
rsrv.blkchan = make(chan []byte, 2048)
rsrv.zero = make([]byte, rsrv.blksz)
root := new(RFile)
err = root.Add(nil, "/", rsrv.user, nil, p.DMDIR|0777, root)
if err != nil {
log.Println(fmt.Sprintf("Error: %s", err))
return
}
l := p.NewLogger(*logsz)
rsrv.srv = srv.NewFileSrv(&root.File)
rsrv.srv.Dotu = true
rsrv.srv.Debuglevel = *debug
rsrv.srv.Start(rsrv.srv)
rsrv.srv.Id = "ramfs"
rsrv.srv.Log = l
cert := make([]tls.Certificate, 1)
cert[0].Certificate = [][]byte{testCertificate}
cert[0].PrivateKey = testPrivateKey
ls, oerr := tls.Listen("tcp", *addr, &tls.Config{
Rand: rand.Reader,
Certificates: cert,
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA},
InsecureSkipVerify: true,
})
if oerr != nil {
log.Println("can't listen:", oerr)
return
}
err = rsrv.srv.StartListener(ls)
if err != nil {
log.Println(fmt.Sprintf("Error: %s", err))
return
}
return
}
// Copied from crypto/tls/handshake_server_test.go from the Go repository
func bigFromString(s string) *big.Int {
ret := new(big.Int)
ret.SetString(s, 10)
return ret
}
func fromHex(s string) []byte {
b, _ := hex.DecodeString(s)
return b
}
var testCertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
var testPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
E: 65537,
},
D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"),
Primes: []*big.Int{
bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"),
bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"),
},
}

View File

@ -0,0 +1,474 @@
// Copyright 2009 The go9p 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 ufs
import (
"io"
"log"
"os"
"os/user"
"strconv"
"syscall"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
)
type Fid struct {
path string
file *os.File
dirs []os.FileInfo
diroffset uint64
st os.FileInfo
}
type Ufs struct {
srv.Srv
}
var addr string
var debug int
var root string
var Enoent = &p.Error{"file not found", p.ENOENT}
func toError(err error) *p.Error {
var ecode uint32
ename := err.Error()
if e, ok := err.(syscall.Errno); ok {
ecode = uint32(e)
} else {
ecode = p.EIO
}
return &p.Error{ename, ecode}
}
func (fid *Fid) stat() *p.Error {
var err error
fid.st, err = os.Lstat(fid.path)
if err != nil {
return toError(err)
}
return nil
}
func omode2uflags(mode uint8) int {
ret := int(0)
switch mode & 3 {
case p.OREAD:
ret = os.O_RDONLY
break
case p.ORDWR:
ret = os.O_RDWR
break
case p.OWRITE:
ret = os.O_WRONLY
break
case p.OEXEC:
ret = os.O_RDONLY
break
}
if mode&p.OTRUNC != 0 {
ret |= os.O_TRUNC
}
return ret
}
func dir2QidType(d os.FileInfo) uint8 {
ret := uint8(0)
if d.IsDir() {
ret |= p.QTDIR
}
if d.Mode()&os.ModeSymlink != 0 {
ret |= p.QTSYMLINK
}
return ret
}
func dir2Npmode(d os.FileInfo, dotu bool) uint32 {
ret := uint32(d.Mode() & 0777)
if d.IsDir() {
ret |= p.DMDIR
}
if dotu {
mode := d.Mode()
if mode&os.ModeSymlink != 0 {
ret |= p.DMSYMLINK
}
if mode&os.ModeSocket != 0 {
ret |= p.DMSOCKET
}
if mode&os.ModeNamedPipe != 0 {
ret |= p.DMNAMEDPIPE
}
if mode&os.ModeDevice != 0 {
ret |= p.DMDEVICE
}
if mode&os.ModeSetuid != 0 {
ret |= p.DMSETUID
}
if mode&os.ModeSetgid != 0 {
ret |= p.DMSETGID
}
}
return ret
}
// Dir is an instantiation of the p.Dir structure
// that can act as a receiver for local methods.
type Dir struct {
p.Dir
}
func (*Ufs) ConnOpened(conn *srv.Conn) {
if conn.Srv.Debuglevel > 0 {
log.Println("connected")
}
}
func (*Ufs) ConnClosed(conn *srv.Conn) {
if conn.Srv.Debuglevel > 0 {
log.Println("disconnected")
}
}
func (*Ufs) FidDestroy(sfid *srv.Fid) {
var fid *Fid
if sfid.Aux == nil {
return
}
fid = sfid.Aux.(*Fid)
if fid.file != nil {
fid.file.Close()
}
}
func (*Ufs) Attach(req *srv.Req) {
if req.Afid != nil {
req.RespondError(srv.Enoauth)
return
}
tc := req.Tc
fid := new(Fid)
if len(tc.Aname) == 0 {
fid.path = root
} else {
fid.path = tc.Aname
}
req.Fid.Aux = fid
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
qid := dir2Qid(fid.st)
req.RespondRattach(qid)
}
func (*Ufs) Flush(req *srv.Req) {}
func (*Ufs) Walk(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
tc := req.Tc
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
if req.Newfid.Aux == nil {
req.Newfid.Aux = new(Fid)
}
nfid := req.Newfid.Aux.(*Fid)
wqids := make([]p.Qid, len(tc.Wname))
path := fid.path
i := 0
for ; i < len(tc.Wname); i++ {
p := path + "/" + tc.Wname[i]
st, err := os.Lstat(p)
if err != nil {
if i == 0 {
req.RespondError(Enoent)
return
}
break
}
wqids[i] = *dir2Qid(st)
path = p
}
nfid.path = path
req.RespondRwalk(wqids[0:i])
}
func (*Ufs) Open(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
tc := req.Tc
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
var e error
fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0)
if e != nil {
req.RespondError(toError(e))
return
}
req.RespondRopen(dir2Qid(fid.st), 0)
}
func (*Ufs) Create(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
tc := req.Tc
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
path := fid.path + "/" + tc.Name
var e error = nil
var file *os.File = nil
switch {
case tc.Perm&p.DMDIR != 0:
e = os.Mkdir(path, os.FileMode(tc.Perm&0777))
case tc.Perm&p.DMSYMLINK != 0:
e = os.Symlink(tc.Ext, path)
case tc.Perm&p.DMLINK != 0:
n, e := strconv.ParseUint(tc.Ext, 10, 0)
if e != nil {
break
}
ofid := req.Conn.FidGet(uint32(n))
if ofid == nil {
req.RespondError(srv.Eunknownfid)
return
}
e = os.Link(ofid.Aux.(*Fid).path, path)
ofid.DecRef()
case tc.Perm&p.DMNAMEDPIPE != 0:
case tc.Perm&p.DMDEVICE != 0:
req.RespondError(&p.Error{"not implemented", p.EIO})
return
default:
var mode uint32 = tc.Perm & 0777
if req.Conn.Dotu {
if tc.Perm&p.DMSETUID > 0 {
mode |= syscall.S_ISUID
}
if tc.Perm&p.DMSETGID > 0 {
mode |= syscall.S_ISGID
}
}
file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode))
}
if file == nil && e == nil {
file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0)
}
if e != nil {
req.RespondError(toError(e))
return
}
fid.path = path
fid.file = file
err = fid.stat()
if err != nil {
req.RespondError(err)
return
}
req.RespondRcreate(dir2Qid(fid.st), 0)
}
func (*Ufs) Read(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
tc := req.Tc
rc := req.Rc
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
p.InitRread(rc, tc.Count)
var count int
var e error
if fid.st.IsDir() {
b := rc.Data
if tc.Offset == 0 {
fid.file.Close()
fid.file, e = os.OpenFile(fid.path, omode2uflags(req.Fid.Omode), 0)
if e != nil {
req.RespondError(toError(e))
return
}
}
for len(b) > 0 {
if fid.dirs == nil {
fid.dirs, e = fid.file.Readdir(16)
if e != nil && e != io.EOF {
req.RespondError(toError(e))
return
}
if len(fid.dirs) == 0 {
break
}
}
var i int
for i = 0; i < len(fid.dirs); i++ {
path := fid.path + "/" + fid.dirs[i].Name()
st := dir2Dir(path, fid.dirs[i], req.Conn.Dotu, req.Conn.Srv.Upool)
sz := p.PackDir(st, b, req.Conn.Dotu)
if sz == 0 {
break
}
b = b[sz:]
count += sz
}
if i < len(fid.dirs) {
fid.dirs = fid.dirs[i:]
break
} else {
fid.dirs = nil
}
}
} else {
count, e = fid.file.ReadAt(rc.Data, int64(tc.Offset))
if e != nil && e != io.EOF {
req.RespondError(toError(e))
return
}
}
p.SetRreadCount(rc, uint32(count))
req.Respond()
}
func (*Ufs) Write(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
tc := req.Tc
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
n, e := fid.file.WriteAt(tc.Data, int64(tc.Offset))
if e != nil {
req.RespondError(toError(e))
return
}
req.RespondRwrite(uint32(n))
}
func (*Ufs) Clunk(req *srv.Req) { req.RespondRclunk() }
func (*Ufs) Remove(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
e := os.Remove(fid.path)
if e != nil {
req.RespondError(toError(e))
return
}
req.RespondRremove()
}
func (*Ufs) Stat(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
st := dir2Dir(fid.path, fid.st, req.Conn.Dotu, req.Conn.Srv.Upool)
req.RespondRstat(st)
}
func lookup(uid string, group bool) (uint32, *p.Error) {
if uid == "" {
return p.NOUID, nil
}
usr, e := user.Lookup(uid)
if e != nil {
return p.NOUID, toError(e)
}
conv := usr.Uid
if group {
conv = usr.Gid
}
u, e := strconv.Atoi(conv)
if e != nil {
return p.NOUID, toError(e)
}
return uint32(u), nil
}
func StartServer(addrVal string, debugVal int, rootVal string) {
addr = addrVal
debug = debugVal
root = rootVal
ufs := new(Ufs)
ufs.Dotu = true
ufs.Id = "ufs"
ufs.Debuglevel = debug
ufs.Start(ufs)
err := ufs.StartNetListener("tcp", addr)
if err != nil {
log.Println(err)
}
}

View File

@ -0,0 +1,213 @@
// Copyright 2012 The go9p 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 ufs
import (
"fmt"
"os"
"os/user"
"strconv"
"strings"
"syscall"
"time"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
)
func atime(stat *syscall.Stat_t) time.Time {
return time.Unix(stat.Atimespec.Unix())
}
// IsBlock reports if the file is a block device
func isBlock(d os.FileInfo) bool {
stat := d.Sys().(*syscall.Stat_t)
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
}
// IsChar reports if the file is a character device
func isChar(d os.FileInfo) bool {
stat := d.Sys().(*syscall.Stat_t)
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
}
func dir2Qid(d os.FileInfo) *p.Qid {
var qid p.Qid
qid.Path = d.Sys().(*syscall.Stat_t).Ino
qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
qid.Type = dir2QidType(d)
return &qid
}
func dir2Dir(path string, d os.FileInfo, dotu bool, upool p.Users) *p.Dir {
sysMode := d.Sys().(*syscall.Stat_t)
dir := new(Dir)
dir.Qid = *dir2Qid(d)
dir.Mode = dir2Npmode(d, dotu)
dir.Atime = uint32(atime(sysMode).Unix())
dir.Mtime = uint32(d.ModTime().Unix())
dir.Length = uint64(d.Size())
dir.Name = path[strings.LastIndex(path, "/")+1:]
if dotu {
dir.dotu(path, d, upool, sysMode)
return &dir.Dir
}
unixUid := int(sysMode.Uid)
unixGid := int(sysMode.Gid)
dir.Uid = strconv.Itoa(unixUid)
dir.Gid = strconv.Itoa(unixGid)
dir.Muid = "none"
// BUG(akumar): LookupId will never find names for
// groups, as it only operates on user ids.
u, err := user.LookupId(dir.Uid)
if err == nil {
dir.Uid = u.Username
}
g, err := user.LookupId(dir.Gid)
if err == nil {
dir.Gid = g.Username
}
return &dir.Dir
}
func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users, sysMode *syscall.Stat_t) {
u := upool.Uid2User(int(sysMode.Uid))
g := upool.Gid2Group(int(sysMode.Gid))
dir.Uid = u.Name()
if dir.Uid == "" {
dir.Uid = "none"
}
dir.Gid = g.Name()
if dir.Gid == "" {
dir.Gid = "none"
}
dir.Muid = "none"
dir.Ext = ""
dir.Uidnum = uint32(u.Id())
dir.Gidnum = uint32(g.Id())
dir.Muidnum = p.NOUID
if d.Mode()&os.ModeSymlink != 0 {
var err error
dir.Ext, err = os.Readlink(path)
if err != nil {
dir.Ext = ""
}
} else if isBlock(d) {
dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
} else if isChar(d) {
dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
}
}
func (*Ufs) Wstat(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
dir := &req.Tc.Dir
if dir.Mode != 0xFFFFFFFF {
mode := dir.Mode & 0777
if req.Conn.Dotu {
if dir.Mode&p.DMSETUID > 0 {
mode |= syscall.S_ISUID
}
if dir.Mode&p.DMSETGID > 0 {
mode |= syscall.S_ISGID
}
}
e := os.Chmod(fid.path, os.FileMode(mode))
if e != nil {
req.RespondError(toError(e))
return
}
}
uid, gid := p.NOUID, p.NOUID
if req.Conn.Dotu {
uid = dir.Uidnum
gid = dir.Gidnum
}
// Try to find local uid, gid by name.
if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
uid, err = lookup(dir.Uid, false)
if err != nil {
req.RespondError(err)
return
}
// BUG(akumar): Lookup will never find gids
// corresponding to group names, because
// it only operates on user names.
gid, err = lookup(dir.Gid, true)
if err != nil {
req.RespondError(err)
return
}
}
if uid != p.NOUID || gid != p.NOUID {
e := os.Chown(fid.path, int(uid), int(gid))
if e != nil {
req.RespondError(toError(e))
return
}
}
if dir.Name != "" {
path := fid.path[0:strings.LastIndex(fid.path, "/")+1] + "/" + dir.Name
err := syscall.Rename(fid.path, path)
if err != nil {
req.RespondError(toError(err))
return
}
fid.path = path
}
if dir.Length != 0xFFFFFFFFFFFFFFFF {
e := os.Truncate(fid.path, int64(dir.Length))
if e != nil {
req.RespondError(toError(e))
return
}
}
// If either mtime or atime need to be changed, then
// we must change both.
if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
st, e := os.Stat(fid.path)
if e != nil {
req.RespondError(toError(e))
return
}
switch cmt {
case true:
mt = st.ModTime()
default:
at = atime(st.Sys().(*syscall.Stat_t))
}
}
e := os.Chtimes(fid.path, at, mt)
if e != nil {
req.RespondError(toError(e))
return
}
}
req.RespondRwstat()
}

View File

@ -0,0 +1,213 @@
// Copyright 2012 The go9p 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 ufs
import (
"fmt"
"os"
"os/user"
"strconv"
"strings"
"syscall"
"time"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
)
func atime(stat *syscall.Stat_t) time.Time {
return time.Unix(stat.Atim.Unix())
}
// IsBlock reports if the file is a block device
func isBlock(d os.FileInfo) bool {
stat := d.Sys().(*syscall.Stat_t)
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
}
// IsChar reports if the file is a character device
func isChar(d os.FileInfo) bool {
stat := d.Sys().(*syscall.Stat_t)
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
}
func dir2Qid(d os.FileInfo) *p.Qid {
var qid p.Qid
qid.Path = d.Sys().(*syscall.Stat_t).Ino
qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
qid.Type = dir2QidType(d)
return &qid
}
func dir2Dir(path string, d os.FileInfo, dotu bool, upool p.Users) *p.Dir {
sysMode := d.Sys().(*syscall.Stat_t)
dir := new(Dir)
dir.Qid = *dir2Qid(d)
dir.Mode = dir2Npmode(d, dotu)
dir.Atime = uint32(atime(sysMode).Unix())
dir.Mtime = uint32(d.ModTime().Unix())
dir.Length = uint64(d.Size())
dir.Name = path[strings.LastIndex(path, "/")+1:]
if dotu {
dir.dotu(path, d, upool, sysMode)
return &dir.Dir
}
unixUid := int(sysMode.Uid)
unixGid := int(sysMode.Gid)
dir.Uid = strconv.Itoa(unixUid)
dir.Gid = strconv.Itoa(unixGid)
dir.Muid = "none"
// BUG(akumar): LookupId will never find names for
// groups, as it only operates on user ids.
u, err := user.LookupId(dir.Uid)
if err == nil {
dir.Uid = u.Username
}
g, err := user.LookupId(dir.Gid)
if err == nil {
dir.Gid = g.Username
}
return &dir.Dir
}
func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users, sysMode *syscall.Stat_t) {
u := upool.Uid2User(int(sysMode.Uid))
g := upool.Gid2Group(int(sysMode.Gid))
dir.Uid = u.Name()
if dir.Uid == "" {
dir.Uid = "none"
}
dir.Gid = g.Name()
if dir.Gid == "" {
dir.Gid = "none"
}
dir.Muid = "none"
dir.Ext = ""
dir.Uidnum = uint32(u.Id())
dir.Gidnum = uint32(g.Id())
dir.Muidnum = p.NOUID
if d.Mode()&os.ModeSymlink != 0 {
var err error
dir.Ext, err = os.Readlink(path)
if err != nil {
dir.Ext = ""
}
} else if isBlock(d) {
dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
} else if isChar(d) {
dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
}
}
func (*Ufs) Wstat(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
dir := &req.Tc.Dir
if dir.Mode != 0xFFFFFFFF {
mode := dir.Mode & 0777
if req.Conn.Dotu {
if dir.Mode&p.DMSETUID > 0 {
mode |= syscall.S_ISUID
}
if dir.Mode&p.DMSETGID > 0 {
mode |= syscall.S_ISGID
}
}
e := os.Chmod(fid.path, os.FileMode(mode))
if e != nil {
req.RespondError(toError(e))
return
}
}
uid, gid := p.NOUID, p.NOUID
if req.Conn.Dotu {
uid = dir.Uidnum
gid = dir.Gidnum
}
// Try to find local uid, gid by name.
if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
uid, err = lookup(dir.Uid, false)
if err != nil {
req.RespondError(err)
return
}
// BUG(akumar): Lookup will never find gids
// corresponding to group names, because
// it only operates on user names.
gid, err = lookup(dir.Gid, true)
if err != nil {
req.RespondError(err)
return
}
}
if uid != p.NOUID || gid != p.NOUID {
e := os.Chown(fid.path, int(uid), int(gid))
if e != nil {
req.RespondError(toError(e))
return
}
}
if dir.Name != "" {
path := fid.path[0:strings.LastIndex(fid.path, "/")+1] + "/" + dir.Name
err := syscall.Rename(fid.path, path)
if err != nil {
req.RespondError(toError(err))
return
}
fid.path = path
}
if dir.Length != 0xFFFFFFFFFFFFFFFF {
e := os.Truncate(fid.path, int64(dir.Length))
if e != nil {
req.RespondError(toError(e))
return
}
}
// If either mtime or atime need to be changed, then
// we must change both.
if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
st, e := os.Stat(fid.path)
if e != nil {
req.RespondError(toError(e))
return
}
switch cmt {
case true:
mt = st.ModTime()
default:
at = atime(st.Sys().(*syscall.Stat_t))
}
}
e := os.Chtimes(fid.path, at, mt)
if e != nil {
req.RespondError(toError(e))
return
}
}
req.RespondRwstat()
}

View File

@ -0,0 +1,245 @@
// 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.
// http://golang.org/src/os/stat_windows.go
package ufs
import (
"os"
"strings"
"syscall"
"time"
"k8s.io/minikube/third_party/go9p/p"
"k8s.io/minikube/third_party/go9p/p/srv"
)
// type Win32FileAttributeData struct {
// FileAttributes uint32
// CreationTime Filetime
// LastAccessTime Filetime
// LastWriteTime Filetime
// FileSizeHigh uint32
// FileSizeLow uint32
// }
// A FileInfo describes a file and is returned by Stat and Lstat.
// type FileInfo interface {
// Name() string // base name of the file
// Size() int64 // length in bytes for regular files; system-dependent for others
// Mode() FileMode // file mode bits
// ModTime() time.Time // modification time
// IsDir() bool // abbreviation for Mode().IsDir()
// Sys() interface{} // underlying data source (can return nil)
// }
func atime(fi os.FileInfo) time.Time {
return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
}
// IsBlock reports if the file is a block device
func isBlock(d os.FileInfo) bool {
// return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
// stat := d.Sys().(os.FileInfo)
// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
return true
}
// IsChar reports if the file is a character device
func isChar(d os.FileInfo) bool {
// stat := d.Sys().(os.FileInfo)
// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
return true
}
func dir2Qid(d os.FileInfo) *p.Qid {
var qid p.Qid
// d.Sys().(*syscall.Win32FileAttributeData)
qid.Path = uint64(d.Sys().(*syscall.Win32FileAttributeData).FileSizeLow)
qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
qid.Type = dir2QidType(d)
return &qid
}
func dir2Dir(path string, d os.FileInfo, dotu bool, upool p.Users) *p.Dir {
// sysMode := d.Sys().(os.FileInfo)
dir := new(Dir)
dir.Qid = *dir2Qid(d)
dir.Mode = dir2Npmode(d, dotu)
// dir.Atime = uint32(atime(sysMode).Unix())
// dir.Mtime = uint32(d.ModTime().Unix())
dir.Length = uint64(d.Size())
dir.Name = path[strings.LastIndex(path, "/")+1:]
if dotu {
// dir.dotu(path, d, upool, sysMode)
return &dir.Dir
}
// unixUid := int(sysMode.Uid)
// unixGid := int(sysMode.Gid)
// dir.Uid = strconv.Itoa(unixUid)
// dir.Gid = strconv.Itoa(unixGid)
dir.Uid = "none"
dir.Gid = "none"
dir.Muid = "none"
// BUG(akumar): LookupId will never find names for
// groups, as it only operates on user ids.
// u, err := user.LookupId(dir.Uid)
// if err == nil {
// dir.Uid = u.Username
// }
// g, err := user.LookupId(dir.Gid)
// if err == nil {
// dir.Gid = g.Username
// }
return &dir.Dir
}
func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users) {
// u := upool.Uid2User(int(sysMode.Uid))
// g := upool.Gid2Group(int(sysMode.Gid))
// dir.Uid = u.Name()
// if dir.Uid == "" {
// dir.Uid = "none"
// }
// dir.Gid = g.Name()
// if dir.Gid == "" {
// dir.Gid = "none"
// }
// dir.Muid = "none"
// dir.Ext = ""
// dir.Uidnum = uint32(u.Id())
// dir.Gidnum = uint32(g.Id())
// dir.Muidnum = p.NOUID
// if d.Mode()&os.ModeSymlink != 0 {
// var err error
// dir.Ext, err = os.Readlink(path)
// if err != nil {
// dir.Ext = ""
// }
// } else if isBlock(d) {
// dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
// } else if isChar(d) {
// dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
// }
dir.Uid = "none"
dir.Gid = "none"
dir.Muid = "none"
dir.Uidnum = 0
dir.Gidnum = 0
dir.Muidnum = p.NOUID
dir.Ext = ""
}
func (*Ufs) Wstat(req *srv.Req) {
fid := req.Fid.Aux.(*Fid)
err := fid.stat()
if err != nil {
req.RespondError(err)
return
}
dir := &req.Tc.Dir
if dir.Mode != 0xFFFFFFFF {
mode := dir.Mode & 0777
if req.Conn.Dotu {
if dir.Mode&p.DMSETUID > 0 {
mode |= syscall.S_ISUID
}
if dir.Mode&p.DMSETGID > 0 {
mode |= syscall.S_ISGID
}
}
e := os.Chmod(fid.path, os.FileMode(mode))
if e != nil {
req.RespondError(toError(e))
return
}
}
uid, gid := p.NOUID, p.NOUID
if req.Conn.Dotu {
uid = dir.Uidnum
gid = dir.Gidnum
}
// Try to find local uid, gid by name.
if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
uid, err = lookup(dir.Uid, false)
if err != nil {
req.RespondError(err)
return
}
// BUG(akumar): Lookup will never find gids
// corresponding to group names, because
// it only operates on user names.
gid, err = lookup(dir.Gid, true)
if err != nil {
req.RespondError(err)
return
}
}
if uid != p.NOUID || gid != p.NOUID {
e := os.Chown(fid.path, int(uid), int(gid))
if e != nil {
req.RespondError(toError(e))
return
}
}
if dir.Name != "" {
path := fid.path[0:strings.LastIndex(fid.path, "/")+1] + "/" + dir.Name
err := syscall.Rename(fid.path, path)
if err != nil {
req.RespondError(toError(err))
return
}
fid.path = path
}
if dir.Length != 0xFFFFFFFFFFFFFFFF {
e := os.Truncate(fid.path, int64(dir.Length))
if e != nil {
req.RespondError(toError(e))
return
}
}
// If either mtime or atime need to be changed, then
// we must change both.
// if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
// mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
// if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
// st, e := os.Stat(fid.path)
// if e != nil {
// req.RespondError(toError(e))
// return
// }
// switch cmt {
// case true:
// mt = st.ModTime()
// default:
// at = atime(st.Sys().(os.FileInfo))
// }
// }
// e := os.Chtimes(fid.path, at, mt)
// if e != nil {
// req.RespondError(toError(e))
// return
// }
// }
req.RespondRwstat()
}

439
third_party/go9p/p/srv/fcall.go vendored Normal file
View File

@ -0,0 +1,439 @@
// Copyright 2009 The Go9p 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 srv
import (
"runtime"
"k8s.io/minikube/third_party/go9p/p"
)
func (srv *Srv) version(req *Req) {
tc := req.Tc
conn := req.Conn
if tc.Msize < p.IOHDRSZ {
req.RespondError(&p.Error{"msize too small", p.EINVAL})
return
}
if tc.Msize < conn.Msize {
conn.Msize = tc.Msize
}
conn.Dotu = tc.Version == "9P2000.u" && srv.Dotu
ver := "9P2000"
if conn.Dotu {
ver = "9P2000.u"
}
/* make sure that the responses of all current requests will be ignored */
conn.Lock()
for tag, r := range conn.reqs {
if tag == p.NOTAG {
continue
}
for rr := r; rr != nil; rr = rr.next {
rr.Lock()
rr.status |= reqFlush
rr.Unlock()
}
}
conn.Unlock()
req.RespondRversion(conn.Msize, ver)
}
func (srv *Srv) auth(req *Req) {
tc := req.Tc
conn := req.Conn
if tc.Afid == p.NOFID {
req.RespondError(Eunknownfid)
return
}
req.Afid = conn.FidNew(tc.Afid)
if req.Afid == nil {
req.RespondError(Einuse)
return
}
var user p.User = nil
if tc.Unamenum != p.NOUID || conn.Dotu {
user = srv.Upool.Uid2User(int(tc.Unamenum))
} else if tc.Uname != "" {
user = srv.Upool.Uname2User(tc.Uname)
}
if runtime.GOOS != "windows" {
if user == nil {
req.RespondError(Enouser)
return
}
}
req.Afid.User = user
req.Afid.Type = p.QTAUTH
if aop, ok := (srv.ops).(AuthOps); ok {
aqid, err := aop.AuthInit(req.Afid, tc.Aname)
if err != nil {
req.RespondError(err)
} else {
aqid.Type |= p.QTAUTH // just in case
req.RespondRauth(aqid)
}
} else {
req.RespondError(Enoauth)
}
}
func (srv *Srv) authPost(req *Req) {
if req.Rc != nil && req.Rc.Type == p.Rauth {
req.Afid.IncRef()
}
}
func (srv *Srv) attach(req *Req) {
tc := req.Tc
conn := req.Conn
if tc.Fid == p.NOFID {
req.RespondError(Eunknownfid)
return
}
req.Fid = conn.FidNew(tc.Fid)
if req.Fid == nil {
req.RespondError(Einuse)
return
}
if tc.Afid != p.NOFID {
req.Afid = conn.FidGet(tc.Afid)
if req.Afid == nil {
req.RespondError(Eunknownfid)
}
}
var user p.User = nil
if tc.Unamenum != p.NOUID || conn.Dotu {
user = srv.Upool.Uid2User(int(tc.Unamenum))
} else if tc.Uname != "" {
user = srv.Upool.Uname2User(tc.Uname)
}
if runtime.GOOS != "windows" {
if user == nil {
req.RespondError(Enouser)
return
}
}
req.Fid.User = user
if aop, ok := (srv.ops).(AuthOps); ok {
err := aop.AuthCheck(req.Fid, req.Afid, tc.Aname)
if err != nil {
req.RespondError(err)
return
}
}
(srv.ops).(ReqOps).Attach(req)
}
func (srv *Srv) attachPost(req *Req) {
if req.Rc != nil && req.Rc.Type == p.Rattach {
req.Fid.Type = req.Rc.Qid.Type
req.Fid.IncRef()
}
}
func (srv *Srv) flush(req *Req) {
conn := req.Conn
tag := req.Tc.Oldtag
p.PackRflush(req.Rc)
conn.Lock()
r := conn.reqs[tag]
if r != nil {
req.flushreq = r.flushreq
r.flushreq = req
}
conn.Unlock()
if r == nil {
// there are no requests with that tag
req.Respond()
return
}
r.Lock()
status := r.status
if (status & (reqWork | reqSaved)) == 0 {
/* the request is not worked on yet */
r.status |= reqFlush
}
r.Unlock()
if (status & (reqWork | reqSaved)) == 0 {
r.Respond()
} else {
if op, ok := (srv.ops).(FlushOp); ok {
op.Flush(r)
}
}
}
func (srv *Srv) walk(req *Req) {
conn := req.Conn
tc := req.Tc
fid := req.Fid
/* we can't walk regular files, only clone them */
if len(tc.Wname) > 0 && (fid.Type&p.QTDIR) == 0 {
req.RespondError(Enotdir)
return
}
/* we can't walk open files */
if fid.opened {
req.RespondError(Ebaduse)
return
}
if tc.Fid != tc.Newfid {
req.Newfid = conn.FidNew(tc.Newfid)
if req.Newfid == nil {
req.RespondError(Einuse)
return
}
req.Newfid.User = fid.User
req.Newfid.Type = fid.Type
} else {
req.Newfid = req.Fid
req.Newfid.IncRef()
}
(req.Conn.Srv.ops).(ReqOps).Walk(req)
}
func (srv *Srv) walkPost(req *Req) {
rc := req.Rc
if rc == nil || rc.Type != p.Rwalk || req.Newfid == nil {
return
}
n := len(rc.Wqid)
if n > 0 {
req.Newfid.Type = rc.Wqid[n-1].Type
} else {
req.Newfid.Type = req.Fid.Type
}
// Don't retain the fid if only a partial walk succeeded
if n != len(req.Tc.Wname) {
return
}
if req.Newfid.fid != req.Fid.fid {
req.Newfid.IncRef()
}
}
func (srv *Srv) open(req *Req) {
fid := req.Fid
tc := req.Tc
if fid.opened {
req.RespondError(Eopen)
return
}
if (fid.Type&p.QTDIR) != 0 && tc.Mode != p.OREAD {
req.RespondError(Eperm)
return
}
fid.Omode = tc.Mode
(req.Conn.Srv.ops).(ReqOps).Open(req)
}
func (srv *Srv) openPost(req *Req) {
if req.Fid != nil {
req.Fid.opened = req.Rc != nil && req.Rc.Type == p.Ropen
}
}
func (srv *Srv) create(req *Req) {
fid := req.Fid
tc := req.Tc
if fid.opened {
req.RespondError(Eopen)
return
}
if (fid.Type & p.QTDIR) == 0 {
req.RespondError(Enotdir)
return
}
/* can't open directories for other than reading */
if (tc.Perm&p.DMDIR) != 0 && tc.Mode != p.OREAD {
req.RespondError(Eperm)
return
}
/* can't create special files if not 9P2000.u */
if (tc.Perm&(p.DMNAMEDPIPE|p.DMSYMLINK|p.DMLINK|p.DMDEVICE|p.DMSOCKET)) != 0 && !req.Conn.Dotu {
req.RespondError(Eperm)
return
}
fid.Omode = tc.Mode
(req.Conn.Srv.ops).(ReqOps).Create(req)
}
func (srv *Srv) createPost(req *Req) {
if req.Rc != nil && req.Rc.Type == p.Rcreate && req.Fid != nil {
req.Fid.Type = req.Rc.Qid.Type
req.Fid.opened = true
}
}
func (srv *Srv) read(req *Req) {
tc := req.Tc
fid := req.Fid
if tc.Count+p.IOHDRSZ > req.Conn.Msize {
req.RespondError(Etoolarge)
return
}
if (fid.Type & p.QTAUTH) != 0 {
var n int
rc := req.Rc
err := p.InitRread(rc, tc.Count)
if err != nil {
req.RespondError(err)
return
}
if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
n, err = op.AuthRead(fid, tc.Offset, rc.Data)
if err != nil {
req.RespondError(err)
return
}
p.SetRreadCount(rc, uint32(n))
req.Respond()
} else {
req.RespondError(Enotimpl)
}
return
}
if (fid.Type & p.QTDIR) != 0 {
if tc.Offset == 0 {
fid.Diroffset = 0
} else if tc.Offset != fid.Diroffset {
req.RespondError(Ebadoffset)
return
}
}
(req.Conn.Srv.ops).(ReqOps).Read(req)
}
func (srv *Srv) readPost(req *Req) {
if req.Rc != nil && req.Rc.Type == p.Rread && (req.Fid.Type&p.QTDIR) != 0 {
req.Fid.Diroffset += uint64(req.Rc.Count)
}
}
func (srv *Srv) write(req *Req) {
fid := req.Fid
tc := req.Tc
if (fid.Type & p.QTAUTH) != 0 {
tc := req.Tc
if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
n, err := op.AuthWrite(req.Fid, tc.Offset, tc.Data)
if err != nil {
req.RespondError(err)
} else {
req.RespondRwrite(uint32(n))
}
} else {
req.RespondError(Enotimpl)
}
return
}
if !fid.opened || (fid.Type&p.QTDIR) != 0 || (fid.Omode&3) == p.OREAD {
req.RespondError(Ebaduse)
return
}
if tc.Count+p.IOHDRSZ > req.Conn.Msize {
req.RespondError(Etoolarge)
return
}
(req.Conn.Srv.ops).(ReqOps).Write(req)
}
func (srv *Srv) clunk(req *Req) {
fid := req.Fid
if (fid.Type & p.QTAUTH) != 0 {
if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
op.AuthDestroy(fid)
req.RespondRclunk()
} else {
req.RespondError(Enotimpl)
}
return
}
(req.Conn.Srv.ops).(ReqOps).Clunk(req)
}
func (srv *Srv) clunkPost(req *Req) {
if req.Rc != nil && req.Rc.Type == p.Rclunk && req.Fid != nil {
req.Fid.DecRef()
}
}
func (srv *Srv) remove(req *Req) { (req.Conn.Srv.ops).(ReqOps).Remove(req) }
func (srv *Srv) removePost(req *Req) {
if req.Rc != nil && req.Fid != nil {
req.Fid.DecRef()
}
}
func (srv *Srv) stat(req *Req) { (req.Conn.Srv.ops).(ReqOps).Stat(req) }
func (srv *Srv) wstat(req *Req) {
/*
fid := req.Fid
d := &req.Tc.Dir
if d.Type != uint16(0xFFFF) || d.Dev != uint32(0xFFFFFFFF) || d.Version != uint32(0xFFFFFFFF) ||
d.Path != uint64(0xFFFFFFFFFFFFFFFF) {
req.RespondError(Eperm)
return
}
if (d.Mode != 0xFFFFFFFF) && (((fid.Type&p.QTDIR) != 0 && (d.Mode&p.DMDIR) == 0) ||
((d.Type&p.QTDIR) == 0 && (d.Mode&p.DMDIR) != 0)) {
req.RespondError(Edirchange)
return
}
*/
(req.Conn.Srv.ops).(ReqOps).Wstat(req)
}

568
third_party/go9p/p/srv/file.go vendored Normal file
View File

@ -0,0 +1,568 @@
// Copyright 2009 The Go9p 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 srv
import (
"k8s.io/minikube/third_party/go9p/p"
"log"
"sync"
"time"
)
// The FStatOp interface provides a single operation (Stat) that will be
// called before a file stat is sent back to the client. If implemented,
// the operation should update the data in the File struct.
type FStatOp interface {
Stat(fid *FFid) error
}
// The FWstatOp interface provides a single operation (Wstat) that will be
// called when the client requests the File metadata to be modified. If
// implemented, the operation will be called when Twstat message is received.
// If not implemented, "permission denied" error will be sent back. If the
// operation returns an Error, the error is send back to the client.
type FWstatOp interface {
Wstat(*FFid, *p.Dir) error
}
// If the FReadOp interface is implemented, the Read operation will be called
// to read from the file. If not implemented, "permission denied" error will
// be send back. The operation returns the number of bytes read, or the
// error occured while reading.
type FReadOp interface {
Read(fid *FFid, buf []byte, offset uint64) (int, error)
}
// If the FWriteOp interface is implemented, the Write operation will be called
// to write to the file. If not implemented, "permission denied" error will
// be send back. The operation returns the number of bytes written, or the
// error occured while writing.
type FWriteOp interface {
Write(fid *FFid, data []byte, offset uint64) (int, error)
}
// If the FCreateOp interface is implemented, the Create operation will be called
// when the client attempts to create a file in the File implementing the interface.
// If not implemented, "permission denied" error will be send back. If successful,
// the operation should call (*File)Add() to add the created file to the directory.
// The operation returns the created file, or the error occured while creating it.
type FCreateOp interface {
Create(fid *FFid, name string, perm uint32) (*File, error)
}
// If the FRemoveOp interface is implemented, the Remove operation will be called
// when the client attempts to create a file in the File implementing the interface.
// If not implemented, "permission denied" error will be send back.
// The operation returns nil if successful, or the error that occured while removing
// the file.
type FRemoveOp interface {
Remove(*FFid) error
}
type FOpenOp interface {
Open(fid *FFid, mode uint8) error
}
type FClunkOp interface {
Clunk(fid *FFid) error
}
type FDestroyOp interface {
FidDestroy(fid *FFid)
}
type FFlags int
const (
Fremoved FFlags = 1 << iota
)
// The File type represents a file (or directory) served by the file server.
type File struct {
sync.Mutex
p.Dir
flags FFlags
Parent *File // parent
next, prev *File // siblings, guarded by parent.Lock
cfirst, clast *File // children (if directory)
Ops interface{}
}
type FFid struct {
F *File
Fid *Fid
dirs []*File // used for readdir
}
// The Fsrv can be used to create file servers that serve
// simple trees of synthetic files.
type Fsrv struct {
Srv
Root *File
}
var lock sync.Mutex
var qnext uint64
var Eexist = &p.Error{"file already exists", p.EEXIST}
var Enoent = &p.Error{"file not found", p.ENOENT}
var Enotempty = &p.Error{"directory not empty", p.EPERM}
// Creates a file server with root as root directory
func NewFileSrv(root *File) *Fsrv {
srv := new(Fsrv)
srv.Root = root
root.Parent = root // make sure we can .. in root
return srv
}
// Initializes the fields of a file and add it to a directory.
// Returns nil if successful, or an error.
func (f *File) Add(dir *File, name string, uid p.User, gid p.Group, mode uint32, ops interface{}) error {
lock.Lock()
qpath := qnext
qnext++
lock.Unlock()
f.Qid.Type = uint8(mode >> 24)
f.Qid.Version = 0
f.Qid.Path = qpath
f.Mode = mode
f.Atime = uint32(time.Now().Unix())
f.Mtime = f.Atime
f.Length = 0
f.Name = name
if uid != nil {
f.Uid = uid.Name()
f.Uidnum = uint32(uid.Id())
} else {
f.Uid = "none"
f.Uidnum = p.NOUID
}
if gid != nil {
f.Gid = gid.Name()
f.Gidnum = uint32(gid.Id())
} else {
f.Gid = "none"
f.Gidnum = p.NOUID
}
f.Muid = ""
f.Muidnum = p.NOUID
f.Ext = ""
if dir != nil {
f.Parent = dir
dir.Lock()
for p := dir.cfirst; p != nil; p = p.next {
if name == p.Name {
dir.Unlock()
return Eexist
}
}
if dir.clast != nil {
dir.clast.next = f
} else {
dir.cfirst = f
}
f.prev = dir.clast
f.next = nil
dir.clast = f
dir.Unlock()
} else {
f.Parent = f
}
f.Ops = ops
return nil
}
// Removes a file from its parent directory.
func (f *File) Remove() {
f.Lock()
if (f.flags & Fremoved) != 0 {
f.Unlock()
return
}
f.flags |= Fremoved
f.Unlock()
p := f.Parent
p.Lock()
if f.next != nil {
f.next.prev = f.prev
} else {
p.clast = f.prev
}
if f.prev != nil {
f.prev.next = f.next
} else {
p.cfirst = f.next
}
f.next = nil
f.prev = nil
p.Unlock()
}
func (f *File) Rename(name string) error {
p := f.Parent
p.Lock()
defer p.Unlock()
for c := p.cfirst; c != nil; c = c.next {
if name == c.Name {
return Eexist
}
}
f.Name = name
return nil
}
// Looks for a file in a directory. Returns nil if the file is not found.
func (p *File) Find(name string) *File {
var f *File
p.Lock()
for f = p.cfirst; f != nil; f = f.next {
if name == f.Name {
break
}
}
p.Unlock()
return f
}
// Checks if the specified user has permission to perform
// certain operation on a file. Perm contains one or more
// of p.DMREAD, p.DMWRITE, and p.DMEXEC.
func (f *File) CheckPerm(user p.User, perm uint32) bool {
if user == nil {
return false
}
perm &= 7
/* other permissions */
fperm := f.Mode & 7
if (fperm & perm) == perm {
return true
}
/* user permissions */
if f.Uid == user.Name() || f.Uidnum == uint32(user.Id()) {
fperm |= (f.Mode >> 6) & 7
}
if (fperm & perm) == perm {
return true
}
/* group permissions */
groups := user.Groups()
if groups != nil && len(groups) > 0 {
for i := 0; i < len(groups); i++ {
if f.Gid == groups[i].Name() || f.Gidnum == uint32(groups[i].Id()) {
fperm |= (f.Mode >> 3) & 7
break
}
}
}
if (fperm & perm) == perm {
return true
}
return false
}
func (s *Fsrv) Attach(req *Req) {
fid := new(FFid)
fid.F = s.Root
fid.Fid = req.Fid
req.Fid.Aux = fid
req.RespondRattach(&s.Root.Qid)
}
func (*Fsrv) Walk(req *Req) {
fid := req.Fid.Aux.(*FFid)
tc := req.Tc
if req.Newfid.Aux == nil {
nfid := new(FFid)
nfid.Fid = req.Newfid
req.Newfid.Aux = nfid
}
nfid := req.Newfid.Aux.(*FFid)
wqids := make([]p.Qid, len(tc.Wname))
i := 0
f := fid.F
for ; i < len(tc.Wname); i++ {
if tc.Wname[i] == ".." {
// handle dotdot
f = f.Parent
wqids[i] = f.Qid
continue
}
if (wqids[i].Type & p.QTDIR) > 0 {
if !f.CheckPerm(req.Fid.User, p.DMEXEC) {
break
}
}
p := f.Find(tc.Wname[i])
if p == nil {
break
}
f = p
wqids[i] = f.Qid
}
if len(tc.Wname) > 0 && i == 0 {
req.RespondError(Enoent)
return
}
nfid.F = f
req.RespondRwalk(wqids[0:i])
}
func mode2Perm(mode uint8) uint32 {
var perm uint32 = 0
switch mode & 3 {
case p.OREAD:
perm = p.DMREAD
case p.OWRITE:
perm = p.DMWRITE
case p.ORDWR:
perm = p.DMREAD | p.DMWRITE
}
if (mode & p.OTRUNC) != 0 {
perm |= p.DMWRITE
}
return perm
}
func (*Fsrv) Open(req *Req) {
fid := req.Fid.Aux.(*FFid)
tc := req.Tc
if !fid.F.CheckPerm(req.Fid.User, mode2Perm(tc.Mode)) {
req.RespondError(Eperm)
return
}
if op, ok := (fid.F.Ops).(FOpenOp); ok {
err := op.Open(fid, tc.Mode)
if err != nil {
req.RespondError(err)
return
}
}
req.RespondRopen(&fid.F.Qid, 0)
}
func (*Fsrv) Create(req *Req) {
fid := req.Fid.Aux.(*FFid)
tc := req.Tc
dir := fid.F
if !dir.CheckPerm(req.Fid.User, p.DMWRITE) {
req.RespondError(Eperm)
return
}
if cop, ok := (dir.Ops).(FCreateOp); ok {
f, err := cop.Create(fid, tc.Name, tc.Perm)
if err != nil {
req.RespondError(err)
} else {
fid.F = f
req.RespondRcreate(&fid.F.Qid, 0)
}
} else {
req.RespondError(Eperm)
}
}
func (*Fsrv) Read(req *Req) {
var i, n int
var err error
fid := req.Fid.Aux.(*FFid)
f := fid.F
tc := req.Tc
rc := req.Rc
p.InitRread(rc, tc.Count)
if f.Mode&p.DMDIR != 0 {
// directory
if tc.Offset == 0 {
var g *File
f.Lock()
for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next {
}
fid.dirs = make([]*File, n)
for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next {
fid.dirs[n] = g
}
f.Unlock()
}
n = 0
b := rc.Data
for i = 0; i < len(fid.dirs); i++ {
g := fid.dirs[i]
g.Lock()
if (g.flags & Fremoved) != 0 {
g.Unlock()
continue
}
sz := p.PackDir(&g.Dir, b, req.Conn.Dotu)
g.Unlock()
if sz == 0 {
break
}
b = b[sz:]
n += sz
}
fid.dirs = fid.dirs[i:]
} else {
// file
if rop, ok := f.Ops.(FReadOp); ok {
n, err = rop.Read(fid, rc.Data, tc.Offset)
if err != nil {
req.RespondError(err)
return
}
} else {
req.RespondError(Eperm)
return
}
}
p.SetRreadCount(rc, uint32(n))
req.Respond()
}
func (*Fsrv) Write(req *Req) {
fid := req.Fid.Aux.(*FFid)
f := fid.F
tc := req.Tc
if wop, ok := (f.Ops).(FWriteOp); ok {
n, err := wop.Write(fid, tc.Data, tc.Offset)
if err != nil {
req.RespondError(err)
} else {
req.RespondRwrite(uint32(n))
}
} else {
req.RespondError(Eperm)
}
}
func (*Fsrv) Clunk(req *Req) {
fid := req.Fid.Aux.(*FFid)
if op, ok := (fid.F.Ops).(FClunkOp); ok {
err := op.Clunk(fid)
if err != nil {
req.RespondError(err)
}
}
req.RespondRclunk()
}
func (*Fsrv) Remove(req *Req) {
fid := req.Fid.Aux.(*FFid)
f := fid.F
f.Lock()
if f.cfirst != nil {
f.Unlock()
req.RespondError(Enotempty)
return
}
f.Unlock()
if rop, ok := (f.Ops).(FRemoveOp); ok {
err := rop.Remove(fid)
if err != nil {
req.RespondError(err)
} else {
f.Remove()
req.RespondRremove()
}
} else {
log.Println("remove not implemented")
req.RespondError(Eperm)
}
}
func (*Fsrv) Stat(req *Req) {
fid := req.Fid.Aux.(*FFid)
f := fid.F
if sop, ok := (f.Ops).(FStatOp); ok {
err := sop.Stat(fid)
if err != nil {
req.RespondError(err)
} else {
req.RespondRstat(&f.Dir)
}
} else {
req.RespondRstat(&f.Dir)
}
}
func (*Fsrv) Wstat(req *Req) {
tc := req.Tc
fid := req.Fid.Aux.(*FFid)
f := fid.F
if wop, ok := (f.Ops).(FWstatOp); ok {
err := wop.Wstat(fid, &tc.Dir)
if err != nil {
req.RespondError(err)
} else {
req.RespondRwstat()
}
} else {
req.RespondError(Eperm)
}
}
func (*Fsrv) FidDestroy(ffid *Fid) {
if ffid.Aux == nil {
return
}
fid := ffid.Aux.(*FFid)
f := fid.F
if f == nil {
return // otherwise errs in bad walks
}
if op, ok := (f.Ops).(FDestroyOp); ok {
op.FidDestroy(fid)
}
}

152
third_party/go9p/p/srv/respond.go vendored Normal file
View File

@ -0,0 +1,152 @@
// Copyright 2009 The Go9p 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 srv
import "fmt"
import "k8s.io/minikube/third_party/go9p/p"
// Respond to the request with Rerror message
func (req *Req) RespondError(err interface{}) {
switch e := err.(type) {
case *p.Error:
p.PackRerror(req.Rc, e.Error(), uint32(e.Errornum), req.Conn.Dotu)
case error:
p.PackRerror(req.Rc, e.Error(), uint32(p.EIO), req.Conn.Dotu)
default:
p.PackRerror(req.Rc, fmt.Sprintf("%v", e), uint32(p.EIO), req.Conn.Dotu)
}
req.Respond()
}
// Respond to the request with Rversion message
func (req *Req) RespondRversion(msize uint32, version string) {
err := p.PackRversion(req.Rc, msize, version)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rauth message
func (req *Req) RespondRauth(aqid *p.Qid) {
err := p.PackRauth(req.Rc, aqid)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rflush message
func (req *Req) RespondRflush() {
err := p.PackRflush(req.Rc)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rattach message
func (req *Req) RespondRattach(aqid *p.Qid) {
err := p.PackRattach(req.Rc, aqid)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rwalk message
func (req *Req) RespondRwalk(wqids []p.Qid) {
err := p.PackRwalk(req.Rc, wqids)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Ropen message
func (req *Req) RespondRopen(qid *p.Qid, iounit uint32) {
err := p.PackRopen(req.Rc, qid, iounit)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rcreate message
func (req *Req) RespondRcreate(qid *p.Qid, iounit uint32) {
err := p.PackRcreate(req.Rc, qid, iounit)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rread message
func (req *Req) RespondRread(data []byte) {
err := p.PackRread(req.Rc, data)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rwrite message
func (req *Req) RespondRwrite(count uint32) {
err := p.PackRwrite(req.Rc, count)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rclunk message
func (req *Req) RespondRclunk() {
err := p.PackRclunk(req.Rc)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rremove message
func (req *Req) RespondRremove() {
err := p.PackRremove(req.Rc)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rstat message
func (req *Req) RespondRstat(st *p.Dir) {
err := p.PackRstat(req.Rc, st, req.Conn.Dotu)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}
// Respond to the request with Rwstat message
func (req *Req) RespondRwstat() {
err := p.PackRwstat(req.Rc)
if err != nil {
req.RespondError(err)
} else {
req.Respond()
}
}

546
third_party/go9p/p/srv/srv.go vendored Normal file
View File

@ -0,0 +1,546 @@
// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The srv package provides definitions and functions used to implement
// a 9P2000 file server.
package srv
import (
"k8s.io/minikube/third_party/go9p/p"
"net"
"runtime"
"sync"
)
type reqStatus int
const (
reqFlush reqStatus = (1 << iota) /* request is flushed (no response will be sent) */
reqWork /* goroutine is currently working on it */
reqResponded /* response is already produced */
reqSaved /* no response was produced after the request is worked on */
)
// Debug flags
const (
DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr
DbgPrintPackets // print the raw packets on stderr
DbgLogFcalls // keep the last N 9P messages (can be accessed over http)
DbgLogPackets // keep the last N 9P messages (can be accessed over http)
)
var Eunknownfid error = &p.Error{"unknown fid", p.EINVAL}
var Enoauth error = &p.Error{"no authentication required", p.EINVAL}
var Einuse error = &p.Error{"fid already in use", p.EINVAL}
var Ebaduse error = &p.Error{"bad use of fid", p.EINVAL}
var Eopen error = &p.Error{"fid already opened", p.EINVAL}
var Enotdir error = &p.Error{"not a directory", p.ENOTDIR}
var Eperm error = &p.Error{"permission denied", p.EPERM}
var Etoolarge error = &p.Error{"i/o count too large", p.EINVAL}
var Ebadoffset error = &p.Error{"bad offset in directory read", p.EINVAL}
var Edirchange error = &p.Error{"cannot convert between files and directories", p.EINVAL}
var Enouser error = &p.Error{"unknown user", p.EINVAL}
var Enotimpl error = &p.Error{"not implemented", p.EINVAL}
// Authentication operations. The file server should implement them if
// it requires user authentication. The authentication in 9P2000 is
// done by creating special authentication fids and performing I/O
// operations on them. Once the authentication is done, the authentication
// fid can be used by the user to get access to the actual files.
type AuthOps interface {
// AuthInit is called when the user starts the authentication
// process on Fid afid. The user that is being authenticated
// is referred by afid.User. The function should return the Qid
// for the authentication file, or an Error if the user can't be
// authenticated
AuthInit(afid *Fid, aname string) (*p.Qid, error)
// AuthDestroy is called when an authentication fid is destroyed.
AuthDestroy(afid *Fid)
// AuthCheck is called after the authentication process is finished
// when the user tries to attach to the file server. If the function
// returns nil, the authentication was successful and the user has
// permission to access the files.
AuthCheck(fid *Fid, afid *Fid, aname string) error
// AuthRead is called when the user attempts to read data from an
// authentication fid.
AuthRead(afid *Fid, offset uint64, data []byte) (count int, err error)
// AuthWrite is called when the user attempts to write data to an
// authentication fid.
AuthWrite(afid *Fid, offset uint64, data []byte) (count int, err error)
}
// Connection operations. These should be implemented if the file server
// needs to be called when a connection is opened or closed.
type ConnOps interface {
ConnOpened(*Conn)
ConnClosed(*Conn)
}
// Fid operations. This interface should be implemented if the file server
// needs to be called when a Fid is destroyed.
type FidOps interface {
FidDestroy(*Fid)
}
// Request operations. This interface should be implemented if the file server
// needs to bypass the default request process, or needs to perform certain
// operations before the (any) request is processed, or before (any) response
// sent back to the client.
type ReqProcessOps interface {
// Called when a new request is received from the client. If the
// interface is not implemented, (req *Req) srv.Process() method is
// called. If the interface is implemented, it is the user's
// responsibility to call srv.Process. If srv.Process isn't called,
// Fid, Afid and Newfid fields in Req are not set, and the ReqOps
// methods are not called.
ReqProcess(*Req)
// Called when a request is responded, i.e. when (req *Req)srv.Respond()
// is called and before the response is sent. If the interface is not
// implemented, (req *Req) srv.PostProcess() method is called to finalize
// the request. If the interface is implemented and ReqProcess calls
// the srv.Process method, ReqRespond should call the srv.PostProcess
// method.
ReqRespond(*Req)
}
// Flush operation. This interface should be implemented if the file server
// can flush pending requests. If the interface is not implemented, requests
// that were passed to the file server implementation won't be flushed.
// The flush method should call the (req *Req) srv.Flush() method if the flush
// was successful so the request can be marked appropriately.
type FlushOp interface {
Flush(*Req)
}
// Request operations. This interface should be implemented by all file servers.
// The operations correspond directly to most of the 9P2000 message types.
type ReqOps interface {
Attach(*Req)
Walk(*Req)
Open(*Req)
Create(*Req)
Read(*Req)
Write(*Req)
Clunk(*Req)
Remove(*Req)
Stat(*Req)
Wstat(*Req)
}
type StatsOps interface {
statsRegister()
statsUnregister()
}
// The Srv type contains the basic fields used to control the 9P2000
// file server. Each file server implementation should create a value
// of Srv type, initialize the values it cares about and pass the
// struct to the (Srv *) srv.Start(ops) method together with the object
// that implements the file server operations.
type Srv struct {
sync.Mutex
Id string // Used for debugging and stats
Msize uint32 // Maximum size of the 9P2000 messages supported by the server
Dotu bool // If true, the server supports the 9P2000.u extension
Debuglevel int // debug level
Upool p.Users // Interface for finding users and groups known to the file server
Maxpend int // Maximum pending outgoing requests
Log *p.Logger
ops interface{} // operations
conns map[*Conn]*Conn // List of connections
}
// The Conn type represents a connection from a client to the file server
type Conn struct {
sync.Mutex
Srv *Srv
Msize uint32 // maximum size of 9P2000 messages for the connection
Dotu bool // if true, both the client and the server speak 9P2000.u
Id string // used for debugging and stats
Debuglevel int
conn net.Conn
fidpool map[uint32]*Fid
reqs map[uint16]*Req // all outstanding requests
reqout chan *Req
rchan chan *p.Fcall
done chan bool
// stats
nreqs int // number of requests processed by the server
tsz uint64 // total size of the T messages received
rsz uint64 // total size of the R messages sent
npend int // number of currently pending messages
maxpend int // maximum number of pending messages
nreads int // number of reads
nwrites int // number of writes
}
// The Fid type identifies a file on the file server.
// A new Fid is created when the user attaches to the file server (the Attach
// operation), or when Walk-ing to a file. The Fid values are created
// automatically by the srv implementation. The FidDestroy operation is called
// when a Fid is destroyed.
type Fid struct {
sync.Mutex
fid uint32
refcount int
opened bool // True if the Fid is opened
Fconn *Conn // Connection the Fid belongs to
Omode uint8 // Open mode (p.O* flags), if the fid is opened
Type uint8 // Fid type (p.QT* flags)
Diroffset uint64 // If directory, the next valid read position
User p.User // The Fid's user
Aux interface{} // Can be used by the file server implementation for per-Fid data
}
// The Req type represents a 9P2000 request. Each request has a
// T-message (Tc) and a R-message (Rc). If the ReqProcessOps don't
// override the default behavior, the implementation initializes Fid,
// Afid and Newfid values and automatically keeps track on when the Fids
// should be destroyed.
type Req struct {
sync.Mutex
Tc *p.Fcall // Incoming 9P2000 message
Rc *p.Fcall // Outgoing 9P2000 response
Fid *Fid // The Fid value for all messages that contain fid[4]
Afid *Fid // The Fid value for the messages that contain afid[4] (Tauth and Tattach)
Newfid *Fid // The Fid value for the messages that contain newfid[4] (Twalk)
Conn *Conn // Connection that the request belongs to
status reqStatus
flushreq *Req
prev, next *Req
}
// The Start method should be called once the file server implementor
// initializes the Srv struct with the preferred values. It sets default
// values to the fields that are not initialized and creates the goroutines
// required for the server's operation. The method receives an empty
// interface value, ops, that should implement the interfaces the file server is
// interested in. Ops must implement the ReqOps interface.
func (srv *Srv) Start(ops interface{}) bool {
if _, ok := (ops).(ReqOps); !ok {
return false
}
srv.ops = ops
if srv.Upool == nil {
srv.Upool = p.OsUsers
}
if srv.Msize < p.IOHDRSZ {
srv.Msize = p.MSIZE
}
if srv.Log == nil {
srv.Log = p.NewLogger(1024)
}
if sop, ok := (interface{}(srv)).(StatsOps); ok {
sop.statsRegister()
}
return true
}
func (srv *Srv) String() string {
return srv.Id
}
func (req *Req) process() {
req.Lock()
flushed := (req.status & reqFlush) != 0
if !flushed {
req.status |= reqWork
}
req.Unlock()
if flushed {
req.Respond()
}
if rop, ok := (req.Conn.Srv.ops).(ReqProcessOps); ok {
rop.ReqProcess(req)
} else {
req.Process()
}
req.Lock()
req.status &= ^reqWork
if !(req.status&reqResponded != 0) {
req.status |= reqSaved
}
req.Unlock()
}
// Performs the default processing of a request. Initializes
// the Fid, Afid and Newfid fields and calls the appropriate
// ReqOps operation for the message. The file server implementer
// should call it only if the file server implements the ReqProcessOps
// within the ReqProcess operation.
func (req *Req) Process() {
conn := req.Conn
srv := conn.Srv
tc := req.Tc
if tc.Fid != p.NOFID && tc.Type != p.Tattach {
srv.Lock()
req.Fid = conn.FidGet(tc.Fid)
srv.Unlock()
if req.Fid == nil {
req.RespondError(Eunknownfid)
return
}
}
switch req.Tc.Type {
default:
req.RespondError(&p.Error{"unknown message type", p.EINVAL})
case p.Tversion:
srv.version(req)
case p.Tauth:
if runtime.GOOS == "windows" {
return
}
srv.auth(req)
case p.Tattach:
srv.attach(req)
case p.Tflush:
srv.flush(req)
case p.Twalk:
srv.walk(req)
case p.Topen:
srv.open(req)
case p.Tcreate:
srv.create(req)
case p.Tread:
srv.read(req)
case p.Twrite:
srv.write(req)
case p.Tclunk:
srv.clunk(req)
case p.Tremove:
srv.remove(req)
case p.Tstat:
srv.stat(req)
case p.Twstat:
srv.wstat(req)
}
}
// Performs the post processing required if the (*Req) Process() method
// is called for a request. The file server implementer should call it
// only if the file server implements the ReqProcessOps within the
// ReqRespond operation.
func (req *Req) PostProcess() {
srv := req.Conn.Srv
/* call the post-handlers (if needed) */
switch req.Tc.Type {
case p.Tauth:
srv.authPost(req)
case p.Tattach:
srv.attachPost(req)
case p.Twalk:
srv.walkPost(req)
case p.Topen:
srv.openPost(req)
case p.Tcreate:
srv.createPost(req)
case p.Tread:
srv.readPost(req)
case p.Tclunk:
srv.clunkPost(req)
case p.Tremove:
srv.removePost(req)
}
if req.Fid != nil {
req.Fid.DecRef()
req.Fid = nil
}
if req.Afid != nil {
req.Afid.DecRef()
req.Afid = nil
}
if req.Newfid != nil {
req.Newfid.DecRef()
req.Newfid = nil
}
}
// The Respond method sends response back to the client. The req.Rc value
// should be initialized and contain valid 9P2000 message. In most cases
// the file server implementer shouldn't call this method directly. Instead
// one of the RespondR* methods should be used.
func (req *Req) Respond() {
var flushreqs *Req
conn := req.Conn
req.Lock()
status := req.status
req.status |= reqResponded
req.status &= ^reqWork
req.Unlock()
if (status & reqResponded) != 0 {
return
}
/* remove the request and all requests flushing it */
conn.Lock()
nextreq := req.prev
if nextreq != nil {
nextreq.next = nil
// if there are flush requests, move them to the next request
if req.flushreq != nil {
var p *Req = nil
r := nextreq.flushreq
for ; r != nil; p, r = r, r.flushreq {
}
if p == nil {
nextreq.flushreq = req.flushreq
} else {
p.next = req.flushreq
}
}
flushreqs = nil
} else {
delete(conn.reqs, req.Tc.Tag)
flushreqs = req.flushreq
}
conn.Unlock()
if rop, ok := (req.Conn.Srv.ops).(ReqProcessOps); ok {
rop.ReqRespond(req)
} else {
req.PostProcess()
}
if (status & reqFlush) == 0 {
conn.reqout <- req
}
// process the next request with the same tag (if available)
if nextreq != nil {
go nextreq.process()
}
// respond to the flush messages
// can't send the responses directly to conn.reqout, because the
// the flushes may be in a tag group too
for freq := flushreqs; freq != nil; freq = freq.flushreq {
freq.Respond()
}
}
// Should be called to cancel a request. Should only be called
// from the Flush operation if the FlushOp is implemented.
func (req *Req) Flush() {
req.Lock()
req.status |= reqFlush
req.Unlock()
req.Respond()
}
// Lookup a Fid struct based on the 32-bit identifier sent over the wire.
// Returns nil if the fid is not found. Increases the reference count of
// the returned fid. The user is responsible to call DecRef once it no
// longer needs it.
func (conn *Conn) FidGet(fidno uint32) *Fid {
conn.Lock()
fid, present := conn.fidpool[fidno]
conn.Unlock()
if present {
fid.IncRef()
}
return fid
}
// Creates a new Fid struct for the fidno integer. Returns nil
// if the Fid for that number already exists. The returned fid
// has reference count set to 1.
func (conn *Conn) FidNew(fidno uint32) *Fid {
conn.Lock()
_, present := conn.fidpool[fidno]
if present {
conn.Unlock()
return nil
}
fid := new(Fid)
fid.fid = fidno
fid.refcount = 1
fid.Fconn = conn
conn.fidpool[fidno] = fid
conn.Unlock()
return fid
}
func (conn *Conn) String() string {
return conn.Srv.Id + "/" + conn.Id
}
// Increase the reference count for the fid.
func (fid *Fid) IncRef() {
fid.Lock()
fid.refcount++
fid.Unlock()
}
// Decrease the reference count for the fid. When the
// reference count reaches 0, the fid is no longer valid.
func (fid *Fid) DecRef() {
fid.Lock()
fid.refcount--
n := fid.refcount
fid.Unlock()
if n > 0 {
return
}
conn := fid.Fconn
conn.Lock()
delete(conn.fidpool, fid.fid)
conn.Unlock()
if fop, ok := (conn.Srv.ops).(FidOps); ok {
fop.FidDestroy(fid)
}
}

131
third_party/go9p/p/srv/stats_http.go vendored Normal file
View File

@ -0,0 +1,131 @@
// +build httpstats
package srv
import (
"fmt"
"io"
"k8s.io/minikube/third_party/go9p/p"
"net/http"
"sync"
)
var mux sync.RWMutex
var stat map[string]http.Handler
var once sync.Once
func register(s string, h http.Handler) {
mux.Lock()
if stat == nil {
stat = make(map[string]http.Handler)
}
if h == nil {
delete(stat, s)
} else {
stat[s] = h
}
mux.Unlock()
}
func (srv *Srv) statsRegister() {
once.Do(func() {
http.HandleFunc("/go9p/", StatsHandler)
go http.ListenAndServe(":6060", nil)
})
register("/go9p/srv/"+srv.Id, srv)
}
func (srv *Srv) statsUnregister() {
register("/go9p/srv/"+srv.Id, nil)
}
func (srv *Srv) ServeHTTP(c http.ResponseWriter, r *http.Request) {
io.WriteString(c, fmt.Sprintf("<html><body><h1>Server %s</h1>", srv.Id))
defer io.WriteString(c, "</body></html>")
// connections
io.WriteString(c, "<h2>Connections</h2><p>")
srv.Lock()
defer srv.Unlock()
if len(srv.conns) == 0 {
io.WriteString(c, "none")
return
}
for _, conn := range srv.conns {
io.WriteString(c, fmt.Sprintf("<a href='/go9p/srv/%s/conn/%s'>%s</a><br>", srv.Id, conn.Id, conn.Id))
}
}
func (conn *Conn) statsRegister() {
register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, conn)
}
func (conn *Conn) statsUnregister() {
register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, nil)
}
func (conn *Conn) ServeHTTP(c http.ResponseWriter, r *http.Request) {
io.WriteString(c, fmt.Sprintf("<html><body><h1>Connection %s/%s</h1>", conn.Srv.Id, conn.Id))
defer io.WriteString(c, "</body></html>")
// statistics
conn.Lock()
io.WriteString(c, fmt.Sprintf("<p>Number of processed requests: %d", conn.nreqs))
io.WriteString(c, fmt.Sprintf("<br>Sent %v bytes", conn.rsz))
io.WriteString(c, fmt.Sprintf("<br>Received %v bytes", conn.tsz))
io.WriteString(c, fmt.Sprintf("<br>Pending requests: %d max %d", conn.npend, conn.maxpend))
io.WriteString(c, fmt.Sprintf("<br>Number of reads: %d", conn.nreads))
io.WriteString(c, fmt.Sprintf("<br>Number of writes: %d", conn.nwrites))
conn.Unlock()
// fcalls
if conn.Debuglevel&DbgLogFcalls != 0 {
fs := conn.Srv.Log.Filter(conn, DbgLogFcalls)
io.WriteString(c, fmt.Sprintf("<h2>Last %d 9P messages</h2>", len(fs)))
for i, l := range fs {
fc := l.Data.(*p.Fcall)
if fc.Type == 0 {
continue
}
lbl := ""
if fc.Type%2 == 0 {
// try to find the response for the T message
for j := i + 1; j < len(fs); j++ {
rc := fs[j].Data.(*p.Fcall)
if rc.Tag == fc.Tag {
lbl = fmt.Sprintf("<a href='#fc%d'>&#10164;</a>", j)
break
}
}
} else {
// try to find the request for the R message
for j := i - 1; j >= 0; j-- {
tc := fs[j].Data.(*p.Fcall)
if tc.Tag == fc.Tag {
lbl = fmt.Sprintf("<a href='#fc%d'>&#10166;</a>", j)
break
}
}
}
io.WriteString(c, fmt.Sprintf("<br id='fc%d'>%d: %s%s", i, i, fc, lbl))
}
}
}
func StatsHandler(c http.ResponseWriter, r *http.Request) {
mux.RLock()
if v, ok := stat[r.URL.Path]; ok {
v.ServeHTTP(c, r)
} else if r.URL.Path == "/go9p/" {
io.WriteString(c, fmt.Sprintf("<html><body><br><h1>On offer: </h1><br>"))
for v := range stat {
io.WriteString(c, fmt.Sprintf("<a href='%s'>%s</a><br>", v, v))
}
io.WriteString(c, "</body></html>")
}
mux.RUnlock()
}

217
third_party/go9p/p/unpack.go vendored Normal file
View File

@ -0,0 +1,217 @@
// Copyright 2009 The Go9p 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 p
import (
"fmt"
)
// Creates a Fcall value from the on-the-wire representation. If
// dotu is true, reads 9P2000.u messages. Returns the unpacked message,
// error and how many bytes from the buffer were used by the message.
func Unpack(buf []byte, dotu bool) (fc *Fcall, err error, fcsz int) {
var m uint16
if len(buf) < 7 {
return nil, &Error{"buffer too short", EINVAL}, 0
}
fc = new(Fcall)
fc.Fid = NOFID
fc.Afid = NOFID
fc.Newfid = NOFID
p := buf
fc.Size, p = gint32(p)
fc.Type, p = gint8(p)
fc.Tag, p = gint16(p)
if int(fc.Size) > len(buf) || fc.Size < 7 {
return nil, &Error{fmt.Sprintf("buffer too short: %d expected %d",
len(buf), fc.Size),
EINVAL},
0
}
p = p[0 : fc.Size-7]
fc.Pkt = buf[0:fc.Size]
fcsz = int(fc.Size)
if fc.Type < Tversion || fc.Type >= Tlast {
return nil, &Error{"invalid id", EINVAL}, 0
}
var sz uint32
if dotu {
sz = minFcsize[fc.Type-Tversion]
} else {
sz = minFcusize[fc.Type-Tversion]
}
if fc.Size < sz {
goto szerror
}
err = nil
switch fc.Type {
default:
return nil, &Error{"invalid message id", EINVAL}, 0
case Tversion, Rversion:
fc.Msize, p = gint32(p)
fc.Version, p = gstr(p)
if p == nil {
goto szerror
}
case Tauth:
fc.Afid, p = gint32(p)
fc.Uname, p = gstr(p)
if p == nil {
goto szerror
}
fc.Aname, p = gstr(p)
if p == nil {
goto szerror
}
fc.Unamenum = NOUID
if dotu && len(p) > 0 {
fc.Unamenum, p = gint32(p)
}
case Rauth, Rattach:
p = gqid(p, &fc.Qid)
case Tflush:
fc.Oldtag, p = gint16(p)
case Tattach:
fc.Fid, p = gint32(p)
fc.Afid, p = gint32(p)
fc.Uname, p = gstr(p)
if p == nil {
goto szerror
}
fc.Aname, p = gstr(p)
if p == nil {
goto szerror
}
fc.Unamenum = NOUID
if dotu && len(p) > 0 {
fc.Unamenum, p = gint32(p)
}
case Rerror:
fc.Error, p = gstr(p)
if p == nil {
goto szerror
}
if dotu {
fc.Errornum, p = gint32(p)
} else {
fc.Errornum = 0
}
case Twalk:
fc.Fid, p = gint32(p)
fc.Newfid, p = gint32(p)
m, p = gint16(p)
fc.Wname = make([]string, m)
for i := 0; i < int(m); i++ {
fc.Wname[i], p = gstr(p)
if p == nil {
goto szerror
}
}
case Rwalk:
m, p = gint16(p)
fc.Wqid = make([]Qid, m)
for i := 0; i < int(m); i++ {
p = gqid(p, &fc.Wqid[i])
}
case Topen:
fc.Fid, p = gint32(p)
fc.Mode, p = gint8(p)
case Ropen, Rcreate:
p = gqid(p, &fc.Qid)
fc.Iounit, p = gint32(p)
case Tcreate:
fc.Fid, p = gint32(p)
fc.Name, p = gstr(p)
if p == nil {
goto szerror
}
fc.Perm, p = gint32(p)
fc.Mode, p = gint8(p)
if dotu {
fc.Ext, p = gstr(p)
if p == nil {
goto szerror
}
}
case Tread:
fc.Fid, p = gint32(p)
fc.Offset, p = gint64(p)
fc.Count, p = gint32(p)
case Rread:
fc.Count, p = gint32(p)
if len(p) < int(fc.Count) {
goto szerror
}
fc.Data = p
p = p[fc.Count:]
case Twrite:
fc.Fid, p = gint32(p)
fc.Offset, p = gint64(p)
fc.Count, p = gint32(p)
if len(p) != int(fc.Count) {
fc.Data = make([]byte, fc.Count)
copy(fc.Data, p)
p = p[len(p):]
} else {
fc.Data = p
p = p[fc.Count:]
}
case Rwrite:
fc.Count, p = gint32(p)
case Tclunk, Tremove, Tstat:
fc.Fid, p = gint32(p)
case Rstat:
m, p = gint16(p)
p = gstat(p, &fc.Dir, dotu)
if p == nil {
goto szerror
}
case Twstat:
fc.Fid, p = gint32(p)
m, p = gint16(p)
p = gstat(p, &fc.Dir, dotu)
case Rflush, Rclunk, Rremove, Rwstat:
}
if len(p) > 0 {
goto szerror
}
return
szerror:
return nil, &Error{"invalid size", EINVAL}, 0
}