package main import ( "fmt" "io/fs" "log" "os" "path/filepath" ) // processHardlinks creates hardlinks in destBase/destSubdir mirroring the // structure found at contentPath (either a single file or a directory tree). func processHardlinks(cfg *Config, contentPath, destSubdir string) error { info, err := os.Stat(contentPath) if err != nil { return fmt.Errorf("stat %q: %w", contentPath, err) } destDir := filepath.Join(cfg.DestBase, destSubdir) if info.IsDir() { return hardlinkDir(contentPath, destDir) } return hardlinkFile(contentPath, filepath.Join(destDir, filepath.Base(contentPath))) } // hardlinkDir walks srcDir and recreates the directory tree under destDir, // creating hardlinks for every file found. func hardlinkDir(srcDir, destDir string) error { return filepath.WalkDir(srcDir, func(srcPath string, d fs.DirEntry, err error) error { if err != nil { return err } rel, err := filepath.Rel(srcDir, srcPath) if err != nil { return err } destPath := filepath.Join(destDir, rel) if d.IsDir() { if err := os.MkdirAll(destPath, 0755); err != nil { return fmt.Errorf("mkdir %q: %w", destPath, err) } return nil } return hardlinkFile(srcPath, destPath) }) } // hardlinkFile creates a hardlink at destPath pointing to srcPath. // It creates parent directories as needed and skips if the link already exists. func hardlinkFile(srcPath, destPath string) error { if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil { return fmt.Errorf("mkdir %q: %w", filepath.Dir(destPath), err) } if _, err := os.Lstat(destPath); err == nil { log.Printf("skip (already exists): %s", destPath) return nil } if err := os.Link(srcPath, destPath); err != nil { return fmt.Errorf("hardlink %q -> %q: %w", srcPath, destPath, err) } log.Printf("hardlinked: %s -> %s", srcPath, destPath) return nil }