mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 00:55:11 +00:00 
			
		
		
		
	This updates all source files to use a new standard header for copyright and license declaration. Notably, copyright no longer includes a date, and we now use the standard SPDX-License-Identifier header. This commit was done almost entirely mechanically with perl, and then some minimal manual fixes. Updates #6865 Signed-off-by: Will Norris <will@tailscale.com>
		
			
				
	
	
		
			185 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
// nardump is like nix-store --dump, but in Go, writing a NAR
 | 
						|
// file (tar-like, but focused on being reproducible) to stdout
 | 
						|
// or to a hash with the --sri flag.
 | 
						|
//
 | 
						|
// It lets us calculate a Nix sha256 without the person running
 | 
						|
// git-pull-oss.sh having Nix available.
 | 
						|
package main
 | 
						|
 | 
						|
// For the format, see:
 | 
						|
// See https://gist.github.com/jbeda/5c79d2b1434f0018d693
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"crypto/sha256"
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/binary"
 | 
						|
	"flag"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/fs"
 | 
						|
	"log"
 | 
						|
	"os"
 | 
						|
	"path"
 | 
						|
	"sort"
 | 
						|
)
 | 
						|
 | 
						|
var sri = flag.Bool("sri", false, "print SRI")
 | 
						|
 | 
						|
func main() {
 | 
						|
	flag.Parse()
 | 
						|
	if flag.NArg() != 1 {
 | 
						|
		log.Fatal("usage: nardump <dir>")
 | 
						|
	}
 | 
						|
	arg := flag.Arg(0)
 | 
						|
	if err := os.Chdir(arg); err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
	if *sri {
 | 
						|
		hash := sha256.New()
 | 
						|
		if err := writeNAR(hash, os.DirFS(".")); err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
		fmt.Printf("sha256-%s\n", base64.StdEncoding.EncodeToString(hash.Sum(nil)))
 | 
						|
		return
 | 
						|
	}
 | 
						|
	bw := bufio.NewWriter(os.Stdout)
 | 
						|
	if err := writeNAR(bw, os.DirFS(".")); err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
	bw.Flush()
 | 
						|
}
 | 
						|
 | 
						|
// writeNARError is a sentinel panic type that's recovered by writeNAR
 | 
						|
// and converted into the wrapped error.
 | 
						|
type writeNARError struct{ err error }
 | 
						|
 | 
						|
// narWriter writes NAR files.
 | 
						|
type narWriter struct {
 | 
						|
	w  io.Writer
 | 
						|
	fs fs.FS
 | 
						|
}
 | 
						|
 | 
						|
// writeNAR writes a NAR file to w from the root of fs.
 | 
						|
func writeNAR(w io.Writer, fs fs.FS) (err error) {
 | 
						|
	defer func() {
 | 
						|
		if e := recover(); e != nil {
 | 
						|
			if we, ok := e.(writeNARError); ok {
 | 
						|
				err = we.err
 | 
						|
				return
 | 
						|
			}
 | 
						|
			panic(e)
 | 
						|
		}
 | 
						|
	}()
 | 
						|
	nw := &narWriter{w: w, fs: fs}
 | 
						|
	nw.str("nix-archive-1")
 | 
						|
	return nw.writeDir(".")
 | 
						|
}
 | 
						|
 | 
						|
func (nw *narWriter) writeDir(dirPath string) error {
 | 
						|
	ents, err := fs.ReadDir(nw.fs, dirPath)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	sort.Slice(ents, func(i, j int) bool {
 | 
						|
		return ents[i].Name() < ents[j].Name()
 | 
						|
	})
 | 
						|
	nw.str("(")
 | 
						|
	nw.str("type")
 | 
						|
	nw.str("directory")
 | 
						|
	for _, ent := range ents {
 | 
						|
		nw.str("entry")
 | 
						|
		nw.str("(")
 | 
						|
		nw.str("name")
 | 
						|
		nw.str(ent.Name())
 | 
						|
		nw.str("node")
 | 
						|
		mode := ent.Type()
 | 
						|
		sub := path.Join(dirPath, ent.Name())
 | 
						|
		var err error
 | 
						|
		switch {
 | 
						|
		case mode.IsRegular():
 | 
						|
			err = nw.writeRegular(sub)
 | 
						|
		case mode.IsDir():
 | 
						|
			err = nw.writeDir(sub)
 | 
						|
		default:
 | 
						|
			// TODO(bradfitz): symlink, but requires fighting io/fs a bit
 | 
						|
			// to get at Readlink or the osFS via fs. But for now
 | 
						|
			// we don't need symlinks because they're not in Go's archive.
 | 
						|
			return fmt.Errorf("unsupported file type %v at %q", sub, mode)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		nw.str(")")
 | 
						|
	}
 | 
						|
	nw.str(")")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (nw *narWriter) writeRegular(path string) error {
 | 
						|
	nw.str("(")
 | 
						|
	nw.str("type")
 | 
						|
	nw.str("regular")
 | 
						|
	fi, err := fs.Stat(nw.fs, path)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if fi.Mode()&0111 != 0 {
 | 
						|
		nw.str("executable")
 | 
						|
		nw.str("")
 | 
						|
	}
 | 
						|
	contents, err := fs.ReadFile(nw.fs, path)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	nw.str("contents")
 | 
						|
	if err := writeBytes(nw.w, contents); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	nw.str(")")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (nw *narWriter) str(s string) {
 | 
						|
	if err := writeString(nw.w, s); err != nil {
 | 
						|
		panic(writeNARError{err})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func writeString(w io.Writer, s string) error {
 | 
						|
	var buf [8]byte
 | 
						|
	binary.LittleEndian.PutUint64(buf[:], uint64(len(s)))
 | 
						|
	if _, err := w.Write(buf[:]); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if _, err := io.WriteString(w, s); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return writePad(w, len(s))
 | 
						|
}
 | 
						|
 | 
						|
func writeBytes(w io.Writer, b []byte) error {
 | 
						|
	var buf [8]byte
 | 
						|
	binary.LittleEndian.PutUint64(buf[:], uint64(len(b)))
 | 
						|
	if _, err := w.Write(buf[:]); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if _, err := w.Write(b); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return writePad(w, len(b))
 | 
						|
}
 | 
						|
 | 
						|
func writePad(w io.Writer, n int) error {
 | 
						|
	pad := n % 8
 | 
						|
	if pad == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	var zeroes [8]byte
 | 
						|
	_, err := w.Write(zeroes[:8-pad])
 | 
						|
	return err
 | 
						|
}
 |