Added 9p server as well as minikube mount command
parent
45262e853e
commit
60a5910f88
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv/examples/ufs"
|
||||
)
|
||||
|
||||
// mountCmd represents the mount command
|
||||
var mountCmd = &cobra.Command{
|
||||
Use: "mount [flags] MOUNT_DIRECTORY(ex:\"/home\")",
|
||||
Short: "Mounts the specified directory into minikube.",
|
||||
Long: `Mounts the specified directory into minikube.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
errText := `Please specify a driver name and a directory to be mounted:
|
||||
\tminikube mount MOUNT_DIRECTORY(ex:"/home")
|
||||
`
|
||||
fmt.Fprintln(os.Stderr, errText)
|
||||
os.Exit(1)
|
||||
}
|
||||
var debugVal int
|
||||
if glog.V(1) {
|
||||
debugVal = 1 // ufs.StartServer takes int debug param
|
||||
}
|
||||
mountDir := args[0]
|
||||
api, err := machine.NewAPIClient(clientType)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting client: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer api.Close()
|
||||
|
||||
fmt.Printf("Mounting %s into /mount-9p on the minikubeVM\n", mountDir)
|
||||
fmt.Println("This daemon process needs to stay alive for the mount to still be accessible...")
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
ufs.StartServer(constants.DefaultUfsAddress, debugVal, mountDir)
|
||||
wg.Done()
|
||||
}()
|
||||
err = cluster.Mount9pHost(api)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
wg.Wait()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(mountCmd)
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
## minikube mount
|
||||
|
||||
Mounts the specified directory into minikube.
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Mounts the specified directory into minikube.
|
||||
|
||||
```
|
||||
minikube mount [flags] MOUNT_DIRECTORY(ex:"/home")
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--alsologtostderr log to standard error as well as files
|
||||
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
|
||||
--log_dir string If non-empty, write log files in this directory (default "")
|
||||
--logtostderr log to standard error instead of files
|
||||
--show-libmachine-logs Deprecated: To enable libmachine logs, set --v=3 or higher
|
||||
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
|
||||
--use-vendored-driver Use the vendored in drivers instead of RPC
|
||||
-v, --v Level log level for V logs
|
||||
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
* [minikube](minikube.md) - Minikube is a tool for managing local Kubernetes clusters.
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# This is the official list of Go9p authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Andrey Mirtchovski <mirtchovski@gmail.com>
|
||||
Latchesar Ionkov <lionkov@gmail.com>
|
||||
Roger Peppe <rogpeppe@gmail.com>
|
|
@ -0,0 +1,18 @@
|
|||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the Go9p repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
# XXX more bumph here?
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Andrey Mirtchovski <mirtchovski@gmail.com>
|
||||
Latchesar Ionkov <lionkov@gmail.com>
|
||||
Akshat Kumar <seed@mail.nanosouffle.net>
|
||||
Roger Peppe <rogpeppe@gmail.com>
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2012 The Go9p Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* The names of Go9p's contributors may not be used to endorse
|
||||
or promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,481 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The clnt package provides definitions and functions used to implement
|
||||
// a 9P2000 file client.
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Debug flags
|
||||
const (
|
||||
DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr
|
||||
DbgPrintPackets // print the raw packets on stderr
|
||||
DbgLogFcalls // keep the last N 9P messages (can be accessed over http)
|
||||
DbgLogPackets // keep the last N 9P messages (can be accessed over http)
|
||||
)
|
||||
|
||||
type StatsOps interface {
|
||||
statsRegister()
|
||||
statsUnregister()
|
||||
}
|
||||
|
||||
// The Clnt type represents a 9P2000 client. The client is connected to
|
||||
// a 9P2000 file server and its methods can be used to access and manipulate
|
||||
// the files exported by the server.
|
||||
type Clnt struct {
|
||||
sync.Mutex
|
||||
Debuglevel int // =0 don't print anything, >0 print Fcalls, >1 print raw packets
|
||||
Msize uint32 // Maximum size of the 9P messages
|
||||
Dotu bool // If true, 9P2000.u protocol is spoken
|
||||
Root *Fid // Fid that points to the rood directory
|
||||
Id string // Used when printing debug messages
|
||||
Log *p.Logger
|
||||
|
||||
conn net.Conn
|
||||
tagpool *pool
|
||||
fidpool *pool
|
||||
reqout chan *Req
|
||||
done chan bool
|
||||
reqfirst *Req
|
||||
reqlast *Req
|
||||
err error
|
||||
|
||||
reqchan chan *Req
|
||||
tchan chan *p.Fcall
|
||||
|
||||
next, prev *Clnt
|
||||
}
|
||||
|
||||
// A Fid type represents a file on the server. Fids are used for the
|
||||
// low level methods that correspond directly to the 9P2000 message requests
|
||||
type Fid struct {
|
||||
sync.Mutex
|
||||
Clnt *Clnt // Client the fid belongs to
|
||||
Iounit uint32
|
||||
p.Qid // The Qid description for the file
|
||||
Mode uint8 // Open mode (one of p.O* values) (if file is open)
|
||||
Fid uint32 // Fid number
|
||||
p.User // The user the fid belongs to
|
||||
walked bool // true if the fid points to a walked file on the server
|
||||
}
|
||||
|
||||
// The file is similar to the Fid, but is used in the high-level client
|
||||
// interface.
|
||||
type File struct {
|
||||
fid *Fid
|
||||
offset uint64
|
||||
}
|
||||
|
||||
type pool struct {
|
||||
sync.Mutex
|
||||
need int
|
||||
nchan chan uint32
|
||||
maxid uint32
|
||||
imap []byte
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
sync.Mutex
|
||||
Clnt *Clnt
|
||||
Tc *p.Fcall
|
||||
Rc *p.Fcall
|
||||
Err error
|
||||
Done chan *Req
|
||||
tag uint16
|
||||
prev, next *Req
|
||||
fid *Fid
|
||||
}
|
||||
|
||||
type ClntList struct {
|
||||
sync.Mutex
|
||||
clntList, clntLast *Clnt
|
||||
}
|
||||
|
||||
var clnts *ClntList
|
||||
var DefaultDebuglevel int
|
||||
var DefaultLogger *p.Logger
|
||||
|
||||
func (clnt *Clnt) Rpcnb(r *Req) error {
|
||||
var tag uint16
|
||||
|
||||
if r.Tc.Type == p.Tversion {
|
||||
tag = p.NOTAG
|
||||
} else {
|
||||
tag = r.tag
|
||||
}
|
||||
|
||||
p.SetTag(r.Tc, tag)
|
||||
clnt.Lock()
|
||||
if clnt.err != nil {
|
||||
clnt.Unlock()
|
||||
return clnt.err
|
||||
}
|
||||
|
||||
if clnt.reqlast != nil {
|
||||
clnt.reqlast.next = r
|
||||
} else {
|
||||
clnt.reqfirst = r
|
||||
}
|
||||
|
||||
r.prev = clnt.reqlast
|
||||
clnt.reqlast = r
|
||||
clnt.Unlock()
|
||||
|
||||
clnt.reqout <- r
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clnt *Clnt) Rpc(tc *p.Fcall) (rc *p.Fcall, err error) {
|
||||
r := clnt.ReqAlloc()
|
||||
r.Tc = tc
|
||||
r.Done = make(chan *Req)
|
||||
err = clnt.Rpcnb(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
<-r.Done
|
||||
rc = r.Rc
|
||||
err = r.Err
|
||||
clnt.ReqFree(r)
|
||||
return
|
||||
}
|
||||
|
||||
func (clnt *Clnt) recv() {
|
||||
var err error
|
||||
|
||||
err = nil
|
||||
buf := make([]byte, clnt.Msize*8)
|
||||
pos := 0
|
||||
for {
|
||||
if len(buf) < int(clnt.Msize) {
|
||||
b := make([]byte, clnt.Msize*8)
|
||||
copy(b, buf[0:pos])
|
||||
buf = b
|
||||
b = nil
|
||||
}
|
||||
|
||||
n, oerr := clnt.conn.Read(buf[pos:])
|
||||
if oerr != nil || n == 0 {
|
||||
err = &p.Error{oerr.Error(), p.EIO}
|
||||
clnt.Lock()
|
||||
clnt.err = err
|
||||
clnt.Unlock()
|
||||
goto closed
|
||||
}
|
||||
|
||||
pos += n
|
||||
for pos > 4 {
|
||||
sz, _ := p.Gint32(buf)
|
||||
if pos < int(sz) {
|
||||
if len(buf) < int(sz) {
|
||||
b := make([]byte, clnt.Msize*8)
|
||||
copy(b, buf[0:pos])
|
||||
buf = b
|
||||
b = nil
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
fc, err, fcsize := p.Unpack(buf, clnt.Dotu)
|
||||
clnt.Lock()
|
||||
if err != nil {
|
||||
clnt.err = err
|
||||
clnt.conn.Close()
|
||||
clnt.Unlock()
|
||||
goto closed
|
||||
}
|
||||
|
||||
if clnt.Debuglevel > 0 {
|
||||
clnt.logFcall(fc)
|
||||
if clnt.Debuglevel&DbgPrintPackets != 0 {
|
||||
log.Println("}-}", clnt.Id, fmt.Sprint(fc.Pkt))
|
||||
}
|
||||
|
||||
if clnt.Debuglevel&DbgPrintFcalls != 0 {
|
||||
log.Println("}}}", clnt.Id, fc.String())
|
||||
}
|
||||
}
|
||||
|
||||
var r *Req = nil
|
||||
for r = clnt.reqfirst; r != nil; r = r.next {
|
||||
if r.Tc.Tag == fc.Tag {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
clnt.err = &p.Error{"unexpected response", p.EINVAL}
|
||||
clnt.conn.Close()
|
||||
clnt.Unlock()
|
||||
goto closed
|
||||
}
|
||||
|
||||
r.Rc = fc
|
||||
if r.prev != nil {
|
||||
r.prev.next = r.next
|
||||
} else {
|
||||
clnt.reqfirst = r.next
|
||||
}
|
||||
|
||||
if r.next != nil {
|
||||
r.next.prev = r.prev
|
||||
} else {
|
||||
clnt.reqlast = r.prev
|
||||
}
|
||||
clnt.Unlock()
|
||||
|
||||
if r.Tc.Type != r.Rc.Type-1 {
|
||||
if r.Rc.Type != p.Rerror {
|
||||
r.Err = &p.Error{"invalid response", p.EINVAL}
|
||||
log.Println(fmt.Sprintf("TTT %v", r.Tc))
|
||||
log.Println(fmt.Sprintf("RRR %v", r.Rc))
|
||||
} else {
|
||||
if r.Err == nil {
|
||||
r.Err = &p.Error{r.Rc.Error, r.Rc.Errornum}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.Done != nil {
|
||||
r.Done <- r
|
||||
}
|
||||
|
||||
pos -= fcsize
|
||||
buf = buf[fcsize:]
|
||||
}
|
||||
}
|
||||
|
||||
closed:
|
||||
clnt.done <- true
|
||||
|
||||
/* send error to all pending requests */
|
||||
clnt.Lock()
|
||||
r := clnt.reqfirst
|
||||
clnt.reqfirst = nil
|
||||
clnt.reqlast = nil
|
||||
if err == nil {
|
||||
err = clnt.err
|
||||
}
|
||||
clnt.Unlock()
|
||||
for ; r != nil; r = r.next {
|
||||
r.Err = err
|
||||
if r.Done != nil {
|
||||
r.Done <- r
|
||||
}
|
||||
}
|
||||
|
||||
clnts.Lock()
|
||||
if clnt.prev != nil {
|
||||
clnt.prev.next = clnt.next
|
||||
} else {
|
||||
clnts.clntList = clnt.next
|
||||
}
|
||||
|
||||
if clnt.next != nil {
|
||||
clnt.next.prev = clnt.prev
|
||||
} else {
|
||||
clnts.clntLast = clnt.prev
|
||||
}
|
||||
clnts.Unlock()
|
||||
|
||||
if sop, ok := (interface{}(clnt)).(StatsOps); ok {
|
||||
sop.statsUnregister()
|
||||
}
|
||||
}
|
||||
|
||||
func (clnt *Clnt) send() {
|
||||
for {
|
||||
select {
|
||||
case <-clnt.done:
|
||||
return
|
||||
|
||||
case req := <-clnt.reqout:
|
||||
if clnt.Debuglevel > 0 {
|
||||
clnt.logFcall(req.Tc)
|
||||
if clnt.Debuglevel&DbgPrintPackets != 0 {
|
||||
log.Println("{-{", clnt.Id, fmt.Sprint(req.Tc.Pkt))
|
||||
}
|
||||
|
||||
if clnt.Debuglevel&DbgPrintFcalls != 0 {
|
||||
log.Println("{{{", clnt.Id, req.Tc.String())
|
||||
}
|
||||
}
|
||||
|
||||
for buf := req.Tc.Pkt; len(buf) > 0; {
|
||||
n, err := clnt.conn.Write(buf)
|
||||
if err != nil {
|
||||
/* just close the socket, will get signal on clnt.done */
|
||||
clnt.conn.Close()
|
||||
break
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates and initializes a new Clnt object. Doesn't send any data
|
||||
// on the wire.
|
||||
func NewClnt(c net.Conn, msize uint32, dotu bool) *Clnt {
|
||||
clnt := new(Clnt)
|
||||
clnt.conn = c
|
||||
clnt.Msize = msize
|
||||
clnt.Dotu = dotu
|
||||
clnt.Debuglevel = DefaultDebuglevel
|
||||
clnt.Log = DefaultLogger
|
||||
clnt.Id = c.RemoteAddr().String() + ":"
|
||||
clnt.tagpool = newPool(uint32(p.NOTAG))
|
||||
clnt.fidpool = newPool(p.NOFID)
|
||||
clnt.reqout = make(chan *Req)
|
||||
clnt.done = make(chan bool)
|
||||
clnt.reqchan = make(chan *Req, 16)
|
||||
clnt.tchan = make(chan *p.Fcall, 16)
|
||||
|
||||
go clnt.recv()
|
||||
go clnt.send()
|
||||
|
||||
clnts.Lock()
|
||||
if clnts.clntLast != nil {
|
||||
clnts.clntLast.next = clnt
|
||||
} else {
|
||||
clnts.clntList = clnt
|
||||
}
|
||||
|
||||
clnt.prev = clnts.clntLast
|
||||
clnts.clntLast = clnt
|
||||
clnts.Unlock()
|
||||
|
||||
if sop, ok := (interface{}(clnt)).(StatsOps); ok {
|
||||
sop.statsRegister()
|
||||
}
|
||||
|
||||
return clnt
|
||||
}
|
||||
|
||||
// Establishes a new socket connection to the 9P server and creates
|
||||
// a client object for it. Negotiates the dialect and msize for the
|
||||
// connection. Returns a Clnt object, or Error.
|
||||
func Connect(c net.Conn, msize uint32, dotu bool) (*Clnt, error) {
|
||||
clnt := NewClnt(c, msize, dotu)
|
||||
ver := "9P2000"
|
||||
if clnt.Dotu {
|
||||
ver = "9P2000.u"
|
||||
}
|
||||
|
||||
tc := p.NewFcall(clnt.Msize)
|
||||
err := p.PackTversion(tc, clnt.Msize, ver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rc.Msize < clnt.Msize {
|
||||
clnt.Msize = rc.Msize
|
||||
}
|
||||
|
||||
clnt.Dotu = rc.Version == "9P2000.u" && clnt.Dotu
|
||||
return clnt, nil
|
||||
}
|
||||
|
||||
// Creates a new Fid object for the client
|
||||
func (clnt *Clnt) FidAlloc() *Fid {
|
||||
fid := new(Fid)
|
||||
fid.Fid = clnt.fidpool.getId()
|
||||
fid.Clnt = clnt
|
||||
|
||||
return fid
|
||||
}
|
||||
|
||||
func (clnt *Clnt) NewFcall() *p.Fcall {
|
||||
select {
|
||||
case tc := <-clnt.tchan:
|
||||
return tc
|
||||
default:
|
||||
}
|
||||
return p.NewFcall(clnt.Msize)
|
||||
}
|
||||
|
||||
func (clnt *Clnt) FreeFcall(fc *p.Fcall) {
|
||||
if fc != nil && len(fc.Buf) >= int(clnt.Msize) {
|
||||
select {
|
||||
case clnt.tchan <- fc:
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (clnt *Clnt) ReqAlloc() *Req {
|
||||
var req *Req
|
||||
select {
|
||||
case req = <-clnt.reqchan:
|
||||
break
|
||||
default:
|
||||
req = new(Req)
|
||||
req.Clnt = clnt
|
||||
req.tag = uint16(clnt.tagpool.getId())
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (clnt *Clnt) ReqFree(req *Req) {
|
||||
clnt.FreeFcall(req.Tc)
|
||||
req.Tc = nil
|
||||
req.Rc = nil
|
||||
req.Err = nil
|
||||
req.Done = nil
|
||||
req.next = nil
|
||||
req.prev = nil
|
||||
|
||||
select {
|
||||
case clnt.reqchan <- req:
|
||||
break
|
||||
default:
|
||||
clnt.tagpool.putId(uint32(req.tag))
|
||||
}
|
||||
}
|
||||
|
||||
func NewFile(f *Fid, offset uint64) *File {
|
||||
return &File{f, offset}
|
||||
}
|
||||
|
||||
func (f *File) Fid() *Fid {
|
||||
return f.fid
|
||||
}
|
||||
|
||||
func (clnt *Clnt) logFcall(fc *p.Fcall) {
|
||||
if clnt.Debuglevel&DbgLogPackets != 0 {
|
||||
pkt := make([]byte, len(fc.Pkt))
|
||||
copy(pkt, fc.Pkt)
|
||||
clnt.Log.Log(pkt, clnt, DbgLogPackets)
|
||||
}
|
||||
|
||||
if clnt.Debuglevel&DbgLogFcalls != 0 {
|
||||
f := new(p.Fcall)
|
||||
*f = *fc
|
||||
f.Pkt = nil
|
||||
clnt.Log.Log(f, clnt, DbgLogFcalls)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
clnts = new(ClntList)
|
||||
if sop, ok := (interface{}(clnts)).(StatsOps); ok {
|
||||
sop.statsRegister()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import "k8s.io/minikube/third_party/go9p/p"
|
||||
|
||||
// Clunks a fid. Returns nil if successful.
|
||||
func (clnt *Clnt) Clunk(fid *Fid) (err error) {
|
||||
err = nil
|
||||
if fid.walked {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTclunk(tc, fid.Fid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = clnt.Rpc(tc)
|
||||
}
|
||||
|
||||
clnt.fidpool.putId(fid.Fid)
|
||||
fid.walked = false
|
||||
fid.Fid = p.NOFID
|
||||
return
|
||||
}
|
||||
|
||||
// Closes a file. Returns nil if successful.
|
||||
func (file *File) Close() error {
|
||||
// Should we cancel all pending requests for the File
|
||||
return file.fid.Clnt.Clunk(file.fid)
|
||||
}
|
|
@ -0,0 +1,489 @@
|
|||
package main
|
||||
|
||||
// An interactive client for 9P servers.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/clnt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
|
||||
var ouser = flag.String("user", "", "user to connect as")
|
||||
var cmdfile = flag.String("file", "", "read commands from file")
|
||||
var prompt = flag.String("prompt", "9p> ", "prompt for interactive client")
|
||||
var debug = flag.Bool("d", false, "enable debugging (fcalls)")
|
||||
var debugall = flag.Bool("D", false, "enable debugging (raw packets)")
|
||||
|
||||
var cwd = "/"
|
||||
var cfid *clnt.Fid
|
||||
|
||||
type Cmd struct {
|
||||
fun func(c *clnt.Clnt, s []string)
|
||||
help string
|
||||
}
|
||||
|
||||
var cmds map[string]*Cmd
|
||||
|
||||
func init() {
|
||||
cmds = make(map[string]*Cmd)
|
||||
cmds["write"] = &Cmd{cmdwrite, "write file string [...]\t«write the unmodified string to file, create file if necessary»"}
|
||||
cmds["echo"] = &Cmd{cmdecho, "echo file string [...]\t«echo string to file (newline appended)»"}
|
||||
cmds["stat"] = &Cmd{cmdstat, "stat file [...]\t«stat file»"}
|
||||
cmds["ls"] = &Cmd{cmdls, "ls [-l] file [...]\t«list contents of directory or file»"}
|
||||
cmds["cd"] = &Cmd{cmdcd, "cd dir\t«change working directory»"}
|
||||
cmds["cat"] = &Cmd{cmdcat, "cat file [...]\t«print the contents of file»"}
|
||||
cmds["mkdir"] = &Cmd{cmdmkdir, "mkdir dir [...]\t«create dir on remote server»"}
|
||||
cmds["get"] = &Cmd{cmdget, "get file [local]\t«get file from remote server»"}
|
||||
cmds["put"] = &Cmd{cmdput, "put file [remote]\t«put file on the remote server as 'file'»"}
|
||||
cmds["pwd"] = &Cmd{cmdpwd, "pwd\t«print working directory»"}
|
||||
cmds["rm"] = &Cmd{cmdrm, "rm file [...]\t«remove file from remote server»"}
|
||||
cmds["help"] = &Cmd{cmdhelp, "help [cmd]\t«print available commands or help on cmd»"}
|
||||
cmds["quit"] = &Cmd{cmdquit, "quit\t«exit»"}
|
||||
cmds["exit"] = &Cmd{cmdquit, "exit\t«quit»"}
|
||||
}
|
||||
|
||||
// normalize user-supplied path. path starting with '/' is left untouched, otherwise is considered
|
||||
// local from cwd
|
||||
func normpath(s string) string {
|
||||
if len(s) > 0 {
|
||||
if s[0] == '/' {
|
||||
return path.Clean(s)
|
||||
}
|
||||
return path.Clean(cwd + "/" + s)
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
|
||||
func b(mode uint32, s uint8) string {
|
||||
var bits = []string{"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"}
|
||||
return bits[(mode>>s)&7]
|
||||
}
|
||||
|
||||
// Convert file mode bits to string representation
|
||||
func modetostr(mode uint32) string {
|
||||
d := "-"
|
||||
if mode&p.DMDIR != 0 {
|
||||
d = "d"
|
||||
} else if mode&p.DMAPPEND != 0 {
|
||||
d = "a"
|
||||
}
|
||||
return fmt.Sprintf("%s%s%s%s", d, b(mode, 6), b(mode, 3), b(mode, 0))
|
||||
}
|
||||
|
||||
// Write the string s to remote file f. Create f if it doesn't exist
|
||||
func writeone(c *clnt.Clnt, f, s string) {
|
||||
fname := normpath(f)
|
||||
file, oserr := c.FCreate(fname, 0666, p.OWRITE)
|
||||
if oserr != nil {
|
||||
file, oserr = c.FOpen(fname, p.OWRITE|p.OTRUNC)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", fname, oserr)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
m, oserr := file.Write([]byte(s))
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing to %s: %v\n", fname, oserr)
|
||||
return
|
||||
}
|
||||
|
||||
if m != len(s) {
|
||||
fmt.Fprintf(os.Stderr, "short write %s\n", fname)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Write s[1:] (with appended spaces) to the file s[0]
|
||||
func cmdwrite(c *clnt.Clnt, s []string) {
|
||||
fname := normpath(s[0])
|
||||
str := strings.Join(s[1:], " ")
|
||||
writeone(c, fname, str)
|
||||
}
|
||||
|
||||
// Echo (append newline) s[1:] to s[0]
|
||||
func cmdecho(c *clnt.Clnt, s []string) {
|
||||
fname := normpath(s[0])
|
||||
str := strings.Join(s[1:], " ") + "\n"
|
||||
writeone(c, fname, str)
|
||||
}
|
||||
|
||||
// Stat the remote file f
|
||||
func statone(c *clnt.Clnt, f string) {
|
||||
fname := normpath(f)
|
||||
|
||||
stat, oserr := c.FStat(fname)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error in stat %s: %v\n", fname, oserr)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s\n", stat)
|
||||
}
|
||||
|
||||
func cmdstat(c *clnt.Clnt, s []string) {
|
||||
for _, f := range s {
|
||||
statone(c, normpath(f))
|
||||
}
|
||||
}
|
||||
|
||||
func dirtostr(d *p.Dir) string {
|
||||
return fmt.Sprintf("%s %s %s %-8d\t\t%s", modetostr(d.Mode), d.Uid, d.Gid, d.Length, d.Name)
|
||||
}
|
||||
|
||||
func lsone(c *clnt.Clnt, s string, long bool) {
|
||||
st, oserr := c.FStat(normpath(s))
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error stat: %v\n", oserr)
|
||||
return
|
||||
}
|
||||
if st.Mode&p.DMDIR != 0 {
|
||||
file, oserr := c.FOpen(s, p.OREAD)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening dir: %s\n", oserr)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
for {
|
||||
d, oserr := file.Readdir(0)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading dir: %v\n", oserr)
|
||||
}
|
||||
if d == nil || len(d) == 0 {
|
||||
break
|
||||
}
|
||||
for _, dir := range d {
|
||||
if long {
|
||||
fmt.Fprintf(os.Stdout, "%s\n", dirtostr(dir))
|
||||
} else {
|
||||
os.Stdout.WriteString(dir.Name + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(os.Stdout, "%s\n", dirtostr(st))
|
||||
}
|
||||
}
|
||||
|
||||
func cmdls(c *clnt.Clnt, s []string) {
|
||||
long := false
|
||||
if len(s) > 0 && s[0] == "-l" {
|
||||
long = true
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
lsone(c, cwd, long)
|
||||
} else {
|
||||
for _, d := range s {
|
||||
lsone(c, cwd+d, long)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func walkone(c *clnt.Clnt, s string, fileok bool) {
|
||||
ncwd := normpath(s)
|
||||
|
||||
fid, err := c.FWalk(ncwd)
|
||||
defer c.Clunk(fid)
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "walk error: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if fileok != true && (fid.Type&p.QTDIR == 0) {
|
||||
fmt.Fprintf(os.Stderr, "can't cd to file [%s]\n", ncwd)
|
||||
return
|
||||
}
|
||||
|
||||
cwd = ncwd
|
||||
}
|
||||
|
||||
func cmdcd(c *clnt.Clnt, s []string) {
|
||||
if s != nil {
|
||||
walkone(c, strings.Join(s, "/"), false)
|
||||
}
|
||||
}
|
||||
|
||||
// Print the contents of f
|
||||
func cmdcat(c *clnt.Clnt, s []string) {
|
||||
buf := make([]byte, 8192)
|
||||
Outer:
|
||||
for _, f := range s {
|
||||
fname := normpath(f)
|
||||
file, oserr := c.FOpen(fname, p.OREAD)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", f, oserr)
|
||||
continue Outer
|
||||
}
|
||||
defer file.Close()
|
||||
for {
|
||||
n, oserr := file.Read(buf)
|
||||
if oserr != nil && oserr != io.EOF {
|
||||
fmt.Fprintf(os.Stderr, "error reading %s: %v\n", f, oserr)
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
os.Stdout.Write(buf[0:n])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a single directory on remote server
|
||||
func mkone(c *clnt.Clnt, s string) {
|
||||
fname := normpath(s)
|
||||
file, oserr := c.FCreate(fname, 0777|p.DMDIR, p.OWRITE)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error creating directory %s: %v\n", fname, oserr)
|
||||
return
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// Create directories on remote server
|
||||
func cmdmkdir(c *clnt.Clnt, s []string) {
|
||||
for _, f := range s {
|
||||
mkone(c, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a remote file to local filesystem
|
||||
func cmdget(c *clnt.Clnt, s []string) {
|
||||
var from, to string
|
||||
switch len(s) {
|
||||
case 1:
|
||||
from = normpath(s[0])
|
||||
_, to = path.Split(s[0])
|
||||
case 2:
|
||||
from, to = normpath(s[0]), s[1]
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "from arguments; usage: get from to\n")
|
||||
}
|
||||
|
||||
tofile, err := os.Create(to)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err)
|
||||
return
|
||||
}
|
||||
defer tofile.Close()
|
||||
|
||||
file, ferr := c.FOpen(from, p.OREAD)
|
||||
if ferr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := make([]byte, 8192)
|
||||
for {
|
||||
n, oserr := file.Read(buf)
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading %s: %s\n", from, oserr)
|
||||
return
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
m, err := tofile.Write(buf[0:n])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing %s: %s\n", to, err)
|
||||
return
|
||||
}
|
||||
|
||||
if m != n {
|
||||
fmt.Fprintf(os.Stderr, "short write %s\n", to)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a local file to remote server
|
||||
func cmdput(c *clnt.Clnt, s []string) {
|
||||
var from, to string
|
||||
switch len(s) {
|
||||
case 1:
|
||||
_, to = path.Split(s[0])
|
||||
to = normpath(to)
|
||||
from = s[0]
|
||||
case 2:
|
||||
from, to = s[0], normpath(s[1])
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "incorrect arguments; usage: put local [remote]\n")
|
||||
}
|
||||
|
||||
fromfile, err := os.Open(from)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s for reading: %s\n", from, err)
|
||||
return
|
||||
}
|
||||
defer fromfile.Close()
|
||||
|
||||
file, ferr := c.FOpen(to, p.OWRITE|p.OTRUNC)
|
||||
if ferr != nil {
|
||||
file, ferr = c.FCreate(to, 0666, p.OWRITE)
|
||||
if ferr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := make([]byte, 8192)
|
||||
for {
|
||||
n, oserr := fromfile.Read(buf)
|
||||
if oserr != nil && oserr != io.EOF {
|
||||
fmt.Fprintf(os.Stderr, "error reading %s: %s\n", from, oserr)
|
||||
return
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
m, oserr := file.Write(buf[0:n])
|
||||
if oserr != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing %s: %v\n", to, oserr)
|
||||
return
|
||||
}
|
||||
|
||||
if m != n {
|
||||
fmt.Fprintf(os.Stderr, "short write %s\n", to)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cmdpwd(c *clnt.Clnt, s []string) { fmt.Fprintf(os.Stdout, cwd+"\n") }
|
||||
|
||||
// Remove f from remote server
|
||||
func rmone(c *clnt.Clnt, f string) {
|
||||
fname := normpath(f)
|
||||
|
||||
err := c.FRemove(fname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error in stat %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Remove one or more files from the server
|
||||
func cmdrm(c *clnt.Clnt, s []string) {
|
||||
for _, f := range s {
|
||||
rmone(c, normpath(f))
|
||||
}
|
||||
}
|
||||
|
||||
// Print available commands
|
||||
func cmdhelp(c *clnt.Clnt, s []string) {
|
||||
cmdstr := ""
|
||||
if len(s) > 0 {
|
||||
for _, h := range s {
|
||||
v, ok := cmds[h]
|
||||
if ok {
|
||||
cmdstr = cmdstr + v.help + "\n"
|
||||
} else {
|
||||
cmdstr = cmdstr + "unknown command: " + h + "\n"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cmdstr = "available commands: "
|
||||
for k := range cmds {
|
||||
cmdstr = cmdstr + " " + k
|
||||
}
|
||||
cmdstr = cmdstr + "\n"
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s", cmdstr)
|
||||
}
|
||||
|
||||
func cmdquit(c *clnt.Clnt, s []string) { os.Exit(0) }
|
||||
|
||||
func cmd(c *clnt.Clnt, cmd string) {
|
||||
ncmd := strings.Fields(cmd)
|
||||
if len(ncmd) <= 0 {
|
||||
return
|
||||
}
|
||||
v, ok := cmds[ncmd[0]]
|
||||
if ok == false {
|
||||
fmt.Fprintf(os.Stderr, "unknown command: %s\n", ncmd[0])
|
||||
return
|
||||
}
|
||||
v.fun(c, ncmd[1:])
|
||||
return
|
||||
}
|
||||
|
||||
func interactive(c *clnt.Clnt) {
|
||||
reader := bufio.NewReaderSize(os.Stdin, 8192)
|
||||
for {
|
||||
fmt.Print(*prompt)
|
||||
line, err := reader.ReadSlice('\n')
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "exiting...\n")
|
||||
break
|
||||
}
|
||||
str := strings.TrimSpace(string(line))
|
||||
// TODO: handle larger input lines by doubling buffer
|
||||
in := strings.Split(str, "\n")
|
||||
for i := range in {
|
||||
if len(in[i]) > 0 {
|
||||
cmd(c, in[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var user p.User
|
||||
var err error
|
||||
var c *clnt.Clnt
|
||||
var file *clnt.File
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *ouser == "" {
|
||||
user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
} else {
|
||||
user = p.OsUsers.Uname2User(*ouser)
|
||||
}
|
||||
|
||||
naddr := *addr
|
||||
if strings.LastIndex(naddr, ":") == -1 {
|
||||
naddr = naddr + ":5640"
|
||||
}
|
||||
c, err = clnt.Mount("tcp", naddr, "", user)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error mounting %s: %s\n", naddr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *debug {
|
||||
c.Debuglevel = 1
|
||||
}
|
||||
if *debugall {
|
||||
c.Debuglevel = 2
|
||||
}
|
||||
|
||||
walkone(c, "/", false)
|
||||
|
||||
if file != nil {
|
||||
//process(c)
|
||||
fmt.Sprint(os.Stderr, "file reading unimplemented\n")
|
||||
} else if flag.NArg() > 0 {
|
||||
flags := flag.Args()
|
||||
for _, uc := range flags {
|
||||
cmd(c, uc)
|
||||
}
|
||||
} else {
|
||||
interactive(c)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/clnt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var debuglevel = flag.Int("d", 0, "debuglevel")
|
||||
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
|
||||
|
||||
func main() {
|
||||
var user p.User
|
||||
var err error
|
||||
var c *clnt.Clnt
|
||||
var file *clnt.File
|
||||
var d []*p.Dir
|
||||
|
||||
flag.Parse()
|
||||
user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
clnt.DefaultDebuglevel = *debuglevel
|
||||
c, err = clnt.Mount("tcp", *addr, "", user)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
lsarg := "/"
|
||||
if flag.NArg() == 1 {
|
||||
lsarg = flag.Arg(0)
|
||||
} else if flag.NArg() > 1 {
|
||||
log.Fatal("error: only one argument expected")
|
||||
}
|
||||
|
||||
file, err = c.FOpen(lsarg, p.OREAD)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
d, err = file.Readdir(0)
|
||||
if d == nil || len(d) == 0 || err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
for i := 0; i < len(d); i++ {
|
||||
os.Stdout.WriteString(d[i].Name + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
file.Close()
|
||||
if err != nil && err != io.EOF {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/clnt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var debuglevel = flag.Int("d", 0, "debuglevel")
|
||||
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
|
||||
|
||||
func main() {
|
||||
var n int
|
||||
var user p.User
|
||||
var err error
|
||||
var c *clnt.Clnt
|
||||
var file *clnt.File
|
||||
var buf []byte
|
||||
|
||||
flag.Parse()
|
||||
user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
clnt.DefaultDebuglevel = *debuglevel
|
||||
c, err = clnt.Mount("tcp", *addr, "", user)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
log.Println("invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
file, err = c.FOpen(flag.Arg(0), p.OREAD)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
buf = make([]byte, 8192)
|
||||
for {
|
||||
n, err = file.Read(buf)
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
os.Stdout.Write(buf[0:n])
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
goto error
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println("Error", err)
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/clnt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var debuglevel = flag.Int("d", 0, "debuglevel")
|
||||
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
|
||||
|
||||
func main() {
|
||||
var user p.User
|
||||
var ba [][]byte
|
||||
var nreqs int
|
||||
var rchan chan *clnt.Req
|
||||
var tag *clnt.Tag
|
||||
var fid *clnt.Fid
|
||||
var wnames []string
|
||||
|
||||
flag.Parse()
|
||||
user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
clnt.DefaultDebuglevel = *debuglevel
|
||||
c, err := clnt.Mount("tcp", *addr, "", user)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
log.Println("invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
ba = make([][]byte, 100)
|
||||
for i := 0; i < len(ba); i++ {
|
||||
ba[i] = make([]byte, 8192)
|
||||
}
|
||||
|
||||
nreqs = 0
|
||||
rchan = make(chan *clnt.Req)
|
||||
tag = c.TagAlloc(rchan)
|
||||
|
||||
// walk the file
|
||||
wnames = strings.Split(flag.Arg(0), "/")
|
||||
for wnames[0] == "" {
|
||||
wnames = wnames[1:]
|
||||
}
|
||||
|
||||
fid = c.FidAlloc()
|
||||
for root := c.Root; len(wnames) > 0; root = fid {
|
||||
n := len(wnames)
|
||||
if n > 8 {
|
||||
n = 8
|
||||
}
|
||||
|
||||
err = tag.Walk(root, fid, wnames[0:n])
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
nreqs++
|
||||
wnames = wnames[n:]
|
||||
}
|
||||
err = tag.Open(fid, p.OREAD)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
for i := 0; i < len(ba); i++ {
|
||||
err = tag.Read(fid, uint64(i*8192), 8192)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
nreqs++
|
||||
}
|
||||
|
||||
err = tag.Clunk(fid)
|
||||
|
||||
// now start reading...
|
||||
for nreqs > 0 {
|
||||
r := <-rchan
|
||||
if r.Tc.Type == p.Tread {
|
||||
i := r.Tc.Offset / 8192
|
||||
copy(ba[i], r.Rc.Data)
|
||||
ba[i] = ba[i][0:r.Rc.Count]
|
||||
}
|
||||
nreqs--
|
||||
}
|
||||
|
||||
for i := 0; i < len(ba); i++ {
|
||||
os.Stdout.Write(ba[i])
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println("error: ", err)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Connects to a server over TLS and lists the specified directory
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/clnt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var debuglevel = flag.Int("d", 0, "debuglevel")
|
||||
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
|
||||
|
||||
func main() {
|
||||
var user p.User
|
||||
var file *clnt.File
|
||||
|
||||
flag.Parse()
|
||||
user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
clnt.DefaultDebuglevel = *debuglevel
|
||||
|
||||
c, oerr := tls.Dial("tcp", *addr, &tls.Config{
|
||||
Rand: rand.Reader,
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if oerr != nil {
|
||||
log.Println("can't dial", oerr)
|
||||
return
|
||||
}
|
||||
|
||||
clnt, err := clnt.MountConn(c, "", user)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
log.Println("invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
file, oerr = clnt.FOpen(flag.Arg(0), p.OREAD)
|
||||
if oerr != nil {
|
||||
goto oerror
|
||||
}
|
||||
|
||||
for {
|
||||
d, oerr := file.Readdir(0)
|
||||
if oerr != nil {
|
||||
goto oerror
|
||||
}
|
||||
|
||||
if d == nil || len(d) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for i := 0; i < len(d); i++ {
|
||||
os.Stdout.WriteString(d[i].Name + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
file.Close()
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println(fmt.Sprintf("Error: %s", err))
|
||||
return
|
||||
|
||||
oerror:
|
||||
log.Println("Error", oerr)
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/clnt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var debuglevel = flag.Int("d", 0, "debuglevel")
|
||||
var addr = flag.String("addr", "127.0.0.1:5640", "network address")
|
||||
|
||||
func main() {
|
||||
var n, m int
|
||||
var user p.User
|
||||
var err error
|
||||
var c *clnt.Clnt
|
||||
var file *clnt.File
|
||||
var buf []byte
|
||||
|
||||
flag.Parse()
|
||||
user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
clnt.DefaultDebuglevel = *debuglevel
|
||||
c, err = clnt.Mount("tcp", *addr, "", user)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
log.Println("invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
file, err = c.FOpen(flag.Arg(0), p.OWRITE|p.OTRUNC)
|
||||
if err != nil {
|
||||
file, err = c.FCreate(flag.Arg(0), 0666, p.OWRITE)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
}
|
||||
|
||||
buf = make([]byte, 8192)
|
||||
for {
|
||||
n, err = os.Stdin.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
goto error
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
m, err = file.Write(buf[0:n])
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
if m != n {
|
||||
err = &p.Error{"short write", 0}
|
||||
goto error
|
||||
}
|
||||
}
|
||||
|
||||
file.Close()
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println("Error", err)
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Creates an authentication fid for the specified user. Returns the fid, if
|
||||
// successful, or an Error.
|
||||
func (clnt *Clnt) Auth(user p.User, aname string) (*Fid, error) {
|
||||
fid := clnt.FidAlloc()
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTauth(tc, fid.Fid, user.Name(), aname, uint32(user.Id()), clnt.Dotu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fid.Iounit = clnt.Msize - p.IOHDRSZ
|
||||
fid.User = user
|
||||
fid.walked = true
|
||||
return fid, nil
|
||||
}
|
||||
|
||||
// Creates a fid for the specified user that points to the root
|
||||
// of the file server's file tree. Returns a Fid pointing to the root,
|
||||
// if successful, or an Error.
|
||||
func (clnt *Clnt) Attach(afid *Fid, user p.User, aname string) (*Fid, error) {
|
||||
var afno uint32
|
||||
|
||||
if afid != nil {
|
||||
afno = afid.Fid
|
||||
} else {
|
||||
afno = p.NOFID
|
||||
}
|
||||
|
||||
fid := clnt.FidAlloc()
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTattach(tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), clnt.Dotu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fid.Qid = rc.Qid
|
||||
fid.User = user
|
||||
fid.walked = true
|
||||
return fid, nil
|
||||
}
|
||||
|
||||
// Connects to a file server and attaches to it as the specified user.
|
||||
func Mount(ntype, addr, aname string, user p.User) (*Clnt, error) {
|
||||
c, e := net.Dial(ntype, addr)
|
||||
if e != nil {
|
||||
return nil, &p.Error{e.Error(), p.EIO}
|
||||
}
|
||||
|
||||
return MountConn(c, aname, user)
|
||||
}
|
||||
|
||||
func MountConn(c net.Conn, aname string, user p.User) (*Clnt, error) {
|
||||
clnt, err := Connect(c, 8192+p.IOHDRSZ, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fid, err := clnt.Attach(nil, user, aname)
|
||||
if err != nil {
|
||||
clnt.Unmount()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clnt.Root = fid
|
||||
return clnt, nil
|
||||
}
|
||||
|
||||
// Closes the connection to the file sever.
|
||||
func (clnt *Clnt) Unmount() {
|
||||
clnt.Lock()
|
||||
clnt.err = &p.Error{"connection closed", p.EIO}
|
||||
clnt.conn.Close()
|
||||
clnt.Unlock()
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Opens the file associated with the fid. Returns nil if
|
||||
// the operation is successful.
|
||||
func (clnt *Clnt) Open(fid *Fid, mode uint8) error {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTopen(tc, fid.Fid, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fid.Qid = rc.Qid
|
||||
fid.Iounit = rc.Iounit
|
||||
if fid.Iounit == 0 || fid.Iounit > clnt.Msize-p.IOHDRSZ {
|
||||
fid.Iounit = clnt.Msize - p.IOHDRSZ
|
||||
}
|
||||
fid.Mode = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a file in the directory associated with the fid. Returns nil
|
||||
// if the operation is successful.
|
||||
func (clnt *Clnt) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTcreate(tc, fid.Fid, name, perm, mode, ext, clnt.Dotu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fid.Qid = rc.Qid
|
||||
fid.Iounit = rc.Iounit
|
||||
if fid.Iounit == 0 || fid.Iounit > clnt.Msize-p.IOHDRSZ {
|
||||
fid.Iounit = clnt.Msize - p.IOHDRSZ
|
||||
}
|
||||
fid.Mode = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates and opens a named file.
|
||||
// Returns the file if the operation is successful, or an Error.
|
||||
func (clnt *Clnt) FCreate(path string, perm uint32, mode uint8) (*File, error) {
|
||||
n := strings.LastIndex(path, "/")
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
|
||||
fid, err := clnt.FWalk(path[0:n])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if path[n] == '/' {
|
||||
n++
|
||||
}
|
||||
|
||||
err = clnt.Create(fid, path[n:], perm, mode, "")
|
||||
if err != nil {
|
||||
clnt.Clunk(fid)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &File{fid, 0}, nil
|
||||
}
|
||||
|
||||
// Opens a named file. Returns the opened file, or an Error.
|
||||
func (clnt *Clnt) FOpen(path string, mode uint8) (*File, error) {
|
||||
fid, err := clnt.FWalk(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = clnt.Open(fid, mode)
|
||||
if err != nil {
|
||||
clnt.Clunk(fid)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &File{fid, 0}, nil
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
var m2id = [...]uint8{
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 5,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 6,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 5,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 7,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 5,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 6,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 5,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 4,
|
||||
0, 1, 0, 2, 0, 1, 0, 3,
|
||||
0, 1, 0, 2, 0, 1, 0, 0,
|
||||
}
|
||||
|
||||
func newPool(maxid uint32) *pool {
|
||||
p := new(pool)
|
||||
p.maxid = maxid
|
||||
p.nchan = make(chan uint32)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *pool) getId() uint32 {
|
||||
var n uint32 = 0
|
||||
var ret uint32
|
||||
|
||||
p.Lock()
|
||||
for n = 0; n < uint32(len(p.imap)); n++ {
|
||||
if p.imap[n] != 0xFF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if int(n) >= len(p.imap) {
|
||||
m := uint32(len(p.imap) + 32)
|
||||
if uint32(m*8) > p.maxid {
|
||||
m = p.maxid/8 + 1
|
||||
}
|
||||
|
||||
b := make([]byte, m)
|
||||
copy(b, p.imap)
|
||||
p.imap = b
|
||||
}
|
||||
|
||||
if n >= uint32(len(p.imap)) {
|
||||
p.need++
|
||||
p.Unlock()
|
||||
ret = <-p.nchan
|
||||
} else {
|
||||
ret = uint32(m2id[p.imap[n]])
|
||||
p.imap[n] |= 1 << ret
|
||||
ret += n * 8
|
||||
p.Unlock()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *pool) putId(id uint32) {
|
||||
p.Lock()
|
||||
if p.need > 0 {
|
||||
p.nchan <- id
|
||||
p.need--
|
||||
p.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
p.imap[id/8] &= ^(1 << (id % 8))
|
||||
p.Unlock()
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
)
|
||||
|
||||
// Reads count bytes starting from offset from the file associated with the fid.
|
||||
// Returns a slice with the data read, if the operation was successful, or an
|
||||
// Error.
|
||||
func (clnt *Clnt) Read(fid *Fid, offset uint64, count uint32) ([]byte, error) {
|
||||
if count > fid.Iounit {
|
||||
count = fid.Iounit
|
||||
}
|
||||
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTread(tc, fid.Fid, offset, count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rc.Data, nil
|
||||
}
|
||||
|
||||
// Reads up to len(buf) bytes from the File. Returns the number
|
||||
// of bytes read, or an Error.
|
||||
func (file *File) Read(buf []byte) (int, error) {
|
||||
n, err := file.ReadAt(buf, int64(file.offset))
|
||||
if err == nil {
|
||||
file.offset += uint64(n)
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Reads up to len(buf) bytes from the file starting from offset.
|
||||
// Returns the number of bytes read, or an Error.
|
||||
func (file *File) ReadAt(buf []byte, offset int64) (int, error) {
|
||||
b, err := file.fid.Clnt.Read(file.fid, uint64(offset), uint32(len(buf)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
copy(buf, b)
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Reads exactly len(buf) bytes from the File starting from offset.
|
||||
// Returns the number of bytes read (could be less than len(buf) if
|
||||
// end-of-file is reached), or an Error.
|
||||
func (file *File) Readn(buf []byte, offset uint64) (int, error) {
|
||||
ret := 0
|
||||
for len(buf) > 0 {
|
||||
n, err := file.ReadAt(buf, int64(offset))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
offset += uint64(n)
|
||||
ret += n
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Reads the content of the directory associated with the File.
|
||||
// Returns an array of maximum num entries (if num is 0, returns
|
||||
// all entries from the directory). If the operation fails, returns
|
||||
// an Error.
|
||||
func (file *File) Readdir(num int) ([]*p.Dir, error) {
|
||||
buf := make([]byte, file.fid.Clnt.Msize-p.IOHDRSZ)
|
||||
dirs := make([]*p.Dir, 32)
|
||||
pos := 0
|
||||
for {
|
||||
n, err := file.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for b := buf[0:n]; len(b) > 0; {
|
||||
d, perr := p.UnpackDir(b, file.fid.Clnt.Dotu)
|
||||
if perr != nil {
|
||||
return nil, perr
|
||||
}
|
||||
|
||||
b = b[d.Size+2:]
|
||||
if pos >= len(dirs) {
|
||||
s := make([]*p.Dir, len(dirs)+32)
|
||||
copy(s, dirs)
|
||||
dirs = s
|
||||
}
|
||||
|
||||
dirs[pos] = d
|
||||
pos++
|
||||
if num != 0 && pos >= num {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dirs[0:pos], nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import "k8s.io/minikube/third_party/go9p/p"
|
||||
|
||||
// Removes the file associated with the Fid. Returns nil if the
|
||||
// operation is successful.
|
||||
func (clnt *Clnt) Remove(fid *Fid) error {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTremove(tc, fid.Fid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = clnt.Rpc(tc)
|
||||
clnt.fidpool.putId(fid.Fid)
|
||||
fid.Fid = p.NOFID
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Removes the named file. Returns nil if the operation is successful.
|
||||
func (clnt *Clnt) FRemove(path string) error {
|
||||
var err error
|
||||
fid, err := clnt.FWalk(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clnt.Remove(fid)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
)
|
||||
|
||||
var Eisdir = &p.Error{"file is a directory", p.EIO}
|
||||
var Enegoff = &p.Error{"negative i/o offset", p.EIO}
|
||||
|
||||
// Seek sets the offset for the next Read or Write to offset,
|
||||
// interpreted according to whence: 0 means relative to the origin of
|
||||
// the file, 1 means relative to the current offset, and 2 means
|
||||
// relative to the end. Seek returns the new offset and an error, if
|
||||
// any.
|
||||
//
|
||||
// Seeking to a negative offset is an error, and results in Enegoff.
|
||||
// Seeking to 0 in a directory is only valid if whence is 0. Seek returns
|
||||
// Eisdir otherwise.
|
||||
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
||||
var off int64
|
||||
|
||||
switch whence {
|
||||
case 0:
|
||||
// origin
|
||||
off = offset
|
||||
if f.fid.Qid.Type&p.QTDIR > 0 && off != 0 {
|
||||
return 0, Eisdir
|
||||
}
|
||||
|
||||
case 1:
|
||||
// current
|
||||
if f.fid.Qid.Type&p.QTDIR > 0 {
|
||||
return 0, Eisdir
|
||||
}
|
||||
off = offset + int64(f.offset)
|
||||
|
||||
case 2:
|
||||
// end
|
||||
if f.fid.Qid.Type&p.QTDIR > 0 {
|
||||
return 0, Eisdir
|
||||
}
|
||||
|
||||
dir, err := f.fid.Clnt.Stat(f.fid)
|
||||
if err != nil {
|
||||
return 0, &p.Error{"stat error in seek: " + err.Error(), p.EIO}
|
||||
}
|
||||
off = int64(dir.Length) + offset
|
||||
|
||||
default:
|
||||
return 0, &p.Error{"bad whence in seek", p.EIO}
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
return 0, Enegoff
|
||||
}
|
||||
f.offset = uint64(off)
|
||||
|
||||
return off, nil
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import "k8s.io/minikube/third_party/go9p/p"
|
||||
|
||||
// Returns the metadata for the file associated with the Fid, or an Error.
|
||||
func (clnt *Clnt) Stat(fid *Fid) (*p.Dir, error) {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTstat(tc, fid.Fid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rc.Dir, nil
|
||||
}
|
||||
|
||||
// Returns the metadata for a named file, or an Error.
|
||||
func (clnt *Clnt) FStat(path string) (*p.Dir, error) {
|
||||
fid, err := clnt.FWalk(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d, err := clnt.Stat(fid)
|
||||
clnt.Clunk(fid)
|
||||
return d, err
|
||||
}
|
||||
|
||||
// Modifies the data of the file associated with the Fid, or an Error.
|
||||
func (clnt *Clnt) Wstat(fid *Fid, dir *p.Dir) error {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTwstat(tc, fid.Fid, dir, clnt.Dotu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = clnt.Rpc(tc)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// +build httpstats
|
||||
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (clnt *Clnt) ServeHTTP(c http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(c, fmt.Sprintf("<html><body><h1>Client %s</h1>", clnt.Id))
|
||||
defer io.WriteString(c, "</body></html>")
|
||||
|
||||
// fcalls
|
||||
if clnt.Debuglevel&DbgLogFcalls != 0 {
|
||||
fs := clnt.Log.Filter(clnt, DbgLogFcalls)
|
||||
io.WriteString(c, fmt.Sprintf("<h2>Last %d 9P messages</h2>", len(fs)))
|
||||
for _, l := range fs {
|
||||
fc := l.Data.(*p.Fcall)
|
||||
if fc.Type != 0 {
|
||||
io.WriteString(c, fmt.Sprintf("<br>%s", fc))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clntServeHTTP(c http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(c, fmt.Sprintf("<html><body>"))
|
||||
defer io.WriteString(c, "</body></html>")
|
||||
|
||||
clnts.Lock()
|
||||
if clnts.clntList == nil {
|
||||
io.WriteString(c, "no clients")
|
||||
}
|
||||
|
||||
for clnt := clnts.clntList; clnt != nil; clnt = clnt.next {
|
||||
io.WriteString(c, fmt.Sprintf("<a href='/go9p/clnt/%s'>%s</a><br>", clnt.Id, clnt.Id))
|
||||
}
|
||||
clnts.Unlock()
|
||||
}
|
||||
|
||||
func (clnt *Clnt) statsRegister() {
|
||||
http.Handle("/go9p/clnt/"+clnt.Id, clnt)
|
||||
}
|
||||
|
||||
func (clnt *Clnt) statsUnregister() {
|
||||
http.Handle("/go9p/clnt/"+clnt.Id, nil)
|
||||
}
|
||||
|
||||
func (c *ClntList) statsRegister() {
|
||||
http.HandleFunc("/go9p/clnt", clntServeHTTP)
|
||||
}
|
||||
|
||||
func (c *ClntList) statsUnregister() {
|
||||
http.HandleFunc("/go9p/clnt", nil)
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import "k8s.io/minikube/third_party/go9p/p"
|
||||
|
||||
type Tag struct {
|
||||
clnt *Clnt
|
||||
tag uint16
|
||||
reqchan chan *Req
|
||||
respchan chan *Req
|
||||
donechan chan bool
|
||||
}
|
||||
|
||||
func (clnt *Clnt) TagAlloc(reqchan chan *Req) *Tag {
|
||||
tag := new(Tag)
|
||||
tag.clnt = clnt
|
||||
tag.tag = uint16(clnt.tagpool.getId())
|
||||
tag.reqchan = reqchan
|
||||
tag.respchan = make(chan *Req, 16)
|
||||
tag.donechan = make(chan bool)
|
||||
go tag.reqproc()
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
func (clnt *Clnt) TagFree(tag *Tag) {
|
||||
tag.donechan <- true
|
||||
clnt.tagpool.putId(uint32(tag.tag))
|
||||
}
|
||||
|
||||
func (tag *Tag) reqAlloc() *Req {
|
||||
r := new(Req)
|
||||
r.tag = tag.tag
|
||||
r.Clnt = tag.clnt
|
||||
r.Done = tag.respchan
|
||||
r.Tc = tag.clnt.NewFcall()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (tag *Tag) ReqFree(r *Req) {
|
||||
tag.clnt.FreeFcall(r.Tc)
|
||||
}
|
||||
|
||||
func (tag *Tag) reqproc() {
|
||||
for {
|
||||
select {
|
||||
case <-tag.donechan:
|
||||
return
|
||||
|
||||
case r := <-tag.respchan:
|
||||
rc := r.Rc
|
||||
fid := r.fid
|
||||
err := r.Rc.Type == p.Rerror
|
||||
|
||||
switch r.Tc.Type {
|
||||
case p.Tauth:
|
||||
if err {
|
||||
fid.User = nil
|
||||
}
|
||||
|
||||
case p.Tattach:
|
||||
if !err {
|
||||
fid.Qid = rc.Qid
|
||||
} else {
|
||||
fid.User = nil
|
||||
}
|
||||
|
||||
case p.Twalk:
|
||||
if !err {
|
||||
fid.walked = true
|
||||
if len(rc.Wqid) > 0 {
|
||||
fid.Qid = rc.Wqid[len(rc.Wqid)-1]
|
||||
}
|
||||
} else {
|
||||
fid.User = nil
|
||||
}
|
||||
|
||||
case p.Topen:
|
||||
case p.Tcreate:
|
||||
if !err {
|
||||
fid.Iounit = rc.Iounit
|
||||
fid.Qid = rc.Qid
|
||||
} else {
|
||||
fid.Mode = 0
|
||||
}
|
||||
|
||||
case p.Tclunk:
|
||||
case p.Tremove:
|
||||
tag.clnt.fidpool.putId(fid.Fid)
|
||||
}
|
||||
|
||||
tag.reqchan <- r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tag *Tag) Auth(afid *Fid, user p.User, aname string) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = afid
|
||||
err := p.PackTauth(req.Tc, afid.Fid, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
afid.User = user
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Attach(fid, afid *Fid, user p.User, aname string) error {
|
||||
var afno uint32
|
||||
|
||||
if afid != nil {
|
||||
afno = afid.Fid
|
||||
} else {
|
||||
afno = p.NOFID
|
||||
}
|
||||
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTattach(req.Tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fid.User = user
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Walk(fid *Fid, newfid *Fid, wnames []string) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = newfid
|
||||
if len(wnames) == 0 {
|
||||
newfid.Qid = fid.Qid
|
||||
}
|
||||
|
||||
err := p.PackTwalk(req.Tc, fid.Fid, newfid.Fid, wnames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newfid.User = fid.User
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Open(fid *Fid, mode uint8) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTopen(req.Tc, fid.Fid, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fid.Mode = mode
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTcreate(req.Tc, fid.Fid, name, perm, mode, ext, tag.clnt.Dotu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fid.Mode = mode
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Read(fid *Fid, offset uint64, count uint32) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTread(req.Tc, fid.Fid, offset, count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Write(fid *Fid, data []byte, offset uint64) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTwrite(req.Tc, fid.Fid, offset, uint32(len(data)), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Clunk(fid *Fid) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTclunk(req.Tc, fid.Fid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Remove(fid *Fid) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTremove(req.Tc, fid.Fid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Stat(fid *Fid) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTstat(req.Tc, fid.Fid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
||||
|
||||
func (tag *Tag) Wstat(fid *Fid, dir *p.Dir) error {
|
||||
req := tag.reqAlloc()
|
||||
req.fid = fid
|
||||
err := p.PackTwstat(req.Tc, fid.Fid, dir, tag.clnt.Dotu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tag.clnt.Rpcnb(req)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import (
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Starting from the file associated with fid, walks all wnames in
|
||||
// sequence and associates the resulting file with newfid. If no wnames
|
||||
// were walked successfully, an Error is returned. Otherwise a slice with a
|
||||
// Qid for each walked name is returned.
|
||||
func (clnt *Clnt) Walk(fid *Fid, newfid *Fid, wnames []string) ([]p.Qid, error) {
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newfid.walked = true
|
||||
return rc.Wqid, nil
|
||||
}
|
||||
|
||||
// Walks to a named file. Returns a Fid associated with the file,
|
||||
// or an Error.
|
||||
func (clnt *Clnt) FWalk(path string) (*Fid, error) {
|
||||
var err error = nil
|
||||
|
||||
var i, m int
|
||||
for i = 0; i < len(path); i++ {
|
||||
if path[i] != '/' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
path = path[i:]
|
||||
}
|
||||
|
||||
wnames := strings.Split(path, "/")
|
||||
newfid := clnt.FidAlloc()
|
||||
fid := clnt.Root
|
||||
newfid.User = fid.User
|
||||
|
||||
/* get rid of the empty names */
|
||||
for i, m = 0, 0; i < len(wnames); i++ {
|
||||
if wnames[i] != "" {
|
||||
wnames[m] = wnames[i]
|
||||
m++
|
||||
}
|
||||
}
|
||||
|
||||
wnames = wnames[0:m]
|
||||
for {
|
||||
n := len(wnames)
|
||||
if n > 16 {
|
||||
n = 16
|
||||
}
|
||||
|
||||
tc := clnt.NewFcall()
|
||||
err = p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames[0:n])
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
var rc *p.Fcall
|
||||
rc, err = clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
newfid.walked = true
|
||||
if len(rc.Wqid) != n {
|
||||
err = &p.Error{"file not found", p.ENOENT}
|
||||
goto error
|
||||
}
|
||||
|
||||
if len(rc.Wqid) > 0 {
|
||||
newfid.Qid = rc.Wqid[len(rc.Wqid)-1]
|
||||
} else {
|
||||
newfid.Qid = fid.Qid
|
||||
}
|
||||
|
||||
wnames = wnames[n:]
|
||||
fid = newfid
|
||||
if len(wnames) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return newfid, nil
|
||||
|
||||
error:
|
||||
clnt.Clunk(newfid)
|
||||
return nil, err
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package clnt
|
||||
|
||||
import "k8s.io/minikube/third_party/go9p/p"
|
||||
|
||||
// Write up to len(data) bytes starting from offset. Returns the
|
||||
// number of bytes written, or an Error.
|
||||
func (clnt *Clnt) Write(fid *Fid, data []byte, offset uint64) (int, error) {
|
||||
if uint32(len(data)) > fid.Iounit {
|
||||
data = data[0:fid.Iounit]
|
||||
}
|
||||
|
||||
tc := clnt.NewFcall()
|
||||
err := p.PackTwrite(tc, fid.Fid, offset, uint32(len(data)), data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rc, err := clnt.Rpc(tc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(rc.Count), nil
|
||||
}
|
||||
|
||||
// Writes up to len(buf) bytes to a file. Returns the number of
|
||||
// bytes written, or an Error.
|
||||
func (file *File) Write(buf []byte) (int, error) {
|
||||
n, err := file.WriteAt(buf, int64(file.offset))
|
||||
if err == nil {
|
||||
file.offset += uint64(n)
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Writes up to len(buf) bytes starting from offset. Returns the number
|
||||
// of bytes written, or an Error.
|
||||
func (file *File) WriteAt(buf []byte, offset int64) (int, error) {
|
||||
return file.fid.Clnt.Write(file.fid, buf, uint64(offset))
|
||||
}
|
||||
|
||||
// Writes exactly len(buf) bytes starting from offset. Returns the number of
|
||||
// bytes written. If Error is returned the number of bytes can be less
|
||||
// than len(buf).
|
||||
func (file *File) Writen(buf []byte, offset uint64) (int, error) {
|
||||
ret := 0
|
||||
for len(buf) > 0 {
|
||||
n, err := file.WriteAt(buf, int64(offset))
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
offset += uint64(n)
|
||||
ret += n
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
import "fmt"
|
||||
|
||||
func permToString(perm uint32) string {
|
||||
ret := ""
|
||||
|
||||
if perm&DMDIR != 0 {
|
||||
ret += "d"
|
||||
}
|
||||
|
||||
if perm&DMAPPEND != 0 {
|
||||
ret += "a"
|
||||
}
|
||||
|
||||
if perm&DMAUTH != 0 {
|
||||
ret += "A"
|
||||
}
|
||||
|
||||
if perm&DMEXCL != 0 {
|
||||
ret += "l"
|
||||
}
|
||||
|
||||
if perm&DMTMP != 0 {
|
||||
ret += "t"
|
||||
}
|
||||
|
||||
if perm&DMDEVICE != 0 {
|
||||
ret += "D"
|
||||
}
|
||||
|
||||
if perm&DMSOCKET != 0 {
|
||||
ret += "S"
|
||||
}
|
||||
|
||||
if perm&DMNAMEDPIPE != 0 {
|
||||
ret += "P"
|
||||
}
|
||||
|
||||
if perm&DMSYMLINK != 0 {
|
||||
ret += "L"
|
||||
}
|
||||
|
||||
ret += fmt.Sprintf("%o", perm&0777)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (qid *Qid) String() string {
|
||||
b := ""
|
||||
if qid.Type&QTDIR != 0 {
|
||||
b += "d"
|
||||
}
|
||||
if qid.Type&QTAPPEND != 0 {
|
||||
b += "a"
|
||||
}
|
||||
if qid.Type&QTAUTH != 0 {
|
||||
b += "A"
|
||||
}
|
||||
if qid.Type&QTEXCL != 0 {
|
||||
b += "l"
|
||||
}
|
||||
if qid.Type&QTTMP != 0 {
|
||||
b += "t"
|
||||
}
|
||||
if qid.Type&QTSYMLINK != 0 {
|
||||
b += "L"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("(%x %x '%s')", qid.Path, qid.Version, b)
|
||||
}
|
||||
|
||||
func (d *Dir) String() string {
|
||||
ret := fmt.Sprintf("'%s' '%s' '%s' '%s' q ", d.Name, d.Uid, d.Gid, d.Muid)
|
||||
ret += d.Qid.String() + " m " + permToString(d.Mode)
|
||||
ret += fmt.Sprintf(" at %d mt %d l %d t %d d %d", d.Atime, d.Mtime,
|
||||
d.Length, d.Type, d.Dev)
|
||||
|
||||
/* dotu ? */
|
||||
ret += " ext " + d.Ext
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (fc *Fcall) String() string {
|
||||
ret := ""
|
||||
|
||||
switch fc.Type {
|
||||
default:
|
||||
ret = fmt.Sprintf("invalid call: %d", fc.Type)
|
||||
case Tversion:
|
||||
ret = fmt.Sprintf("Tversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
|
||||
case Rversion:
|
||||
ret = fmt.Sprintf("Rversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
|
||||
case Tauth:
|
||||
ret = fmt.Sprintf("Tauth tag %d afid %d uname '%s' nuname %d aname '%s'",
|
||||
fc.Tag, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
|
||||
case Rauth:
|
||||
ret = fmt.Sprintf("Rauth tag %d aqid %v", fc.Tag, &fc.Qid)
|
||||
case Rattach:
|
||||
ret = fmt.Sprintf("Rattach tag %d aqid %v", fc.Tag, &fc.Qid)
|
||||
case Tattach:
|
||||
ret = fmt.Sprintf("Tattach tag %d fid %d afid %d uname '%s' nuname %d aname '%s'",
|
||||
fc.Tag, fc.Fid, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
|
||||
case Tflush:
|
||||
ret = fmt.Sprintf("Tflush tag %d oldtag %d", fc.Tag, fc.Oldtag)
|
||||
case Rerror:
|
||||
ret = fmt.Sprintf("Rerror tag %d ename '%s' ecode %d", fc.Tag, fc.Error, fc.Errornum)
|
||||
case Twalk:
|
||||
ret = fmt.Sprintf("Twalk tag %d fid %d newfid %d ", fc.Tag, fc.Fid, fc.Newfid)
|
||||
for i := 0; i < len(fc.Wname); i++ {
|
||||
ret += fmt.Sprintf("%d:'%s' ", i, fc.Wname[i])
|
||||
}
|
||||
case Rwalk:
|
||||
ret = fmt.Sprintf("Rwalk tag %d ", fc.Tag)
|
||||
for i := 0; i < len(fc.Wqid); i++ {
|
||||
ret += fmt.Sprintf("%v ", &fc.Wqid[i])
|
||||
}
|
||||
case Topen:
|
||||
ret = fmt.Sprintf("Topen tag %d fid %d mode %x", fc.Tag, fc.Fid, fc.Mode)
|
||||
case Ropen:
|
||||
ret = fmt.Sprintf("Ropen tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
|
||||
case Rcreate:
|
||||
ret = fmt.Sprintf("Rcreate tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
|
||||
case Tcreate:
|
||||
ret = fmt.Sprintf("Tcreate tag %d fid %d name '%s' perm ", fc.Tag, fc.Fid, fc.Name)
|
||||
ret += permToString(fc.Perm)
|
||||
ret += fmt.Sprintf(" mode %x ", fc.Mode)
|
||||
case Tread:
|
||||
ret = fmt.Sprintf("Tread tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
|
||||
case Rread:
|
||||
ret = fmt.Sprintf("Rread tag %d count %d", fc.Tag, fc.Count)
|
||||
case Twrite:
|
||||
ret = fmt.Sprintf("Twrite tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
|
||||
case Rwrite:
|
||||
ret = fmt.Sprintf("Rwrite tag %d count %d", fc.Tag, fc.Count)
|
||||
case Tclunk:
|
||||
ret = fmt.Sprintf("Tclunk tag %d fid %d", fc.Tag, fc.Fid)
|
||||
case Rclunk:
|
||||
ret = fmt.Sprintf("Rclunk tag %d", fc.Tag)
|
||||
case Tremove:
|
||||
ret = fmt.Sprintf("Tremove tag %d fid %d", fc.Tag, fc.Fid)
|
||||
case Tstat:
|
||||
ret = fmt.Sprintf("Tstat tag %d fid %d", fc.Tag, fc.Fid)
|
||||
case Rstat:
|
||||
ret = fmt.Sprintf("Rstat tag %d st (%v)", fc.Tag, &fc.Dir)
|
||||
case Twstat:
|
||||
ret = fmt.Sprintf("Twstat tag %d fid %d st (%v)", fc.Tag, fc.Fid, &fc.Dir)
|
||||
case Rflush:
|
||||
ret = fmt.Sprintf("Rflush tag %d", fc.Tag)
|
||||
case Rremove:
|
||||
ret = fmt.Sprintf("Rremove tag %d", fc.Tag)
|
||||
case Rwstat:
|
||||
ret = fmt.Sprintf("Rwstat tag %d", fc.Tag)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package p
|
||||
|
||||
type Log struct {
|
||||
Data interface{}
|
||||
Owner interface{}
|
||||
Type int
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
items []*Log
|
||||
idx int
|
||||
logchan chan *Log
|
||||
fltchan chan *flt
|
||||
rszchan chan int
|
||||
}
|
||||
|
||||
type flt struct {
|
||||
owner interface{}
|
||||
itype int
|
||||
fltchan chan []*Log
|
||||
}
|
||||
|
||||
func NewLogger(sz int) *Logger {
|
||||
if sz == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
l := new(Logger)
|
||||
l.items = make([]*Log, sz)
|
||||
l.logchan = make(chan *Log, 16)
|
||||
l.fltchan = make(chan *flt)
|
||||
l.rszchan = make(chan int)
|
||||
|
||||
go l.doLog()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Resize(sz int) {
|
||||
if sz == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
l.rszchan <- sz
|
||||
}
|
||||
|
||||
func (l *Logger) Log(data, owner interface{}, itype int) {
|
||||
l.logchan <- &Log{data, owner, itype}
|
||||
}
|
||||
|
||||
func (l *Logger) Filter(owner interface{}, itype int) []*Log {
|
||||
c := make(chan []*Log)
|
||||
l.fltchan <- &flt{owner, itype, c}
|
||||
return <-c
|
||||
}
|
||||
|
||||
func (l *Logger) doLog() {
|
||||
for {
|
||||
select {
|
||||
case it := <-l.logchan:
|
||||
if l.idx >= len(l.items) {
|
||||
l.idx = 0
|
||||
}
|
||||
|
||||
l.items[l.idx] = it
|
||||
l.idx++
|
||||
|
||||
case sz := <-l.rszchan:
|
||||
it := make([]*Log, sz)
|
||||
for i, j := l.idx, 0; j < len(it); j++ {
|
||||
if i >= len(l.items) {
|
||||
i = 0
|
||||
}
|
||||
|
||||
it[j] = l.items[i]
|
||||
i++
|
||||
if i == l.idx {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
l.items = it
|
||||
l.idx = 0
|
||||
|
||||
case flt := <-l.fltchan:
|
||||
n := 0
|
||||
// we don't care about the order while counting
|
||||
for _, it := range l.items {
|
||||
if it == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if (flt.owner == nil || it.Owner == flt.owner) &&
|
||||
(flt.itype == 0 || it.Type == flt.itype) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
its := make([]*Log, n)
|
||||
for i, m := l.idx, 0; m < len(its); i++ {
|
||||
if i >= len(l.items) {
|
||||
i = 0
|
||||
}
|
||||
|
||||
it := l.items[i]
|
||||
if it != nil && (flt.owner == nil || it.Owner == flt.owner) &&
|
||||
(flt.itype == 0 || it.Type == flt.itype) {
|
||||
its[m] = it
|
||||
m++
|
||||
}
|
||||
}
|
||||
|
||||
flt.fltchan <- its
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
|
||||
type osUser struct {
|
||||
*user.User
|
||||
uid int
|
||||
gid int
|
||||
}
|
||||
|
||||
type osUsers struct {
|
||||
groups map[int]*osGroup
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// Simple Users implementation that defers to os/user and fakes
|
||||
// looking up groups by gid only.
|
||||
var OsUsers *osUsers
|
||||
|
||||
func (u *osUser) Name() string { return u.Username }
|
||||
|
||||
func (u *osUser) Id() int { return u.uid }
|
||||
|
||||
func (u *osUser) Groups() []Group { return []Group{OsUsers.Gid2Group(u.gid)} }
|
||||
|
||||
func (u *osUser) IsMember(g Group) bool { return u.gid == g.Id() }
|
||||
|
||||
type osGroup struct {
|
||||
gid int
|
||||
}
|
||||
|
||||
func (g *osGroup) Name() string { return "" }
|
||||
|
||||
func (g *osGroup) Id() int { return g.gid }
|
||||
|
||||
func (g *osGroup) Members() []User { return nil }
|
||||
|
||||
func initOsusers() {
|
||||
OsUsers = new(osUsers)
|
||||
OsUsers.groups = make(map[int]*osGroup)
|
||||
}
|
||||
|
||||
func newUser(u *user.User) *osUser {
|
||||
uid, uerr := strconv.Atoi(u.Uid)
|
||||
gid, gerr := strconv.Atoi(u.Gid)
|
||||
if uerr != nil || gerr != nil {
|
||||
/* non-numeric uid/gid => unsupported system */
|
||||
return nil
|
||||
}
|
||||
return &osUser{u, uid, gid}
|
||||
}
|
||||
|
||||
func (up *osUsers) Uid2User(uid int) User {
|
||||
u, err := user.LookupId(strconv.Itoa(uid))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return newUser(u)
|
||||
}
|
||||
|
||||
func (up *osUsers) Uname2User(uname string) User {
|
||||
u, err := user.Lookup(uname)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return newUser(u)
|
||||
}
|
||||
|
||||
func (up *osUsers) Gid2Group(gid int) Group {
|
||||
once.Do(initOsusers)
|
||||
OsUsers.Lock()
|
||||
group, present := OsUsers.groups[gid]
|
||||
if present {
|
||||
OsUsers.Unlock()
|
||||
return group
|
||||
}
|
||||
|
||||
group = new(osGroup)
|
||||
group.gid = gid
|
||||
OsUsers.groups[gid] = group
|
||||
OsUsers.Unlock()
|
||||
return group
|
||||
}
|
||||
|
||||
func (up *osUsers) Gname2Group(gname string) Group {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,529 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The p9 package provides the definitions and functions used to implement
|
||||
// the 9P2000 protocol.
|
||||
package p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 9P2000 message types
|
||||
const (
|
||||
Tversion = 100 + iota
|
||||
Rversion
|
||||
Tauth
|
||||
Rauth
|
||||
Tattach
|
||||
Rattach
|
||||
Terror
|
||||
Rerror
|
||||
Tflush
|
||||
Rflush
|
||||
Twalk
|
||||
Rwalk
|
||||
Topen
|
||||
Ropen
|
||||
Tcreate
|
||||
Rcreate
|
||||
Tread
|
||||
Rread
|
||||
Twrite
|
||||
Rwrite
|
||||
Tclunk
|
||||
Rclunk
|
||||
Tremove
|
||||
Rremove
|
||||
Tstat
|
||||
Rstat
|
||||
Twstat
|
||||
Rwstat
|
||||
Tlast
|
||||
)
|
||||
|
||||
const (
|
||||
MSIZE = 8192 + IOHDRSZ // default message size (8192+IOHdrSz)
|
||||
IOHDRSZ = 24 // the non-data size of the Twrite messages
|
||||
PORT = 564 // default port for 9P file servers
|
||||
)
|
||||
|
||||
// Qid types
|
||||
const (
|
||||
QTDIR = 0x80 // directories
|
||||
QTAPPEND = 0x40 // append only files
|
||||
QTEXCL = 0x20 // exclusive use files
|
||||
QTMOUNT = 0x10 // mounted channel
|
||||
QTAUTH = 0x08 // authentication file
|
||||
QTTMP = 0x04 // non-backed-up file
|
||||
QTSYMLINK = 0x02 // symbolic link (Unix, 9P2000.u)
|
||||
QTLINK = 0x01 // hard link (Unix, 9P2000.u)
|
||||
QTFILE = 0x00
|
||||
)
|
||||
|
||||
// Flags for the mode field in Topen and Tcreate messages
|
||||
const (
|
||||
OREAD = 0 // open read-only
|
||||
OWRITE = 1 // open write-only
|
||||
ORDWR = 2 // open read-write
|
||||
OEXEC = 3 // execute (== read but check execute permission)
|
||||
OTRUNC = 16 // or'ed in (except for exec), truncate file first
|
||||
OCEXEC = 32 // or'ed in, close on exec
|
||||
ORCLOSE = 64 // or'ed in, remove on close
|
||||
)
|
||||
|
||||
// File modes
|
||||
const (
|
||||
DMDIR = 0x80000000 // mode bit for directories
|
||||
DMAPPEND = 0x40000000 // mode bit for append only files
|
||||
DMEXCL = 0x20000000 // mode bit for exclusive use files
|
||||
DMMOUNT = 0x10000000 // mode bit for mounted channel
|
||||
DMAUTH = 0x08000000 // mode bit for authentication file
|
||||
DMTMP = 0x04000000 // mode bit for non-backed-up file
|
||||
DMSYMLINK = 0x02000000 // mode bit for symbolic link (Unix, 9P2000.u)
|
||||
DMLINK = 0x01000000 // mode bit for hard link (Unix, 9P2000.u)
|
||||
DMDEVICE = 0x00800000 // mode bit for device file (Unix, 9P2000.u)
|
||||
DMNAMEDPIPE = 0x00200000 // mode bit for named pipe (Unix, 9P2000.u)
|
||||
DMSOCKET = 0x00100000 // mode bit for socket (Unix, 9P2000.u)
|
||||
DMSETUID = 0x00080000 // mode bit for setuid (Unix, 9P2000.u)
|
||||
DMSETGID = 0x00040000 // mode bit for setgid (Unix, 9P2000.u)
|
||||
DMREAD = 0x4 // mode bit for read permission
|
||||
DMWRITE = 0x2 // mode bit for write permission
|
||||
DMEXEC = 0x1 // mode bit for execute permission
|
||||
)
|
||||
|
||||
const (
|
||||
NOTAG uint16 = 0xFFFF // no tag specified
|
||||
NOFID uint32 = 0xFFFFFFFF // no fid specified
|
||||
NOUID uint32 = 0xFFFFFFFF // no uid specified
|
||||
)
|
||||
|
||||
// Error values
|
||||
const (
|
||||
EPERM = 1
|
||||
ENOENT = 2
|
||||
EIO = 5
|
||||
EEXIST = 17
|
||||
ENOTDIR = 20
|
||||
EINVAL = 22
|
||||
)
|
||||
|
||||
// Error represents a 9P2000 (and 9P2000.u) error
|
||||
type Error struct {
|
||||
Err string // textual representation of the error
|
||||
Errornum uint32 // numeric representation of the error (9P2000.u)
|
||||
}
|
||||
|
||||
// File identifier
|
||||
type Qid struct {
|
||||
Type uint8 // type of the file (high 8 bits of the mode)
|
||||
Version uint32 // version number for the path
|
||||
Path uint64 // server's unique identification of the file
|
||||
}
|
||||
|
||||
// Dir describes a file
|
||||
type Dir struct {
|
||||
Size uint16 // size-2 of the Dir on the wire
|
||||
Type uint16
|
||||
Dev uint32
|
||||
Qid // file's Qid
|
||||
Mode uint32 // permissions and flags
|
||||
Atime uint32 // last access time in seconds
|
||||
Mtime uint32 // last modified time in seconds
|
||||
Length uint64 // file length in bytes
|
||||
Name string // file name
|
||||
Uid string // owner name
|
||||
Gid string // group name
|
||||
Muid string // name of the last user that modified the file
|
||||
|
||||
/* 9P2000.u extension */
|
||||
Ext string // special file's descriptor
|
||||
Uidnum uint32 // owner ID
|
||||
Gidnum uint32 // group ID
|
||||
Muidnum uint32 // ID of the last user that modified the file
|
||||
}
|
||||
|
||||
// Fcall represents a 9P2000 message
|
||||
type Fcall struct {
|
||||
Size uint32 // size of the message
|
||||
Type uint8 // message type
|
||||
Fid uint32 // file identifier
|
||||
Tag uint16 // message tag
|
||||
Msize uint32 // maximum message size (used by Tversion, Rversion)
|
||||
Version string // protocol version (used by Tversion, Rversion)
|
||||
Oldtag uint16 // tag of the message to flush (used by Tflush)
|
||||
Error string // error (used by Rerror)
|
||||
Qid // file Qid (used by Rauth, Rattach, Ropen, Rcreate)
|
||||
Iounit uint32 // maximum bytes read without breaking in multiple messages (used by Ropen, Rcreate)
|
||||
Afid uint32 // authentication fid (used by Tauth, Tattach)
|
||||
Uname string // user name (used by Tauth, Tattach)
|
||||
Aname string // attach name (used by Tauth, Tattach)
|
||||
Perm uint32 // file permission (mode) (used by Tcreate)
|
||||
Name string // file name (used by Tcreate)
|
||||
Mode uint8 // open mode (used by Topen, Tcreate)
|
||||
Newfid uint32 // the fid that represents the file walked to (used by Twalk)
|
||||
Wname []string // list of names to walk (used by Twalk)
|
||||
Wqid []Qid // list of Qids for the walked files (used by Rwalk)
|
||||
Offset uint64 // offset in the file to read/write from/to (used by Tread, Twrite)
|
||||
Count uint32 // number of bytes read/written (used by Tread, Rread, Twrite, Rwrite)
|
||||
Data []uint8 // data read/to-write (used by Rread, Twrite)
|
||||
Dir // file description (used by Rstat, Twstat)
|
||||
|
||||
/* 9P2000.u extensions */
|
||||
Errornum uint32 // error code, 9P2000.u only (used by Rerror)
|
||||
Ext string // special file description, 9P2000.u only (used by Tcreate)
|
||||
Unamenum uint32 // user ID, 9P2000.u only (used by Tauth, Tattach)
|
||||
|
||||
Pkt []uint8 // raw packet data
|
||||
Buf []uint8 // buffer to put the raw data in
|
||||
}
|
||||
|
||||
// Interface for accessing users and groups
|
||||
type Users interface {
|
||||
Uid2User(uid int) User
|
||||
Uname2User(uname string) User
|
||||
Gid2Group(gid int) Group
|
||||
Gname2Group(gname string) Group
|
||||
}
|
||||
|
||||
// Represents a user
|
||||
type User interface {
|
||||
Name() string // user name
|
||||
Id() int // user id
|
||||
Groups() []Group // groups the user belongs to (can return nil)
|
||||
IsMember(g Group) bool // returns true if the user is member of the specified group
|
||||
}
|
||||
|
||||
// Represents a group of users
|
||||
type Group interface {
|
||||
Name() string // group name
|
||||
Id() int // group id
|
||||
Members() []User // list of members that belong to the group (can return nil)
|
||||
}
|
||||
|
||||
// minimum size of a 9P2000 message for a type
|
||||
var minFcsize = [...]uint32{
|
||||
6, /* Tversion msize[4] version[s] */
|
||||
6, /* Rversion msize[4] version[s] */
|
||||
8, /* Tauth fid[4] uname[s] aname[s] */
|
||||
13, /* Rauth aqid[13] */
|
||||
12, /* Tattach fid[4] afid[4] uname[s] aname[s] */
|
||||
13, /* Rattach qid[13] */
|
||||
0, /* Terror */
|
||||
2, /* Rerror ename[s] (ecode[4]) */
|
||||
2, /* Tflush oldtag[2] */
|
||||
0, /* Rflush */
|
||||
10, /* Twalk fid[4] newfid[4] nwname[2] */
|
||||
2, /* Rwalk nwqid[2] */
|
||||
5, /* Topen fid[4] mode[1] */
|
||||
17, /* Ropen qid[13] iounit[4] */
|
||||
11, /* Tcreate fid[4] name[s] perm[4] mode[1] */
|
||||
17, /* Rcreate qid[13] iounit[4] */
|
||||
16, /* Tread fid[4] offset[8] count[4] */
|
||||
4, /* Rread count[4] */
|
||||
16, /* Twrite fid[4] offset[8] count[4] */
|
||||
4, /* Rwrite count[4] */
|
||||
4, /* Tclunk fid[4] */
|
||||
0, /* Rclunk */
|
||||
4, /* Tremove fid[4] */
|
||||
0, /* Rremove */
|
||||
4, /* Tstat fid[4] */
|
||||
4, /* Rstat stat[n] */
|
||||
8, /* Twstat fid[4] stat[n] */
|
||||
0, /* Rwstat */
|
||||
20, /* Tbread fileid[8] offset[8] count[4] */
|
||||
4, /* Rbread count[4] */
|
||||
20, /* Tbwrite fileid[8] offset[8] count[4] */
|
||||
4, /* Rbwrite count[4] */
|
||||
16, /* Tbtrunc fileid[8] offset[8] */
|
||||
0, /* Rbtrunc */
|
||||
}
|
||||
|
||||
// minimum size of a 9P2000.u message for a type
|
||||
var minFcusize = [...]uint32{
|
||||
6, /* Tversion msize[4] version[s] */
|
||||
6, /* Rversion msize[4] version[s] */
|
||||
12, /* Tauth fid[4] uname[s] aname[s] */
|
||||
13, /* Rauth aqid[13] */
|
||||
16, /* Tattach fid[4] afid[4] uname[s] aname[s] */
|
||||
13, /* Rattach qid[13] */
|
||||
0, /* Terror */
|
||||
6, /* Rerror ename[s] (ecode[4]) */
|
||||
2, /* Tflush oldtag[2] */
|
||||
0, /* Rflush */
|
||||
10, /* Twalk fid[4] newfid[4] nwname[2] */
|
||||
2, /* Rwalk nwqid[2] */
|
||||
5, /* Topen fid[4] mode[1] */
|
||||
17, /* Ropen qid[13] iounit[4] */
|
||||
13, /* Tcreate fid[4] name[s] perm[4] mode[1] */
|
||||
17, /* Rcreate qid[13] iounit[4] */
|
||||
16, /* Tread fid[4] offset[8] count[4] */
|
||||
4, /* Rread count[4] */
|
||||
16, /* Twrite fid[4] offset[8] count[4] */
|
||||
4, /* Rwrite count[4] */
|
||||
4, /* Tclunk fid[4] */
|
||||
0, /* Rclunk */
|
||||
4, /* Tremove fid[4] */
|
||||
0, /* Rremove */
|
||||
4, /* Tstat fid[4] */
|
||||
4, /* Rstat stat[n] */
|
||||
8, /* Twstat fid[4] stat[n] */
|
||||
20, /* Tbread fileid[8] offset[8] count[4] */
|
||||
4, /* Rbread count[4] */
|
||||
20, /* Tbwrite fileid[8] offset[8] count[4] */
|
||||
4, /* Rbwrite count[4] */
|
||||
16, /* Tbtrunc fileid[8] offset[8] */
|
||||
0, /* Rbtrunc */
|
||||
}
|
||||
|
||||
func gint8(buf []byte) (uint8, []byte) { return buf[0], buf[1:] }
|
||||
|
||||
func gint16(buf []byte) (uint16, []byte) {
|
||||
return uint16(buf[0]) | (uint16(buf[1]) << 8), buf[2:]
|
||||
}
|
||||
|
||||
func gint32(buf []byte) (uint32, []byte) {
|
||||
return uint32(buf[0]) | (uint32(buf[1]) << 8) | (uint32(buf[2]) << 16) |
|
||||
(uint32(buf[3]) << 24),
|
||||
buf[4:]
|
||||
}
|
||||
|
||||
func Gint32(buf []byte) (uint32, []byte) { return gint32(buf) }
|
||||
|
||||
func gint64(buf []byte) (uint64, []byte) {
|
||||
return uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) |
|
||||
(uint64(buf[3]) << 24) | (uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) |
|
||||
(uint64(buf[6]) << 48) | (uint64(buf[7]) << 56),
|
||||
buf[8:]
|
||||
}
|
||||
|
||||
func gstr(buf []byte) (string, []byte) {
|
||||
var n uint16
|
||||
|
||||
if buf == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
n, buf = gint16(buf)
|
||||
if int(n) > len(buf) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return string(buf[0:n]), buf[n:]
|
||||
}
|
||||
|
||||
func gqid(buf []byte, qid *Qid) []byte {
|
||||
qid.Type, buf = gint8(buf)
|
||||
qid.Version, buf = gint32(buf)
|
||||
qid.Path, buf = gint64(buf)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func gstat(buf []byte, d *Dir, dotu bool) []byte {
|
||||
d.Size, buf = gint16(buf)
|
||||
d.Type, buf = gint16(buf)
|
||||
d.Dev, buf = gint32(buf)
|
||||
buf = gqid(buf, &d.Qid)
|
||||
d.Mode, buf = gint32(buf)
|
||||
d.Atime, buf = gint32(buf)
|
||||
d.Mtime, buf = gint32(buf)
|
||||
d.Length, buf = gint64(buf)
|
||||
d.Name, buf = gstr(buf)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Uid, buf = gstr(buf)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
d.Gid, buf = gstr(buf)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Muid, buf = gstr(buf)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dotu {
|
||||
d.Ext, buf = gstr(buf)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Uidnum, buf = gint32(buf)
|
||||
d.Gidnum, buf = gint32(buf)
|
||||
d.Muidnum, buf = gint32(buf)
|
||||
} else {
|
||||
d.Uidnum = NOUID
|
||||
d.Gidnum = NOUID
|
||||
d.Muidnum = NOUID
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func pint8(val uint8, buf []byte) []byte {
|
||||
buf[0] = val
|
||||
return buf[1:]
|
||||
}
|
||||
|
||||
func pint16(val uint16, buf []byte) []byte {
|
||||
buf[0] = uint8(val)
|
||||
buf[1] = uint8(val >> 8)
|
||||
return buf[2:]
|
||||
}
|
||||
|
||||
func pint32(val uint32, buf []byte) []byte {
|
||||
buf[0] = uint8(val)
|
||||
buf[1] = uint8(val >> 8)
|
||||
buf[2] = uint8(val >> 16)
|
||||
buf[3] = uint8(val >> 24)
|
||||
return buf[4:]
|
||||
}
|
||||
|
||||
func pint64(val uint64, buf []byte) []byte {
|
||||
buf[0] = uint8(val)
|
||||
buf[1] = uint8(val >> 8)
|
||||
buf[2] = uint8(val >> 16)
|
||||
buf[3] = uint8(val >> 24)
|
||||
buf[4] = uint8(val >> 32)
|
||||
buf[5] = uint8(val >> 40)
|
||||
buf[6] = uint8(val >> 48)
|
||||
buf[7] = uint8(val >> 56)
|
||||
return buf[8:]
|
||||
}
|
||||
|
||||
func pstr(val string, buf []byte) []byte {
|
||||
n := uint16(len(val))
|
||||
buf = pint16(n, buf)
|
||||
b := []byte(val)
|
||||
copy(buf, b)
|
||||
return buf[n:]
|
||||
}
|
||||
|
||||
func pqid(val *Qid, buf []byte) []byte {
|
||||
buf = pint8(val.Type, buf)
|
||||
buf = pint32(val.Version, buf)
|
||||
buf = pint64(val.Path, buf)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func statsz(d *Dir, dotu bool) int {
|
||||
sz := 2 + 2 + 4 + 13 + 4 + 4 + 4 + 8 + 2 + 2 + 2 + 2 + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
|
||||
if dotu {
|
||||
sz += 2 + 4 + 4 + 4 + len(d.Ext)
|
||||
}
|
||||
|
||||
return sz
|
||||
}
|
||||
|
||||
func pstat(d *Dir, buf []byte, dotu bool) []byte {
|
||||
sz := statsz(d, dotu)
|
||||
buf = pint16(uint16(sz-2), buf)
|
||||
buf = pint16(d.Type, buf)
|
||||
buf = pint32(d.Dev, buf)
|
||||
buf = pqid(&d.Qid, buf)
|
||||
buf = pint32(d.Mode, buf)
|
||||
buf = pint32(d.Atime, buf)
|
||||
buf = pint32(d.Mtime, buf)
|
||||
buf = pint64(d.Length, buf)
|
||||
buf = pstr(d.Name, buf)
|
||||
buf = pstr(d.Uid, buf)
|
||||
buf = pstr(d.Gid, buf)
|
||||
buf = pstr(d.Muid, buf)
|
||||
if dotu {
|
||||
buf = pstr(d.Ext, buf)
|
||||
buf = pint32(d.Uidnum, buf)
|
||||
buf = pint32(d.Gidnum, buf)
|
||||
buf = pint32(d.Muidnum, buf)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// Converts a Dir value to its on-the-wire representation and writes it to
|
||||
// the buf. Returns the number of bytes written, 0 if there is not enough space.
|
||||
func PackDir(d *Dir, buf []byte, dotu bool) int {
|
||||
sz := statsz(d, dotu)
|
||||
if sz > len(buf) {
|
||||
return 0
|
||||
}
|
||||
|
||||
buf = pstat(d, buf, dotu)
|
||||
return sz
|
||||
}
|
||||
|
||||
// Converts the on-the-wire representation of a stat to Stat value.
|
||||
// Returns an error if the conversion is impossible, otherwise
|
||||
// a pointer to a Stat value.
|
||||
func UnpackDir(buf []byte, dotu bool) (d *Dir, err error) {
|
||||
sz := 2 + 2 + 4 + 13 + 4 + /* size[2] type[2] dev[4] qid[13] mode[4] */
|
||||
4 + 4 + 8 + /* atime[4] mtime[4] length[8] */
|
||||
2 + 2 + 2 + 2 /* name[s] uid[s] gid[s] muid[s] */
|
||||
|
||||
if dotu {
|
||||
sz += 2 + 4 + 4 + 4 /* extension[s] n_uid[4] n_gid[4] n_muid[4] */
|
||||
}
|
||||
|
||||
if len(buf) < sz {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
d = new(Dir)
|
||||
buf = gstat(buf, d, dotu)
|
||||
if buf == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
return d, nil
|
||||
|
||||
szerror:
|
||||
return nil, &Error{"short buffer", EINVAL}
|
||||
|
||||
}
|
||||
|
||||
// Allocates a new Fcall.
|
||||
func NewFcall(sz uint32) *Fcall {
|
||||
fc := new(Fcall)
|
||||
fc.Buf = make([]byte, sz)
|
||||
|
||||
return fc
|
||||
}
|
||||
|
||||
// Sets the tag of a Fcall.
|
||||
func SetTag(fc *Fcall, tag uint16) {
|
||||
fc.Tag = tag
|
||||
pint16(tag, fc.Pkt[5:])
|
||||
}
|
||||
|
||||
func packCommon(fc *Fcall, size int, id uint8) ([]byte, error) {
|
||||
size += 4 + 1 + 2 /* size[4] id[1] tag[2] */
|
||||
if len(fc.Buf) < int(size) {
|
||||
return nil, &Error{"buffer too small", EINVAL}
|
||||
}
|
||||
|
||||
fc.Size = uint32(size)
|
||||
fc.Type = id
|
||||
fc.Tag = NOTAG
|
||||
p := fc.Buf
|
||||
p = pint32(uint32(size), p)
|
||||
p = pint8(id, p)
|
||||
p = pint16(NOTAG, p)
|
||||
fc.Pkt = fc.Buf[0:size]
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%s: %d", err.Err, err.Errornum)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
// Create a Rversion message in the specified Fcall.
|
||||
func PackRversion(fc *Fcall, msize uint32, version string) error {
|
||||
size := 4 + 2 + len(version) /* msize[4] version[s] */
|
||||
p, err := packCommon(fc, size, Rversion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Msize = msize
|
||||
fc.Version = version
|
||||
p = pint32(msize, p)
|
||||
p = pstr(version, p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rauth message in the specified Fcall.
|
||||
func PackRauth(fc *Fcall, aqid *Qid) error {
|
||||
size := 13 /* aqid[13] */
|
||||
p, err := packCommon(fc, size, Rauth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Qid = *aqid
|
||||
p = pqid(aqid, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rerror message in the specified Fcall. If dotu is true,
|
||||
// the function will create a 9P2000.u message. If false, nerror is
|
||||
// ignored.
|
||||
func PackRerror(fc *Fcall, error string, errornum uint32, dotu bool) error {
|
||||
size := 2 + len(error) /* ename[s] */
|
||||
if dotu {
|
||||
size += 4 /* ecode[4] */
|
||||
}
|
||||
|
||||
p, err := packCommon(fc, size, Rerror)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Error = error
|
||||
p = pstr(error, p)
|
||||
if dotu {
|
||||
fc.Errornum = errornum
|
||||
p = pint32(errornum, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rflush message in the specified Fcall.
|
||||
func PackRflush(fc *Fcall) error {
|
||||
_, err := packCommon(fc, 0, Rflush)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a Rattach message in the specified Fcall.
|
||||
func PackRattach(fc *Fcall, aqid *Qid) error {
|
||||
size := 13 /* aqid[13] */
|
||||
p, err := packCommon(fc, size, Rattach)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Qid = *aqid
|
||||
p = pqid(aqid, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rwalk message in the specified Fcall.
|
||||
func PackRwalk(fc *Fcall, wqids []Qid) error {
|
||||
nwqid := len(wqids)
|
||||
size := 2 + nwqid*13 /* nwqid[2] nwname*wqid[13] */
|
||||
p, err := packCommon(fc, size, Rwalk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p = pint16(uint16(nwqid), p)
|
||||
fc.Wqid = make([]Qid, nwqid)
|
||||
for i := 0; i < nwqid; i++ {
|
||||
fc.Wqid[i] = wqids[i]
|
||||
p = pqid(&wqids[i], p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Ropen message in the specified Fcall.
|
||||
func PackRopen(fc *Fcall, qid *Qid, iounit uint32) error {
|
||||
size := 13 + 4 /* qid[13] iounit[4] */
|
||||
p, err := packCommon(fc, size, Ropen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Qid = *qid
|
||||
fc.Iounit = iounit
|
||||
p = pqid(qid, p)
|
||||
p = pint32(iounit, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rcreate message in the specified Fcall.
|
||||
func PackRcreate(fc *Fcall, qid *Qid, iounit uint32) error {
|
||||
size := 13 + 4 /* qid[13] iounit[4] */
|
||||
p, err := packCommon(fc, size, Rcreate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Qid = *qid
|
||||
fc.Iounit = iounit
|
||||
p = pqid(qid, p)
|
||||
p = pint32(iounit, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Initializes the specified Fcall value to contain Rread message.
|
||||
// The user should copy the returned data to the slice pointed by
|
||||
// fc.Data and call SetRreadCount to update the data size to the
|
||||
// actual value.
|
||||
func InitRread(fc *Fcall, count uint32) error {
|
||||
size := int(4 + count) /* count[4] data[count] */
|
||||
p, err := packCommon(fc, size, Rread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Count = count
|
||||
fc.Data = p[4 : fc.Count+4]
|
||||
p = pint32(count, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Updates the size of the data returned by Rread. Expects that
|
||||
// the Fcall value is already initialized by InitRread.
|
||||
func SetRreadCount(fc *Fcall, count uint32) {
|
||||
/* we need to update both the packet size as well as the data count */
|
||||
size := 4 + 1 + 2 + 4 + count /* size[4] id[1] tag[2] count[4] data[count] */
|
||||
pint32(size, fc.Pkt)
|
||||
pint32(count, fc.Pkt[7:])
|
||||
fc.Size = size
|
||||
fc.Count = count
|
||||
fc.Pkt = fc.Pkt[0:size]
|
||||
fc.Data = fc.Data[0:count]
|
||||
fc.Size = size
|
||||
}
|
||||
|
||||
// Create a Rread message in the specified Fcall.
|
||||
func PackRread(fc *Fcall, data []byte) error {
|
||||
count := uint32(len(data))
|
||||
err := InitRread(fc, count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(fc.Data, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rwrite message in the specified Fcall.
|
||||
func PackRwrite(fc *Fcall, count uint32) error {
|
||||
p, err := packCommon(fc, 4, Rwrite) /* count[4] */
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Count = count
|
||||
|
||||
p = pint32(count, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rclunk message in the specified Fcall.
|
||||
func PackRclunk(fc *Fcall) error {
|
||||
_, err := packCommon(fc, 0, Rclunk)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a Rremove message in the specified Fcall.
|
||||
func PackRremove(fc *Fcall) error {
|
||||
_, err := packCommon(fc, 0, Rremove)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a Rstat message in the specified Fcall. If dotu is true, the
|
||||
// function will create a 9P2000.u stat representation that includes
|
||||
// st.Nuid, st.Ngid, st.Nmuid and st.Ext. Otherwise these values will be
|
||||
// ignored.
|
||||
func PackRstat(fc *Fcall, d *Dir, dotu bool) error {
|
||||
stsz := statsz(d, dotu)
|
||||
size := 2 + stsz /* stat[n] */
|
||||
p, err := packCommon(fc, size, Rstat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p = pint16(uint16(stsz), p)
|
||||
p = pstat(d, p, dotu)
|
||||
fc.Dir = *d
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Rwstat message in the specified Fcall.
|
||||
func PackRwstat(fc *Fcall) error {
|
||||
_, err := packCommon(fc, 0, Rwstat)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
// Create a Tversion message in the specified Fcall.
|
||||
func PackTversion(fc *Fcall, msize uint32, version string) error {
|
||||
size := 4 + 2 + len(version) /* msize[4] version[s] */
|
||||
p, err := packCommon(fc, size, Tversion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Msize = msize
|
||||
fc.Version = version
|
||||
p = pint32(msize, p)
|
||||
p = pstr(version, p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tauth message in the specified Fcall.
|
||||
func PackTauth(fc *Fcall, fid uint32, uname string, aname string, unamenum uint32, dotu bool) error {
|
||||
size := 4 + 2 + 2 + len(uname) + len(aname) /* fid[4] uname[s] aname[s] */
|
||||
if dotu {
|
||||
size += 4 /* n_uname[4] */
|
||||
}
|
||||
|
||||
p, err := packCommon(fc, size, Tauth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Uname = uname
|
||||
fc.Aname = aname
|
||||
p = pint32(fid, p)
|
||||
p = pstr(uname, p)
|
||||
p = pstr(aname, p)
|
||||
if dotu {
|
||||
fc.Unamenum = unamenum
|
||||
p = pint32(unamenum, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tflush message in the specified Fcall.
|
||||
func PackTflush(fc *Fcall, oldtag uint16) error {
|
||||
p, err := packCommon(fc, 2, Tflush)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Oldtag = oldtag
|
||||
p = pint16(oldtag, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tattach message in the specified Fcall. If dotu is true,
|
||||
// the function will create 9P2000.u including the nuname value, otherwise
|
||||
// nuname is ignored.
|
||||
func PackTattach(fc *Fcall, fid uint32, afid uint32, uname string, aname string, unamenum uint32, dotu bool) error {
|
||||
size := 4 + 4 + 2 + len(uname) + 2 + len(aname) /* fid[4] afid[4] uname[s] aname[s] */
|
||||
if dotu {
|
||||
size += 4
|
||||
}
|
||||
|
||||
p, err := packCommon(fc, size, Tattach)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Afid = afid
|
||||
fc.Uname = uname
|
||||
fc.Aname = aname
|
||||
p = pint32(fid, p)
|
||||
p = pint32(afid, p)
|
||||
p = pstr(uname, p)
|
||||
p = pstr(aname, p)
|
||||
if dotu {
|
||||
fc.Unamenum = unamenum
|
||||
p = pint32(unamenum, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Twalk message in the specified Fcall.
|
||||
func PackTwalk(fc *Fcall, fid uint32, newfid uint32, wnames []string) error {
|
||||
nwname := len(wnames)
|
||||
size := 4 + 4 + 2 + nwname*2 /* fid[4] newfid[4] nwname[2] nwname*wname[s] */
|
||||
for i := 0; i < nwname; i++ {
|
||||
size += len(wnames[i])
|
||||
}
|
||||
|
||||
p, err := packCommon(fc, size, Twalk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Newfid = newfid
|
||||
p = pint32(fid, p)
|
||||
p = pint32(newfid, p)
|
||||
p = pint16(uint16(nwname), p)
|
||||
fc.Wname = make([]string, nwname)
|
||||
for i := 0; i < nwname; i++ {
|
||||
fc.Wname[i] = wnames[i]
|
||||
p = pstr(wnames[i], p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Topen message in the specified Fcall.
|
||||
func PackTopen(fc *Fcall, fid uint32, mode uint8) error {
|
||||
size := 4 + 1 /* fid[4] mode[1] */
|
||||
p, err := packCommon(fc, size, Topen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Mode = mode
|
||||
p = pint32(fid, p)
|
||||
p = pint8(mode, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tcreate message in the specified Fcall. If dotu is true,
|
||||
// the function will create a 9P2000.u message that includes ext.
|
||||
// Otherwise the ext value is ignored.
|
||||
func PackTcreate(fc *Fcall, fid uint32, name string, perm uint32, mode uint8, ext string, dotu bool) error {
|
||||
size := 4 + 2 + len(name) + 4 + 1 /* fid[4] name[s] perm[4] mode[1] */
|
||||
|
||||
if dotu {
|
||||
size += 2 + len(ext)
|
||||
}
|
||||
|
||||
p, err := packCommon(fc, size, Tcreate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Name = name
|
||||
fc.Perm = perm
|
||||
fc.Mode = mode
|
||||
p = pint32(fid, p)
|
||||
p = pstr(name, p)
|
||||
p = pint32(perm, p)
|
||||
p = pint8(mode, p)
|
||||
|
||||
if dotu {
|
||||
fc.Ext = ext
|
||||
p = pstr(ext, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tread message in the specified Fcall.
|
||||
func PackTread(fc *Fcall, fid uint32, offset uint64, count uint32) error {
|
||||
size := 4 + 8 + 4 /* fid[4] offset[8] count[4] */
|
||||
p, err := packCommon(fc, size, Tread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Offset = offset
|
||||
fc.Count = count
|
||||
p = pint32(fid, p)
|
||||
p = pint64(offset, p)
|
||||
p = pint32(count, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Twrite message in the specified Fcall.
|
||||
func PackTwrite(fc *Fcall, fid uint32, offset uint64, count uint32, data []byte) error {
|
||||
c := len(data)
|
||||
size := 4 + 8 + 4 + c /* fid[4] offset[8] count[4] data[count] */
|
||||
p, err := packCommon(fc, size, Twrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Offset = offset
|
||||
fc.Count = count
|
||||
p = pint32(fid, p)
|
||||
p = pint64(offset, p)
|
||||
p = pint32(count, p)
|
||||
fc.Data = p
|
||||
copy(fc.Data, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tclunk message in the specified Fcall.
|
||||
func PackTclunk(fc *Fcall, fid uint32) error {
|
||||
p, err := packCommon(fc, 4, Tclunk) /* fid[4] */
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
p = pint32(fid, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tremove message in the specified Fcall.
|
||||
func PackTremove(fc *Fcall, fid uint32) error {
|
||||
p, err := packCommon(fc, 4, Tremove) /* fid[4] */
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
p = pint32(fid, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Tstat message in the specified Fcall.
|
||||
func PackTstat(fc *Fcall, fid uint32) error {
|
||||
p, err := packCommon(fc, 4, Tstat) /* fid[4] */
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
p = pint32(fid, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a Twstat message in the specified Fcall. If dotu is true
|
||||
// the function will create 9P2000.u message, otherwise the 9P2000.u
|
||||
// specific fields from the Stat value will be ignored.
|
||||
func PackTwstat(fc *Fcall, fid uint32, d *Dir, dotu bool) error {
|
||||
stsz := statsz(d, dotu)
|
||||
size := 4 + 2 + stsz /* fid[4] stat[n] */
|
||||
p, err := packCommon(fc, size, Twstat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc.Fid = fid
|
||||
fc.Dir = *d
|
||||
p = pint32(fid, p)
|
||||
p = pint16(uint16(stsz), p)
|
||||
p = pstat(d, p, dotu)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package srv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
func (srv *Srv) NewConn(c net.Conn) {
|
||||
conn := new(Conn)
|
||||
conn.Srv = srv
|
||||
conn.Msize = srv.Msize
|
||||
conn.Dotu = srv.Dotu
|
||||
conn.Debuglevel = srv.Debuglevel
|
||||
conn.conn = c
|
||||
conn.fidpool = make(map[uint32]*Fid)
|
||||
conn.reqs = make(map[uint16]*Req)
|
||||
conn.reqout = make(chan *Req, srv.Maxpend)
|
||||
conn.done = make(chan bool)
|
||||
conn.rchan = make(chan *p.Fcall, 64)
|
||||
|
||||
srv.Lock()
|
||||
if srv.conns == nil {
|
||||
srv.conns = make(map[*Conn]*Conn)
|
||||
}
|
||||
srv.conns[conn] = conn
|
||||
srv.Unlock()
|
||||
|
||||
conn.Id = c.RemoteAddr().String()
|
||||
if op, ok := (conn.Srv.ops).(ConnOps); ok {
|
||||
op.ConnOpened(conn)
|
||||
}
|
||||
|
||||
if sop, ok := (interface{}(conn)).(StatsOps); ok {
|
||||
sop.statsRegister()
|
||||
}
|
||||
|
||||
go conn.recv()
|
||||
go conn.send()
|
||||
}
|
||||
|
||||
func (conn *Conn) close() {
|
||||
conn.done <- true
|
||||
conn.Srv.Lock()
|
||||
delete(conn.Srv.conns, conn)
|
||||
conn.Srv.Unlock()
|
||||
|
||||
if sop, ok := (interface{}(conn)).(StatsOps); ok {
|
||||
sop.statsUnregister()
|
||||
}
|
||||
if op, ok := (conn.Srv.ops).(ConnOps); ok {
|
||||
op.ConnClosed(conn)
|
||||
}
|
||||
|
||||
/* call FidDestroy for all remaining fids */
|
||||
if op, ok := (conn.Srv.ops).(FidOps); ok {
|
||||
for _, fid := range conn.fidpool {
|
||||
op.FidDestroy(fid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) recv() {
|
||||
var err error
|
||||
var n int
|
||||
|
||||
buf := make([]byte, conn.Msize*8)
|
||||
pos := 0
|
||||
for {
|
||||
if len(buf) < int(conn.Msize) {
|
||||
b := make([]byte, conn.Msize*8)
|
||||
copy(b, buf[0:pos])
|
||||
buf = b
|
||||
b = nil
|
||||
}
|
||||
|
||||
n, err = conn.conn.Read(buf[pos:])
|
||||
if err != nil || n == 0 {
|
||||
conn.close()
|
||||
return
|
||||
}
|
||||
|
||||
pos += n
|
||||
for pos > 4 {
|
||||
sz, _ := p.Gint32(buf)
|
||||
if sz > conn.Msize {
|
||||
log.Println("bad client connection: ", conn.conn.RemoteAddr())
|
||||
conn.conn.Close()
|
||||
conn.close()
|
||||
return
|
||||
}
|
||||
if pos < int(sz) {
|
||||
if len(buf) < int(sz) {
|
||||
b := make([]byte, conn.Msize*8)
|
||||
copy(b, buf[0:pos])
|
||||
buf = b
|
||||
b = nil
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
fc, err, fcsize := p.Unpack(buf, conn.Dotu)
|
||||
if err != nil {
|
||||
log.Println(fmt.Sprintf("invalid packet : %v %v", err, buf))
|
||||
conn.conn.Close()
|
||||
conn.close()
|
||||
return
|
||||
}
|
||||
|
||||
tag := fc.Tag
|
||||
req := new(Req)
|
||||
select {
|
||||
case req.Rc = <-conn.rchan:
|
||||
break
|
||||
default:
|
||||
req.Rc = p.NewFcall(conn.Msize)
|
||||
}
|
||||
|
||||
req.Conn = conn
|
||||
req.Tc = fc
|
||||
// req.Rc = rc
|
||||
if conn.Debuglevel > 0 {
|
||||
conn.logFcall(req.Tc)
|
||||
if conn.Debuglevel&DbgPrintPackets != 0 {
|
||||
log.Println(">->", conn.Id, fmt.Sprint(req.Tc.Pkt))
|
||||
}
|
||||
|
||||
if conn.Debuglevel&DbgPrintFcalls != 0 {
|
||||
log.Println(">>>", conn.Id, req.Tc.String())
|
||||
}
|
||||
}
|
||||
|
||||
conn.Lock()
|
||||
conn.nreqs++
|
||||
conn.tsz += uint64(fc.Size)
|
||||
conn.npend++
|
||||
if conn.npend > conn.maxpend {
|
||||
conn.maxpend = conn.npend
|
||||
}
|
||||
|
||||
req.next = conn.reqs[tag]
|
||||
conn.reqs[tag] = req
|
||||
process := req.next == nil
|
||||
if req.next != nil {
|
||||
req.next.prev = req
|
||||
}
|
||||
conn.Unlock()
|
||||
if process {
|
||||
go req.process()
|
||||
}
|
||||
|
||||
buf = buf[fcsize:]
|
||||
pos -= fcsize
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
func (conn *Conn) send() {
|
||||
for {
|
||||
select {
|
||||
case <-conn.done:
|
||||
return
|
||||
|
||||
case req := <-conn.reqout:
|
||||
p.SetTag(req.Rc, req.Tc.Tag)
|
||||
conn.Lock()
|
||||
conn.rsz += uint64(req.Rc.Size)
|
||||
conn.npend--
|
||||
conn.Unlock()
|
||||
if conn.Debuglevel > 0 {
|
||||
conn.logFcall(req.Rc)
|
||||
if conn.Debuglevel&DbgPrintPackets != 0 {
|
||||
log.Println("<-<", conn.Id, fmt.Sprint(req.Rc.Pkt))
|
||||
}
|
||||
|
||||
if conn.Debuglevel&DbgPrintFcalls != 0 {
|
||||
log.Println("<<<", conn.Id, req.Rc.String())
|
||||
}
|
||||
}
|
||||
|
||||
for buf := req.Rc.Pkt; len(buf) > 0; {
|
||||
n, err := conn.conn.Write(buf)
|
||||
if err != nil {
|
||||
/* just close the socket, will get signal on conn.done */
|
||||
log.Println("error while writing")
|
||||
conn.conn.Close()
|
||||
break
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
}
|
||||
|
||||
select {
|
||||
case conn.rchan <- req.Rc:
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
func (conn *Conn) RemoteAddr() net.Addr {
|
||||
return conn.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() net.Addr {
|
||||
return conn.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (conn *Conn) logFcall(fc *p.Fcall) {
|
||||
if conn.Debuglevel&DbgLogPackets != 0 {
|
||||
pkt := make([]byte, len(fc.Pkt))
|
||||
copy(pkt, fc.Pkt)
|
||||
conn.Srv.Log.Log(pkt, conn, DbgLogPackets)
|
||||
}
|
||||
|
||||
if conn.Debuglevel&DbgLogFcalls != 0 {
|
||||
f := new(p.Fcall)
|
||||
*f = *fc
|
||||
f.Pkt = nil
|
||||
conn.Srv.Log.Log(f, conn, DbgLogFcalls)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) StartNetListener(ntype, addr string) error {
|
||||
l, err := net.Listen(ntype, addr)
|
||||
if err != nil {
|
||||
return &p.Error{err.Error(), p.EIO}
|
||||
}
|
||||
|
||||
return srv.StartListener(l)
|
||||
}
|
||||
|
||||
// Start listening on the specified network and address for incoming
|
||||
// connections. Once a connection is established, create a new Conn
|
||||
// value, read messages from the socket, send them to the specified
|
||||
// server, and send back responses received from the server.
|
||||
func (srv *Srv) StartListener(l net.Listener) error {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return &p.Error{err.Error(), p.EIO}
|
||||
}
|
||||
|
||||
srv.NewConn(c)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// A synthetic filesystem emulating a persistent cloning interface
|
||||
// from Plan 9. Reading the /clone file creates new entries in the filesystem
|
||||
// each containing unique information/data. Clone files remember what it written
|
||||
// to them. Removing a clone file does what is expected.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ClFile struct {
|
||||
srv.File
|
||||
created string
|
||||
id int
|
||||
data []byte
|
||||
}
|
||||
|
||||
type Clone struct {
|
||||
srv.File
|
||||
clones int
|
||||
}
|
||||
|
||||
var addr = flag.String("addr", ":5640", "network address")
|
||||
var debug = flag.Bool("d", false, "print debug messages")
|
||||
|
||||
var root *srv.File
|
||||
|
||||
func (cl *ClFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
var b []byte
|
||||
if len(cl.data) == 0 {
|
||||
str := strconv.Itoa(cl.id) + " created on:" + cl.created
|
||||
b = []byte(str)
|
||||
} else {
|
||||
b = cl.data
|
||||
}
|
||||
n := len(b)
|
||||
if offset >= uint64(n) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
b = b[int(offset):n]
|
||||
n -= int(offset)
|
||||
if len(buf) < n {
|
||||
n = len(buf)
|
||||
}
|
||||
|
||||
copy(buf[offset:int(offset)+n], b[offset:])
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (cl *ClFile) Write(fid *srv.FFid, data []byte, offset uint64) (int, error) {
|
||||
n := uint64(len(cl.data))
|
||||
nlen := offset + uint64(len(data))
|
||||
if nlen > n {
|
||||
ndata := make([]byte, nlen)
|
||||
copy(ndata, cl.data[0:n])
|
||||
cl.data = ndata
|
||||
}
|
||||
|
||||
copy(cl.data[offset:], data[offset:])
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (cl *ClFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cl *ClFile) Remove(fid *srv.FFid) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cl *Clone) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
// we only allow a single read from us, change the offset and we're done
|
||||
if offset > uint64(0) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
cl.clones += 1
|
||||
ncl := new(ClFile)
|
||||
ncl.id = cl.clones
|
||||
ncl.created = time.Now().String()
|
||||
name := strconv.Itoa(ncl.id)
|
||||
|
||||
err := ncl.Add(root, name, p.OsUsers.Uid2User(os.Geteuid()), nil, 0666, ncl)
|
||||
if err != nil {
|
||||
return 0, &p.Error{"can not create file", 0}
|
||||
}
|
||||
|
||||
b := []byte(name)
|
||||
if len(buf) < len(b) {
|
||||
// cleanup
|
||||
ncl.File.Remove()
|
||||
return 0, &p.Error{"not enough buffer space for result", 0}
|
||||
}
|
||||
|
||||
copy(buf, b)
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var cl *Clone
|
||||
var s *srv.Fsrv
|
||||
|
||||
flag.Parse()
|
||||
user := p.OsUsers.Uid2User(os.Geteuid())
|
||||
root = new(srv.File)
|
||||
err = root.Add(nil, "/", user, nil, p.DMDIR|0777, nil)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
cl = new(Clone)
|
||||
err = cl.Add(root, "clone", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, cl)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
s = srv.NewFileSrv(root)
|
||||
s.Dotu = true
|
||||
|
||||
if *debug {
|
||||
s.Debuglevel = 1
|
||||
}
|
||||
|
||||
s.Start(s)
|
||||
err = s.StartNetListener("tcp", *addr)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println(fmt.Sprintf("Error: %s", err))
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Ramfs struct {
|
||||
srv *srv.Fsrv
|
||||
user p.User
|
||||
group p.Group
|
||||
blksz int
|
||||
blkchan chan []byte
|
||||
zero []byte // blksz array of zeroes
|
||||
}
|
||||
|
||||
type RFile struct {
|
||||
srv.File
|
||||
data [][]byte
|
||||
}
|
||||
|
||||
var addr = flag.String("addr", ":5640", "network address")
|
||||
var debug = flag.Int("d", 0, "debuglevel")
|
||||
var blksize = flag.Int("b", 8192, "block size")
|
||||
var logsz = flag.Int("l", 2048, "log size")
|
||||
var rsrv Ramfs
|
||||
|
||||
func (f *RFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
if offset > f.Length {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
count := uint32(len(buf))
|
||||
if offset+uint64(count) > f.Length {
|
||||
count = uint32(f.Length - offset)
|
||||
}
|
||||
|
||||
for n, off, b := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz), buf[0:count]; len(b) > 0; n++ {
|
||||
m := rsrv.blksz - int(off)
|
||||
if m > len(b) {
|
||||
m = len(b)
|
||||
}
|
||||
|
||||
blk := rsrv.zero
|
||||
if len(f.data[n]) != 0 {
|
||||
blk = f.data[n]
|
||||
}
|
||||
|
||||
// log.Stderr("read block", n, "off", off, "len", m, "l", len(blk), "ll", len(b))
|
||||
copy(b, blk[off:off+uint64(m)])
|
||||
b = b[m:]
|
||||
off = 0
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
func (f *RFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
// make sure the data array is big enough
|
||||
sz := offset + uint64(len(buf))
|
||||
if f.Length < sz {
|
||||
f.expand(sz)
|
||||
}
|
||||
|
||||
count := 0
|
||||
for n, off := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz); len(buf) > 0; n++ {
|
||||
blk := f.data[n]
|
||||
if len(blk) == 0 {
|
||||
select {
|
||||
case blk = <-rsrv.blkchan:
|
||||
break
|
||||
default:
|
||||
blk = make([]byte, rsrv.blksz)
|
||||
}
|
||||
|
||||
// if off>0 {
|
||||
copy(blk, rsrv.zero /*[0:off]*/)
|
||||
// }
|
||||
|
||||
f.data[n] = blk
|
||||
}
|
||||
|
||||
m := copy(blk[off:], buf)
|
||||
buf = buf[m:]
|
||||
count += m
|
||||
off = 0
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (f *RFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) {
|
||||
ff := new(RFile)
|
||||
err := ff.Add(&f.File, name, rsrv.user, rsrv.group, perm, ff)
|
||||
return &ff.File, err
|
||||
}
|
||||
|
||||
func (f *RFile) Remove(fid *srv.FFid) error {
|
||||
f.trunc(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
|
||||
var uid, gid uint32
|
||||
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
up := rsrv.srv.Upool
|
||||
uid = dir.Uidnum
|
||||
gid = dir.Gidnum
|
||||
if uid == p.NOUID && dir.Uid != "" {
|
||||
user := up.Uname2User(dir.Uid)
|
||||
if user == nil {
|
||||
return srv.Enouser
|
||||
}
|
||||
|
||||
f.Uidnum = uint32(user.Id())
|
||||
}
|
||||
|
||||
if gid == p.NOUID && dir.Gid != "" {
|
||||
group := up.Gname2Group(dir.Gid)
|
||||
if group == nil {
|
||||
return srv.Enouser
|
||||
}
|
||||
|
||||
f.Gidnum = uint32(group.Id())
|
||||
}
|
||||
|
||||
if dir.Mode != 0xFFFFFFFF {
|
||||
f.Mode = (f.Mode &^ 0777) | (dir.Mode & 0777)
|
||||
}
|
||||
|
||||
if dir.Name != "" {
|
||||
if err := f.Rename(dir.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dir.Length != 0xFFFFFFFFFFFFFFFF {
|
||||
f.trunc(dir.Length)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// called with f locked
|
||||
func (f *RFile) trunc(sz uint64) {
|
||||
if f.Length == sz {
|
||||
return
|
||||
}
|
||||
|
||||
if f.Length > sz {
|
||||
f.shrink(sz)
|
||||
} else {
|
||||
f.expand(sz)
|
||||
}
|
||||
}
|
||||
|
||||
// called with f lock held
|
||||
func (f *RFile) shrink(sz uint64) {
|
||||
blknum := sz / uint64(rsrv.blksz)
|
||||
off := sz % uint64(rsrv.blksz)
|
||||
if off > 0 {
|
||||
if len(f.data[blknum]) > 0 {
|
||||
copy(f.data[blknum][off:], rsrv.zero)
|
||||
}
|
||||
|
||||
blknum++
|
||||
}
|
||||
|
||||
for i := blknum; i < uint64(len(f.data)); i++ {
|
||||
if len(f.data[i]) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case rsrv.blkchan <- f.data[i]:
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
f.data = f.data[0:blknum]
|
||||
f.Length = sz
|
||||
}
|
||||
|
||||
func (f *RFile) expand(sz uint64) {
|
||||
blknum := sz / uint64(rsrv.blksz)
|
||||
if sz%uint64(rsrv.blksz) != 0 {
|
||||
blknum++
|
||||
}
|
||||
|
||||
data := make([][]byte, blknum)
|
||||
if f.data != nil {
|
||||
copy(data, f.data)
|
||||
}
|
||||
|
||||
f.data = data
|
||||
f.Length = sz
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var l *p.Logger
|
||||
|
||||
flag.Parse()
|
||||
rsrv.user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
rsrv.group = p.OsUsers.Gid2Group(os.Getegid())
|
||||
rsrv.blksz = *blksize
|
||||
rsrv.blkchan = make(chan []byte, 2048)
|
||||
rsrv.zero = make([]byte, rsrv.blksz)
|
||||
|
||||
root := new(RFile)
|
||||
err = root.Add(nil, "/", rsrv.user, nil, p.DMDIR|0777, root)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
l = p.NewLogger(*logsz)
|
||||
rsrv.srv = srv.NewFileSrv(&root.File)
|
||||
rsrv.srv.Dotu = true
|
||||
rsrv.srv.Debuglevel = *debug
|
||||
rsrv.srv.Start(rsrv.srv)
|
||||
rsrv.srv.Id = "ramfs"
|
||||
rsrv.srv.Log = l
|
||||
|
||||
err = rsrv.srv.StartNetListener("tcp", *addr)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println(fmt.Sprintf("Error: %s", err))
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Time struct {
|
||||
srv.File
|
||||
}
|
||||
type InfTime struct {
|
||||
srv.File
|
||||
}
|
||||
|
||||
var addr = flag.String("addr", ":5640", "network address")
|
||||
var debug = flag.Bool("d", false, "print debug messages")
|
||||
var debugall = flag.Bool("D", false, "print packets as well as debug messages")
|
||||
|
||||
func (*InfTime) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
// push out time ignoring offset (infinite read)
|
||||
t := time.Now().String() + "\n"
|
||||
b := []byte(t)
|
||||
ml := len(b)
|
||||
if ml > len(buf) {
|
||||
ml = len(buf)
|
||||
}
|
||||
|
||||
copy(buf, b[0:ml])
|
||||
return ml, nil
|
||||
}
|
||||
|
||||
func (*Time) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
b := []byte(time.Now().String())
|
||||
have := len(b)
|
||||
off := int(offset)
|
||||
|
||||
if off >= have {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return copy(buf, b[off:]), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var tm *Time
|
||||
var ntm *InfTime
|
||||
var s *srv.Fsrv
|
||||
|
||||
flag.Parse()
|
||||
user := p.OsUsers.Uid2User(os.Geteuid())
|
||||
root := new(srv.File)
|
||||
err = root.Add(nil, "/", user, nil, p.DMDIR|0555, nil)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
tm = new(Time)
|
||||
err = tm.Add(root, "time", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, tm)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
ntm = new(InfTime)
|
||||
err = ntm.Add(root, "inftime", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, ntm)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
s = srv.NewFileSrv(root)
|
||||
s.Dotu = true
|
||||
|
||||
if *debug {
|
||||
s.Debuglevel = 1
|
||||
}
|
||||
if *debugall {
|
||||
s.Debuglevel = 2
|
||||
}
|
||||
|
||||
s.Start(s)
|
||||
err = s.StartNetListener("tcp", *addr)
|
||||
if err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
error:
|
||||
log.Println(fmt.Sprintf("Error: %s", err))
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Listen on SSL connection, can be used as an example with p/clnt/examples/tls.go
|
||||
// Sample certificate was copied from the Go source code
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Ramfs struct {
|
||||
srv *srv.Fsrv
|
||||
user p.User
|
||||
group p.Group
|
||||
blksz int
|
||||
blkchan chan []byte
|
||||
zero []byte // blksz array of zeroes
|
||||
}
|
||||
|
||||
type RFile struct {
|
||||
srv.File
|
||||
data [][]byte
|
||||
}
|
||||
|
||||
var addr = flag.String("addr", ":5640", "network address")
|
||||
var debug = flag.Int("d", 0, "debuglevel")
|
||||
var blksize = flag.Int("b", 8192, "block size")
|
||||
var logsz = flag.Int("l", 2048, "log size")
|
||||
var rsrv Ramfs
|
||||
|
||||
func (f *RFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
if offset > f.Length {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
count := uint32(len(buf))
|
||||
if offset+uint64(count) > f.Length {
|
||||
count = uint32(f.Length - offset)
|
||||
}
|
||||
|
||||
for n, off, b := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz), buf[0:count]; len(b) > 0; n++ {
|
||||
m := rsrv.blksz - int(off)
|
||||
if m > len(b) {
|
||||
m = len(b)
|
||||
}
|
||||
|
||||
blk := rsrv.zero
|
||||
if len(f.data[n]) != 0 {
|
||||
blk = f.data[n]
|
||||
}
|
||||
|
||||
// log.Stderr("read block", n, "off", off, "len", m, "l", len(blk), "ll", len(b))
|
||||
copy(b, blk[off:off+uint64(m)])
|
||||
b = b[m:]
|
||||
off = 0
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
func (f *RFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
// make sure the data array is big enough
|
||||
sz := offset + uint64(len(buf))
|
||||
if f.Length < sz {
|
||||
f.expand(sz)
|
||||
}
|
||||
|
||||
count := 0
|
||||
for n, off := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz); len(buf) > 0; n++ {
|
||||
blk := f.data[n]
|
||||
if len(blk) == 0 {
|
||||
select {
|
||||
case blk = <-rsrv.blkchan:
|
||||
break
|
||||
default:
|
||||
blk = make([]byte, rsrv.blksz)
|
||||
}
|
||||
|
||||
// if off>0 {
|
||||
copy(blk, rsrv.zero /*[0:off]*/)
|
||||
// }
|
||||
|
||||
f.data[n] = blk
|
||||
}
|
||||
|
||||
m := copy(blk[off:], buf)
|
||||
buf = buf[m:]
|
||||
count += m
|
||||
off = 0
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (f *RFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) {
|
||||
ff := new(RFile)
|
||||
err := ff.Add(&f.File, name, rsrv.user, rsrv.group, perm, ff)
|
||||
return &ff.File, err
|
||||
}
|
||||
|
||||
func (f *RFile) Remove(fid *srv.FFid) error {
|
||||
f.trunc(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
|
||||
var uid, gid uint32
|
||||
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
up := rsrv.srv.Upool
|
||||
uid = dir.Uidnum
|
||||
gid = dir.Gidnum
|
||||
if uid == p.NOUID && dir.Uid != "" {
|
||||
user := up.Uname2User(dir.Uid)
|
||||
if user == nil {
|
||||
return srv.Enouser
|
||||
}
|
||||
|
||||
f.Uidnum = uint32(user.Id())
|
||||
}
|
||||
|
||||
if gid == p.NOUID && dir.Gid != "" {
|
||||
group := up.Gname2Group(dir.Gid)
|
||||
if group == nil {
|
||||
return srv.Enouser
|
||||
}
|
||||
|
||||
f.Gidnum = uint32(group.Id())
|
||||
}
|
||||
|
||||
if dir.Mode != 0xFFFFFFFF {
|
||||
f.Mode = (f.Mode &^ 0777) | (dir.Mode & 0777)
|
||||
}
|
||||
|
||||
if dir.Name != "" {
|
||||
if err := f.Rename(dir.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dir.Length != 0xFFFFFFFFFFFFFFFF {
|
||||
f.trunc(dir.Length)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// called with f locked
|
||||
func (f *RFile) trunc(sz uint64) {
|
||||
if f.Length == sz {
|
||||
return
|
||||
}
|
||||
|
||||
if f.Length > sz {
|
||||
f.shrink(sz)
|
||||
} else {
|
||||
f.expand(sz)
|
||||
}
|
||||
}
|
||||
|
||||
// called with f lock held
|
||||
func (f *RFile) shrink(sz uint64) {
|
||||
blknum := sz / uint64(rsrv.blksz)
|
||||
off := sz % uint64(rsrv.blksz)
|
||||
if off > 0 {
|
||||
if len(f.data[blknum]) > 0 {
|
||||
copy(f.data[blknum][off:], rsrv.zero)
|
||||
}
|
||||
|
||||
blknum++
|
||||
}
|
||||
|
||||
for i := blknum; i < uint64(len(f.data)); i++ {
|
||||
if len(f.data[i]) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case rsrv.blkchan <- f.data[i]:
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
f.data = f.data[0:blknum]
|
||||
f.Length = sz
|
||||
}
|
||||
|
||||
func (f *RFile) expand(sz uint64) {
|
||||
blknum := sz / uint64(rsrv.blksz)
|
||||
if sz%uint64(rsrv.blksz) != 0 {
|
||||
blknum++
|
||||
}
|
||||
|
||||
data := make([][]byte, blknum)
|
||||
if f.data != nil {
|
||||
copy(data, f.data)
|
||||
}
|
||||
|
||||
f.data = data
|
||||
f.Length = sz
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.Parse()
|
||||
rsrv.user = p.OsUsers.Uid2User(os.Geteuid())
|
||||
rsrv.group = p.OsUsers.Gid2Group(os.Getegid())
|
||||
rsrv.blksz = *blksize
|
||||
rsrv.blkchan = make(chan []byte, 2048)
|
||||
rsrv.zero = make([]byte, rsrv.blksz)
|
||||
|
||||
root := new(RFile)
|
||||
err = root.Add(nil, "/", rsrv.user, nil, p.DMDIR|0777, root)
|
||||
if err != nil {
|
||||
log.Println(fmt.Sprintf("Error: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
l := p.NewLogger(*logsz)
|
||||
rsrv.srv = srv.NewFileSrv(&root.File)
|
||||
rsrv.srv.Dotu = true
|
||||
rsrv.srv.Debuglevel = *debug
|
||||
rsrv.srv.Start(rsrv.srv)
|
||||
rsrv.srv.Id = "ramfs"
|
||||
rsrv.srv.Log = l
|
||||
|
||||
cert := make([]tls.Certificate, 1)
|
||||
cert[0].Certificate = [][]byte{testCertificate}
|
||||
cert[0].PrivateKey = testPrivateKey
|
||||
|
||||
ls, oerr := tls.Listen("tcp", *addr, &tls.Config{
|
||||
Rand: rand.Reader,
|
||||
Certificates: cert,
|
||||
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if oerr != nil {
|
||||
log.Println("can't listen:", oerr)
|
||||
return
|
||||
}
|
||||
|
||||
err = rsrv.srv.StartListener(ls)
|
||||
if err != nil {
|
||||
log.Println(fmt.Sprintf("Error: %s", err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Copied from crypto/tls/handshake_server_test.go from the Go repository
|
||||
func bigFromString(s string) *big.Int {
|
||||
ret := new(big.Int)
|
||||
ret.SetString(s, 10)
|
||||
return ret
|
||||
}
|
||||
|
||||
func fromHex(s string) []byte {
|
||||
b, _ := hex.DecodeString(s)
|
||||
return b
|
||||
}
|
||||
|
||||
var testCertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
|
||||
|
||||
var testPrivateKey = &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
|
||||
E: 65537,
|
||||
},
|
||||
D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"),
|
||||
Primes: []*big.Int{
|
||||
bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"),
|
||||
bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"),
|
||||
},
|
||||
}
|
|
@ -0,0 +1,474 @@
|
|||
// Copyright 2009 The go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ufs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
)
|
||||
|
||||
type Fid struct {
|
||||
path string
|
||||
file *os.File
|
||||
dirs []os.FileInfo
|
||||
diroffset uint64
|
||||
st os.FileInfo
|
||||
}
|
||||
|
||||
type Ufs struct {
|
||||
srv.Srv
|
||||
}
|
||||
|
||||
var addr string
|
||||
var debug int
|
||||
var root string
|
||||
var Enoent = &p.Error{"file not found", p.ENOENT}
|
||||
|
||||
func toError(err error) *p.Error {
|
||||
var ecode uint32
|
||||
|
||||
ename := err.Error()
|
||||
if e, ok := err.(syscall.Errno); ok {
|
||||
ecode = uint32(e)
|
||||
} else {
|
||||
ecode = p.EIO
|
||||
}
|
||||
|
||||
return &p.Error{ename, ecode}
|
||||
}
|
||||
|
||||
func (fid *Fid) stat() *p.Error {
|
||||
var err error
|
||||
|
||||
fid.st, err = os.Lstat(fid.path)
|
||||
if err != nil {
|
||||
return toError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func omode2uflags(mode uint8) int {
|
||||
ret := int(0)
|
||||
switch mode & 3 {
|
||||
case p.OREAD:
|
||||
ret = os.O_RDONLY
|
||||
break
|
||||
|
||||
case p.ORDWR:
|
||||
ret = os.O_RDWR
|
||||
break
|
||||
|
||||
case p.OWRITE:
|
||||
ret = os.O_WRONLY
|
||||
break
|
||||
|
||||
case p.OEXEC:
|
||||
ret = os.O_RDONLY
|
||||
break
|
||||
}
|
||||
|
||||
if mode&p.OTRUNC != 0 {
|
||||
ret |= os.O_TRUNC
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func dir2QidType(d os.FileInfo) uint8 {
|
||||
ret := uint8(0)
|
||||
if d.IsDir() {
|
||||
ret |= p.QTDIR
|
||||
}
|
||||
|
||||
if d.Mode()&os.ModeSymlink != 0 {
|
||||
ret |= p.QTSYMLINK
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func dir2Npmode(d os.FileInfo, dotu bool) uint32 {
|
||||
ret := uint32(d.Mode() & 0777)
|
||||
if d.IsDir() {
|
||||
ret |= p.DMDIR
|
||||
}
|
||||
|
||||
if dotu {
|
||||
mode := d.Mode()
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
ret |= p.DMSYMLINK
|
||||
}
|
||||
|
||||
if mode&os.ModeSocket != 0 {
|
||||
ret |= p.DMSOCKET
|
||||
}
|
||||
|
||||
if mode&os.ModeNamedPipe != 0 {
|
||||
ret |= p.DMNAMEDPIPE
|
||||
}
|
||||
|
||||
if mode&os.ModeDevice != 0 {
|
||||
ret |= p.DMDEVICE
|
||||
}
|
||||
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
ret |= p.DMSETUID
|
||||
}
|
||||
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
ret |= p.DMSETGID
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Dir is an instantiation of the p.Dir structure
|
||||
// that can act as a receiver for local methods.
|
||||
type Dir struct {
|
||||
p.Dir
|
||||
}
|
||||
|
||||
func (*Ufs) ConnOpened(conn *srv.Conn) {
|
||||
if conn.Srv.Debuglevel > 0 {
|
||||
log.Println("connected")
|
||||
}
|
||||
}
|
||||
|
||||
func (*Ufs) ConnClosed(conn *srv.Conn) {
|
||||
if conn.Srv.Debuglevel > 0 {
|
||||
log.Println("disconnected")
|
||||
}
|
||||
}
|
||||
|
||||
func (*Ufs) FidDestroy(sfid *srv.Fid) {
|
||||
var fid *Fid
|
||||
|
||||
if sfid.Aux == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fid = sfid.Aux.(*Fid)
|
||||
if fid.file != nil {
|
||||
fid.file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (*Ufs) Attach(req *srv.Req) {
|
||||
if req.Afid != nil {
|
||||
req.RespondError(srv.Enoauth)
|
||||
return
|
||||
}
|
||||
|
||||
tc := req.Tc
|
||||
fid := new(Fid)
|
||||
if len(tc.Aname) == 0 {
|
||||
fid.path = root
|
||||
} else {
|
||||
fid.path = tc.Aname
|
||||
}
|
||||
|
||||
req.Fid.Aux = fid
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
qid := dir2Qid(fid.st)
|
||||
req.RespondRattach(qid)
|
||||
}
|
||||
|
||||
func (*Ufs) Flush(req *srv.Req) {}
|
||||
|
||||
func (*Ufs) Walk(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
tc := req.Tc
|
||||
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Newfid.Aux == nil {
|
||||
req.Newfid.Aux = new(Fid)
|
||||
}
|
||||
|
||||
nfid := req.Newfid.Aux.(*Fid)
|
||||
wqids := make([]p.Qid, len(tc.Wname))
|
||||
path := fid.path
|
||||
i := 0
|
||||
for ; i < len(tc.Wname); i++ {
|
||||
p := path + "/" + tc.Wname[i]
|
||||
st, err := os.Lstat(p)
|
||||
if err != nil {
|
||||
if i == 0 {
|
||||
req.RespondError(Enoent)
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
wqids[i] = *dir2Qid(st)
|
||||
path = p
|
||||
}
|
||||
|
||||
nfid.path = path
|
||||
req.RespondRwalk(wqids[0:i])
|
||||
}
|
||||
|
||||
func (*Ufs) Open(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
tc := req.Tc
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
var e error
|
||||
fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
|
||||
req.RespondRopen(dir2Qid(fid.st), 0)
|
||||
}
|
||||
|
||||
func (*Ufs) Create(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
tc := req.Tc
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
path := fid.path + "/" + tc.Name
|
||||
var e error = nil
|
||||
var file *os.File = nil
|
||||
switch {
|
||||
case tc.Perm&p.DMDIR != 0:
|
||||
e = os.Mkdir(path, os.FileMode(tc.Perm&0777))
|
||||
|
||||
case tc.Perm&p.DMSYMLINK != 0:
|
||||
e = os.Symlink(tc.Ext, path)
|
||||
|
||||
case tc.Perm&p.DMLINK != 0:
|
||||
n, e := strconv.ParseUint(tc.Ext, 10, 0)
|
||||
if e != nil {
|
||||
break
|
||||
}
|
||||
|
||||
ofid := req.Conn.FidGet(uint32(n))
|
||||
if ofid == nil {
|
||||
req.RespondError(srv.Eunknownfid)
|
||||
return
|
||||
}
|
||||
|
||||
e = os.Link(ofid.Aux.(*Fid).path, path)
|
||||
ofid.DecRef()
|
||||
|
||||
case tc.Perm&p.DMNAMEDPIPE != 0:
|
||||
case tc.Perm&p.DMDEVICE != 0:
|
||||
req.RespondError(&p.Error{"not implemented", p.EIO})
|
||||
return
|
||||
|
||||
default:
|
||||
var mode uint32 = tc.Perm & 0777
|
||||
if req.Conn.Dotu {
|
||||
if tc.Perm&p.DMSETUID > 0 {
|
||||
mode |= syscall.S_ISUID
|
||||
}
|
||||
if tc.Perm&p.DMSETGID > 0 {
|
||||
mode |= syscall.S_ISGID
|
||||
}
|
||||
}
|
||||
file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode))
|
||||
}
|
||||
|
||||
if file == nil && e == nil {
|
||||
file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0)
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
|
||||
fid.path = path
|
||||
fid.file = file
|
||||
err = fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
req.RespondRcreate(dir2Qid(fid.st), 0)
|
||||
}
|
||||
|
||||
func (*Ufs) Read(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
tc := req.Tc
|
||||
rc := req.Rc
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
p.InitRread(rc, tc.Count)
|
||||
var count int
|
||||
var e error
|
||||
if fid.st.IsDir() {
|
||||
b := rc.Data
|
||||
if tc.Offset == 0 {
|
||||
fid.file.Close()
|
||||
fid.file, e = os.OpenFile(fid.path, omode2uflags(req.Fid.Omode), 0)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for len(b) > 0 {
|
||||
if fid.dirs == nil {
|
||||
fid.dirs, e = fid.file.Readdir(16)
|
||||
if e != nil && e != io.EOF {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
|
||||
if len(fid.dirs) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var i int
|
||||
for i = 0; i < len(fid.dirs); i++ {
|
||||
path := fid.path + "/" + fid.dirs[i].Name()
|
||||
st := dir2Dir(path, fid.dirs[i], req.Conn.Dotu, req.Conn.Srv.Upool)
|
||||
sz := p.PackDir(st, b, req.Conn.Dotu)
|
||||
if sz == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
b = b[sz:]
|
||||
count += sz
|
||||
}
|
||||
|
||||
if i < len(fid.dirs) {
|
||||
fid.dirs = fid.dirs[i:]
|
||||
break
|
||||
} else {
|
||||
fid.dirs = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
count, e = fid.file.ReadAt(rc.Data, int64(tc.Offset))
|
||||
if e != nil && e != io.EOF {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p.SetRreadCount(rc, uint32(count))
|
||||
req.Respond()
|
||||
}
|
||||
|
||||
func (*Ufs) Write(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
tc := req.Tc
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
n, e := fid.file.WriteAt(tc.Data, int64(tc.Offset))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
|
||||
req.RespondRwrite(uint32(n))
|
||||
}
|
||||
|
||||
func (*Ufs) Clunk(req *srv.Req) { req.RespondRclunk() }
|
||||
|
||||
func (*Ufs) Remove(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
e := os.Remove(fid.path)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
|
||||
req.RespondRremove()
|
||||
}
|
||||
|
||||
func (*Ufs) Stat(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
st := dir2Dir(fid.path, fid.st, req.Conn.Dotu, req.Conn.Srv.Upool)
|
||||
req.RespondRstat(st)
|
||||
}
|
||||
|
||||
func lookup(uid string, group bool) (uint32, *p.Error) {
|
||||
if uid == "" {
|
||||
return p.NOUID, nil
|
||||
}
|
||||
usr, e := user.Lookup(uid)
|
||||
if e != nil {
|
||||
return p.NOUID, toError(e)
|
||||
}
|
||||
conv := usr.Uid
|
||||
if group {
|
||||
conv = usr.Gid
|
||||
}
|
||||
u, e := strconv.Atoi(conv)
|
||||
if e != nil {
|
||||
return p.NOUID, toError(e)
|
||||
}
|
||||
return uint32(u), nil
|
||||
}
|
||||
|
||||
func StartServer(addrVal string, debugVal int, rootVal string) {
|
||||
addr = addrVal
|
||||
debug = debugVal
|
||||
root = rootVal
|
||||
ufs := new(Ufs)
|
||||
ufs.Dotu = true
|
||||
ufs.Id = "ufs"
|
||||
ufs.Debuglevel = debug
|
||||
ufs.Start(ufs)
|
||||
|
||||
err := ufs.StartNetListener("tcp", addr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2012 The go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ufs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
)
|
||||
|
||||
func atime(stat *syscall.Stat_t) time.Time {
|
||||
return time.Unix(stat.Atimespec.Unix())
|
||||
}
|
||||
|
||||
// IsBlock reports if the file is a block device
|
||||
func isBlock(d os.FileInfo) bool {
|
||||
stat := d.Sys().(*syscall.Stat_t)
|
||||
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
|
||||
}
|
||||
|
||||
// IsChar reports if the file is a character device
|
||||
func isChar(d os.FileInfo) bool {
|
||||
stat := d.Sys().(*syscall.Stat_t)
|
||||
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
|
||||
}
|
||||
|
||||
func dir2Qid(d os.FileInfo) *p.Qid {
|
||||
var qid p.Qid
|
||||
|
||||
qid.Path = d.Sys().(*syscall.Stat_t).Ino
|
||||
qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
|
||||
qid.Type = dir2QidType(d)
|
||||
|
||||
return &qid
|
||||
}
|
||||
|
||||
func dir2Dir(path string, d os.FileInfo, dotu bool, upool p.Users) *p.Dir {
|
||||
sysMode := d.Sys().(*syscall.Stat_t)
|
||||
|
||||
dir := new(Dir)
|
||||
dir.Qid = *dir2Qid(d)
|
||||
dir.Mode = dir2Npmode(d, dotu)
|
||||
dir.Atime = uint32(atime(sysMode).Unix())
|
||||
dir.Mtime = uint32(d.ModTime().Unix())
|
||||
dir.Length = uint64(d.Size())
|
||||
dir.Name = path[strings.LastIndex(path, "/")+1:]
|
||||
|
||||
if dotu {
|
||||
dir.dotu(path, d, upool, sysMode)
|
||||
return &dir.Dir
|
||||
}
|
||||
|
||||
unixUid := int(sysMode.Uid)
|
||||
unixGid := int(sysMode.Gid)
|
||||
dir.Uid = strconv.Itoa(unixUid)
|
||||
dir.Gid = strconv.Itoa(unixGid)
|
||||
dir.Muid = "none"
|
||||
|
||||
// BUG(akumar): LookupId will never find names for
|
||||
// groups, as it only operates on user ids.
|
||||
u, err := user.LookupId(dir.Uid)
|
||||
if err == nil {
|
||||
dir.Uid = u.Username
|
||||
}
|
||||
g, err := user.LookupId(dir.Gid)
|
||||
if err == nil {
|
||||
dir.Gid = g.Username
|
||||
}
|
||||
|
||||
return &dir.Dir
|
||||
}
|
||||
|
||||
func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users, sysMode *syscall.Stat_t) {
|
||||
u := upool.Uid2User(int(sysMode.Uid))
|
||||
g := upool.Gid2Group(int(sysMode.Gid))
|
||||
dir.Uid = u.Name()
|
||||
if dir.Uid == "" {
|
||||
dir.Uid = "none"
|
||||
}
|
||||
|
||||
dir.Gid = g.Name()
|
||||
if dir.Gid == "" {
|
||||
dir.Gid = "none"
|
||||
}
|
||||
dir.Muid = "none"
|
||||
dir.Ext = ""
|
||||
dir.Uidnum = uint32(u.Id())
|
||||
dir.Gidnum = uint32(g.Id())
|
||||
dir.Muidnum = p.NOUID
|
||||
if d.Mode()&os.ModeSymlink != 0 {
|
||||
var err error
|
||||
dir.Ext, err = os.Readlink(path)
|
||||
if err != nil {
|
||||
dir.Ext = ""
|
||||
}
|
||||
} else if isBlock(d) {
|
||||
dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
|
||||
} else if isChar(d) {
|
||||
dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Ufs) Wstat(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
dir := &req.Tc.Dir
|
||||
if dir.Mode != 0xFFFFFFFF {
|
||||
mode := dir.Mode & 0777
|
||||
if req.Conn.Dotu {
|
||||
if dir.Mode&p.DMSETUID > 0 {
|
||||
mode |= syscall.S_ISUID
|
||||
}
|
||||
if dir.Mode&p.DMSETGID > 0 {
|
||||
mode |= syscall.S_ISGID
|
||||
}
|
||||
}
|
||||
e := os.Chmod(fid.path, os.FileMode(mode))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
uid, gid := p.NOUID, p.NOUID
|
||||
if req.Conn.Dotu {
|
||||
uid = dir.Uidnum
|
||||
gid = dir.Gidnum
|
||||
}
|
||||
|
||||
// Try to find local uid, gid by name.
|
||||
if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
|
||||
uid, err = lookup(dir.Uid, false)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// BUG(akumar): Lookup will never find gids
|
||||
// corresponding to group names, because
|
||||
// it only operates on user names.
|
||||
gid, err = lookup(dir.Gid, true)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if uid != p.NOUID || gid != p.NOUID {
|
||||
e := os.Chown(fid.path, int(uid), int(gid))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if dir.Name != "" {
|
||||
path := fid.path[0:strings.LastIndex(fid.path, "/")+1] + "/" + dir.Name
|
||||
err := syscall.Rename(fid.path, path)
|
||||
if err != nil {
|
||||
req.RespondError(toError(err))
|
||||
return
|
||||
}
|
||||
fid.path = path
|
||||
}
|
||||
|
||||
if dir.Length != 0xFFFFFFFFFFFFFFFF {
|
||||
e := os.Truncate(fid.path, int64(dir.Length))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If either mtime or atime need to be changed, then
|
||||
// we must change both.
|
||||
if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
|
||||
mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
|
||||
if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
|
||||
st, e := os.Stat(fid.path)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
switch cmt {
|
||||
case true:
|
||||
mt = st.ModTime()
|
||||
default:
|
||||
at = atime(st.Sys().(*syscall.Stat_t))
|
||||
}
|
||||
}
|
||||
e := os.Chtimes(fid.path, at, mt)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req.RespondRwstat()
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2012 The go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ufs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
)
|
||||
|
||||
func atime(stat *syscall.Stat_t) time.Time {
|
||||
return time.Unix(stat.Atim.Unix())
|
||||
}
|
||||
|
||||
// IsBlock reports if the file is a block device
|
||||
func isBlock(d os.FileInfo) bool {
|
||||
stat := d.Sys().(*syscall.Stat_t)
|
||||
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
|
||||
}
|
||||
|
||||
// IsChar reports if the file is a character device
|
||||
func isChar(d os.FileInfo) bool {
|
||||
stat := d.Sys().(*syscall.Stat_t)
|
||||
return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
|
||||
}
|
||||
|
||||
func dir2Qid(d os.FileInfo) *p.Qid {
|
||||
var qid p.Qid
|
||||
|
||||
qid.Path = d.Sys().(*syscall.Stat_t).Ino
|
||||
qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
|
||||
qid.Type = dir2QidType(d)
|
||||
|
||||
return &qid
|
||||
}
|
||||
|
||||
func dir2Dir(path string, d os.FileInfo, dotu bool, upool p.Users) *p.Dir {
|
||||
sysMode := d.Sys().(*syscall.Stat_t)
|
||||
|
||||
dir := new(Dir)
|
||||
dir.Qid = *dir2Qid(d)
|
||||
dir.Mode = dir2Npmode(d, dotu)
|
||||
dir.Atime = uint32(atime(sysMode).Unix())
|
||||
dir.Mtime = uint32(d.ModTime().Unix())
|
||||
dir.Length = uint64(d.Size())
|
||||
dir.Name = path[strings.LastIndex(path, "/")+1:]
|
||||
|
||||
if dotu {
|
||||
dir.dotu(path, d, upool, sysMode)
|
||||
return &dir.Dir
|
||||
}
|
||||
|
||||
unixUid := int(sysMode.Uid)
|
||||
unixGid := int(sysMode.Gid)
|
||||
dir.Uid = strconv.Itoa(unixUid)
|
||||
dir.Gid = strconv.Itoa(unixGid)
|
||||
dir.Muid = "none"
|
||||
|
||||
// BUG(akumar): LookupId will never find names for
|
||||
// groups, as it only operates on user ids.
|
||||
u, err := user.LookupId(dir.Uid)
|
||||
if err == nil {
|
||||
dir.Uid = u.Username
|
||||
}
|
||||
g, err := user.LookupId(dir.Gid)
|
||||
if err == nil {
|
||||
dir.Gid = g.Username
|
||||
}
|
||||
|
||||
return &dir.Dir
|
||||
}
|
||||
|
||||
func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users, sysMode *syscall.Stat_t) {
|
||||
u := upool.Uid2User(int(sysMode.Uid))
|
||||
g := upool.Gid2Group(int(sysMode.Gid))
|
||||
dir.Uid = u.Name()
|
||||
if dir.Uid == "" {
|
||||
dir.Uid = "none"
|
||||
}
|
||||
|
||||
dir.Gid = g.Name()
|
||||
if dir.Gid == "" {
|
||||
dir.Gid = "none"
|
||||
}
|
||||
dir.Muid = "none"
|
||||
dir.Ext = ""
|
||||
dir.Uidnum = uint32(u.Id())
|
||||
dir.Gidnum = uint32(g.Id())
|
||||
dir.Muidnum = p.NOUID
|
||||
if d.Mode()&os.ModeSymlink != 0 {
|
||||
var err error
|
||||
dir.Ext, err = os.Readlink(path)
|
||||
if err != nil {
|
||||
dir.Ext = ""
|
||||
}
|
||||
} else if isBlock(d) {
|
||||
dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
|
||||
} else if isChar(d) {
|
||||
dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Ufs) Wstat(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
dir := &req.Tc.Dir
|
||||
if dir.Mode != 0xFFFFFFFF {
|
||||
mode := dir.Mode & 0777
|
||||
if req.Conn.Dotu {
|
||||
if dir.Mode&p.DMSETUID > 0 {
|
||||
mode |= syscall.S_ISUID
|
||||
}
|
||||
if dir.Mode&p.DMSETGID > 0 {
|
||||
mode |= syscall.S_ISGID
|
||||
}
|
||||
}
|
||||
e := os.Chmod(fid.path, os.FileMode(mode))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
uid, gid := p.NOUID, p.NOUID
|
||||
if req.Conn.Dotu {
|
||||
uid = dir.Uidnum
|
||||
gid = dir.Gidnum
|
||||
}
|
||||
|
||||
// Try to find local uid, gid by name.
|
||||
if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
|
||||
uid, err = lookup(dir.Uid, false)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// BUG(akumar): Lookup will never find gids
|
||||
// corresponding to group names, because
|
||||
// it only operates on user names.
|
||||
gid, err = lookup(dir.Gid, true)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if uid != p.NOUID || gid != p.NOUID {
|
||||
e := os.Chown(fid.path, int(uid), int(gid))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if dir.Name != "" {
|
||||
path := fid.path[0:strings.LastIndex(fid.path, "/")+1] + "/" + dir.Name
|
||||
err := syscall.Rename(fid.path, path)
|
||||
if err != nil {
|
||||
req.RespondError(toError(err))
|
||||
return
|
||||
}
|
||||
fid.path = path
|
||||
}
|
||||
|
||||
if dir.Length != 0xFFFFFFFFFFFFFFFF {
|
||||
e := os.Truncate(fid.path, int64(dir.Length))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If either mtime or atime need to be changed, then
|
||||
// we must change both.
|
||||
if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
|
||||
mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
|
||||
if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
|
||||
st, e := os.Stat(fid.path)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
switch cmt {
|
||||
case true:
|
||||
mt = st.ModTime()
|
||||
default:
|
||||
at = atime(st.Sys().(*syscall.Stat_t))
|
||||
}
|
||||
}
|
||||
e := os.Chtimes(fid.path, at, mt)
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req.RespondRwstat()
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// http://golang.org/src/os/stat_windows.go
|
||||
|
||||
package ufs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"k8s.io/minikube/third_party/go9p/p/srv"
|
||||
)
|
||||
|
||||
// type Win32FileAttributeData struct {
|
||||
// FileAttributes uint32
|
||||
// CreationTime Filetime
|
||||
// LastAccessTime Filetime
|
||||
// LastWriteTime Filetime
|
||||
// FileSizeHigh uint32
|
||||
// FileSizeLow uint32
|
||||
// }
|
||||
|
||||
// A FileInfo describes a file and is returned by Stat and Lstat.
|
||||
// type FileInfo interface {
|
||||
// Name() string // base name of the file
|
||||
// Size() int64 // length in bytes for regular files; system-dependent for others
|
||||
// Mode() FileMode // file mode bits
|
||||
// ModTime() time.Time // modification time
|
||||
// IsDir() bool // abbreviation for Mode().IsDir()
|
||||
// Sys() interface{} // underlying data source (can return nil)
|
||||
// }
|
||||
|
||||
func atime(fi os.FileInfo) time.Time {
|
||||
return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
|
||||
}
|
||||
|
||||
// IsBlock reports if the file is a block device
|
||||
func isBlock(d os.FileInfo) bool {
|
||||
// return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
|
||||
// stat := d.Sys().(os.FileInfo)
|
||||
// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
|
||||
return true
|
||||
}
|
||||
|
||||
// IsChar reports if the file is a character device
|
||||
func isChar(d os.FileInfo) bool {
|
||||
// stat := d.Sys().(os.FileInfo)
|
||||
// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
|
||||
return true
|
||||
}
|
||||
|
||||
func dir2Qid(d os.FileInfo) *p.Qid {
|
||||
var qid p.Qid
|
||||
|
||||
// d.Sys().(*syscall.Win32FileAttributeData)
|
||||
qid.Path = uint64(d.Sys().(*syscall.Win32FileAttributeData).FileSizeLow)
|
||||
qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
|
||||
qid.Type = dir2QidType(d)
|
||||
|
||||
return &qid
|
||||
}
|
||||
|
||||
func dir2Dir(path string, d os.FileInfo, dotu bool, upool p.Users) *p.Dir {
|
||||
// sysMode := d.Sys().(os.FileInfo)
|
||||
|
||||
dir := new(Dir)
|
||||
dir.Qid = *dir2Qid(d)
|
||||
dir.Mode = dir2Npmode(d, dotu)
|
||||
// dir.Atime = uint32(atime(sysMode).Unix())
|
||||
// dir.Mtime = uint32(d.ModTime().Unix())
|
||||
dir.Length = uint64(d.Size())
|
||||
dir.Name = path[strings.LastIndex(path, "/")+1:]
|
||||
|
||||
if dotu {
|
||||
// dir.dotu(path, d, upool, sysMode)
|
||||
return &dir.Dir
|
||||
}
|
||||
|
||||
// unixUid := int(sysMode.Uid)
|
||||
// unixGid := int(sysMode.Gid)
|
||||
// dir.Uid = strconv.Itoa(unixUid)
|
||||
// dir.Gid = strconv.Itoa(unixGid)
|
||||
|
||||
dir.Uid = "none"
|
||||
dir.Gid = "none"
|
||||
dir.Muid = "none"
|
||||
|
||||
// BUG(akumar): LookupId will never find names for
|
||||
// groups, as it only operates on user ids.
|
||||
// u, err := user.LookupId(dir.Uid)
|
||||
// if err == nil {
|
||||
// dir.Uid = u.Username
|
||||
// }
|
||||
// g, err := user.LookupId(dir.Gid)
|
||||
// if err == nil {
|
||||
// dir.Gid = g.Username
|
||||
// }
|
||||
|
||||
return &dir.Dir
|
||||
}
|
||||
|
||||
func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users) {
|
||||
// u := upool.Uid2User(int(sysMode.Uid))
|
||||
// g := upool.Gid2Group(int(sysMode.Gid))
|
||||
// dir.Uid = u.Name()
|
||||
// if dir.Uid == "" {
|
||||
// dir.Uid = "none"
|
||||
// }
|
||||
|
||||
// dir.Gid = g.Name()
|
||||
// if dir.Gid == "" {
|
||||
// dir.Gid = "none"
|
||||
// }
|
||||
// dir.Muid = "none"
|
||||
// dir.Ext = ""
|
||||
// dir.Uidnum = uint32(u.Id())
|
||||
// dir.Gidnum = uint32(g.Id())
|
||||
// dir.Muidnum = p.NOUID
|
||||
// if d.Mode()&os.ModeSymlink != 0 {
|
||||
// var err error
|
||||
// dir.Ext, err = os.Readlink(path)
|
||||
// if err != nil {
|
||||
// dir.Ext = ""
|
||||
// }
|
||||
// } else if isBlock(d) {
|
||||
// dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
|
||||
// } else if isChar(d) {
|
||||
// dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
|
||||
// }
|
||||
dir.Uid = "none"
|
||||
dir.Gid = "none"
|
||||
dir.Muid = "none"
|
||||
dir.Uidnum = 0
|
||||
dir.Gidnum = 0
|
||||
dir.Muidnum = p.NOUID
|
||||
dir.Ext = ""
|
||||
}
|
||||
|
||||
func (*Ufs) Wstat(req *srv.Req) {
|
||||
fid := req.Fid.Aux.(*Fid)
|
||||
err := fid.stat()
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
dir := &req.Tc.Dir
|
||||
if dir.Mode != 0xFFFFFFFF {
|
||||
mode := dir.Mode & 0777
|
||||
if req.Conn.Dotu {
|
||||
if dir.Mode&p.DMSETUID > 0 {
|
||||
mode |= syscall.S_ISUID
|
||||
}
|
||||
if dir.Mode&p.DMSETGID > 0 {
|
||||
mode |= syscall.S_ISGID
|
||||
}
|
||||
}
|
||||
e := os.Chmod(fid.path, os.FileMode(mode))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
uid, gid := p.NOUID, p.NOUID
|
||||
if req.Conn.Dotu {
|
||||
uid = dir.Uidnum
|
||||
gid = dir.Gidnum
|
||||
}
|
||||
|
||||
// Try to find local uid, gid by name.
|
||||
if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
|
||||
uid, err = lookup(dir.Uid, false)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// BUG(akumar): Lookup will never find gids
|
||||
// corresponding to group names, because
|
||||
// it only operates on user names.
|
||||
gid, err = lookup(dir.Gid, true)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if uid != p.NOUID || gid != p.NOUID {
|
||||
e := os.Chown(fid.path, int(uid), int(gid))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if dir.Name != "" {
|
||||
path := fid.path[0:strings.LastIndex(fid.path, "/")+1] + "/" + dir.Name
|
||||
err := syscall.Rename(fid.path, path)
|
||||
if err != nil {
|
||||
req.RespondError(toError(err))
|
||||
return
|
||||
}
|
||||
fid.path = path
|
||||
}
|
||||
|
||||
if dir.Length != 0xFFFFFFFFFFFFFFFF {
|
||||
e := os.Truncate(fid.path, int64(dir.Length))
|
||||
if e != nil {
|
||||
req.RespondError(toError(e))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If either mtime or atime need to be changed, then
|
||||
// we must change both.
|
||||
// if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
|
||||
// mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
|
||||
// if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
|
||||
// st, e := os.Stat(fid.path)
|
||||
// if e != nil {
|
||||
// req.RespondError(toError(e))
|
||||
// return
|
||||
// }
|
||||
// switch cmt {
|
||||
// case true:
|
||||
// mt = st.ModTime()
|
||||
// default:
|
||||
// at = atime(st.Sys().(os.FileInfo))
|
||||
// }
|
||||
// }
|
||||
// e := os.Chtimes(fid.path, at, mt)
|
||||
// if e != nil {
|
||||
// req.RespondError(toError(e))
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
req.RespondRwstat()
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package srv
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
)
|
||||
|
||||
func (srv *Srv) version(req *Req) {
|
||||
tc := req.Tc
|
||||
conn := req.Conn
|
||||
|
||||
if tc.Msize < p.IOHDRSZ {
|
||||
req.RespondError(&p.Error{"msize too small", p.EINVAL})
|
||||
return
|
||||
}
|
||||
|
||||
if tc.Msize < conn.Msize {
|
||||
conn.Msize = tc.Msize
|
||||
}
|
||||
|
||||
conn.Dotu = tc.Version == "9P2000.u" && srv.Dotu
|
||||
ver := "9P2000"
|
||||
if conn.Dotu {
|
||||
ver = "9P2000.u"
|
||||
}
|
||||
|
||||
/* make sure that the responses of all current requests will be ignored */
|
||||
conn.Lock()
|
||||
for tag, r := range conn.reqs {
|
||||
if tag == p.NOTAG {
|
||||
continue
|
||||
}
|
||||
|
||||
for rr := r; rr != nil; rr = rr.next {
|
||||
rr.Lock()
|
||||
rr.status |= reqFlush
|
||||
rr.Unlock()
|
||||
}
|
||||
}
|
||||
conn.Unlock()
|
||||
|
||||
req.RespondRversion(conn.Msize, ver)
|
||||
}
|
||||
|
||||
func (srv *Srv) auth(req *Req) {
|
||||
tc := req.Tc
|
||||
conn := req.Conn
|
||||
if tc.Afid == p.NOFID {
|
||||
req.RespondError(Eunknownfid)
|
||||
return
|
||||
}
|
||||
|
||||
req.Afid = conn.FidNew(tc.Afid)
|
||||
if req.Afid == nil {
|
||||
req.RespondError(Einuse)
|
||||
return
|
||||
}
|
||||
|
||||
var user p.User = nil
|
||||
if tc.Unamenum != p.NOUID || conn.Dotu {
|
||||
user = srv.Upool.Uid2User(int(tc.Unamenum))
|
||||
} else if tc.Uname != "" {
|
||||
user = srv.Upool.Uname2User(tc.Uname)
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
if user == nil {
|
||||
req.RespondError(Enouser)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req.Afid.User = user
|
||||
req.Afid.Type = p.QTAUTH
|
||||
if aop, ok := (srv.ops).(AuthOps); ok {
|
||||
aqid, err := aop.AuthInit(req.Afid, tc.Aname)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
aqid.Type |= p.QTAUTH // just in case
|
||||
req.RespondRauth(aqid)
|
||||
}
|
||||
} else {
|
||||
req.RespondError(Enoauth)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (srv *Srv) authPost(req *Req) {
|
||||
if req.Rc != nil && req.Rc.Type == p.Rauth {
|
||||
req.Afid.IncRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) attach(req *Req) {
|
||||
tc := req.Tc
|
||||
conn := req.Conn
|
||||
if tc.Fid == p.NOFID {
|
||||
req.RespondError(Eunknownfid)
|
||||
return
|
||||
}
|
||||
|
||||
req.Fid = conn.FidNew(tc.Fid)
|
||||
if req.Fid == nil {
|
||||
req.RespondError(Einuse)
|
||||
return
|
||||
}
|
||||
|
||||
if tc.Afid != p.NOFID {
|
||||
req.Afid = conn.FidGet(tc.Afid)
|
||||
if req.Afid == nil {
|
||||
req.RespondError(Eunknownfid)
|
||||
}
|
||||
}
|
||||
|
||||
var user p.User = nil
|
||||
if tc.Unamenum != p.NOUID || conn.Dotu {
|
||||
user = srv.Upool.Uid2User(int(tc.Unamenum))
|
||||
} else if tc.Uname != "" {
|
||||
user = srv.Upool.Uname2User(tc.Uname)
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
if user == nil {
|
||||
req.RespondError(Enouser)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req.Fid.User = user
|
||||
if aop, ok := (srv.ops).(AuthOps); ok {
|
||||
err := aop.AuthCheck(req.Fid, req.Afid, tc.Aname)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
(srv.ops).(ReqOps).Attach(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) attachPost(req *Req) {
|
||||
if req.Rc != nil && req.Rc.Type == p.Rattach {
|
||||
req.Fid.Type = req.Rc.Qid.Type
|
||||
req.Fid.IncRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) flush(req *Req) {
|
||||
conn := req.Conn
|
||||
tag := req.Tc.Oldtag
|
||||
p.PackRflush(req.Rc)
|
||||
conn.Lock()
|
||||
r := conn.reqs[tag]
|
||||
if r != nil {
|
||||
req.flushreq = r.flushreq
|
||||
r.flushreq = req
|
||||
}
|
||||
conn.Unlock()
|
||||
|
||||
if r == nil {
|
||||
// there are no requests with that tag
|
||||
req.Respond()
|
||||
return
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
status := r.status
|
||||
if (status & (reqWork | reqSaved)) == 0 {
|
||||
/* the request is not worked on yet */
|
||||
r.status |= reqFlush
|
||||
}
|
||||
r.Unlock()
|
||||
|
||||
if (status & (reqWork | reqSaved)) == 0 {
|
||||
r.Respond()
|
||||
} else {
|
||||
if op, ok := (srv.ops).(FlushOp); ok {
|
||||
op.Flush(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) walk(req *Req) {
|
||||
conn := req.Conn
|
||||
tc := req.Tc
|
||||
fid := req.Fid
|
||||
|
||||
/* we can't walk regular files, only clone them */
|
||||
if len(tc.Wname) > 0 && (fid.Type&p.QTDIR) == 0 {
|
||||
req.RespondError(Enotdir)
|
||||
return
|
||||
}
|
||||
|
||||
/* we can't walk open files */
|
||||
if fid.opened {
|
||||
req.RespondError(Ebaduse)
|
||||
return
|
||||
}
|
||||
|
||||
if tc.Fid != tc.Newfid {
|
||||
req.Newfid = conn.FidNew(tc.Newfid)
|
||||
if req.Newfid == nil {
|
||||
req.RespondError(Einuse)
|
||||
return
|
||||
}
|
||||
|
||||
req.Newfid.User = fid.User
|
||||
req.Newfid.Type = fid.Type
|
||||
} else {
|
||||
req.Newfid = req.Fid
|
||||
req.Newfid.IncRef()
|
||||
}
|
||||
|
||||
(req.Conn.Srv.ops).(ReqOps).Walk(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) walkPost(req *Req) {
|
||||
rc := req.Rc
|
||||
if rc == nil || rc.Type != p.Rwalk || req.Newfid == nil {
|
||||
return
|
||||
}
|
||||
|
||||
n := len(rc.Wqid)
|
||||
if n > 0 {
|
||||
req.Newfid.Type = rc.Wqid[n-1].Type
|
||||
} else {
|
||||
req.Newfid.Type = req.Fid.Type
|
||||
}
|
||||
|
||||
// Don't retain the fid if only a partial walk succeeded
|
||||
if n != len(req.Tc.Wname) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Newfid.fid != req.Fid.fid {
|
||||
req.Newfid.IncRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) open(req *Req) {
|
||||
fid := req.Fid
|
||||
tc := req.Tc
|
||||
if fid.opened {
|
||||
req.RespondError(Eopen)
|
||||
return
|
||||
}
|
||||
|
||||
if (fid.Type&p.QTDIR) != 0 && tc.Mode != p.OREAD {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
|
||||
fid.Omode = tc.Mode
|
||||
(req.Conn.Srv.ops).(ReqOps).Open(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) openPost(req *Req) {
|
||||
if req.Fid != nil {
|
||||
req.Fid.opened = req.Rc != nil && req.Rc.Type == p.Ropen
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) create(req *Req) {
|
||||
fid := req.Fid
|
||||
tc := req.Tc
|
||||
if fid.opened {
|
||||
req.RespondError(Eopen)
|
||||
return
|
||||
}
|
||||
|
||||
if (fid.Type & p.QTDIR) == 0 {
|
||||
req.RespondError(Enotdir)
|
||||
return
|
||||
}
|
||||
|
||||
/* can't open directories for other than reading */
|
||||
if (tc.Perm&p.DMDIR) != 0 && tc.Mode != p.OREAD {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
|
||||
/* can't create special files if not 9P2000.u */
|
||||
if (tc.Perm&(p.DMNAMEDPIPE|p.DMSYMLINK|p.DMLINK|p.DMDEVICE|p.DMSOCKET)) != 0 && !req.Conn.Dotu {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
|
||||
fid.Omode = tc.Mode
|
||||
(req.Conn.Srv.ops).(ReqOps).Create(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) createPost(req *Req) {
|
||||
if req.Rc != nil && req.Rc.Type == p.Rcreate && req.Fid != nil {
|
||||
req.Fid.Type = req.Rc.Qid.Type
|
||||
req.Fid.opened = true
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) read(req *Req) {
|
||||
tc := req.Tc
|
||||
fid := req.Fid
|
||||
if tc.Count+p.IOHDRSZ > req.Conn.Msize {
|
||||
req.RespondError(Etoolarge)
|
||||
return
|
||||
}
|
||||
|
||||
if (fid.Type & p.QTAUTH) != 0 {
|
||||
var n int
|
||||
|
||||
rc := req.Rc
|
||||
err := p.InitRread(rc, tc.Count)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
|
||||
n, err = op.AuthRead(fid, tc.Offset, rc.Data)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
|
||||
p.SetRreadCount(rc, uint32(n))
|
||||
req.Respond()
|
||||
} else {
|
||||
req.RespondError(Enotimpl)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (fid.Type & p.QTDIR) != 0 {
|
||||
if tc.Offset == 0 {
|
||||
fid.Diroffset = 0
|
||||
} else if tc.Offset != fid.Diroffset {
|
||||
req.RespondError(Ebadoffset)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
(req.Conn.Srv.ops).(ReqOps).Read(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) readPost(req *Req) {
|
||||
if req.Rc != nil && req.Rc.Type == p.Rread && (req.Fid.Type&p.QTDIR) != 0 {
|
||||
req.Fid.Diroffset += uint64(req.Rc.Count)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) write(req *Req) {
|
||||
fid := req.Fid
|
||||
tc := req.Tc
|
||||
if (fid.Type & p.QTAUTH) != 0 {
|
||||
tc := req.Tc
|
||||
if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
|
||||
n, err := op.AuthWrite(req.Fid, tc.Offset, tc.Data)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.RespondRwrite(uint32(n))
|
||||
}
|
||||
} else {
|
||||
req.RespondError(Enotimpl)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !fid.opened || (fid.Type&p.QTDIR) != 0 || (fid.Omode&3) == p.OREAD {
|
||||
req.RespondError(Ebaduse)
|
||||
return
|
||||
}
|
||||
|
||||
if tc.Count+p.IOHDRSZ > req.Conn.Msize {
|
||||
req.RespondError(Etoolarge)
|
||||
return
|
||||
}
|
||||
|
||||
(req.Conn.Srv.ops).(ReqOps).Write(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) clunk(req *Req) {
|
||||
fid := req.Fid
|
||||
if (fid.Type & p.QTAUTH) != 0 {
|
||||
if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
|
||||
op.AuthDestroy(fid)
|
||||
req.RespondRclunk()
|
||||
} else {
|
||||
req.RespondError(Enotimpl)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
(req.Conn.Srv.ops).(ReqOps).Clunk(req)
|
||||
}
|
||||
|
||||
func (srv *Srv) clunkPost(req *Req) {
|
||||
if req.Rc != nil && req.Rc.Type == p.Rclunk && req.Fid != nil {
|
||||
req.Fid.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) remove(req *Req) { (req.Conn.Srv.ops).(ReqOps).Remove(req) }
|
||||
|
||||
func (srv *Srv) removePost(req *Req) {
|
||||
if req.Rc != nil && req.Fid != nil {
|
||||
req.Fid.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Srv) stat(req *Req) { (req.Conn.Srv.ops).(ReqOps).Stat(req) }
|
||||
|
||||
func (srv *Srv) wstat(req *Req) {
|
||||
/*
|
||||
fid := req.Fid
|
||||
d := &req.Tc.Dir
|
||||
if d.Type != uint16(0xFFFF) || d.Dev != uint32(0xFFFFFFFF) || d.Version != uint32(0xFFFFFFFF) ||
|
||||
d.Path != uint64(0xFFFFFFFFFFFFFFFF) {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
|
||||
if (d.Mode != 0xFFFFFFFF) && (((fid.Type&p.QTDIR) != 0 && (d.Mode&p.DMDIR) == 0) ||
|
||||
((d.Type&p.QTDIR) == 0 && (d.Mode&p.DMDIR) != 0)) {
|
||||
req.RespondError(Edirchange)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
(req.Conn.Srv.ops).(ReqOps).Wstat(req)
|
||||
}
|
|
@ -0,0 +1,568 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package srv
|
||||
|
||||
import (
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The FStatOp interface provides a single operation (Stat) that will be
|
||||
// called before a file stat is sent back to the client. If implemented,
|
||||
// the operation should update the data in the File struct.
|
||||
type FStatOp interface {
|
||||
Stat(fid *FFid) error
|
||||
}
|
||||
|
||||
// The FWstatOp interface provides a single operation (Wstat) that will be
|
||||
// called when the client requests the File metadata to be modified. If
|
||||
// implemented, the operation will be called when Twstat message is received.
|
||||
// If not implemented, "permission denied" error will be sent back. If the
|
||||
// operation returns an Error, the error is send back to the client.
|
||||
type FWstatOp interface {
|
||||
Wstat(*FFid, *p.Dir) error
|
||||
}
|
||||
|
||||
// If the FReadOp interface is implemented, the Read operation will be called
|
||||
// to read from the file. If not implemented, "permission denied" error will
|
||||
// be send back. The operation returns the number of bytes read, or the
|
||||
// error occured while reading.
|
||||
type FReadOp interface {
|
||||
Read(fid *FFid, buf []byte, offset uint64) (int, error)
|
||||
}
|
||||
|
||||
// If the FWriteOp interface is implemented, the Write operation will be called
|
||||
// to write to the file. If not implemented, "permission denied" error will
|
||||
// be send back. The operation returns the number of bytes written, or the
|
||||
// error occured while writing.
|
||||
type FWriteOp interface {
|
||||
Write(fid *FFid, data []byte, offset uint64) (int, error)
|
||||
}
|
||||
|
||||
// If the FCreateOp interface is implemented, the Create operation will be called
|
||||
// when the client attempts to create a file in the File implementing the interface.
|
||||
// If not implemented, "permission denied" error will be send back. If successful,
|
||||
// the operation should call (*File)Add() to add the created file to the directory.
|
||||
// The operation returns the created file, or the error occured while creating it.
|
||||
type FCreateOp interface {
|
||||
Create(fid *FFid, name string, perm uint32) (*File, error)
|
||||
}
|
||||
|
||||
// If the FRemoveOp interface is implemented, the Remove operation will be called
|
||||
// when the client attempts to create a file in the File implementing the interface.
|
||||
// If not implemented, "permission denied" error will be send back.
|
||||
// The operation returns nil if successful, or the error that occured while removing
|
||||
// the file.
|
||||
type FRemoveOp interface {
|
||||
Remove(*FFid) error
|
||||
}
|
||||
|
||||
type FOpenOp interface {
|
||||
Open(fid *FFid, mode uint8) error
|
||||
}
|
||||
|
||||
type FClunkOp interface {
|
||||
Clunk(fid *FFid) error
|
||||
}
|
||||
|
||||
type FDestroyOp interface {
|
||||
FidDestroy(fid *FFid)
|
||||
}
|
||||
|
||||
type FFlags int
|
||||
|
||||
const (
|
||||
Fremoved FFlags = 1 << iota
|
||||
)
|
||||
|
||||
// The File type represents a file (or directory) served by the file server.
|
||||
type File struct {
|
||||
sync.Mutex
|
||||
p.Dir
|
||||
flags FFlags
|
||||
|
||||
Parent *File // parent
|
||||
next, prev *File // siblings, guarded by parent.Lock
|
||||
cfirst, clast *File // children (if directory)
|
||||
Ops interface{}
|
||||
}
|
||||
|
||||
type FFid struct {
|
||||
F *File
|
||||
Fid *Fid
|
||||
dirs []*File // used for readdir
|
||||
}
|
||||
|
||||
// The Fsrv can be used to create file servers that serve
|
||||
// simple trees of synthetic files.
|
||||
type Fsrv struct {
|
||||
Srv
|
||||
Root *File
|
||||
}
|
||||
|
||||
var lock sync.Mutex
|
||||
var qnext uint64
|
||||
var Eexist = &p.Error{"file already exists", p.EEXIST}
|
||||
var Enoent = &p.Error{"file not found", p.ENOENT}
|
||||
var Enotempty = &p.Error{"directory not empty", p.EPERM}
|
||||
|
||||
// Creates a file server with root as root directory
|
||||
func NewFileSrv(root *File) *Fsrv {
|
||||
srv := new(Fsrv)
|
||||
srv.Root = root
|
||||
root.Parent = root // make sure we can .. in root
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
// Initializes the fields of a file and add it to a directory.
|
||||
// Returns nil if successful, or an error.
|
||||
func (f *File) Add(dir *File, name string, uid p.User, gid p.Group, mode uint32, ops interface{}) error {
|
||||
|
||||
lock.Lock()
|
||||
qpath := qnext
|
||||
qnext++
|
||||
lock.Unlock()
|
||||
|
||||
f.Qid.Type = uint8(mode >> 24)
|
||||
f.Qid.Version = 0
|
||||
f.Qid.Path = qpath
|
||||
f.Mode = mode
|
||||
f.Atime = uint32(time.Now().Unix())
|
||||
f.Mtime = f.Atime
|
||||
f.Length = 0
|
||||
f.Name = name
|
||||
if uid != nil {
|
||||
f.Uid = uid.Name()
|
||||
f.Uidnum = uint32(uid.Id())
|
||||
} else {
|
||||
f.Uid = "none"
|
||||
f.Uidnum = p.NOUID
|
||||
}
|
||||
|
||||
if gid != nil {
|
||||
f.Gid = gid.Name()
|
||||
f.Gidnum = uint32(gid.Id())
|
||||
} else {
|
||||
f.Gid = "none"
|
||||
f.Gidnum = p.NOUID
|
||||
}
|
||||
|
||||
f.Muid = ""
|
||||
f.Muidnum = p.NOUID
|
||||
f.Ext = ""
|
||||
|
||||
if dir != nil {
|
||||
f.Parent = dir
|
||||
dir.Lock()
|
||||
for p := dir.cfirst; p != nil; p = p.next {
|
||||
if name == p.Name {
|
||||
dir.Unlock()
|
||||
return Eexist
|
||||
}
|
||||
}
|
||||
|
||||
if dir.clast != nil {
|
||||
dir.clast.next = f
|
||||
} else {
|
||||
dir.cfirst = f
|
||||
}
|
||||
|
||||
f.prev = dir.clast
|
||||
f.next = nil
|
||||
dir.clast = f
|
||||
dir.Unlock()
|
||||
} else {
|
||||
f.Parent = f
|
||||
}
|
||||
|
||||
f.Ops = ops
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes a file from its parent directory.
|
||||
func (f *File) Remove() {
|
||||
f.Lock()
|
||||
if (f.flags & Fremoved) != 0 {
|
||||
f.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
f.flags |= Fremoved
|
||||
f.Unlock()
|
||||
|
||||
p := f.Parent
|
||||
p.Lock()
|
||||
if f.next != nil {
|
||||
f.next.prev = f.prev
|
||||
} else {
|
||||
p.clast = f.prev
|
||||
}
|
||||
|
||||
if f.prev != nil {
|
||||
f.prev.next = f.next
|
||||
} else {
|
||||
p.cfirst = f.next
|
||||
}
|
||||
|
||||
f.next = nil
|
||||
f.prev = nil
|
||||
p.Unlock()
|
||||
}
|
||||
|
||||
func (f *File) Rename(name string) error {
|
||||
p := f.Parent
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
for c := p.cfirst; c != nil; c = c.next {
|
||||
if name == c.Name {
|
||||
return Eexist
|
||||
}
|
||||
}
|
||||
|
||||
f.Name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
// Looks for a file in a directory. Returns nil if the file is not found.
|
||||
func (p *File) Find(name string) *File {
|
||||
var f *File
|
||||
|
||||
p.Lock()
|
||||
for f = p.cfirst; f != nil; f = f.next {
|
||||
if name == f.Name {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.Unlock()
|
||||
return f
|
||||
}
|
||||
|
||||
// Checks if the specified user has permission to perform
|
||||
// certain operation on a file. Perm contains one or more
|
||||
// of p.DMREAD, p.DMWRITE, and p.DMEXEC.
|
||||
func (f *File) CheckPerm(user p.User, perm uint32) bool {
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
perm &= 7
|
||||
|
||||
/* other permissions */
|
||||
fperm := f.Mode & 7
|
||||
if (fperm & perm) == perm {
|
||||
return true
|
||||
}
|
||||
|
||||
/* user permissions */
|
||||
if f.Uid == user.Name() || f.Uidnum == uint32(user.Id()) {
|
||||
fperm |= (f.Mode >> 6) & 7
|
||||
}
|
||||
|
||||
if (fperm & perm) == perm {
|
||||
return true
|
||||
}
|
||||
|
||||
/* group permissions */
|
||||
groups := user.Groups()
|
||||
if groups != nil && len(groups) > 0 {
|
||||
for i := 0; i < len(groups); i++ {
|
||||
if f.Gid == groups[i].Name() || f.Gidnum == uint32(groups[i].Id()) {
|
||||
fperm |= (f.Mode >> 3) & 7
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fperm & perm) == perm {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Fsrv) Attach(req *Req) {
|
||||
fid := new(FFid)
|
||||
fid.F = s.Root
|
||||
fid.Fid = req.Fid
|
||||
req.Fid.Aux = fid
|
||||
req.RespondRattach(&s.Root.Qid)
|
||||
}
|
||||
|
||||
func (*Fsrv) Walk(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
tc := req.Tc
|
||||
|
||||
if req.Newfid.Aux == nil {
|
||||
nfid := new(FFid)
|
||||
nfid.Fid = req.Newfid
|
||||
req.Newfid.Aux = nfid
|
||||
}
|
||||
|
||||
nfid := req.Newfid.Aux.(*FFid)
|
||||
wqids := make([]p.Qid, len(tc.Wname))
|
||||
i := 0
|
||||
f := fid.F
|
||||
for ; i < len(tc.Wname); i++ {
|
||||
if tc.Wname[i] == ".." {
|
||||
// handle dotdot
|
||||
f = f.Parent
|
||||
wqids[i] = f.Qid
|
||||
continue
|
||||
}
|
||||
if (wqids[i].Type & p.QTDIR) > 0 {
|
||||
if !f.CheckPerm(req.Fid.User, p.DMEXEC) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
p := f.Find(tc.Wname[i])
|
||||
if p == nil {
|
||||
break
|
||||
}
|
||||
|
||||
f = p
|
||||
wqids[i] = f.Qid
|
||||
}
|
||||
|
||||
if len(tc.Wname) > 0 && i == 0 {
|
||||
req.RespondError(Enoent)
|
||||
return
|
||||
}
|
||||
|
||||
nfid.F = f
|
||||
req.RespondRwalk(wqids[0:i])
|
||||
}
|
||||
|
||||
func mode2Perm(mode uint8) uint32 {
|
||||
var perm uint32 = 0
|
||||
|
||||
switch mode & 3 {
|
||||
case p.OREAD:
|
||||
perm = p.DMREAD
|
||||
case p.OWRITE:
|
||||
perm = p.DMWRITE
|
||||
case p.ORDWR:
|
||||
perm = p.DMREAD | p.DMWRITE
|
||||
}
|
||||
|
||||
if (mode & p.OTRUNC) != 0 {
|
||||
perm |= p.DMWRITE
|
||||
}
|
||||
|
||||
return perm
|
||||
}
|
||||
|
||||
func (*Fsrv) Open(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
tc := req.Tc
|
||||
|
||||
if !fid.F.CheckPerm(req.Fid.User, mode2Perm(tc.Mode)) {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
|
||||
if op, ok := (fid.F.Ops).(FOpenOp); ok {
|
||||
err := op.Open(fid, tc.Mode)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
req.RespondRopen(&fid.F.Qid, 0)
|
||||
}
|
||||
|
||||
func (*Fsrv) Create(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
tc := req.Tc
|
||||
|
||||
dir := fid.F
|
||||
if !dir.CheckPerm(req.Fid.User, p.DMWRITE) {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
|
||||
if cop, ok := (dir.Ops).(FCreateOp); ok {
|
||||
f, err := cop.Create(fid, tc.Name, tc.Perm)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
fid.F = f
|
||||
req.RespondRcreate(&fid.F.Qid, 0)
|
||||
}
|
||||
} else {
|
||||
req.RespondError(Eperm)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Fsrv) Read(req *Req) {
|
||||
var i, n int
|
||||
var err error
|
||||
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
f := fid.F
|
||||
tc := req.Tc
|
||||
rc := req.Rc
|
||||
p.InitRread(rc, tc.Count)
|
||||
|
||||
if f.Mode&p.DMDIR != 0 {
|
||||
// directory
|
||||
if tc.Offset == 0 {
|
||||
var g *File
|
||||
f.Lock()
|
||||
for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next {
|
||||
}
|
||||
|
||||
fid.dirs = make([]*File, n)
|
||||
for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next {
|
||||
fid.dirs[n] = g
|
||||
}
|
||||
f.Unlock()
|
||||
}
|
||||
|
||||
n = 0
|
||||
b := rc.Data
|
||||
for i = 0; i < len(fid.dirs); i++ {
|
||||
g := fid.dirs[i]
|
||||
g.Lock()
|
||||
if (g.flags & Fremoved) != 0 {
|
||||
g.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
sz := p.PackDir(&g.Dir, b, req.Conn.Dotu)
|
||||
g.Unlock()
|
||||
if sz == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
b = b[sz:]
|
||||
n += sz
|
||||
}
|
||||
|
||||
fid.dirs = fid.dirs[i:]
|
||||
} else {
|
||||
// file
|
||||
if rop, ok := f.Ops.(FReadOp); ok {
|
||||
n, err = rop.Read(fid, rc.Data, tc.Offset)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
req.RespondError(Eperm)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p.SetRreadCount(rc, uint32(n))
|
||||
req.Respond()
|
||||
}
|
||||
|
||||
func (*Fsrv) Write(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
f := fid.F
|
||||
tc := req.Tc
|
||||
|
||||
if wop, ok := (f.Ops).(FWriteOp); ok {
|
||||
n, err := wop.Write(fid, tc.Data, tc.Offset)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.RespondRwrite(uint32(n))
|
||||
}
|
||||
} else {
|
||||
req.RespondError(Eperm)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (*Fsrv) Clunk(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
|
||||
if op, ok := (fid.F.Ops).(FClunkOp); ok {
|
||||
err := op.Clunk(fid)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
}
|
||||
}
|
||||
req.RespondRclunk()
|
||||
}
|
||||
|
||||
func (*Fsrv) Remove(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
f := fid.F
|
||||
f.Lock()
|
||||
if f.cfirst != nil {
|
||||
f.Unlock()
|
||||
req.RespondError(Enotempty)
|
||||
return
|
||||
}
|
||||
f.Unlock()
|
||||
|
||||
if rop, ok := (f.Ops).(FRemoveOp); ok {
|
||||
err := rop.Remove(fid)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
f.Remove()
|
||||
req.RespondRremove()
|
||||
}
|
||||
} else {
|
||||
log.Println("remove not implemented")
|
||||
req.RespondError(Eperm)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Fsrv) Stat(req *Req) {
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
f := fid.F
|
||||
|
||||
if sop, ok := (f.Ops).(FStatOp); ok {
|
||||
err := sop.Stat(fid)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.RespondRstat(&f.Dir)
|
||||
}
|
||||
} else {
|
||||
req.RespondRstat(&f.Dir)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Fsrv) Wstat(req *Req) {
|
||||
tc := req.Tc
|
||||
fid := req.Fid.Aux.(*FFid)
|
||||
f := fid.F
|
||||
|
||||
if wop, ok := (f.Ops).(FWstatOp); ok {
|
||||
err := wop.Wstat(fid, &tc.Dir)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.RespondRwstat()
|
||||
}
|
||||
} else {
|
||||
req.RespondError(Eperm)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Fsrv) FidDestroy(ffid *Fid) {
|
||||
if ffid.Aux == nil {
|
||||
return
|
||||
}
|
||||
fid := ffid.Aux.(*FFid)
|
||||
f := fid.F
|
||||
|
||||
if f == nil {
|
||||
return // otherwise errs in bad walks
|
||||
}
|
||||
|
||||
if op, ok := (f.Ops).(FDestroyOp); ok {
|
||||
op.FidDestroy(fid)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package srv
|
||||
|
||||
import "fmt"
|
||||
import "k8s.io/minikube/third_party/go9p/p"
|
||||
|
||||
// Respond to the request with Rerror message
|
||||
func (req *Req) RespondError(err interface{}) {
|
||||
switch e := err.(type) {
|
||||
case *p.Error:
|
||||
p.PackRerror(req.Rc, e.Error(), uint32(e.Errornum), req.Conn.Dotu)
|
||||
case error:
|
||||
p.PackRerror(req.Rc, e.Error(), uint32(p.EIO), req.Conn.Dotu)
|
||||
default:
|
||||
p.PackRerror(req.Rc, fmt.Sprintf("%v", e), uint32(p.EIO), req.Conn.Dotu)
|
||||
}
|
||||
|
||||
req.Respond()
|
||||
}
|
||||
|
||||
// Respond to the request with Rversion message
|
||||
func (req *Req) RespondRversion(msize uint32, version string) {
|
||||
err := p.PackRversion(req.Rc, msize, version)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rauth message
|
||||
func (req *Req) RespondRauth(aqid *p.Qid) {
|
||||
err := p.PackRauth(req.Rc, aqid)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rflush message
|
||||
func (req *Req) RespondRflush() {
|
||||
err := p.PackRflush(req.Rc)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rattach message
|
||||
func (req *Req) RespondRattach(aqid *p.Qid) {
|
||||
err := p.PackRattach(req.Rc, aqid)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rwalk message
|
||||
func (req *Req) RespondRwalk(wqids []p.Qid) {
|
||||
err := p.PackRwalk(req.Rc, wqids)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Ropen message
|
||||
func (req *Req) RespondRopen(qid *p.Qid, iounit uint32) {
|
||||
err := p.PackRopen(req.Rc, qid, iounit)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rcreate message
|
||||
func (req *Req) RespondRcreate(qid *p.Qid, iounit uint32) {
|
||||
err := p.PackRcreate(req.Rc, qid, iounit)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rread message
|
||||
func (req *Req) RespondRread(data []byte) {
|
||||
err := p.PackRread(req.Rc, data)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rwrite message
|
||||
func (req *Req) RespondRwrite(count uint32) {
|
||||
err := p.PackRwrite(req.Rc, count)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rclunk message
|
||||
func (req *Req) RespondRclunk() {
|
||||
err := p.PackRclunk(req.Rc)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rremove message
|
||||
func (req *Req) RespondRremove() {
|
||||
err := p.PackRremove(req.Rc)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rstat message
|
||||
func (req *Req) RespondRstat(st *p.Dir) {
|
||||
err := p.PackRstat(req.Rc, st, req.Conn.Dotu)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the request with Rwstat message
|
||||
func (req *Req) RespondRwstat() {
|
||||
err := p.PackRwstat(req.Rc)
|
||||
if err != nil {
|
||||
req.RespondError(err)
|
||||
} else {
|
||||
req.Respond()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,546 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The srv package provides definitions and functions used to implement
|
||||
// a 9P2000 file server.
|
||||
package srv
|
||||
|
||||
import (
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type reqStatus int
|
||||
|
||||
const (
|
||||
reqFlush reqStatus = (1 << iota) /* request is flushed (no response will be sent) */
|
||||
reqWork /* goroutine is currently working on it */
|
||||
reqResponded /* response is already produced */
|
||||
reqSaved /* no response was produced after the request is worked on */
|
||||
)
|
||||
|
||||
// Debug flags
|
||||
const (
|
||||
DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr
|
||||
DbgPrintPackets // print the raw packets on stderr
|
||||
DbgLogFcalls // keep the last N 9P messages (can be accessed over http)
|
||||
DbgLogPackets // keep the last N 9P messages (can be accessed over http)
|
||||
)
|
||||
|
||||
var Eunknownfid error = &p.Error{"unknown fid", p.EINVAL}
|
||||
var Enoauth error = &p.Error{"no authentication required", p.EINVAL}
|
||||
var Einuse error = &p.Error{"fid already in use", p.EINVAL}
|
||||
var Ebaduse error = &p.Error{"bad use of fid", p.EINVAL}
|
||||
var Eopen error = &p.Error{"fid already opened", p.EINVAL}
|
||||
var Enotdir error = &p.Error{"not a directory", p.ENOTDIR}
|
||||
var Eperm error = &p.Error{"permission denied", p.EPERM}
|
||||
var Etoolarge error = &p.Error{"i/o count too large", p.EINVAL}
|
||||
var Ebadoffset error = &p.Error{"bad offset in directory read", p.EINVAL}
|
||||
var Edirchange error = &p.Error{"cannot convert between files and directories", p.EINVAL}
|
||||
var Enouser error = &p.Error{"unknown user", p.EINVAL}
|
||||
var Enotimpl error = &p.Error{"not implemented", p.EINVAL}
|
||||
|
||||
// Authentication operations. The file server should implement them if
|
||||
// it requires user authentication. The authentication in 9P2000 is
|
||||
// done by creating special authentication fids and performing I/O
|
||||
// operations on them. Once the authentication is done, the authentication
|
||||
// fid can be used by the user to get access to the actual files.
|
||||
type AuthOps interface {
|
||||
// AuthInit is called when the user starts the authentication
|
||||
// process on Fid afid. The user that is being authenticated
|
||||
// is referred by afid.User. The function should return the Qid
|
||||
// for the authentication file, or an Error if the user can't be
|
||||
// authenticated
|
||||
AuthInit(afid *Fid, aname string) (*p.Qid, error)
|
||||
|
||||
// AuthDestroy is called when an authentication fid is destroyed.
|
||||
AuthDestroy(afid *Fid)
|
||||
|
||||
// AuthCheck is called after the authentication process is finished
|
||||
// when the user tries to attach to the file server. If the function
|
||||
// returns nil, the authentication was successful and the user has
|
||||
// permission to access the files.
|
||||
AuthCheck(fid *Fid, afid *Fid, aname string) error
|
||||
|
||||
// AuthRead is called when the user attempts to read data from an
|
||||
// authentication fid.
|
||||
AuthRead(afid *Fid, offset uint64, data []byte) (count int, err error)
|
||||
|
||||
// AuthWrite is called when the user attempts to write data to an
|
||||
// authentication fid.
|
||||
AuthWrite(afid *Fid, offset uint64, data []byte) (count int, err error)
|
||||
}
|
||||
|
||||
// Connection operations. These should be implemented if the file server
|
||||
// needs to be called when a connection is opened or closed.
|
||||
type ConnOps interface {
|
||||
ConnOpened(*Conn)
|
||||
ConnClosed(*Conn)
|
||||
}
|
||||
|
||||
// Fid operations. This interface should be implemented if the file server
|
||||
// needs to be called when a Fid is destroyed.
|
||||
type FidOps interface {
|
||||
FidDestroy(*Fid)
|
||||
}
|
||||
|
||||
// Request operations. This interface should be implemented if the file server
|
||||
// needs to bypass the default request process, or needs to perform certain
|
||||
// operations before the (any) request is processed, or before (any) response
|
||||
// sent back to the client.
|
||||
type ReqProcessOps interface {
|
||||
// Called when a new request is received from the client. If the
|
||||
// interface is not implemented, (req *Req) srv.Process() method is
|
||||
// called. If the interface is implemented, it is the user's
|
||||
// responsibility to call srv.Process. If srv.Process isn't called,
|
||||
// Fid, Afid and Newfid fields in Req are not set, and the ReqOps
|
||||
// methods are not called.
|
||||
ReqProcess(*Req)
|
||||
|
||||
// Called when a request is responded, i.e. when (req *Req)srv.Respond()
|
||||
// is called and before the response is sent. If the interface is not
|
||||
// implemented, (req *Req) srv.PostProcess() method is called to finalize
|
||||
// the request. If the interface is implemented and ReqProcess calls
|
||||
// the srv.Process method, ReqRespond should call the srv.PostProcess
|
||||
// method.
|
||||
ReqRespond(*Req)
|
||||
}
|
||||
|
||||
// Flush operation. This interface should be implemented if the file server
|
||||
// can flush pending requests. If the interface is not implemented, requests
|
||||
// that were passed to the file server implementation won't be flushed.
|
||||
// The flush method should call the (req *Req) srv.Flush() method if the flush
|
||||
// was successful so the request can be marked appropriately.
|
||||
type FlushOp interface {
|
||||
Flush(*Req)
|
||||
}
|
||||
|
||||
// Request operations. This interface should be implemented by all file servers.
|
||||
// The operations correspond directly to most of the 9P2000 message types.
|
||||
type ReqOps interface {
|
||||
Attach(*Req)
|
||||
Walk(*Req)
|
||||
Open(*Req)
|
||||
Create(*Req)
|
||||
Read(*Req)
|
||||
Write(*Req)
|
||||
Clunk(*Req)
|
||||
Remove(*Req)
|
||||
Stat(*Req)
|
||||
Wstat(*Req)
|
||||
}
|
||||
|
||||
type StatsOps interface {
|
||||
statsRegister()
|
||||
statsUnregister()
|
||||
}
|
||||
|
||||
// The Srv type contains the basic fields used to control the 9P2000
|
||||
// file server. Each file server implementation should create a value
|
||||
// of Srv type, initialize the values it cares about and pass the
|
||||
// struct to the (Srv *) srv.Start(ops) method together with the object
|
||||
// that implements the file server operations.
|
||||
type Srv struct {
|
||||
sync.Mutex
|
||||
Id string // Used for debugging and stats
|
||||
Msize uint32 // Maximum size of the 9P2000 messages supported by the server
|
||||
Dotu bool // If true, the server supports the 9P2000.u extension
|
||||
Debuglevel int // debug level
|
||||
Upool p.Users // Interface for finding users and groups known to the file server
|
||||
Maxpend int // Maximum pending outgoing requests
|
||||
Log *p.Logger
|
||||
|
||||
ops interface{} // operations
|
||||
conns map[*Conn]*Conn // List of connections
|
||||
}
|
||||
|
||||
// The Conn type represents a connection from a client to the file server
|
||||
type Conn struct {
|
||||
sync.Mutex
|
||||
Srv *Srv
|
||||
Msize uint32 // maximum size of 9P2000 messages for the connection
|
||||
Dotu bool // if true, both the client and the server speak 9P2000.u
|
||||
Id string // used for debugging and stats
|
||||
Debuglevel int
|
||||
|
||||
conn net.Conn
|
||||
fidpool map[uint32]*Fid
|
||||
reqs map[uint16]*Req // all outstanding requests
|
||||
|
||||
reqout chan *Req
|
||||
rchan chan *p.Fcall
|
||||
done chan bool
|
||||
|
||||
// stats
|
||||
nreqs int // number of requests processed by the server
|
||||
tsz uint64 // total size of the T messages received
|
||||
rsz uint64 // total size of the R messages sent
|
||||
npend int // number of currently pending messages
|
||||
maxpend int // maximum number of pending messages
|
||||
nreads int // number of reads
|
||||
nwrites int // number of writes
|
||||
}
|
||||
|
||||
// The Fid type identifies a file on the file server.
|
||||
// A new Fid is created when the user attaches to the file server (the Attach
|
||||
// operation), or when Walk-ing to a file. The Fid values are created
|
||||
// automatically by the srv implementation. The FidDestroy operation is called
|
||||
// when a Fid is destroyed.
|
||||
type Fid struct {
|
||||
sync.Mutex
|
||||
fid uint32
|
||||
refcount int
|
||||
opened bool // True if the Fid is opened
|
||||
Fconn *Conn // Connection the Fid belongs to
|
||||
Omode uint8 // Open mode (p.O* flags), if the fid is opened
|
||||
Type uint8 // Fid type (p.QT* flags)
|
||||
Diroffset uint64 // If directory, the next valid read position
|
||||
User p.User // The Fid's user
|
||||
Aux interface{} // Can be used by the file server implementation for per-Fid data
|
||||
}
|
||||
|
||||
// The Req type represents a 9P2000 request. Each request has a
|
||||
// T-message (Tc) and a R-message (Rc). If the ReqProcessOps don't
|
||||
// override the default behavior, the implementation initializes Fid,
|
||||
// Afid and Newfid values and automatically keeps track on when the Fids
|
||||
// should be destroyed.
|
||||
type Req struct {
|
||||
sync.Mutex
|
||||
Tc *p.Fcall // Incoming 9P2000 message
|
||||
Rc *p.Fcall // Outgoing 9P2000 response
|
||||
Fid *Fid // The Fid value for all messages that contain fid[4]
|
||||
Afid *Fid // The Fid value for the messages that contain afid[4] (Tauth and Tattach)
|
||||
Newfid *Fid // The Fid value for the messages that contain newfid[4] (Twalk)
|
||||
Conn *Conn // Connection that the request belongs to
|
||||
|
||||
status reqStatus
|
||||
flushreq *Req
|
||||
prev, next *Req
|
||||
}
|
||||
|
||||
// The Start method should be called once the file server implementor
|
||||
// initializes the Srv struct with the preferred values. It sets default
|
||||
// values to the fields that are not initialized and creates the goroutines
|
||||
// required for the server's operation. The method receives an empty
|
||||
// interface value, ops, that should implement the interfaces the file server is
|
||||
// interested in. Ops must implement the ReqOps interface.
|
||||
func (srv *Srv) Start(ops interface{}) bool {
|
||||
if _, ok := (ops).(ReqOps); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
srv.ops = ops
|
||||
if srv.Upool == nil {
|
||||
srv.Upool = p.OsUsers
|
||||
}
|
||||
|
||||
if srv.Msize < p.IOHDRSZ {
|
||||
srv.Msize = p.MSIZE
|
||||
}
|
||||
|
||||
if srv.Log == nil {
|
||||
srv.Log = p.NewLogger(1024)
|
||||
}
|
||||
|
||||
if sop, ok := (interface{}(srv)).(StatsOps); ok {
|
||||
sop.statsRegister()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (srv *Srv) String() string {
|
||||
return srv.Id
|
||||
}
|
||||
|
||||
func (req *Req) process() {
|
||||
req.Lock()
|
||||
flushed := (req.status & reqFlush) != 0
|
||||
if !flushed {
|
||||
req.status |= reqWork
|
||||
}
|
||||
req.Unlock()
|
||||
|
||||
if flushed {
|
||||
req.Respond()
|
||||
}
|
||||
|
||||
if rop, ok := (req.Conn.Srv.ops).(ReqProcessOps); ok {
|
||||
rop.ReqProcess(req)
|
||||
} else {
|
||||
req.Process()
|
||||
}
|
||||
|
||||
req.Lock()
|
||||
req.status &= ^reqWork
|
||||
if !(req.status&reqResponded != 0) {
|
||||
req.status |= reqSaved
|
||||
}
|
||||
req.Unlock()
|
||||
}
|
||||
|
||||
// Performs the default processing of a request. Initializes
|
||||
// the Fid, Afid and Newfid fields and calls the appropriate
|
||||
// ReqOps operation for the message. The file server implementer
|
||||
// should call it only if the file server implements the ReqProcessOps
|
||||
// within the ReqProcess operation.
|
||||
func (req *Req) Process() {
|
||||
conn := req.Conn
|
||||
srv := conn.Srv
|
||||
tc := req.Tc
|
||||
|
||||
if tc.Fid != p.NOFID && tc.Type != p.Tattach {
|
||||
srv.Lock()
|
||||
req.Fid = conn.FidGet(tc.Fid)
|
||||
srv.Unlock()
|
||||
if req.Fid == nil {
|
||||
req.RespondError(Eunknownfid)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch req.Tc.Type {
|
||||
default:
|
||||
req.RespondError(&p.Error{"unknown message type", p.EINVAL})
|
||||
|
||||
case p.Tversion:
|
||||
srv.version(req)
|
||||
|
||||
case p.Tauth:
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
srv.auth(req)
|
||||
|
||||
case p.Tattach:
|
||||
srv.attach(req)
|
||||
|
||||
case p.Tflush:
|
||||
srv.flush(req)
|
||||
|
||||
case p.Twalk:
|
||||
srv.walk(req)
|
||||
|
||||
case p.Topen:
|
||||
srv.open(req)
|
||||
|
||||
case p.Tcreate:
|
||||
srv.create(req)
|
||||
|
||||
case p.Tread:
|
||||
srv.read(req)
|
||||
|
||||
case p.Twrite:
|
||||
srv.write(req)
|
||||
|
||||
case p.Tclunk:
|
||||
srv.clunk(req)
|
||||
|
||||
case p.Tremove:
|
||||
srv.remove(req)
|
||||
|
||||
case p.Tstat:
|
||||
srv.stat(req)
|
||||
|
||||
case p.Twstat:
|
||||
srv.wstat(req)
|
||||
}
|
||||
}
|
||||
|
||||
// Performs the post processing required if the (*Req) Process() method
|
||||
// is called for a request. The file server implementer should call it
|
||||
// only if the file server implements the ReqProcessOps within the
|
||||
// ReqRespond operation.
|
||||
func (req *Req) PostProcess() {
|
||||
srv := req.Conn.Srv
|
||||
|
||||
/* call the post-handlers (if needed) */
|
||||
switch req.Tc.Type {
|
||||
case p.Tauth:
|
||||
srv.authPost(req)
|
||||
|
||||
case p.Tattach:
|
||||
srv.attachPost(req)
|
||||
|
||||
case p.Twalk:
|
||||
srv.walkPost(req)
|
||||
|
||||
case p.Topen:
|
||||
srv.openPost(req)
|
||||
|
||||
case p.Tcreate:
|
||||
srv.createPost(req)
|
||||
|
||||
case p.Tread:
|
||||
srv.readPost(req)
|
||||
|
||||
case p.Tclunk:
|
||||
srv.clunkPost(req)
|
||||
|
||||
case p.Tremove:
|
||||
srv.removePost(req)
|
||||
}
|
||||
|
||||
if req.Fid != nil {
|
||||
req.Fid.DecRef()
|
||||
req.Fid = nil
|
||||
}
|
||||
|
||||
if req.Afid != nil {
|
||||
req.Afid.DecRef()
|
||||
req.Afid = nil
|
||||
}
|
||||
|
||||
if req.Newfid != nil {
|
||||
req.Newfid.DecRef()
|
||||
req.Newfid = nil
|
||||
}
|
||||
}
|
||||
|
||||
// The Respond method sends response back to the client. The req.Rc value
|
||||
// should be initialized and contain valid 9P2000 message. In most cases
|
||||
// the file server implementer shouldn't call this method directly. Instead
|
||||
// one of the RespondR* methods should be used.
|
||||
func (req *Req) Respond() {
|
||||
var flushreqs *Req
|
||||
|
||||
conn := req.Conn
|
||||
req.Lock()
|
||||
status := req.status
|
||||
req.status |= reqResponded
|
||||
req.status &= ^reqWork
|
||||
req.Unlock()
|
||||
|
||||
if (status & reqResponded) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
/* remove the request and all requests flushing it */
|
||||
conn.Lock()
|
||||
nextreq := req.prev
|
||||
if nextreq != nil {
|
||||
nextreq.next = nil
|
||||
// if there are flush requests, move them to the next request
|
||||
if req.flushreq != nil {
|
||||
var p *Req = nil
|
||||
r := nextreq.flushreq
|
||||
for ; r != nil; p, r = r, r.flushreq {
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
nextreq.flushreq = req.flushreq
|
||||
} else {
|
||||
p.next = req.flushreq
|
||||
}
|
||||
}
|
||||
|
||||
flushreqs = nil
|
||||
} else {
|
||||
delete(conn.reqs, req.Tc.Tag)
|
||||
flushreqs = req.flushreq
|
||||
}
|
||||
conn.Unlock()
|
||||
|
||||
if rop, ok := (req.Conn.Srv.ops).(ReqProcessOps); ok {
|
||||
rop.ReqRespond(req)
|
||||
} else {
|
||||
req.PostProcess()
|
||||
}
|
||||
|
||||
if (status & reqFlush) == 0 {
|
||||
conn.reqout <- req
|
||||
}
|
||||
|
||||
// process the next request with the same tag (if available)
|
||||
if nextreq != nil {
|
||||
go nextreq.process()
|
||||
}
|
||||
|
||||
// respond to the flush messages
|
||||
// can't send the responses directly to conn.reqout, because the
|
||||
// the flushes may be in a tag group too
|
||||
for freq := flushreqs; freq != nil; freq = freq.flushreq {
|
||||
freq.Respond()
|
||||
}
|
||||
}
|
||||
|
||||
// Should be called to cancel a request. Should only be called
|
||||
// from the Flush operation if the FlushOp is implemented.
|
||||
func (req *Req) Flush() {
|
||||
req.Lock()
|
||||
req.status |= reqFlush
|
||||
req.Unlock()
|
||||
req.Respond()
|
||||
}
|
||||
|
||||
// Lookup a Fid struct based on the 32-bit identifier sent over the wire.
|
||||
// Returns nil if the fid is not found. Increases the reference count of
|
||||
// the returned fid. The user is responsible to call DecRef once it no
|
||||
// longer needs it.
|
||||
func (conn *Conn) FidGet(fidno uint32) *Fid {
|
||||
conn.Lock()
|
||||
fid, present := conn.fidpool[fidno]
|
||||
conn.Unlock()
|
||||
if present {
|
||||
fid.IncRef()
|
||||
}
|
||||
|
||||
return fid
|
||||
}
|
||||
|
||||
// Creates a new Fid struct for the fidno integer. Returns nil
|
||||
// if the Fid for that number already exists. The returned fid
|
||||
// has reference count set to 1.
|
||||
func (conn *Conn) FidNew(fidno uint32) *Fid {
|
||||
conn.Lock()
|
||||
_, present := conn.fidpool[fidno]
|
||||
if present {
|
||||
conn.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
fid := new(Fid)
|
||||
fid.fid = fidno
|
||||
fid.refcount = 1
|
||||
fid.Fconn = conn
|
||||
conn.fidpool[fidno] = fid
|
||||
conn.Unlock()
|
||||
|
||||
return fid
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.Srv.Id + "/" + conn.Id
|
||||
}
|
||||
|
||||
// Increase the reference count for the fid.
|
||||
func (fid *Fid) IncRef() {
|
||||
fid.Lock()
|
||||
fid.refcount++
|
||||
fid.Unlock()
|
||||
}
|
||||
|
||||
// Decrease the reference count for the fid. When the
|
||||
// reference count reaches 0, the fid is no longer valid.
|
||||
func (fid *Fid) DecRef() {
|
||||
fid.Lock()
|
||||
fid.refcount--
|
||||
n := fid.refcount
|
||||
fid.Unlock()
|
||||
|
||||
if n > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
conn := fid.Fconn
|
||||
conn.Lock()
|
||||
delete(conn.fidpool, fid.fid)
|
||||
conn.Unlock()
|
||||
|
||||
if fop, ok := (conn.Srv.ops).(FidOps); ok {
|
||||
fop.FidDestroy(fid)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// +build httpstats
|
||||
|
||||
package srv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"k8s.io/minikube/third_party/go9p/p"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var mux sync.RWMutex
|
||||
var stat map[string]http.Handler
|
||||
var once sync.Once
|
||||
|
||||
func register(s string, h http.Handler) {
|
||||
mux.Lock()
|
||||
if stat == nil {
|
||||
stat = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
if h == nil {
|
||||
delete(stat, s)
|
||||
} else {
|
||||
stat[s] = h
|
||||
}
|
||||
mux.Unlock()
|
||||
}
|
||||
func (srv *Srv) statsRegister() {
|
||||
once.Do(func() {
|
||||
http.HandleFunc("/go9p/", StatsHandler)
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
})
|
||||
|
||||
register("/go9p/srv/"+srv.Id, srv)
|
||||
}
|
||||
|
||||
func (srv *Srv) statsUnregister() {
|
||||
register("/go9p/srv/"+srv.Id, nil)
|
||||
}
|
||||
|
||||
func (srv *Srv) ServeHTTP(c http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(c, fmt.Sprintf("<html><body><h1>Server %s</h1>", srv.Id))
|
||||
defer io.WriteString(c, "</body></html>")
|
||||
|
||||
// connections
|
||||
io.WriteString(c, "<h2>Connections</h2><p>")
|
||||
srv.Lock()
|
||||
defer srv.Unlock()
|
||||
if len(srv.conns) == 0 {
|
||||
io.WriteString(c, "none")
|
||||
return
|
||||
}
|
||||
|
||||
for _, conn := range srv.conns {
|
||||
io.WriteString(c, fmt.Sprintf("<a href='/go9p/srv/%s/conn/%s'>%s</a><br>", srv.Id, conn.Id, conn.Id))
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) statsRegister() {
|
||||
register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, conn)
|
||||
}
|
||||
|
||||
func (conn *Conn) statsUnregister() {
|
||||
register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, nil)
|
||||
}
|
||||
|
||||
func (conn *Conn) ServeHTTP(c http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(c, fmt.Sprintf("<html><body><h1>Connection %s/%s</h1>", conn.Srv.Id, conn.Id))
|
||||
defer io.WriteString(c, "</body></html>")
|
||||
|
||||
// statistics
|
||||
conn.Lock()
|
||||
io.WriteString(c, fmt.Sprintf("<p>Number of processed requests: %d", conn.nreqs))
|
||||
io.WriteString(c, fmt.Sprintf("<br>Sent %v bytes", conn.rsz))
|
||||
io.WriteString(c, fmt.Sprintf("<br>Received %v bytes", conn.tsz))
|
||||
io.WriteString(c, fmt.Sprintf("<br>Pending requests: %d max %d", conn.npend, conn.maxpend))
|
||||
io.WriteString(c, fmt.Sprintf("<br>Number of reads: %d", conn.nreads))
|
||||
io.WriteString(c, fmt.Sprintf("<br>Number of writes: %d", conn.nwrites))
|
||||
conn.Unlock()
|
||||
|
||||
// fcalls
|
||||
if conn.Debuglevel&DbgLogFcalls != 0 {
|
||||
fs := conn.Srv.Log.Filter(conn, DbgLogFcalls)
|
||||
io.WriteString(c, fmt.Sprintf("<h2>Last %d 9P messages</h2>", len(fs)))
|
||||
for i, l := range fs {
|
||||
fc := l.Data.(*p.Fcall)
|
||||
if fc.Type == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
lbl := ""
|
||||
if fc.Type%2 == 0 {
|
||||
// try to find the response for the T message
|
||||
for j := i + 1; j < len(fs); j++ {
|
||||
rc := fs[j].Data.(*p.Fcall)
|
||||
if rc.Tag == fc.Tag {
|
||||
lbl = fmt.Sprintf("<a href='#fc%d'>➴</a>", j)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to find the request for the R message
|
||||
for j := i - 1; j >= 0; j-- {
|
||||
tc := fs[j].Data.(*p.Fcall)
|
||||
if tc.Tag == fc.Tag {
|
||||
lbl = fmt.Sprintf("<a href='#fc%d'>➶</a>", j)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io.WriteString(c, fmt.Sprintf("<br id='fc%d'>%d: %s%s", i, i, fc, lbl))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StatsHandler(c http.ResponseWriter, r *http.Request) {
|
||||
mux.RLock()
|
||||
if v, ok := stat[r.URL.Path]; ok {
|
||||
v.ServeHTTP(c, r)
|
||||
} else if r.URL.Path == "/go9p/" {
|
||||
io.WriteString(c, fmt.Sprintf("<html><body><br><h1>On offer: </h1><br>"))
|
||||
for v := range stat {
|
||||
io.WriteString(c, fmt.Sprintf("<a href='%s'>%s</a><br>", v, v))
|
||||
}
|
||||
io.WriteString(c, "</body></html>")
|
||||
}
|
||||
mux.RUnlock()
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2009 The Go9p Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Creates a Fcall value from the on-the-wire representation. If
|
||||
// dotu is true, reads 9P2000.u messages. Returns the unpacked message,
|
||||
// error and how many bytes from the buffer were used by the message.
|
||||
func Unpack(buf []byte, dotu bool) (fc *Fcall, err error, fcsz int) {
|
||||
var m uint16
|
||||
|
||||
if len(buf) < 7 {
|
||||
return nil, &Error{"buffer too short", EINVAL}, 0
|
||||
}
|
||||
|
||||
fc = new(Fcall)
|
||||
fc.Fid = NOFID
|
||||
fc.Afid = NOFID
|
||||
fc.Newfid = NOFID
|
||||
|
||||
p := buf
|
||||
fc.Size, p = gint32(p)
|
||||
fc.Type, p = gint8(p)
|
||||
fc.Tag, p = gint16(p)
|
||||
|
||||
if int(fc.Size) > len(buf) || fc.Size < 7 {
|
||||
return nil, &Error{fmt.Sprintf("buffer too short: %d expected %d",
|
||||
len(buf), fc.Size),
|
||||
EINVAL},
|
||||
0
|
||||
}
|
||||
|
||||
p = p[0 : fc.Size-7]
|
||||
fc.Pkt = buf[0:fc.Size]
|
||||
fcsz = int(fc.Size)
|
||||
if fc.Type < Tversion || fc.Type >= Tlast {
|
||||
return nil, &Error{"invalid id", EINVAL}, 0
|
||||
}
|
||||
|
||||
var sz uint32
|
||||
if dotu {
|
||||
sz = minFcsize[fc.Type-Tversion]
|
||||
} else {
|
||||
sz = minFcusize[fc.Type-Tversion]
|
||||
}
|
||||
|
||||
if fc.Size < sz {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
err = nil
|
||||
switch fc.Type {
|
||||
default:
|
||||
return nil, &Error{"invalid message id", EINVAL}, 0
|
||||
|
||||
case Tversion, Rversion:
|
||||
fc.Msize, p = gint32(p)
|
||||
fc.Version, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
case Tauth:
|
||||
fc.Afid, p = gint32(p)
|
||||
fc.Uname, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
fc.Aname, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
fc.Unamenum = NOUID
|
||||
if dotu && len(p) > 0 {
|
||||
fc.Unamenum, p = gint32(p)
|
||||
}
|
||||
|
||||
case Rauth, Rattach:
|
||||
p = gqid(p, &fc.Qid)
|
||||
|
||||
case Tflush:
|
||||
fc.Oldtag, p = gint16(p)
|
||||
|
||||
case Tattach:
|
||||
fc.Fid, p = gint32(p)
|
||||
fc.Afid, p = gint32(p)
|
||||
fc.Uname, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
fc.Aname, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
fc.Unamenum = NOUID
|
||||
if dotu && len(p) > 0 {
|
||||
fc.Unamenum, p = gint32(p)
|
||||
}
|
||||
|
||||
case Rerror:
|
||||
fc.Error, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
if dotu {
|
||||
fc.Errornum, p = gint32(p)
|
||||
} else {
|
||||
fc.Errornum = 0
|
||||
}
|
||||
|
||||
case Twalk:
|
||||
fc.Fid, p = gint32(p)
|
||||
fc.Newfid, p = gint32(p)
|
||||
m, p = gint16(p)
|
||||
fc.Wname = make([]string, m)
|
||||
for i := 0; i < int(m); i++ {
|
||||
fc.Wname[i], p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
}
|
||||
|
||||
case Rwalk:
|
||||
m, p = gint16(p)
|
||||
fc.Wqid = make([]Qid, m)
|
||||
for i := 0; i < int(m); i++ {
|
||||
p = gqid(p, &fc.Wqid[i])
|
||||
}
|
||||
|
||||
case Topen:
|
||||
fc.Fid, p = gint32(p)
|
||||
fc.Mode, p = gint8(p)
|
||||
|
||||
case Ropen, Rcreate:
|
||||
p = gqid(p, &fc.Qid)
|
||||
fc.Iounit, p = gint32(p)
|
||||
|
||||
case Tcreate:
|
||||
fc.Fid, p = gint32(p)
|
||||
fc.Name, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
fc.Perm, p = gint32(p)
|
||||
fc.Mode, p = gint8(p)
|
||||
if dotu {
|
||||
fc.Ext, p = gstr(p)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
}
|
||||
|
||||
case Tread:
|
||||
fc.Fid, p = gint32(p)
|
||||
fc.Offset, p = gint64(p)
|
||||
fc.Count, p = gint32(p)
|
||||
|
||||
case Rread:
|
||||
fc.Count, p = gint32(p)
|
||||
if len(p) < int(fc.Count) {
|
||||
goto szerror
|
||||
}
|
||||
fc.Data = p
|
||||
p = p[fc.Count:]
|
||||
|
||||
case Twrite:
|
||||
fc.Fid, p = gint32(p)
|
||||
fc.Offset, p = gint64(p)
|
||||
fc.Count, p = gint32(p)
|
||||
if len(p) != int(fc.Count) {
|
||||
fc.Data = make([]byte, fc.Count)
|
||||
copy(fc.Data, p)
|
||||
p = p[len(p):]
|
||||
} else {
|
||||
fc.Data = p
|
||||
p = p[fc.Count:]
|
||||
}
|
||||
|
||||
case Rwrite:
|
||||
fc.Count, p = gint32(p)
|
||||
|
||||
case Tclunk, Tremove, Tstat:
|
||||
fc.Fid, p = gint32(p)
|
||||
|
||||
case Rstat:
|
||||
m, p = gint16(p)
|
||||
p = gstat(p, &fc.Dir, dotu)
|
||||
if p == nil {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
case Twstat:
|
||||
fc.Fid, p = gint32(p)
|
||||
m, p = gint16(p)
|
||||
p = gstat(p, &fc.Dir, dotu)
|
||||
|
||||
case Rflush, Rclunk, Rremove, Rwstat:
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
goto szerror
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
szerror:
|
||||
return nil, &Error{"invalid size", EINVAL}, 0
|
||||
}
|
Loading…
Reference in New Issue