| 332 | } |
| 333 | |
| 334 | func TestOpenUserFile_AbsoluteSymlink(t *testing.T) { |
| 335 | if runtime.GOOS == "windows" { |
| 336 | t.Skip("absolute symlink handling is only supported on non-Windows platforms") |
| 337 | } |
| 338 | |
| 339 | expectedContent := []byte("root:x:0:0:root:/root:/bin/bash" + t.Name()) |
| 340 | |
| 341 | root := t.TempDir() |
| 342 | // Use 'continuity' library to create a directory structure simulating NixOS |
| 343 | if err := fstest.Apply( |
| 344 | fstest.CreateDir("/etc", 0o755), |
| 345 | fstest.CreateDir("/nix/store/abcd", 0o755), |
| 346 | fstest.CreateFile("/nix/store/abcd/passwd", expectedContent, 0o644), |
| 347 | // /etc/passwd -> /nix/store/abcd/passwd (absolute symlink) |
| 348 | fstest.Symlink("/nix/store/abcd/passwd", "/etc/passwd"), |
| 349 | ).Apply(root); err != nil { |
| 350 | t.Fatal(err) |
| 351 | } |
| 352 | |
| 353 | rootFS := os.DirFS(root) |
| 354 | |
| 355 | // Ensure the FS implements the ReadLink interface. |
| 356 | // If the native os.DirFS doesn't implement it (depending on Go version), |
| 357 | // wrap it in our readLinkFS helper. |
| 358 | if _, ok := rootFS.(readLinker); !ok { |
| 359 | t.Logf("os.DirFS does not implement ReadLink; wrapping to use ReadLink") |
| 360 | rootFS = readLinkFS{root: root, fs: rootFS} |
| 361 | } |
| 362 | |
| 363 | f, err := openUserFile(rootFS, "etc/passwd") |
| 364 | if err != nil { |
| 365 | t.Fatalf("openUserFile failed on absolute symlink: %v", err) |
| 366 | } |
| 367 | defer f.Close() |
| 368 | |
| 369 | content, err := io.ReadAll(f) |
| 370 | if err != nil { |
| 371 | t.Fatal(err) |
| 372 | } |
| 373 | if string(content) != string(expectedContent) { |
| 374 | t.Errorf("expected content %q, got %q", string(expectedContent), string(content)) |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | func TestGroupLookup_AbsoluteSymlink(t *testing.T) { |
| 379 | if runtime.GOOS == "windows" { |