gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/erofs/filesystem.go (about) 1 // Copyright 2023 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 erofs 16 17 import ( 18 "gvisor.dev/gvisor/pkg/abi/linux" 19 "gvisor.dev/gvisor/pkg/context" 20 "gvisor.dev/gvisor/pkg/erofs" 21 "gvisor.dev/gvisor/pkg/errors/linuxerr" 22 "gvisor.dev/gvisor/pkg/fspath" 23 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 24 "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" 25 "gvisor.dev/gvisor/pkg/sentry/vfs" 26 ) 27 28 // step resolves rp.Component() to an existing file, starting from the given directory. 29 // 30 // step is loosely analogous to fs/namei.c:walk_component(). 31 // 32 // Preconditions: 33 // - !rp.Done(). 34 func step(ctx context.Context, rp *vfs.ResolvingPath, d *dentry) (*dentry, bool, error) { 35 if !d.inode.IsDir() { 36 return nil, false, linuxerr.ENOTDIR 37 } 38 if err := d.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil { 39 return nil, false, err 40 } 41 name := rp.Component() 42 if name == "." { 43 rp.Advance() 44 return d, false, nil 45 } 46 if name == ".." { 47 parent := d.parent.Load() 48 if isRoot, err := rp.CheckRoot(ctx, &d.vfsd); err != nil { 49 return nil, false, err 50 } else if isRoot || parent == nil { 51 rp.Advance() 52 return d, false, nil 53 } 54 if err := rp.CheckMount(ctx, &parent.vfsd); err != nil { 55 return nil, false, err 56 } 57 rp.Advance() 58 return parent, false, nil 59 } 60 if len(name) > erofs.MaxNameLen { 61 return nil, false, linuxerr.ENAMETOOLONG 62 } 63 child, err := d.lookup(ctx, name) 64 if err != nil { 65 return nil, false, err 66 } 67 if err := rp.CheckMount(ctx, &child.vfsd); err != nil { 68 return nil, false, err 69 } 70 if child.inode.IsSymlink() && rp.ShouldFollowSymlink() { 71 target, err := child.inode.Readlink() 72 if err != nil { 73 return nil, false, err 74 } 75 followedSymlink, err := rp.HandleSymlink(target) 76 return d, followedSymlink, err 77 } 78 rp.Advance() 79 return child, false, nil 80 } 81 82 // walkParentDir resolves all but the last path component of rp to an existing 83 // directory, starting from the gvien directory. It does not check that the 84 // returned directory is searchable by the provider of rp. 85 // 86 // walkParentDir is loosely analogous to Linux's fs/namei.c:path_parentat(). 87 // 88 // Preconditions: 89 // - !rp.Done(). 90 func walkParentDir(ctx context.Context, rp *vfs.ResolvingPath, d *dentry) (*dentry, error) { 91 for !rp.Final() { 92 next, _, err := step(ctx, rp, d) 93 if err != nil { 94 return nil, err 95 } 96 d = next 97 } 98 if !d.inode.IsDir() { 99 return nil, linuxerr.ENOTDIR 100 } 101 return d, nil 102 } 103 104 // resolve resolves rp to an existing file. 105 // 106 // resolve is loosely analogous to Linux's fs/namei.c:path_lookupat(). 107 func resolve(ctx context.Context, rp *vfs.ResolvingPath) (*dentry, error) { 108 d := rp.Start().Impl().(*dentry) 109 for !rp.Done() { 110 next, _, err := step(ctx, rp, d) 111 if err != nil { 112 return nil, err 113 } 114 d = next 115 } 116 if rp.MustBeDir() && !d.inode.IsDir() { 117 return nil, linuxerr.ENOTDIR 118 } 119 return d, nil 120 } 121 122 // doCreateAt checks that creating a file at rp is permitted. 123 // 124 // doCreateAt is loosely analogous to a conjunction of Linux's 125 // fs/namei.c:filename_create() and done_path_create(). 126 // 127 // Preconditions: 128 // - !rp.Done(). 129 // - For the final path component in rp, !rp.ShouldFollowSymlink(). 130 func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir bool) error { 131 parentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry)) 132 if err != nil { 133 return err 134 } 135 // Order of checks is important. First check if parent directory can be 136 // executed, then check for existence, and lastly check if mount is writable. 137 if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil { 138 return err 139 } 140 name := rp.Component() 141 if name == "." || name == ".." { 142 return linuxerr.EEXIST 143 } 144 if len(name) > erofs.MaxNameLen { 145 return linuxerr.ENAMETOOLONG 146 } 147 if _, err := parentDir.lookup(ctx, name); err == nil { 148 return linuxerr.EEXIST 149 } else if !linuxerr.Equals(linuxerr.ENOENT, err) { 150 return err 151 } 152 if !dir && rp.MustBeDir() { 153 return linuxerr.ENOENT 154 } 155 return linuxerr.EROFS 156 } 157 158 // Sync implements vfs.FilesystemImpl.Sync. 159 func (fs *filesystem) Sync(ctx context.Context) error { 160 return nil 161 } 162 163 // AccessAt implements vfs.FilesystemImpl.AccessAt. 164 func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error { 165 d, err := resolve(ctx, rp) 166 if err != nil { 167 return err 168 } 169 if ats.MayWrite() { 170 return linuxerr.EROFS 171 } 172 return d.inode.checkPermissions(creds, ats) 173 } 174 175 // GetDentryAt implements vfs.FilesystemImpl.GetDentryAt. 176 func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) { 177 d, err := resolve(ctx, rp) 178 if err != nil { 179 return nil, err 180 } 181 if opts.CheckSearchable { 182 if !d.inode.IsDir() { 183 return nil, linuxerr.ENOTDIR 184 } 185 if err := d.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil { 186 return nil, err 187 } 188 } 189 d.IncRef() 190 return &d.vfsd, nil 191 } 192 193 // GetParentDentryAt implements vfs.FilesystemImpl.GetParentDentryAt. 194 func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPath) (*vfs.Dentry, error) { 195 dir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry)) 196 if err != nil { 197 return nil, err 198 } 199 dir.IncRef() 200 return &dir.vfsd, nil 201 } 202 203 // LinkAt implements vfs.FilesystemImpl.LinkAt. 204 func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error { 205 return fs.doCreateAt(ctx, rp, false /* dir */) 206 } 207 208 // MkdirAt implements vfs.FilesystemImpl.MkdirAt. 209 func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error { 210 return fs.doCreateAt(ctx, rp, true /* dir */) 211 } 212 213 // MknodAt implements vfs.FilesystemImpl.MknodAt. 214 func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error { 215 return fs.doCreateAt(ctx, rp, false /* dir */) 216 } 217 218 // OpenAt implements vfs.FilesystemImpl.OpenAt. 219 func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 220 if opts.Flags&linux.O_TMPFILE != 0 { 221 return nil, linuxerr.EOPNOTSUPP 222 } 223 224 if opts.Flags&linux.O_CREAT == 0 { 225 d, err := resolve(ctx, rp) 226 if err != nil { 227 return nil, err 228 } 229 return d.open(ctx, rp, &opts) 230 } 231 232 mustCreate := opts.Flags&linux.O_EXCL != 0 233 start := rp.Start().Impl().(*dentry) 234 if rp.Done() { 235 // Reject attempts to open mount root directory with O_CREAT. 236 if rp.MustBeDir() { 237 return nil, linuxerr.EISDIR 238 } 239 if mustCreate { 240 return nil, linuxerr.EEXIST 241 } 242 return start.open(ctx, rp, &opts) 243 } 244 afterTrailingSymlink: 245 parentDir, err := walkParentDir(ctx, rp, start) 246 if err != nil { 247 return nil, err 248 } 249 // Check for search permission in the parent directory. 250 if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil { 251 return nil, err 252 } 253 // Reject attempts to open directories with O_CREAT. 254 if rp.MustBeDir() { 255 return nil, linuxerr.EISDIR 256 } 257 child, followedSymlink, err := step(ctx, rp, parentDir) 258 if followedSymlink { 259 if mustCreate { 260 // EEXIST must be returned if an existing symlink is opened with O_EXCL. 261 return nil, linuxerr.EEXIST 262 } 263 if err != nil { 264 // If followedSymlink && err != nil, then this symlink resolution error 265 // must be handled by the VFS layer. 266 return nil, err 267 } 268 start = parentDir 269 goto afterTrailingSymlink 270 } 271 if linuxerr.Equals(linuxerr.ENOENT, err) { 272 return nil, linuxerr.EROFS 273 } 274 if err != nil { 275 return nil, err 276 } 277 if mustCreate { 278 return nil, linuxerr.EEXIST 279 } 280 if rp.MustBeDir() && !child.inode.IsDir() { 281 return nil, linuxerr.ENOTDIR 282 } 283 return child.open(ctx, rp, &opts) 284 } 285 286 // ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt. 287 func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) { 288 d, err := resolve(ctx, rp) 289 if err != nil { 290 return "", err 291 } 292 return d.inode.Readlink() 293 } 294 295 // RenameAt implements vfs.FilesystemImpl.RenameAt. 296 func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error { 297 // Resolve newParent first to verify that it's on this Mount. 298 newParentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry)) 299 if err != nil { 300 return err 301 } 302 newName := rp.Component() 303 if len(newName) > erofs.MaxNameLen { 304 return linuxerr.ENAMETOOLONG 305 } 306 mnt := rp.Mount() 307 if mnt != oldParentVD.Mount() { 308 return linuxerr.EXDEV 309 } 310 if err := newParentDir.inode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil { 311 return err 312 } 313 oldParentDir := oldParentVD.Dentry().Impl().(*dentry) 314 if err := oldParentDir.inode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil { 315 return err 316 } 317 return linuxerr.EROFS 318 } 319 320 // RmdirAt implements vfs.FilesystemImpl.RmdirAt. 321 func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error { 322 parentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry)) 323 if err != nil { 324 return err 325 } 326 if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil { 327 return err 328 } 329 name := rp.Component() 330 if name == "." { 331 return linuxerr.EINVAL 332 } 333 if name == ".." { 334 return linuxerr.ENOTEMPTY 335 } 336 return linuxerr.EROFS 337 } 338 339 // SetStatAt implements vfs.FilesystemImpl.SetStatAt. 340 func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error { 341 if _, err := resolve(ctx, rp); err != nil { 342 return err 343 } 344 return linuxerr.EROFS 345 } 346 347 // StatAt implements vfs.FilesystemImpl.StatAt. 348 func (fs *filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) { 349 d, err := resolve(ctx, rp) 350 if err != nil { 351 return linux.Statx{}, err 352 } 353 var stat linux.Statx 354 d.inode.statTo(&stat) 355 return stat, nil 356 } 357 358 // StatFSAt implements vfs.FilesystemImpl.StatFSAt. 359 func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) { 360 if _, err := resolve(ctx, rp); err != nil { 361 return linux.Statfs{}, err 362 } 363 return fs.statFS(), nil 364 } 365 366 // SymlinkAt implements vfs.FilesystemImpl.SymlinkAt. 367 func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error { 368 return fs.doCreateAt(ctx, rp, false /* dir */) 369 } 370 371 // UnlinkAt implements vfs.FilesystemImpl.UnlinkAt. 372 func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error { 373 parentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry)) 374 if err != nil { 375 return err 376 } 377 if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil { 378 return err 379 } 380 name := rp.Component() 381 if name == "." || name == ".." { 382 return linuxerr.EISDIR 383 } 384 return linuxerr.EROFS 385 } 386 387 // BoundEndpointAt implements vfs.FilesystemImpl.BoundEndpointAt. 388 func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.BoundEndpointOptions) (transport.BoundEndpoint, error) { 389 d, err := resolve(ctx, rp) 390 if err != nil { 391 return nil, err 392 } 393 if err := d.inode.checkPermissions(rp.Credentials(), vfs.MayWrite); err != nil { 394 return nil, err 395 } 396 return nil, linuxerr.ECONNREFUSED 397 } 398 399 // ListXattrAt implements vfs.FilesystemImpl.ListXattrAt. 400 func (fs *filesystem) ListXattrAt(ctx context.Context, rp *vfs.ResolvingPath, size uint64) ([]string, error) { 401 if _, err := resolve(ctx, rp); err != nil { 402 return nil, err 403 } 404 return nil, linuxerr.ENOTSUP 405 } 406 407 // GetXattrAt implements vfs.FilesystemImpl.GetXattrAt. 408 func (fs *filesystem) GetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetXattrOptions) (string, error) { 409 if _, err := resolve(ctx, rp); err != nil { 410 return "", err 411 } 412 return "", linuxerr.ENOTSUP 413 } 414 415 // SetXattrAt implements vfs.FilesystemImpl.SetXattrAt. 416 func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetXattrOptions) error { 417 if _, err := resolve(ctx, rp); err != nil { 418 return err 419 } 420 return linuxerr.EROFS 421 } 422 423 // RemoveXattrAt implements vfs.FilesystemImpl.RemoveXattrAt. 424 func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) error { 425 if _, err := resolve(ctx, rp); err != nil { 426 return err 427 } 428 return linuxerr.EROFS 429 } 430 431 // PrependPath implements vfs.FilesystemImpl.PrependPath. 432 func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error { 433 return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b) 434 } 435 436 // MountOptions implements vfs.FilesystemImpl.MountOptions. 437 func (fs *filesystem) MountOptions() string { 438 return fs.mopts 439 } 440 441 // IsDescendant implements vfs.FilesystemImpl.IsDescendant. 442 func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool { 443 return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*dentry)) 444 }