Source file src/os/getwd_unix_test.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 unix
     6  
     7  package os_test
     8  
     9  import (
    10  	"errors"
    11  	. "os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"syscall"
    16  	"testing"
    17  )
    18  
    19  func TestGetwdDeep(t *testing.T) {
    20  	testGetwdDeep(t, false)
    21  }
    22  
    23  func TestGetwdDeepWithPWDSet(t *testing.T) {
    24  	testGetwdDeep(t, true)
    25  }
    26  
    27  // testGetwdDeep checks that os.Getwd is able to return paths
    28  // longer than syscall.PathMax (with or without PWD set).
    29  func testGetwdDeep(t *testing.T, setPWD bool) {
    30  	tempDir := t.TempDir()
    31  
    32  	dir := tempDir
    33  	t.Chdir(dir)
    34  
    35  	if setPWD {
    36  		t.Setenv("PWD", dir)
    37  	} else {
    38  		// When testing os.Getwd, setting PWD to empty string
    39  		// is the same as unsetting it, but the latter would
    40  		// be more complicated since we don't have t.Unsetenv.
    41  		t.Setenv("PWD", "")
    42  	}
    43  
    44  	name := strings.Repeat("a", 200)
    45  	for {
    46  		if err := Mkdir(name, 0o700); err != nil {
    47  			t.Fatal(err)
    48  		}
    49  		if err := Chdir(name); err != nil {
    50  			t.Fatal(err)
    51  		}
    52  		if setPWD {
    53  			dir += "/" + name
    54  			if err := Setenv("PWD", dir); err != nil {
    55  				t.Fatal(err)
    56  			}
    57  			t.Logf(" $PWD len: %d", len(dir))
    58  		}
    59  
    60  		wd, err := Getwd()
    61  		t.Logf("Getwd len: %d", len(wd))
    62  		if err != nil {
    63  			// We can get an EACCES error if we can't read up
    64  			// to root, which happens on the Android builders.
    65  			if errors.Is(err, syscall.EACCES) {
    66  				t.Logf("ignoring EACCES error: %v", err)
    67  				break
    68  			}
    69  			t.Fatal(err)
    70  		}
    71  		if setPWD && wd != dir {
    72  			// It's possible for the stat of PWD to fail
    73  			// with ENAMETOOLONG, and for getwd to fail for
    74  			// the same reason, and it's possible for $TMPDIR
    75  			// to contain a symlink. In that case the fallback
    76  			// code will not return the same directory.
    77  			if len(dir) > 1000 {
    78  				symDir, err := filepath.EvalSymlinks(tempDir)
    79  				if err == nil && symDir != tempDir {
    80  					t.Logf("EvalSymlinks(%q) = %q", tempDir, symDir)
    81  					if strings.Replace(dir, tempDir, symDir, 1) == wd {
    82  						// Symlink confusion is OK.
    83  						break
    84  					}
    85  				}
    86  			}
    87  
    88  			t.Fatalf("Getwd: got %q, want same value as $PWD: %q", wd, dir)
    89  		}
    90  		// Ideally the success criterion should be len(wd) > syscall.PathMax,
    91  		// but the latter is not public for some platforms, so use Stat(wd).
    92  		// When it fails with ENAMETOOLONG, it means:
    93  		//  - wd is longer than PathMax;
    94  		//  - Getwd have used the slow fallback code.
    95  		//
    96  		// To avoid an endless loop here in case Stat keeps working,
    97  		// check if len(wd) is above the largest known PathMax among
    98  		// all Unix platforms (4096, on Linux).
    99  		if _, err := Stat(wd); err != nil || len(wd) > 4096 {
   100  			t.Logf("Done; len(wd)=%d", len(wd))
   101  			// Most systems return ENAMETOOLONG.
   102  			// Dragonfly returns EFAULT.
   103  			switch {
   104  			case err == nil:
   105  			case errors.Is(err, syscall.ENAMETOOLONG):
   106  			case runtime.GOOS == "dragonfly" && errors.Is(err, syscall.EFAULT):
   107  			default:
   108  				t.Fatalf("unexpected Stat error: %v", err)
   109  			}
   110  			break
   111  		}
   112  	}
   113  }
   114  

View as plain text