git » summer » commit a03d8f9

Implement -x to not cross filesystem boundaries

author Alberto Bertogli
2023-04-03 02:33:11 UTC
committer Alberto Bertogli
2023-04-03 02:33:11 UTC
parent 8a455f10e2b12f6d335ff570f809a6ff317b4b7b

Implement -x to not cross filesystem boundaries

summer.go +39 -29
test/access.t +9 -0
test/help.t +3 -0

diff --git a/summer.go b/summer.go
index c31ac59..40545ca 100644
--- a/summer.go
+++ b/summer.go
@@ -8,6 +8,7 @@ import (
 	"io/fs"
 	"os"
 	"path/filepath"
+	"syscall"
 )
 
 const usage = `# summer 🌞 🏖
@@ -34,7 +35,8 @@ Flags:
 `
 
 var (
-	dbPath = flag.String("db", "", "database to read from/write to")
+	dbPath        = flag.String("db", "", "database to read from/write to")
+	oneFilesystem = flag.Bool("x", false, "don't cross filesystem boundaries")
 )
 
 func Usage() {
@@ -94,39 +96,53 @@ type ChecksumV1 struct {
 	ModTimeUsec int64
 }
 
-func isFileRelevant(path string, d fs.DirEntry, err error) bool {
+func openAndInfo(path string, d fs.DirEntry, err error, rootDev uint64) (bool, *os.File, fs.FileInfo, error) {
 	if err != nil {
-		return false
+		return false, nil, nil, err
 	}
-	if d.IsDir() {
-		return false
+	if d.IsDir() || !d.Type().IsRegular() {
+		return false, nil, nil, nil
 	}
-	return d.Type().IsRegular()
-}
 
-func openAndInfo(path string, d fs.DirEntry) (*os.File, fs.FileInfo, error) {
 	info, err := d.Info()
 	if err != nil {
-		return nil, nil, err
+		return true, nil, nil, err
 	}
+
 	fd, err := os.Open(path)
 	if err != nil {
-		return nil, nil, err
+		return true, nil, nil, err
+	}
+
+	if *oneFilesystem && rootDev != getDevice(info) {
+		fd.Close()
+		return false, nil, nil, fs.SkipDir
 	}
 
-	return fd, info, nil
+	return true, fd, info, nil
+}
+
+func getDevice(info fs.FileInfo) uint64 {
+	return info.Sys().(*syscall.Stat_t).Dev
+}
+
+func getDeviceForPath(path string) uint64 {
+	fi, err := os.Stat(path)
+	if err != nil {
+		// Doesn't matter, because we'll get an error during WalkDir.
+		return 0
+	}
+	return getDevice(fi)
 }
 
 func generate(db DB, root string) error {
+	rootDev := getDeviceForPath(root)
 	p := NewProgress()
 	defer p.Stop()
-	fn := func(path string, d fs.DirEntry, err error) error {
-		if !isFileRelevant(path, d, err) {
-			return err
-		}
 
-		fd, info, err := openAndInfo(path, d)
-		if err != nil {
+	fn := func(path string, d fs.DirEntry, err error) error {
+		ok, fd, info, err := openAndInfo(path, d, err, rootDev)
+		if !ok || err != nil {
 			return err
 		}
 		defer fd.Close()
@@ -156,16 +172,13 @@ func generate(db DB, root string) error {
 }
 
 func verify(db DB, root string) error {
+	rootDev := getDeviceForPath(root)
 	p := NewProgress()
 	defer p.Stop()
 
 	fn := func(path string, d fs.DirEntry, err error) error {
-		if !isFileRelevant(path, d, err) {
-			return err
-		}
-
-		fd, info, err := openAndInfo(path, d)
-		if err != nil {
+		ok, fd, info, err := openAndInfo(path, d, err, rootDev)
+		if !ok || err != nil {
 			return err
 		}
 		defer fd.Close()
@@ -215,16 +228,13 @@ func verify(db DB, root string) error {
 }
 
 func update(db DB, root string) error {
+	rootDev := getDeviceForPath(root)
 	p := NewProgress()
 	defer p.Stop()
 
 	fn := func(path string, d fs.DirEntry, err error) error {
-		if !isFileRelevant(path, d, err) {
-			return err
-		}
-
-		fd, info, err := openAndInfo(path, d)
-		if err != nil {
+		ok, fd, info, err := openAndInfo(path, d, err, rootDev)
+		if !ok || err != nil {
 			return err
 		}
 		defer fd.Close()
diff --git a/test/access.t b/test/access.t
index b0893b9..426ac8f 100644
--- a/test/access.t
+++ b/test/access.t
@@ -21,3 +21,12 @@ interfere.
   0s: 0 matched, 0 modified, 0 new, 0 corrupted
   open root/empty: permission denied
   [1]
+
+Test behaviour when the root does not exist. This exercises some different
+code paths, because the root is special.
+
+  $ summer verify doesnotexist
+  \r (no-eol) (esc)
+  0s: 0 matched, 0 modified, 0 new, 0 corrupted
+  lstat doesnotexist: no such file or directory
+  [1]
diff --git a/test/help.t b/test/help.t
index ffb1568..cd660c5 100644
--- a/test/help.t
+++ b/test/help.t
@@ -30,6 +30,7 @@ No arguments.
       \tdatabase to read from/write to (esc)
     -q\tquiet mode (esc)
     -v\tverbose mode (list each file) (esc)
+    -x\tdon't cross filesystem boundaries (esc)
   [1]
 
 
@@ -61,6 +62,7 @@ Too few arguments.
       \tdatabase to read from/write to (esc)
     -q\tquiet mode (esc)
     -v\tverbose mode (list each file) (esc)
+    -x\tdon't cross filesystem boundaries (esc)
   [1]
 
 
@@ -92,6 +94,7 @@ No valid path (the argument is given, but it is empty).
       \tdatabase to read from/write to (esc)
     -q\tquiet mode (esc)
     -v\tverbose mode (list each file) (esc)
+    -x\tdon't cross filesystem boundaries (esc)
   [1]