Source file src/os/root_js.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build js && wasm
     6  
     7  package os
     8  
     9  import (
    10  	"errors"
    11  	"slices"
    12  	"syscall"
    13  )
    14  
    15  // checkPathEscapes reports whether name escapes the root.
    16  //
    17  // Due to the lack of openat, checkPathEscapes is subject to TOCTOU races
    18  // when symlinks change during the resolution process.
    19  func checkPathEscapes(r *Root, name string) error {
    20  	return checkPathEscapesInternal(r, name, false)
    21  }
    22  
    23  // checkPathEscapesLstat reports whether name escapes the root.
    24  // It does not resolve symlinks in the final path component.
    25  //
    26  // Due to the lack of openat, checkPathEscapes is subject to TOCTOU races
    27  // when symlinks change during the resolution process.
    28  func checkPathEscapesLstat(r *Root, name string) error {
    29  	return checkPathEscapesInternal(r, name, true)
    30  }
    31  
    32  func checkPathEscapesInternal(r *Root, name string, lstat bool) error {
    33  	if r.root.closed.Load() {
    34  		return ErrClosed
    35  	}
    36  	parts, err := splitPathInRoot(name, nil, nil)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	i := 0
    42  	symlinks := 0
    43  	base := r.root.name
    44  	for i < len(parts) {
    45  		if parts[i] == ".." {
    46  			// Resolve one or more parent ("..") path components.
    47  			end := i + 1
    48  			for end < len(parts) && parts[end] == ".." {
    49  				end++
    50  			}
    51  			count := end - i
    52  			if count > i {
    53  				return errPathEscapes
    54  			}
    55  			parts = slices.Delete(parts, i-count, end)
    56  			i -= count
    57  			base = r.root.name
    58  			for j := range i {
    59  				base = joinPath(base, parts[j])
    60  			}
    61  			continue
    62  		}
    63  
    64  		if lstat && i == len(parts)-1 {
    65  			break
    66  		}
    67  
    68  		next := joinPath(base, parts[i])
    69  		fi, err := Lstat(next)
    70  		if err != nil {
    71  			if IsNotExist(err) {
    72  				return nil
    73  			}
    74  			return underlyingError(err)
    75  		}
    76  		if fi.Mode()&ModeSymlink != 0 {
    77  			link, err := Readlink(next)
    78  			if err != nil {
    79  				return errPathEscapes
    80  			}
    81  			symlinks++
    82  			if symlinks > rootMaxSymlinks {
    83  				return errors.New("too many symlinks")
    84  			}
    85  			newparts, err := splitPathInRoot(link, parts[:i], parts[i+1:])
    86  			if err != nil {
    87  				return err
    88  			}
    89  			parts = newparts
    90  			continue
    91  		}
    92  		if !fi.IsDir() && i < len(parts)-1 {
    93  			return syscall.ENOTDIR
    94  		}
    95  
    96  		base = next
    97  		i++
    98  	}
    99  	return nil
   100  }
   101  

View as plain text