提交 60a5910f 编写于 作者: A Aaron Prindle

Added 9p server as well as minikube mount command

上级 45262e85
/*
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)
}
......@@ -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")
......
......@@ -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.
......
## 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.
......@@ -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 {
......
......@@ -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")
}
}
......@@ -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")
}
}
......@@ -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)
}
......@@ -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)
}
......@@ -118,3 +118,8 @@ const (
LocalkubeRunning = "active"
LocalkubeStopped = "inactive"
)
const (
DefaultUfsAddress = ":5640"
DefaultUfsDebugLvl = 0
)
# 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>
# 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>
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.
// 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()
}
}
// 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)
}
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
}
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
}
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)
}
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)
}
// 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)
}
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)
}
// 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()
}
// 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
}
// 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()
}
// 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
}
// 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
}
// 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
}
// 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
}
// +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)
}
// 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)
}
// 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
}
// 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
}
// 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
}
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
}
}
}
// 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
}
// 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 ""
}
// 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
}
// 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
}
// 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
}
// 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))
}
// 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))
}
// 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))
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册