github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/gofer/attr.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gofer
    16  
    17  import (
    18  	"golang.org/x/sys/unix"
    19  	"github.com/SagerNet/gvisor/pkg/context"
    20  	"github.com/SagerNet/gvisor/pkg/hostarch"
    21  	"github.com/SagerNet/gvisor/pkg/p9"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    24  	ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time"
    25  )
    26  
    27  // getattr returns the 9p attributes of the p9.File. On success, Mode, Size, and RDev
    28  // are guaranteed to be masked as valid.
    29  func getattr(ctx context.Context, file contextFile) (p9.QID, p9.AttrMask, p9.Attr, error) {
    30  	// Retrieve attributes over the wire.
    31  	qid, valid, attr, err := file.getAttr(ctx, p9.AttrMaskAll())
    32  	if err != nil {
    33  		return qid, valid, attr, err
    34  	}
    35  
    36  	// Require mode, size, and raw device id.
    37  	if !valid.Mode || !valid.Size || !valid.RDev {
    38  		return qid, valid, attr, unix.EIO
    39  	}
    40  
    41  	return qid, valid, attr, nil
    42  }
    43  
    44  func unstable(ctx context.Context, valid p9.AttrMask, pattr p9.Attr, mounter fs.FileOwner, client *p9.Client) fs.UnstableAttr {
    45  	return fs.UnstableAttr{
    46  		Size:             int64(pattr.Size),
    47  		Usage:            int64(pattr.Size),
    48  		Perms:            perms(valid, pattr, client),
    49  		Owner:            owner(mounter, valid, pattr),
    50  		AccessTime:       atime(ctx, valid, pattr),
    51  		ModificationTime: mtime(ctx, valid, pattr),
    52  		StatusChangeTime: ctime(ctx, valid, pattr),
    53  		Links:            links(valid, pattr),
    54  	}
    55  }
    56  
    57  func perms(valid p9.AttrMask, pattr p9.Attr, client *p9.Client) fs.FilePermissions {
    58  	if pattr.Mode.IsDir() && !p9.VersionSupportsMultiUser(client.Version()) {
    59  		// If user and group permissions bits are not supplied, use
    60  		// "other" bits to supplement them.
    61  		//
    62  		// Older Gofer's fake directories only have "other" permission,
    63  		// but will often be accessed via user or group permissions.
    64  		if pattr.Mode&0770 == 0 {
    65  			other := pattr.Mode & 07
    66  			pattr.Mode = pattr.Mode | other<<3 | other<<6
    67  		}
    68  	}
    69  	return fs.FilePermsFromP9(pattr.Mode)
    70  }
    71  
    72  func owner(mounter fs.FileOwner, valid p9.AttrMask, pattr p9.Attr) fs.FileOwner {
    73  	// Unless the file returned its UID and GID, it belongs to the mounting
    74  	// task's EUID/EGID.
    75  	owner := mounter
    76  	if valid.UID {
    77  		if pattr.UID.Ok() {
    78  			owner.UID = auth.KUID(pattr.UID)
    79  		} else {
    80  			owner.UID = auth.KUID(auth.OverflowUID)
    81  		}
    82  	}
    83  	if valid.GID {
    84  		if pattr.GID.Ok() {
    85  			owner.GID = auth.KGID(pattr.GID)
    86  		} else {
    87  			owner.GID = auth.KGID(auth.OverflowGID)
    88  		}
    89  	}
    90  	return owner
    91  }
    92  
    93  // bsize returns a block size from 9p attributes.
    94  func bsize(pattr p9.Attr) int64 {
    95  	if pattr.BlockSize > 0 {
    96  		return int64(pattr.BlockSize)
    97  	}
    98  	// Some files, particularly those that are not on a local file system,
    99  	// may have no clue of their block size. Better not to report something
   100  	// misleading or buggy and have a safe default.
   101  	return hostarch.PageSize
   102  }
   103  
   104  // ntype returns an fs.InodeType from 9p attributes.
   105  func ntype(pattr p9.Attr) fs.InodeType {
   106  	switch {
   107  	case pattr.Mode.IsNamedPipe():
   108  		return fs.Pipe
   109  	case pattr.Mode.IsDir():
   110  		return fs.Directory
   111  	case pattr.Mode.IsSymlink():
   112  		return fs.Symlink
   113  	case pattr.Mode.IsCharacterDevice():
   114  		return fs.CharacterDevice
   115  	case pattr.Mode.IsBlockDevice():
   116  		return fs.BlockDevice
   117  	case pattr.Mode.IsSocket():
   118  		return fs.Socket
   119  	default:
   120  		return fs.RegularFile
   121  	}
   122  }
   123  
   124  // ctime returns a change time from 9p attributes.
   125  func ctime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
   126  	if valid.CTime {
   127  		return ktime.FromUnix(int64(pattr.CTimeSeconds), int64(pattr.CTimeNanoSeconds))
   128  	}
   129  	// Approximate ctime with mtime if ctime isn't available.
   130  	return mtime(ctx, valid, pattr)
   131  }
   132  
   133  // atime returns an access time from 9p attributes.
   134  func atime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
   135  	if valid.ATime {
   136  		return ktime.FromUnix(int64(pattr.ATimeSeconds), int64(pattr.ATimeNanoSeconds))
   137  	}
   138  	return ktime.NowFromContext(ctx)
   139  }
   140  
   141  // mtime returns a modification time from 9p attributes.
   142  func mtime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
   143  	if valid.MTime {
   144  		return ktime.FromUnix(int64(pattr.MTimeSeconds), int64(pattr.MTimeNanoSeconds))
   145  	}
   146  	return ktime.NowFromContext(ctx)
   147  }
   148  
   149  // links returns a hard link count from 9p attributes.
   150  func links(valid p9.AttrMask, pattr p9.Attr) uint64 {
   151  	// For gofer file systems that support link count (such as a local file gofer),
   152  	// we return the link count reported by the underlying file system.
   153  	if valid.NLink {
   154  		return pattr.NLink
   155  	}
   156  
   157  	// This node is likely backed by a file system that doesn't support links.
   158  	//
   159  	// We could readdir() and count children directories to provide an accurate
   160  	// link count. However this may be expensive since the gofer may be backed by remote
   161  	// storage. Instead, simply return 2 links for directories and 1 for everything else
   162  	// since no one relies on an accurate link count for gofer-based file systems.
   163  	switch ntype(pattr) {
   164  	case fs.Directory:
   165  		return 2
   166  	default:
   167  		return 1
   168  	}
   169  }