beta.blog

News

Golang: Creating and repairing par2 files

by on Apr.01, 2025, under News

PAR2 files (short for Parity Archive Volume Set, version 2) are a form of forward‑error‑correction (“erasure code”) data designed to verify and—if needed—repair missing or corrupted files in any collection of data. Instead of simply telling you a file is bad (as a checksum does), PAR2 files store enough redundancy to reconstruct damaged or missing portions up to the total amount of parity data provided

The following sample code provides a sample implementation using the github.com/akalin/gopar/par2 library to achieve this.

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/akalin/gopar/par2"
)

// CreatePAR2 creates a par2 file at outputPath from the given file paths using the provided slice and parity counts.
func CreatePAR2(outputPath string, files []string, sliceByteCount, parityCount int) error {
	opts := par2.CreateOptions{
		SliceByteCount:  sliceByteCount,
		NumParityShards: parityCount,
	}
	return par2.Create(outputPath, files, opts)
}

// VerifyPAR2 verifies the par2 file at parPath using default verify options and returns the result.
func VerifyPAR2(parPath string) (par2.VerifyResult, error) {
	return par2.Verify(parPath, par2.VerifyOptions{})
}

// RepairPAR2 repairs files as indicated by the par2 file at parPath using default repair options.
func RepairPAR2(parPath string) (par2.RepairResult, error) {
	return par2.Repair(parPath, par2.RepairOptions{})
}

func main() {
	action := flag.String("action", "create", "action to perform: create, verify, repair")
	sliceSize := flag.Int("slice", 1024, "slice byte count")
	parityCount := flag.Int("parity", 10, "number of parity shards")
	parFile := flag.String("par", "parity.par2", "output par2 file (for create) or par2 file to verify/repair")
	flag.Parse()

	switch *action {
	case "create":
		if flag.NArg() == 0 {
			fmt.Fprintln(os.Stderr, "Usage: -action=create -slice=1024 -parity=10 -par=parity.par2 file1 file2 ...")
			os.Exit(1)
		}
		files := flag.Args()
		absFiles := make([]string, len(files))
		for i, f := range files {
			a, err := filepath.Abs(f)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error converting %s to absolute path: %v\n", f, err)
				os.Exit(1)
			}
			absFiles[i] = a
		}
		if err := CreatePAR2(*parFile, absFiles, *sliceSize, *parityCount); err != nil {
			fmt.Fprintf(os.Stderr, "Error creating par2 file: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("par2 file created successfully.")
	case "verify":
		res, err := VerifyPAR2(*parFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error verifying par2 file: %v\n", err)
			os.Exit(1)
		}
		fmt.Printf("Verification result:\n%+v\n", res)
	case "repair":
		res, err := RepairPAR2(*parFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error repairing par2 file: %v\n", err)
			os.Exit(1)
		}
		fmt.Printf("Repair result:\n%+v\n", res)
	default:
		fmt.Fprintf(os.Stderr, "Unknown action: %s\n", *action)
		os.Exit(1)
	}
}

Leave a Comment more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!