//go:build linux package instancefs import ( "errors" "os" "path/filepath" "syscall" "testing" "time" ) // OpenBeneathOrAbsent is the one place absence is decided: absent only // when the name is not found beneath the descriptor dir was opened as. func TestOpenBeneathOrAbsentDecidesAbsenceBeneathOneDescriptor(t *testing.T) { setup := func(t *testing.T) (dir string) { t.Helper() dir = filepath.Join(t.TempDir(), "inst") if err := os.Mkdir(dir, 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "f"), []byte("bytes"), 0o600); err != nil { t.Fatal(err) } return dir } open := func(t *testing.T, dir, name string) (string, bool, error) { t.Helper() type res struct { b string absent bool err error } done := make(chan res, 1) go func() { f, absent, err := OpenBeneathOrAbsent(dir, name) var b []byte if f != nil { b, _ = readLimit(f, name, 1<<10) f.Close() } done <- res{string(b), absent, err} }() select { case r := <-done: return r.b, r.absent, r.err case <-time.After(20 * time.Second): t.Fatal("OpenBeneathOrAbsent blocked") return "", false, nil } } t.Run("present", func(t *testing.T) { if b, absent, err := open(t, setup(t), "f"); err != nil || absent || b != "bytes" { t.Fatalf("got %q absent=%v %v", b, absent, err) } }) t.Run("absent beneath a directory that opened", func(t *testing.T) { if _, absent, err := open(t, setup(t), "g"); err != nil || !absent { t.Fatalf("absent=%v %v", absent, err) } }) t.Run("a missing directory is an error", func(t *testing.T) { _, absent, err := open(t, filepath.Join(t.TempDir(), "gone"), "f") if absent || !errors.Is(err, ErrDirUnopened) || !errors.Is(err, os.ErrNotExist) { t.Fatalf("absent=%v %v", absent, err) } }) t.Run("a symlinked directory is an error", func(t *testing.T) { dir := setup(t) if err := os.Symlink(dir, dir+".link"); err != nil { t.Fatal(err) } _, absent, err := open(t, dir+".link", "f") if absent || !errors.Is(err, ErrDirUnopened) || !errors.Is(err, ErrSymlink) { t.Fatalf("absent=%v %v", absent, err) } }) t.Run("a symlink at the name is the name's, not the directory's", func(t *testing.T) { dir := setup(t) if err := os.Symlink(filepath.Join(dir, "f"), filepath.Join(dir, "l")); err != nil { t.Fatal(err) } _, absent, err := open(t, dir, "l") if absent || !errors.Is(err, ErrSymlink) || errors.Is(err, ErrDirUnopened) { t.Fatalf("absent=%v %v", absent, err) } }) t.Run("a FIFO", func(t *testing.T) { dir := setup(t) if err := syscall.Mkfifo(filepath.Join(dir, "p"), 0o600); err != nil { t.Fatal(err) } if _, absent, err := open(t, dir, "p"); absent || !errors.Is(err, ErrNotRegular) { t.Fatalf("absent=%v %v", absent, err) } }) // Astra P2: the directory renamed away after it was opened, and an // empty one put at its path, is still the directory looked in. A // lookup that reopened the path would read the file as absent. t.Run("a directory renamed away after the open", func(t *testing.T) { dir := setup(t) prev := betweenDirAndLookup betweenDirAndLookup = func() { if err := os.Rename(dir, dir+".away"); err != nil { t.Error(err) } if err := os.Mkdir(dir, 0o700); err != nil { t.Error(err) } } t.Cleanup(func() { betweenDirAndLookup = prev }) if b, absent, err := open(t, dir, "f"); err != nil || absent || b != "bytes" { t.Fatalf("got %q absent=%v %v; want the file the opened directory holds", b, absent, err) } }) } // Opus M3: without /proc, OpenRootBeneath is an error, never a fallback to // opening the path. func TestOpenRootBeneathWithoutProcIsAnError(t *testing.T) { parent := t.TempDir() if err := os.Mkdir(filepath.Join(parent, "d"), 0o700); err != nil { t.Fatal(err) } prev := procSelfFD procSelfFD = filepath.Join(t.TempDir(), "no-proc") + "/" t.Cleanup(func() { procSelfFD = prev }) r, err := OpenRootBeneath(parent, "d") if err == nil || r != nil { if r != nil { r.Close() } t.Fatalf("OpenRootBeneath without /proc: root %v, %v; want an error", r != nil, err) } }