summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/fsimpl/gofer/filesystem.go3
-rw-r--r--pkg/sentry/fsimpl/gofer/gofer.go13
-rw-r--r--runsc/boot/fs.go14
-rw-r--r--runsc/boot/vfs.go4
-rw-r--r--runsc/cli/main.go2
-rw-r--r--runsc/cmd/gofer.go120
-rw-r--r--runsc/config/config.go3
-rw-r--r--runsc/config/flags.go1
-rw-r--r--runsc/container/container.go2
-rw-r--r--runsc/fsgofer/BUILD2
-rw-r--r--runsc/fsgofer/lisafs.go52
-rw-r--r--runsc/specutils/specutils.go4
12 files changed, 185 insertions, 35 deletions
diff --git a/pkg/sentry/fsimpl/gofer/filesystem.go b/pkg/sentry/fsimpl/gofer/filesystem.go
index 00228c469..9d943bd4a 100644
--- a/pkg/sentry/fsimpl/gofer/filesystem.go
+++ b/pkg/sentry/fsimpl/gofer/filesystem.go
@@ -1612,6 +1612,9 @@ func (fs *filesystem) MountOptions() string {
if fs.opts.overlayfsStaleRead {
optsKV = append(optsKV, mopt{moptOverlayfsStaleRead, nil})
}
+ if fs.opts.lisaEnabled {
+ optsKV = append(optsKV, mopt{moptLisafs, nil})
+ }
opts := make([]string, 0, len(optsKV))
for _, opt := range optsKV {
diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go
index 43440ec19..13971d086 100644
--- a/pkg/sentry/fsimpl/gofer/gofer.go
+++ b/pkg/sentry/fsimpl/gofer/gofer.go
@@ -83,6 +83,7 @@ const (
moptForcePageCache = "force_page_cache"
moptLimitHostFDTranslation = "limit_host_fd_translation"
moptOverlayfsStaleRead = "overlayfs_stale_read"
+ moptLisafs = "lisafs"
)
// Valid values for the "cache" mount option.
@@ -214,6 +215,10 @@ type filesystemOptions struct {
// way that application FDs representing "special files" such as sockets
// do. Note that this disables client caching and mmap for regular files.
regularFilesUseSpecialFileFD bool
+
+ // lisaEnabled indicates whether the client will use lisafs protocol to
+ // communicate with the server instead of 9P.
+ lisaEnabled bool
}
// InteropMode controls the client's interaction with other remote filesystem
@@ -427,6 +432,14 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
delete(mopts, moptOverlayfsStaleRead)
fsopts.overlayfsStaleRead = true
}
+ if lisafs, ok := mopts[moptLisafs]; ok {
+ delete(mopts, moptLisafs)
+ fsopts.lisaEnabled, err = strconv.ParseBool(lisafs)
+ if err != nil {
+ ctx.Warningf("gofer.FilesystemType.GetFilesystem: invalid lisafs option: %s", lisafs)
+ return nil, nil, linuxerr.EINVAL
+ }
+ }
// fsopts.regularFilesUseSpecialFileFD can only be enabled by specifying
// "cache=none".
diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go
index 40cf2a3df..ba5460f1c 100644
--- a/runsc/boot/fs.go
+++ b/runsc/boot/fs.go
@@ -188,8 +188,8 @@ func compileMounts(spec *specs.Spec, conf *config.Config, vfs2Enabled bool) []sp
return mounts
}
-// p9MountData creates a slice of p9 mount data.
-func p9MountData(fd int, fa config.FileAccessType, vfs2 bool) []string {
+// goferMountData creates a slice of gofer mount data.
+func goferMountData(fd int, fa config.FileAccessType, attachPath string, vfs2 bool, lisafs bool) []string {
opts := []string{
"trans=fd",
"rfdno=" + strconv.Itoa(fd),
@@ -203,6 +203,10 @@ func p9MountData(fd int, fa config.FileAccessType, vfs2 bool) []string {
if fa == config.FileAccessShared {
opts = append(opts, "cache=remote_revalidating")
}
+ if vfs2 && lisafs {
+ opts = append(opts, "lisafs=true")
+ opts = append(opts, "aname="+attachPath)
+ }
return opts
}
@@ -761,7 +765,7 @@ func (c *containerMounter) createRootMount(ctx context.Context, conf *config.Con
fd := c.fds.remove()
log.Infof("Mounting root over 9P, ioFD: %d", fd)
p9FS := mustFindFilesystem("9p")
- opts := p9MountData(fd, conf.FileAccess, false /* vfs2 */)
+ opts := goferMountData(fd, conf.FileAccess, "/", false /* vfs2 */, false /* lisafs */)
// We can't check for overlayfs here because sandbox is chroot'ed and gofer
// can only send mount options for specs.Mounts (specs.Root is missing
@@ -822,7 +826,7 @@ func (c *containerMounter) getMountNameAndOptions(conf *config.Config, m *specs.
case bind:
fd := c.fds.remove()
fsName = gofervfs2.Name
- opts = p9MountData(fd, c.getMountAccessType(conf, m), conf.VFS2)
+ opts = goferMountData(fd, c.getMountAccessType(conf, m), m.Destination, conf.VFS2, conf.Lisafs)
// If configured, add overlay to all writable mounts.
useOverlay = conf.Overlay && !mountFlags(m.Options).ReadOnly
case cgroupfs.Name:
@@ -980,7 +984,7 @@ func (c *containerMounter) createRestoreEnvironment(conf *config.Config) (*fs.Re
// Add root mount.
fd := c.fds.remove()
- opts := p9MountData(fd, conf.FileAccess, false /* vfs2 */)
+ opts := goferMountData(fd, conf.FileAccess, "/", false /* vfs2 */, false /* lisafs */)
mf := fs.MountSourceFlags{}
if c.root.Readonly || conf.Overlay {
diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go
index 346796d9c..2f1332566 100644
--- a/runsc/boot/vfs.go
+++ b/runsc/boot/vfs.go
@@ -208,7 +208,7 @@ func (c *containerMounter) mountAll(conf *config.Config, procArgs *kernel.Create
// createMountNamespaceVFS2 creates the container's root mount and namespace.
func (c *containerMounter) createMountNamespaceVFS2(ctx context.Context, conf *config.Config, creds *auth.Credentials) (*vfs.MountNamespace, error) {
fd := c.fds.remove()
- data := p9MountData(fd, conf.FileAccess, true /* vfs2 */)
+ data := goferMountData(fd, conf.FileAccess, "/", true /* vfs2 */, conf.Lisafs)
// We can't check for overlayfs here because sandbox is chroot'ed and gofer
// can only send mount options for specs.Mounts (specs.Root is missing
@@ -515,7 +515,7 @@ func (c *containerMounter) getMountNameAndOptionsVFS2(conf *config.Config, m *mo
// but unlikely to be correct in this context.
return "", nil, false, fmt.Errorf("9P mount requires a connection FD")
}
- data = p9MountData(m.fd, c.getMountAccessType(conf, m.mount), true /* vfs2 */)
+ data = goferMountData(m.fd, c.getMountAccessType(conf, m.mount), m.mount.Destination, true /* vfs2 */, conf.Lisafs)
internalData = gofer.InternalFilesystemOptions{
UniqueID: m.mount.Destination,
}
diff --git a/runsc/cli/main.go b/runsc/cli/main.go
index 3556d7665..058ab8232 100644
--- a/runsc/cli/main.go
+++ b/runsc/cli/main.go
@@ -228,7 +228,7 @@ func Main(version string) {
log.Infof("\t\tFileAccess: %v, overlay: %t", conf.FileAccess, conf.Overlay)
log.Infof("\t\tNetwork: %v, logging: %t", conf.Network, conf.LogPackets)
log.Infof("\t\tStrace: %t, max size: %d, syscalls: %s", conf.Strace, conf.StraceLogSize, conf.StraceSyscalls)
- log.Infof("\t\tVFS2 enabled: %v", conf.VFS2)
+ log.Infof("\t\tVFS2 enabled: %t, LISAFS: %t", conf.VFS2, conf.Lisafs)
log.Infof("***************************")
if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go
index 2193e9040..c65e0267a 100644
--- a/runsc/cmd/gofer.go
+++ b/runsc/cmd/gofer.go
@@ -71,8 +71,8 @@ func (*Gofer) Name() string {
}
// Synopsis implements subcommands.Command.
-func (*Gofer) Synopsis() string {
- return "launch a gofer process that serves files over 9P protocol (internal use only)"
+func (g *Gofer) Synopsis() string {
+ return fmt.Sprintf("launch a gofer process that serves files over the protocol (9P or lisafs) defined in the config (internal use only)")
}
// Usage implements subcommands.Command.
@@ -83,7 +83,7 @@ func (*Gofer) Usage() string {
// SetFlags implements subcommands.Command.
func (g *Gofer) SetFlags(f *flag.FlagSet) {
f.StringVar(&g.bundleDir, "bundle", "", "path to the root of the bundle directory, defaults to the current directory")
- f.Var(&g.ioFDs, "io-fds", "list of FDs to connect 9P servers. They must follow this order: root first, then mounts as defined in the spec")
+ f.Var(&g.ioFDs, "io-fds", "list of FDs to connect gofer servers. They must follow this order: root first, then mounts as defined in the spec")
f.BoolVar(&g.applyCaps, "apply-caps", true, "if true, apply capabilities to restrict what the Gofer process can do")
f.BoolVar(&g.setUpRoot, "setup-root", true, "if true, set up an empty root for the process")
f.IntVar(&g.specFD, "spec-fd", -1, "required fd with the container spec")
@@ -160,10 +160,98 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
}
log.Infof("Process chroot'd to %q", root)
+ // Initialize filters.
+ if conf.FSGoferHostUDS {
+ filter.InstallUDSFilters()
+ }
+
+ if conf.Verity {
+ filter.InstallXattrFilters()
+ }
+
+ if err := filter.Install(); err != nil {
+ Fatalf("installing seccomp filters: %v", err)
+ }
+
+ if conf.Lisafs {
+ return g.serveLisafs(spec, conf, root)
+ }
+ return g.serve9P(spec, conf, root)
+}
+
+func newSocket(ioFD int) *unet.Socket {
+ socket, err := unet.NewSocket(ioFD)
+ if err != nil {
+ Fatalf("creating server on FD %d: %v", ioFD, err)
+ }
+ return socket
+}
+
+func (g *Gofer) serveLisafs(spec *specs.Spec, conf *config.Config, root string) subcommands.ExitStatus {
+ type connectionConfig struct {
+ sock *unet.Socket
+ readonly bool
+ }
+ cfgs := make([]connectionConfig, 0, len(spec.Mounts)+1)
+ server := fsgofer.NewLisafsServer(fsgofer.Config{
+ // These are global options. Ignore readonly configuration, that is set on
+ // a per connection basis.
+ HostUDS: conf.FSGoferHostUDS,
+ EnableVerityXattr: conf.Verity,
+ })
+
+ // Start with root mount, then add any other additional mount as needed.
+ cfgs = append(cfgs, connectionConfig{
+ sock: newSocket(g.ioFDs[0]),
+ readonly: spec.Root.Readonly || conf.Overlay,
+ })
+ log.Infof("Serving %q mapped to %q on FD %d (ro: %t)", "/", root, g.ioFDs[0], cfgs[0].readonly)
+
+ mountIdx := 1 // first one is the root
+ for _, m := range spec.Mounts {
+ if !specutils.IsGoferMount(m, conf.VFS2) {
+ continue
+ }
+
+ if !filepath.IsAbs(m.Destination) {
+ Fatalf("mount destination must be absolute: %q", m.Destination)
+ }
+ if mountIdx >= len(g.ioFDs) {
+ Fatalf("no FD found for mount. Did you forget --io-fd? FDs: %d, Mount: %+v", len(g.ioFDs), m)
+ }
+
+ cfgs = append(cfgs, connectionConfig{
+ sock: newSocket(g.ioFDs[mountIdx]),
+ readonly: isReadonlyMount(m.Options) || conf.Overlay,
+ })
+
+ log.Infof("Serving %q mapped on FD %d (ro: %t)", m.Destination, g.ioFDs[mountIdx], cfgs[mountIdx].readonly)
+ mountIdx++
+ }
+
+ if mountIdx != len(g.ioFDs) {
+ Fatalf("too many FDs passed for mounts. mounts: %d, FDs: %d", mountIdx, len(g.ioFDs))
+ }
+ cfgs = cfgs[:mountIdx]
+
+ for _, cfg := range cfgs {
+ conn, err := server.CreateConnection(cfg.sock, cfg.readonly)
+ if err != nil {
+ Fatalf("starting connection on FD %d for gofer mount failed: %v", cfg.sock.FD(), err)
+ }
+ server.StartConnection(conn)
+ }
+ server.Wait()
+ log.Infof("All lisafs servers exited.")
+ return subcommands.ExitSuccess
+}
+
+func (g *Gofer) serve9P(spec *specs.Spec, conf *config.Config, root string) subcommands.ExitStatus {
// Start with root mount, then add any other additional mount as needed.
ats := make([]p9.Attacher, 0, len(spec.Mounts)+1)
ap, err := fsgofer.NewAttachPoint("/", fsgofer.Config{
ROMount: spec.Root.Readonly || conf.Overlay,
+ HostUDS: conf.FSGoferHostUDS,
EnableVerityXattr: conf.Verity,
})
if err != nil {
@@ -174,7 +262,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
mountIdx := 1 // first one is the root
for _, m := range spec.Mounts {
- if specutils.Is9PMount(m, conf.VFS2) {
+ if specutils.IsGoferMount(m, conf.VFS2) {
cfg := fsgofer.Config{
ROMount: isReadonlyMount(m.Options) || conf.Overlay,
HostUDS: conf.FSGoferHostUDS,
@@ -197,26 +285,9 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
Fatalf("too many FDs passed for mounts. mounts: %d, FDs: %d", mountIdx, len(g.ioFDs))
}
- if conf.FSGoferHostUDS {
- filter.InstallUDSFilters()
- }
-
- if conf.Verity {
- filter.InstallXattrFilters()
- }
-
- if err := filter.Install(); err != nil {
- Fatalf("installing seccomp filters: %v", err)
- }
-
- runServers(ats, g.ioFDs)
- return subcommands.ExitSuccess
-}
-
-func runServers(ats []p9.Attacher, ioFDs []int) {
// Run the loops and wait for all to exit.
var wg sync.WaitGroup
- for i, ioFD := range ioFDs {
+ for i, ioFD := range g.ioFDs {
wg.Add(1)
go func(ioFD int, at p9.Attacher) {
socket, err := unet.NewSocket(ioFD)
@@ -232,6 +303,7 @@ func runServers(ats []p9.Attacher, ioFDs []int) {
}
wg.Wait()
log.Infof("All 9P servers exited.")
+ return subcommands.ExitSuccess
}
func (g *Gofer) writeMounts(mounts []specs.Mount) error {
@@ -362,7 +434,7 @@ func setupRootFS(spec *specs.Spec, conf *config.Config) error {
// creates directories as needed.
func setupMounts(conf *config.Config, mounts []specs.Mount, root, procPath string) error {
for _, m := range mounts {
- if !specutils.Is9PMount(m, conf.VFS2) {
+ if !specutils.IsGoferMount(m, conf.VFS2) {
continue
}
@@ -402,7 +474,7 @@ func setupMounts(conf *config.Config, mounts []specs.Mount, root, procPath strin
func resolveMounts(conf *config.Config, mounts []specs.Mount, root string) ([]specs.Mount, error) {
cleanMounts := make([]specs.Mount, 0, len(mounts))
for _, m := range mounts {
- if !specutils.Is9PMount(m, conf.VFS2) {
+ if !specutils.IsGoferMount(m, conf.VFS2) {
cleanMounts = append(cleanMounts, m)
continue
}
diff --git a/runsc/config/config.go b/runsc/config/config.go
index a562f7bf4..91142888f 100644
--- a/runsc/config/config.go
+++ b/runsc/config/config.go
@@ -193,6 +193,9 @@ type Config struct {
// Enables VFS2.
VFS2 bool `flag:"vfs2"`
+ // Enable lisafs.
+ Lisafs bool `flag:"lisafs"`
+
// Enables FUSE usage.
FUSE bool `flag:"fuse"`
diff --git a/runsc/config/flags.go b/runsc/config/flags.go
index 1bf23951a..11cea71b8 100644
--- a/runsc/config/flags.go
+++ b/runsc/config/flags.go
@@ -82,6 +82,7 @@ func RegisterFlags() {
flag.Bool("fsgofer-host-uds", false, "allow the gofer to mount Unix Domain Sockets.")
flag.Bool("vfs2", false, "enables VFSv2. This uses the new VFS layer that is faster than the previous one.")
flag.Bool("fuse", false, "TEST ONLY; use while FUSE in VFSv2 is landing. This allows the use of the new experimental FUSE filesystem.")
+ flag.Bool("lisafs", false, "Enables lisafs protocol instead of 9P. This is only effective with VFS2.")
flag.Bool("cgroupfs", false, "Automatically mount cgroupfs.")
// Flags that control sandbox runtime behavior: network related.
diff --git a/runsc/container/container.go b/runsc/container/container.go
index 50b0dd5e7..9c0004753 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -917,7 +917,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu
// Add root mount and then add any other additional mounts.
mountCount := 1
for _, m := range spec.Mounts {
- if specutils.Is9PMount(m, conf.VFS2) {
+ if specutils.IsGoferMount(m, conf.VFS2) {
mountCount++
}
}
diff --git a/runsc/fsgofer/BUILD b/runsc/fsgofer/BUILD
index 3280b74fe..c6967cc57 100644
--- a/runsc/fsgofer/BUILD
+++ b/runsc/fsgofer/BUILD
@@ -9,11 +9,13 @@ go_library(
"fsgofer_amd64_unsafe.go",
"fsgofer_arm64_unsafe.go",
"fsgofer_unsafe.go",
+ "lisafs.go",
],
visibility = ["//runsc:__subpackages__"],
deps = [
"//pkg/cleanup",
"//pkg/fd",
+ "//pkg/lisafs",
"//pkg/log",
"//pkg/p9",
"//pkg/sync",
diff --git a/runsc/fsgofer/lisafs.go b/runsc/fsgofer/lisafs.go
new file mode 100644
index 000000000..9d745f461
--- /dev/null
+++ b/runsc/fsgofer/lisafs.go
@@ -0,0 +1,52 @@
+// 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 fsgofer
+
+import (
+ "gvisor.dev/gvisor/pkg/lisafs"
+)
+
+// LisafsServer implements lisafs.ServerImpl for fsgofer.
+type LisafsServer struct {
+ lisafs.Server
+ config Config
+}
+
+var _ lisafs.ServerImpl = (*LisafsServer)(nil)
+
+// NewLisafsServer initializes a new lisafs server for fsgofer.
+func NewLisafsServer(config Config) *LisafsServer {
+ s := &LisafsServer{config: config}
+ s.Server.Init(s)
+ return s
+}
+
+// Mount implements lisafs.ServerImpl.Mount.
+func (s *LisafsServer) Mount(c *lisafs.Connection, mountPath string) (lisafs.ControlFDImpl, lisafs.Inode, error) {
+ panic("unimplemented")
+}
+
+// MaxMessageSize implements lisafs.ServerImpl.MaxMessageSize.
+func (s *LisafsServer) MaxMessageSize() uint32 {
+ return lisafs.MaxMessageSize()
+}
+
+// SupportedMessages implements lisafs.ServerImpl.SupportedMessages.
+func (s *LisafsServer) SupportedMessages() []lisafs.MID {
+ return []lisafs.MID{
+ lisafs.Mount,
+ lisafs.Channel,
+ }
+}
diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go
index 5365b5b1b..9d3b97277 100644
--- a/runsc/specutils/specutils.go
+++ b/runsc/specutils/specutils.go
@@ -332,9 +332,9 @@ func capsFromNames(names []string, skipSet map[linux.Capability]struct{}) (auth.
return auth.CapabilitySetOfMany(caps), nil
}
-// Is9PMount returns true if the given mount can be mounted as an external
+// IsGoferMount returns true if the given mount can be mounted as an external
// gofer.
-func Is9PMount(m specs.Mount, vfs2Enabled bool) bool {
+func IsGoferMount(m specs.Mount, vfs2Enabled bool) bool {
MaybeConvertToBindMount(&m)
return m.Type == "bind" && m.Source != "" && IsSupportedDevMount(m, vfs2Enabled)
}