2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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 gofer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"syscall"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/p9"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
|
|
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// getattr returns the 9p attributes of the p9.File. On success, Mode, Size, and RDev
|
|
|
|
// are guaranteed to be masked as valid.
|
|
|
|
func getattr(ctx context.Context, file contextFile) (p9.QID, p9.AttrMask, p9.Attr, error) {
|
|
|
|
// Retrieve attributes over the wire.
|
|
|
|
qid, valid, attr, err := file.getAttr(ctx, p9.AttrMaskAll())
|
|
|
|
if err != nil {
|
|
|
|
return qid, valid, attr, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Require mode, size, and raw device id.
|
|
|
|
if !valid.Mode || !valid.Size || !valid.RDev {
|
|
|
|
return qid, valid, attr, syscall.EIO
|
|
|
|
}
|
|
|
|
|
|
|
|
return qid, valid, attr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func unstable(ctx context.Context, valid p9.AttrMask, pattr p9.Attr, mounter fs.FileOwner, client *p9.Client) fs.UnstableAttr {
|
|
|
|
return fs.UnstableAttr{
|
|
|
|
Size: int64(pattr.Size),
|
|
|
|
Usage: int64(pattr.Size),
|
|
|
|
Perms: perms(valid, pattr, client),
|
|
|
|
Owner: owner(mounter, valid, pattr),
|
|
|
|
AccessTime: atime(ctx, valid, pattr),
|
|
|
|
ModificationTime: mtime(ctx, valid, pattr),
|
|
|
|
StatusChangeTime: ctime(ctx, valid, pattr),
|
|
|
|
Links: links(valid, pattr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func perms(valid p9.AttrMask, pattr p9.Attr, client *p9.Client) fs.FilePermissions {
|
|
|
|
if pattr.Mode.IsDir() && !p9.VersionSupportsMultiUser(client.Version()) {
|
|
|
|
// If user and group permissions bits are not supplied, use
|
|
|
|
// "other" bits to supplement them.
|
|
|
|
//
|
|
|
|
// Older Gofer's fake directories only have "other" permission,
|
|
|
|
// but will often be accessed via user or group permissions.
|
|
|
|
if pattr.Mode&0770 == 0 {
|
|
|
|
other := pattr.Mode & 07
|
|
|
|
pattr.Mode = pattr.Mode | other<<3 | other<<6
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fs.FilePermsFromP9(pattr.Mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
func owner(mounter fs.FileOwner, valid p9.AttrMask, pattr p9.Attr) fs.FileOwner {
|
|
|
|
// Unless the file returned its UID and GID, it belongs to the mounting
|
|
|
|
// task's EUID/EGID.
|
|
|
|
owner := mounter
|
|
|
|
if valid.UID {
|
|
|
|
owner.UID = auth.KUID(pattr.UID)
|
|
|
|
}
|
|
|
|
if valid.GID {
|
|
|
|
owner.GID = auth.KGID(pattr.GID)
|
|
|
|
}
|
|
|
|
return owner
|
|
|
|
}
|
|
|
|
|
|
|
|
// bsize returns a block size from 9p attributes.
|
|
|
|
func bsize(pattr p9.Attr) int64 {
|
|
|
|
if pattr.BlockSize > 0 {
|
|
|
|
return int64(pattr.BlockSize)
|
|
|
|
}
|
|
|
|
// Some files may have no clue of their block size. Better not to report
|
|
|
|
// something misleading or buggy and have a safe default.
|
|
|
|
return usermem.PageSize
|
|
|
|
}
|
|
|
|
|
|
|
|
// ntype returns an fs.InodeType from 9p attributes.
|
|
|
|
func ntype(pattr p9.Attr) fs.InodeType {
|
|
|
|
switch {
|
|
|
|
case pattr.Mode.IsNamedPipe():
|
|
|
|
return fs.Pipe
|
|
|
|
case pattr.Mode.IsDir():
|
|
|
|
return fs.Directory
|
|
|
|
case pattr.Mode.IsSymlink():
|
|
|
|
return fs.Symlink
|
|
|
|
case pattr.Mode.IsCharacterDevice():
|
|
|
|
return fs.CharacterDevice
|
|
|
|
case pattr.Mode.IsBlockDevice():
|
|
|
|
return fs.BlockDevice
|
|
|
|
case pattr.Mode.IsSocket():
|
|
|
|
return fs.Socket
|
|
|
|
case pattr.Mode.IsRegular():
|
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
return fs.RegularFile
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ctime returns a change time from 9p attributes.
|
|
|
|
func ctime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
|
|
|
|
if valid.CTime {
|
|
|
|
return ktime.FromUnix(int64(pattr.CTimeSeconds), int64(pattr.CTimeNanoSeconds))
|
|
|
|
}
|
|
|
|
// Approximate ctime with mtime if ctime isn't available.
|
|
|
|
return mtime(ctx, valid, pattr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// atime returns an access time from 9p attributes.
|
|
|
|
func atime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
|
|
|
|
if valid.ATime {
|
|
|
|
return ktime.FromUnix(int64(pattr.ATimeSeconds), int64(pattr.ATimeNanoSeconds))
|
|
|
|
}
|
|
|
|
return ktime.NowFromContext(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// mtime returns a modification time from 9p attributes.
|
|
|
|
func mtime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
|
|
|
|
if valid.MTime {
|
|
|
|
return ktime.FromUnix(int64(pattr.MTimeSeconds), int64(pattr.MTimeNanoSeconds))
|
|
|
|
}
|
|
|
|
return ktime.NowFromContext(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// links returns a hard link count from 9p attributes.
|
|
|
|
func links(valid p9.AttrMask, pattr p9.Attr) uint64 {
|
|
|
|
// For gofer file systems that support link count (such as a local file gofer),
|
|
|
|
// we return the link count reported by the underlying file system.
|
|
|
|
if valid.NLink {
|
|
|
|
return pattr.NLink
|
|
|
|
}
|
|
|
|
|
|
|
|
// This node is likely backed by a file system that doesn't support links.
|
|
|
|
// We could readdir() and count children directories to provide an accurate
|
|
|
|
// link count. However this may be expensive since the gofer may be backed by remote
|
|
|
|
// storage. Instead, simply return 2 links for directories and 1 for everything else
|
|
|
|
// since no one relies on an accurate link count for gofer-based file systems.
|
|
|
|
switch ntype(pattr) {
|
|
|
|
case fs.Directory:
|
|
|
|
return 2
|
|
|
|
default:
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
}
|