github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/vfs/pathname.go (about) 1 // Copyright 2019 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 vfs 16 17 import ( 18 "github.com/metacubex/gvisor/pkg/context" 19 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 20 "github.com/metacubex/gvisor/pkg/fspath" 21 "github.com/metacubex/gvisor/pkg/sync" 22 ) 23 24 var fspathBuilderPool = sync.Pool{ 25 New: func() any { 26 return &fspath.Builder{} 27 }, 28 } 29 30 func getFSPathBuilder() *fspath.Builder { 31 return fspathBuilderPool.Get().(*fspath.Builder) 32 } 33 34 func putFSPathBuilder(b *fspath.Builder) { 35 // No methods can be called on b after b.String(), so reset it to its zero 36 // value (as returned by fspathBuilderPool.New) instead. 37 *b = fspath.Builder{} 38 fspathBuilderPool.Put(b) 39 } 40 41 // PathnameWithDeleted returns an absolute pathname to vd, consistent with 42 // Linux's d_path(). In particular, if vd.Dentry() has been disowned, 43 // PathnameWithDeleted appends " (deleted)" to the returned pathname. 44 func (vfs *VirtualFilesystem) PathnameWithDeleted(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) { 45 b := getFSPathBuilder() 46 defer putFSPathBuilder(b) 47 haveRef := false 48 defer func() { 49 if haveRef { 50 vd.DecRef(ctx) 51 } 52 }() 53 54 origD := vd.dentry 55 loop: 56 for { 57 err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b) 58 switch err.(type) { 59 case nil: 60 if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry { 61 // genericfstree.PrependPath() will have returned 62 // PrependPathAtVFSRootError in this case since it checks 63 // against vfsroot before mnt.root, but other implementations 64 // of FilesystemImpl.PrependPath() may return nil instead. 65 break loop 66 } 67 nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot) 68 if !nextVD.Ok() { 69 break loop 70 } 71 if haveRef { 72 vd.DecRef(ctx) 73 } 74 vd = nextVD 75 haveRef = true 76 // continue loop 77 case PrependPathSyntheticError: 78 // Skip prepending "/" and appending " (deleted)". 79 return b.String(), nil 80 case PrependPathAtVFSRootError, PrependPathAtNonMountRootError: 81 break loop 82 default: 83 return "", err 84 } 85 } 86 b.PrependByte('/') 87 if origD.IsDead() { 88 b.AppendString(" (deleted)") 89 } 90 return b.String(), nil 91 } 92 93 // PathnameReachable returns an absolute pathname to vd, consistent with 94 // Linux's __d_path() (as used by seq_path_root()). If vfsroot.Ok() and vd is 95 // not reachable from vfsroot, such that seq_path_root() would return SEQ_SKIP 96 // (causing the entire containing entry to be skipped), PathnameReachable 97 // returns ("", nil). 98 func (vfs *VirtualFilesystem) PathnameReachable(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) { 99 b := getFSPathBuilder() 100 defer putFSPathBuilder(b) 101 haveRef := false 102 defer func() { 103 if haveRef { 104 vd.DecRef(ctx) 105 } 106 }() 107 loop: 108 for { 109 err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b) 110 switch err.(type) { 111 case nil: 112 if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry { 113 break loop 114 } 115 nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot) 116 if !nextVD.Ok() { 117 return "", nil 118 } 119 if haveRef { 120 vd.DecRef(ctx) 121 } 122 vd = nextVD 123 haveRef = true 124 case PrependPathAtVFSRootError: 125 break loop 126 case PrependPathAtNonMountRootError, PrependPathSyntheticError: 127 return "", nil 128 default: 129 return "", err 130 } 131 } 132 b.PrependByte('/') 133 return b.String(), nil 134 } 135 136 // PathnameInFilesystem returns an absolute path to vd relative to vd's 137 // Filesystem root. It also appends //deleted to for disowned entries. It is 138 // equivalent to Linux's dentry_path(). 139 func (vfs *VirtualFilesystem) PathnameInFilesystem(ctx context.Context, vd VirtualDentry) (string, error) { 140 b := getFSPathBuilder() 141 defer putFSPathBuilder(b) 142 if vd.dentry.IsDead() { 143 b.PrependString("//deleted") 144 } 145 if err := vd.mount.fs.impl.PrependPath(ctx, VirtualDentry{}, VirtualDentry{dentry: vd.dentry}, b); err != nil { 146 // PrependPath returns an error if it encounters a filesystem root before 147 // the provided vfsroot. We don't provide a vfsroot, so encountering this 148 // error is expected and can be ignored. 149 switch err.(type) { 150 case PrependPathAtNonMountRootError: 151 default: 152 return "", err 153 } 154 } 155 b.PrependByte('/') 156 return b.String(), nil 157 } 158 159 // PathnameForGetcwd returns an absolute pathname to vd, consistent with 160 // Linux's sys_getcwd(). 161 func (vfs *VirtualFilesystem) PathnameForGetcwd(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) { 162 if vd.dentry.IsDead() { 163 return "", linuxerr.ENOENT 164 } 165 166 b := getFSPathBuilder() 167 defer putFSPathBuilder(b) 168 haveRef := false 169 defer func() { 170 if haveRef { 171 vd.DecRef(ctx) 172 } 173 }() 174 unreachable := false 175 loop: 176 for { 177 err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b) 178 switch err.(type) { 179 case nil: 180 if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry { 181 break loop 182 } 183 nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot) 184 if !nextVD.Ok() { 185 unreachable = true 186 break loop 187 } 188 if haveRef { 189 vd.DecRef(ctx) 190 } 191 vd = nextVD 192 haveRef = true 193 case PrependPathAtVFSRootError: 194 break loop 195 case PrependPathAtNonMountRootError, PrependPathSyntheticError: 196 unreachable = true 197 break loop 198 default: 199 return "", err 200 } 201 } 202 b.PrependByte('/') 203 if unreachable { 204 b.PrependString("(unreachable)") 205 } 206 return b.String(), nil 207 } 208 209 // As of this writing, we do not have equivalents to: 210 // 211 // - d_absolute_path(), which returns EINVAL if (effectively) any call to 212 // FilesystemImpl.PrependPath() would return PrependPathAtNonMountRootError. 213 // 214 // - dentry_path(), which does not walk up mounts (and only returns the path 215 // relative to Filesystem root), but also appends "//deleted" for disowned 216 // Dentries. 217 // 218 // These should be added as necessary.