summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/control/fs.go93
-rw-r--r--runsc/boot/controller.go6
-rw-r--r--runsc/cmd/debug.go8
-rw-r--r--runsc/container/container.go6
-rw-r--r--runsc/sandbox/sandbox.go18
5 files changed, 131 insertions, 0 deletions
diff --git a/pkg/sentry/control/fs.go b/pkg/sentry/control/fs.go
new file mode 100644
index 000000000..d19b21f2d
--- /dev/null
+++ b/pkg/sentry/control/fs.go
@@ -0,0 +1,93 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package control
+
+import (
+ "fmt"
+ "io"
+ "os"
+
+ "gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/sentry/fs"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
+ "gvisor.dev/gvisor/pkg/urpc"
+ "gvisor.dev/gvisor/pkg/usermem"
+)
+
+// CatOpts contains options for the Cat RPC call.
+type CatOpts struct {
+ // Files are the filesystem paths for the files to cat.
+ Files []string `json:"files"`
+
+ // FilePayload contains the destination for output.
+ urpc.FilePayload
+}
+
+// Fs includes fs-related functions.
+type Fs struct {
+ Kernel *kernel.Kernel
+}
+
+// Cat is a RPC stub which prints out and returns the content of the files.
+func (f *Fs) Cat(o *CatOpts, _ *struct{}) error {
+ // Create an output stream.
+ if len(o.FilePayload.Files) != 1 {
+ return ErrInvalidFiles
+ }
+
+ output := o.FilePayload.Files[0]
+ for _, file := range o.Files {
+ if err := cat(f.Kernel, file, output); err != nil {
+ return fmt.Errorf("cannot read from file %s: %v", file, err)
+ }
+ }
+
+ return nil
+}
+
+// fileReader encapsulates a fs.File and provides an io.Reader interface.
+type fileReader struct {
+ ctx context.Context
+ file *fs.File
+}
+
+// Read implements io.Reader.Read.
+func (f *fileReader) Read(p []byte) (int, error) {
+ n, err := f.file.Readv(f.ctx, usermem.BytesIOSequence(p))
+ return int(n), err
+}
+
+func cat(k *kernel.Kernel, path string, output *os.File) error {
+ ctx := k.SupervisorContext()
+ mns := k.GlobalInit().Leader().MountNamespace()
+ root := mns.Root()
+ defer root.DecRef(ctx)
+
+ remainingTraversals := uint(fs.DefaultTraversalLimit)
+ d, err := mns.FindInode(ctx, root, nil, path, &remainingTraversals)
+ if err != nil {
+ return fmt.Errorf("cannot find file %s: %v", path, err)
+ }
+ defer d.DecRef(ctx)
+
+ file, err := d.Inode.GetFile(ctx, d, fs.FileFlags{Read: true})
+ if err != nil {
+ return fmt.Errorf("cannot get file for path %s: %v", path, err)
+ }
+ defer file.DecRef(ctx)
+
+ _, err = io.Copy(output, &fileReader{ctx: ctx, file: file})
+ return err
+}
diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go
index 548797788..60b532798 100644
--- a/runsc/boot/controller.go
+++ b/runsc/boot/controller.go
@@ -109,6 +109,11 @@ const (
LifecycleResume = "Lifecycle.Resume"
)
+// Filesystem related commands (see fs.go for more details).
+const (
+ FsCat = "Fs.Cat"
+)
+
// ControlSocketAddr generates an abstract unix socket name for the given ID.
func ControlSocketAddr(id string) string {
return fmt.Sprintf("\x00runsc-sandbox.%s", id)
@@ -151,6 +156,7 @@ func newController(fd int, l *Loader) (*controller, error) {
ctrl.srv.Register(&debug{})
ctrl.srv.Register(&control.Logging{})
ctrl.srv.Register(&control.Lifecycle{l.k})
+ ctrl.srv.Register(&control.Fs{l.k})
if l.root.conf.ProfileEnable {
ctrl.srv.Register(control.NewProfile(l.k))
diff --git a/runsc/cmd/debug.go b/runsc/cmd/debug.go
index da81cf048..f773ccca0 100644
--- a/runsc/cmd/debug.go
+++ b/runsc/cmd/debug.go
@@ -48,6 +48,7 @@ type Debug struct {
delay time.Duration
duration time.Duration
ps bool
+ cat stringSlice
}
// Name implements subcommands.Command.
@@ -81,6 +82,7 @@ func (d *Debug) SetFlags(f *flag.FlagSet) {
f.StringVar(&d.logLevel, "log-level", "", "The log level to set: warning (0), info (1), or debug (2).")
f.StringVar(&d.logPackets, "log-packets", "", "A boolean value to enable or disable packet logging: true or false.")
f.BoolVar(&d.ps, "ps", false, "lists processes")
+ f.Var(&d.cat, "cat", "reads files and print to standard output")
}
// Execute implements subcommands.Command.Execute.
@@ -367,5 +369,11 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
return subcommands.ExitFailure
}
+ if d.cat != nil {
+ if err := c.Cat(d.cat, os.Stdout); err != nil {
+ return Errorf("Cat failed: %v", err)
+ }
+ }
+
return subcommands.ExitSuccess
}
diff --git a/runsc/container/container.go b/runsc/container/container.go
index 6a9a07afe..d1f979eb2 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -646,6 +646,12 @@ func (c *Container) Resume() error {
return c.saveLocked()
}
+// Cat prints out the content of the files.
+func (c *Container) Cat(files []string, out *os.File) error {
+ log.Debugf("Cat in container, cid: %s, files: %+v", c.ID, files)
+ return c.Sandbox.Cat(c.ID, files, out)
+}
+
// State returns the metadata of the container.
func (c *Container) State() specs.State {
return specs.State{
diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go
index 822da8c5e..b15572a98 100644
--- a/runsc/sandbox/sandbox.go
+++ b/runsc/sandbox/sandbox.go
@@ -1002,6 +1002,24 @@ func (s *Sandbox) Resume(cid string) error {
return nil
}
+// Cat sends the cat call for a container in the sandbox.
+func (s *Sandbox) Cat(cid string, files []string, out *os.File) error {
+ log.Debugf("Cat sandbox %q", s.ID)
+ conn, err := s.sandboxConnect()
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ if err := conn.Call(boot.FsCat, &control.CatOpts{
+ Files: files,
+ FilePayload: urpc.FilePayload{Files: []*os.File{out}},
+ }, nil); err != nil {
+ return fmt.Errorf("Cat container %q: %v", cid, err)
+ }
+ return nil
+}
+
// IsRunning returns true if the sandbox or gofer process is running.
func (s *Sandbox) IsRunning() bool {
if s.Pid != 0 {