sumdb/dirhash: fix a panic when argument is not a directory

This patch fixes a case where a path to a non directory can cause DirHash func to panic.

Fixes golang/go#57269

Change-Id: I743970dcafdf3bfee94baf4b714429a404decda9
GitHub-Last-Rev: a17ca1aa310d922853912b5592bebc11c5cbe256
GitHub-Pull-Request: golang/mod#16
Reviewed-on: https://go-review.googlesource.com/c/mod/+/457075
Run-TryBot: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/sumdb/dirhash/hash.go b/sumdb/dirhash/hash.go
index ef5df6f..350403f 100644
--- a/sumdb/dirhash/hash.go
+++ b/sumdb/dirhash/hash.go
@@ -90,7 +90,10 @@
 		}
 		if info.IsDir() {
 			return nil
+		} else if file == dir {
+			return fmt.Errorf("%s is not a directory", dir)
 		}
+
 		rel := file
 		if dir != "." {
 			rel = file[len(dir)+1:]
diff --git a/sumdb/dirhash/hash_test.go b/sumdb/dirhash/hash_test.go
index ed463c1..f3c504d 100644
--- a/sumdb/dirhash/hash_test.go
+++ b/sumdb/dirhash/hash_test.go
@@ -105,31 +105,49 @@
 }
 
 func TestDirFiles(t *testing.T) {
-	dir, err := ioutil.TempDir("", "dirfiles-test-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(dir)
-	if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
-		t.Fatal(err)
-	}
-	if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
-		t.Fatal(err)
-	}
-	if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
-		t.Fatal(err)
-	}
-	if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
-		t.Fatal(err)
-	}
-	prefix := "foo/bar@v2.3.4"
-	out, err := DirFiles(dir, prefix)
-	if err != nil {
-		t.Fatalf("DirFiles: %v", err)
-	}
-	for _, file := range out {
-		if !strings.HasPrefix(file, prefix) {
-			t.Errorf("Dir file = %s, want prefix %s", file, prefix)
+	t.Run("valid directory with files", func(t *testing.T) {
+		dir, err := ioutil.TempDir("", "dirfiles-test-")
+		if err != nil {
+			t.Fatal(err)
 		}
-	}
+		defer os.RemoveAll(dir)
+		if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
+			t.Fatal(err)
+		}
+		if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
+			t.Fatal(err)
+		}
+		if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
+			t.Fatal(err)
+		}
+		if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
+			t.Fatal(err)
+		}
+		prefix := "foo/bar@v2.3.4"
+		out, err := DirFiles(dir, prefix)
+		if err != nil {
+			t.Fatalf("DirFiles: %v", err)
+		}
+		for _, file := range out {
+			if !strings.HasPrefix(file, prefix) {
+				t.Errorf("Dir file = %s, want prefix %s", file, prefix)
+			}
+		}
+	})
+
+	t.Run("invalid directory", func(t *testing.T) {
+		path := filepath.Join(t.TempDir(), "not-a-directory.txt")
+		if err := os.WriteFile(path, []byte("This is a file."), 0644); err != nil {
+			t.Fatal(err)
+		}
+		defer os.RemoveAll(path)
+
+		out, err := DirFiles(path, "")
+		if err == nil {
+			t.Errorf("DirFiles(...) = %v, expected an error", err)
+		}
+		if len(out) > 0 {
+			t.Errorf("DirFiles(...) = unexpected files %s", out)
+		}
+	})
 }