diff options
author | Googler <noreply@google.com> | 2018-04-27 10:37:02 -0700 |
---|---|---|
committer | Adin Scannell <ascannell@google.com> | 2018-04-28 01:44:26 -0400 |
commit | d02b74a5dcfed4bfc8f2f8e545bca4d2afabb296 (patch) | |
tree | 54f95eef73aee6bacbfc736fffc631be2605ed53 /pkg/sentry/fs/proc/mounts.go | |
parent | f70210e742919f40aa2f0934a22f1c9ba6dada62 (diff) |
Check in gVisor.
PiperOrigin-RevId: 194583126
Change-Id: Ica1d8821a90f74e7e745962d71801c598c652463
Diffstat (limited to 'pkg/sentry/fs/proc/mounts.go')
-rw-r--r-- | pkg/sentry/fs/proc/mounts.go | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/pkg/sentry/fs/proc/mounts.go b/pkg/sentry/fs/proc/mounts.go new file mode 100644 index 000000000..76092567d --- /dev/null +++ b/pkg/sentry/fs/proc/mounts.go @@ -0,0 +1,176 @@ +// Copyright 2018 Google Inc. +// +// 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 proc + +import ( + "bytes" + "fmt" + "sort" + + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/proc/seqfile" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" +) + +// forEachMountSource runs f for the process root mount and each mount that is a +// descendant of the root. +func forEachMountSource(t *kernel.Task, fn func(string, *fs.MountSource)) { + // All mount points must be relative to the rootDir, and mounts outside + // will be excluded. + rootDir := t.FSContext().RootDirectory() + defer rootDir.DecRef() + + if rootDir.Inode == nil { + panic(fmt.Sprintf("root dirent has nil inode: %+v", rootDir)) + } + if rootDir.Inode.MountSource == nil { + panic(fmt.Sprintf("root dirent has nil mount: %+v", rootDir)) + } + + ms := append(rootDir.Inode.MountSource.Submounts(), rootDir.Inode.MountSource) + sort.Slice(ms, func(i, j int) bool { + return ms[i].ID() < ms[j].ID() + }) + for _, m := range ms { + mountPath, desc := m.Root().FullName(rootDir) + if !desc { + // MountSources that are not descendants of the chroot jail are ignored. + continue + } + + fn(mountPath, m) + } +} + +// mountInfoFile is used to implement /proc/[pid]/mountinfo. +type mountInfoFile struct { + t *kernel.Task +} + +// NeedsUpdate implements SeqSource.NeedsUpdate. +func (mif *mountInfoFile) NeedsUpdate(_ int64) bool { + return true +} + +// ReadSeqFileData implements SeqSource.ReadSeqFileData. +func (mif *mountInfoFile) ReadSeqFileData(handle seqfile.SeqHandle) ([]seqfile.SeqData, int64) { + if handle != nil { + return nil, 0 + } + + var buf bytes.Buffer + forEachMountSource(mif.t, func(mountPath string, m *fs.MountSource) { + // Format: + // 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue + // (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) + + // (1) MountSource ID. + fmt.Fprintf(&buf, "%d ", m.ID()) + + // (2) Parent ID (or this ID if there is no parent). + pID := m.ID() + if p := m.Parent(); p != nil { + pID = p.ID() + } + fmt.Fprintf(&buf, "%d ", pID) + + // (3) Major:Minor device ID. We don't have a superblock, so we + // just use the root inode device number. + sa := m.Root().Inode.StableAttr + fmt.Fprintf(&buf, "%d:%d ", sa.DeviceFileMajor, sa.DeviceFileMinor) + + // (4) Root: the pathname of the directory in the filesystem + // which forms the root of this mount. + // + // NOTE: This will always be "/" until we implement + // bind mounts. + fmt.Fprintf(&buf, "/ ") + + // (5) Mount point (relative to process root). + fmt.Fprintf(&buf, "%s ", mountPath) + + // (6) Mount options. + opts := "rw" + if m.Flags.ReadOnly { + opts = "ro" + } + if m.Flags.NoAtime { + opts += ",noatime" + } + fmt.Fprintf(&buf, "%s ", opts) + + // (7) Optional fields: zero or more fields of the form "tag[:value]". + // (8) Separator: the end of the optional fields is marked by a single hyphen. + fmt.Fprintf(&buf, "- ") + + // (9) Filesystem type. + name := "none" + if m.Filesystem != nil { + name = m.Filesystem.Name() + } + fmt.Fprintf(&buf, "%s ", name) + + // (10) Mount source: filesystem-specific information or "none". + fmt.Fprintf(&buf, "none ") + + // (11) Superblock options. Only "ro/rw" is supported for now, + // and is the same as the filesystem option. + fmt.Fprintf(&buf, "%s\n", opts) + }) + + return []seqfile.SeqData{{Buf: buf.Bytes(), Handle: (*mountInfoFile)(nil)}}, 0 +} + +// mountsFile is used to implement /proc/[pid]/mountinfo. +type mountsFile struct { + t *kernel.Task +} + +// NeedsUpdate implements SeqSource.NeedsUpdate. +func (mf *mountsFile) NeedsUpdate(_ int64) bool { + return true +} + +// ReadSeqFileData implements SeqSource.ReadSeqFileData. +func (mf *mountsFile) ReadSeqFileData(handle seqfile.SeqHandle) ([]seqfile.SeqData, int64) { + if handle != nil { + return nil, 0 + } + + var buf bytes.Buffer + forEachMountSource(mf.t, func(mountPath string, m *fs.MountSource) { + // Format (tab-separated): + // <special device or remote filesystem> <mount point> <filesystem type> <mount options> <needs dump> <fsck order> + // + // We use the filesystem name as the first field, since there + // is no real block device we can point to, and we also should + // not expose anything about the remote filesystem. + // + // Only ro/rw option is supported for now. + // + // The "needs dump"and fsck flags are always 0, which is allowed. + opts := "rw" + if m.Flags.ReadOnly { + opts = "ro" + } + name := "none" + if m.Filesystem != nil { + name = m.Filesystem.Name() + } + fmt.Fprintf(&buf, "%s\t%s\t%s\t%s\t%d\t%d\n", "none", mountPath, name, opts, 0, 0) + }) + + return []seqfile.SeqData{{Buf: buf.Bytes(), Handle: (*mountsFile)(nil)}}, 0 +} |