//go:build linux package hostfiles import ( "context" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/release" ) // SyncDirAt publishes the directory it was handed, whatever is renamed // into its path afterwards, and leaves the caller's descriptor open. func TestSyncDirAtPublishesTheDirectoryItWasHanded(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) p := open(t, s) base := t.TempDir() dir, other := filepath.Join(base, "files"), t.TempDir() if err := os.Mkdir(dir, 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "mine.txt"), []byte("mine"), 0o600); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(other, "secret"), []byte("not the tenant's"), 0o600); err != nil { t.Fatal(err) } f, err := os.Open(dir) if err != nil { t.Fatal(err) } defer f.Close() if err := os.Rename(dir, dir+".moved"); err != nil { t.Fatal(err) } if err := os.Symlink(other, dir); err != nil { t.Fatal(err) } if _, err := SyncDirAt(ctx, p, f, nil); err != nil { t.Fatal(err) } commit(t, p) m, ok, err := Load(ctx, s, pfx) if err != nil || !ok { t.Fatalf("manifest: %v %v", ok, err) } if _, ok := m.Entries["secret"]; ok { t.Fatal("SyncDirAt published the directory the path names now") } if _, ok := m.Entries["mine.txt"]; !ok { t.Fatalf("SyncDirAt did not publish the directory it was handed: %v", m.Entries) } if _, err := f.Stat(); err != nil { t.Fatalf("SyncDirAt closed the caller's directory: %v", err) } reg, err := os.Open(filepath.Join(dir+".moved", "mine.txt")) if err != nil { t.Fatal(err) } defer reg.Close() if _, err := SyncDirAt(ctx, open(t, release.NewDirStore(t.TempDir())), reg, nil); err == nil { t.Fatal("SyncDirAt took a regular file for a directory") } } // RestoreRoot restores into the root it was handed, whatever is renamed // into its path afterwards, and Restore is RestoreRoot on the path. func TestRestoreRootWritesIntoTheRootItWasHanded(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) p := open(t, s) stage(t, p, "a/b.txt", "restored") commit(t, p) base := t.TempDir() dir, other := filepath.Join(base, "files"), t.TempDir() if err := os.Mkdir(dir, 0o700); err != nil { t.Fatal(err) } root, err := os.OpenRoot(dir) if err != nil { t.Fatal(err) } defer root.Close() if err := os.Rename(dir, dir+".moved"); err != nil { t.Fatal(err) } if err := os.Symlink(other, dir); err != nil { t.Fatal(err) } if _, st, err := RestoreRoot(ctx, s, pfx, root, nil); err != nil || st.Written != 1 { t.Fatalf("RestoreRoot: %+v %v", st, err) } if names, _ := os.ReadDir(other); len(names) != 0 { t.Fatalf("RestoreRoot wrote into the directory the path names now: %v", names) } if b, err := os.ReadFile(filepath.Join(dir+".moved", "a", "b.txt")); err != nil || string(b) != "restored" { t.Fatalf("RestoreRoot did not write into its root: %q %v", b, err) } if _, err := root.Stat("."); err != nil { t.Fatalf("RestoreRoot closed the caller's root: %v", err) } if _, _, err := RestoreRoot(ctx, s, pfx, nil, nil); err == nil { t.Fatal("RestoreRoot took a nil root") } }