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 }