Repository: buchgr/bazel-remote (go)

Issue:
Conditional check for cache directories in scanDir method encounters lost+found
As of yesterday it looks like there was some conditional logic added to the scanDir() method of the diskCache struct. This conditional checks whether or not the directories name is equal to a few different values; please see [here](https://github.com/buchgr/bazel-remote/blob/8ac1f9007ce420ff62c7d0a136a0691da80b972c/cache/disk/load.go#L449).

This has caused the remote cache in our environment to go into crashloop after the scanDir method encounters the `lost+found` directory on the linux host, this directory is created by fsck on our ext4 filesystem and will always be recreated even if deleted.

Pull request description:
ignore lost+found dirs inside the cache dir
Some unix style filesystems automatically create this dir in the root of the filesystem. This is most likely to exist in the root of the cache dir, but could occur in any directory if there's a filesystem mounted there.

Fixes #582.

Patch:
diff --git a/cache/disk/load.go b/cache/disk/load.go
index ba005d0..063c6b0 100644
--- a/cache/disk/load.go
+++ b/cache/disk/load.go
@@ -349,6 +349,10 @@ func (c *diskCache) scanDir() ([]*nameAndInfo, error) {
 	// AC and RAW items: <hash>-<random digits/ascii letters>
 	re := regexp.MustCompile(`^([a-f0-9]{64})(?:-([1-9][0-9]*))?-([0-9a-zA-Z]+)(\.v1)?$`)
 
+	// Ignore lost+found dirs, which are automatically created in the
+	// root dir of some unix style filesystems.
+	const lostAndFound = "lost+found"
+
 	for i := 0; i < numWorkers; i++ {
 		dirListers.Go(func() error {
 			for d := range dc {
@@ -359,11 +363,14 @@ func (c *diskCache) scanDir() ([]*nameAndInfo, error) {
 					return err
 				}
 
-				chunk := make([]*nameAndInfo, len(des))
+				chunk := make([]*nameAndInfo, 0, len(des))
 
-				i := 0
 				for _, de := range des {
 					if de.IsDir() {
+						if de.Name() == lostAndFound {
+							continue
+						}
+
 						return fmt.Errorf("Unexpected directory: %s", de.Name())
 					}
 
@@ -412,7 +419,7 @@ func (c *diskCache) scanDir() ([]*nameAndInfo, error) {
 						return fmt.Errorf("Unrecognised file in cache dir: %q", d)
 					}
 
-					chunk[i] = &nameAndInfo{
+					chunk = append(chunk, &nameAndInfo{
 						info: info,
 
 						lookupKey:  lookupKey,
@@ -420,9 +427,7 @@ func (c *diskCache) scanDir() ([]*nameAndInfo, error) {
 						sizeOnDisk: sizeOnDisk,
 						random:     random,
 						legacy:     legacy,
-					}
-
-					i++
+					})
 				}
 
 				nis <- chunk
@@ -446,6 +451,10 @@ func (c *diskCache) scanDir() ([]*nameAndInfo, error) {
 			return nil, fmt.Errorf("Unexpected file: %s", name)
 		}
 
+		if name == lostAndFound {
+			continue
+		}
+
 		if name != "ac.v2" && name != "cas.v2" && name != "raw.v2" {
 			return nil, fmt.Errorf("Unexpected dir: %s", name)
 		}
@@ -465,6 +474,10 @@ func (c *diskCache) scanDir() ([]*nameAndInfo, error) {
 				return nil, fmt.Errorf("Unexpected file: %s", dirPath)
 			}
 
+			if name2 == lostAndFound {
+				continue
+			}
+
 			if !dre.MatchString(name2) {
 				return nil, fmt.Errorf("Unexpected dir: %s", dirPath)
 			}


Test patch:
diff --git a/cache/disk/disk_test.go b/cache/disk/disk_test.go
index b56b017..8b7e85a 100644
--- a/cache/disk/disk_test.go
+++ b/cache/disk/disk_test.go
@@ -1429,3 +1429,37 @@ func TestMetricsValidatedAC(t *testing.T) {
 		t.Fatalf("Expected rawMiss counter to be 0, found %f", rawMisses)
 	}
 }
+
+func TestCacheDirLostAndFound(t *testing.T) {
+	cacheDir := tempDir(t)
+	defer os.RemoveAll(cacheDir)
+
+	var err error
+
+	// Create "lost+found" directories in every expected subdir of the cache dir.
+	// We expect to be able to load this cache dir and ignore them.
+	err = os.Mkdir(path.Join(cacheDir, "lost+found"), os.ModePerm)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	hexChars := []rune("0123456789abcdef")
+	keySpaces := []string{"ac.v2", "cas.v2", "raw.v2"}
+
+	for _, i := range hexChars {
+		for _, j := range hexChars {
+			bd := string(i) + string(j)
+			for _, sd := range keySpaces {
+				err = os.MkdirAll(path.Join(cacheDir, sd, bd, "lost+found"), os.ModePerm)
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
+		}
+	}
+
+	_, err = New(cacheDir, 4096, WithAccessLogger(testutils.NewSilentLogger()))
+	if err != nil {
+		t.Fatal(err)
+	}
+}