004 Project 4: File Integrity Checker
004 Build a File Integrity Checker
This CLI calculates SHA-256 checksums and verifies files against a manifest.
manifest mode: file -> hash -> write "hash path"
verify mode: read manifest -> re-hash files -> compare -> report
Full main.go
package main
import (
"bufio"
"crypto/sha256"
"encoding/hex"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
func hashFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func createManifest(root, out string) error {
mf, err := os.Create(out)
if err != nil {
return err
}
defer mf.Close()
return filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
h, err := hashFile(path)
if err != nil {
return err
}
_, err = fmt.Fprintf(mf, "%s %s\n", h, path)
return err
})
}
func verifyManifest(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
s := bufio.NewScanner(f)
failed := 0
for s.Scan() {
line := s.Text()
parts := strings.SplitN(line, " ", 2)
if len(parts) != 2 {
return fmt.Errorf("bad line: %q", line)
}
expected, filePath := parts[0], parts[1]
got, err := hashFile(filePath)
if err != nil || got != expected {
failed++
fmt.Printf("FAIL %s\n", filePath)
} else {
fmt.Printf("OK %s\n", filePath)
}
}
if err := s.Err(); err != nil {
return err
}
if failed > 0 {
return fmt.Errorf("%d file(s) failed verification", failed)
}
return nil
}
func main() {
mode := flag.String("mode", "manifest", "manifest|verify")
root := flag.String("root", ".", "root directory for manifest mode")
manifest := flag.String("manifest", "checksums.txt", "manifest path")
flag.Parse()
var err error
switch *mode {
case "manifest":
err = createManifest(*root, *manifest)
case "verify":
err = verifyManifest(*manifest)
default:
err = fmt.Errorf("unknown mode: %s", *mode)
}
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}Run
go run . -mode manifest -root ./data -manifest checksums.txt
go run . -mode verify -manifest checksums.txtWhat to Extend
- Support ignore patterns (
.git,node_modules). - Output machine-readable JSON reports.
- Add optional HMAC signing for manifest authenticity.
Step-by-Step Explanation
- Parse command-line flags and validate inputs early.
- Keep the core operation in a small, testable function.
- Process data as a stream when possible to reduce memory use.
- Print stable output and meaningful exit codes.
- Add one extension feature and test edge cases.
Code Anatomy
mainhandles flags, orchestration, and errors.- Worker/helper functions hold business logic.
- Output section should be deterministic for scripting and CI usage.
Learning Goals
- Write composable Unix-style Go tools.
- Improve error messages and operator experience.
- Practice iterative improvement over one clear baseline.