|
|
|
@@ -787,15 +787,14 @@ func TestReconcileVariantRowsDropsOnlyRowsOfMissingFiles(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// makeEntriesTooLongToRead makes a chain of directories inside dir, each
|
|
|
|
|
// openDirectoryNearPathLimit makes a chain of directories inside dir, each
|
|
|
|
|
// named with 255 zeros, the longest name Linux allows. Linux refuses a path
|
|
|
|
|
// of 4096 bytes or more, and the chain goes as deep as it can below that.
|
|
|
|
|
// In its deepest directory it makes a directory and a file whose paths
|
|
|
|
|
// reach that length. Even root can then neither list that directory nor
|
|
|
|
|
// read the file's info. It returns their two paths. It makes them through
|
|
|
|
|
// an os.Root, which names one directory at a time, as no call may name
|
|
|
|
|
// the whole path.
|
|
|
|
|
func makeEntriesTooLongToRead(t *testing.T, dir string) []string {
|
|
|
|
|
// It returns the chain's deepest directory as an os.Root, closed when the
|
|
|
|
|
// test ends. An entry made through it with a 255-byte name has a path even
|
|
|
|
|
// the root user cannot read. The os.Root can make that entry because it
|
|
|
|
|
// names the entry only by its name, not by the whole path.
|
|
|
|
|
func openDirectoryNearPathLimit(t *testing.T, dir string) *os.Root {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
name := strings.Repeat("0", 255)
|
|
|
|
@@ -805,32 +804,64 @@ func makeEntriesTooLongToRead(t *testing.T, dir string) []string {
|
|
|
|
|
deepest = filepath.Join(deepest, name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chain, err := filepath.Rel(dir, deepest)
|
|
|
|
|
err := os.MkdirAll(deepest, StorageDirPerm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("filepath.Rel() error = %v", err)
|
|
|
|
|
t.Fatalf("failed to make a chain of directories: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root, err := os.OpenRoot(dir)
|
|
|
|
|
root, err := os.OpenRoot(deepest)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("os.OpenRoot(%s) error = %v", dir, err)
|
|
|
|
|
t.Fatalf("os.OpenRoot(%s) error = %v", deepest, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer func() { _ = root.Close() }()
|
|
|
|
|
t.Cleanup(func() { _ = root.Close() })
|
|
|
|
|
|
|
|
|
|
err = root.MkdirAll(filepath.Join(chain, name), StorageDirPerm)
|
|
|
|
|
return root
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// makeEntriesTooLongToRead makes a directory and a file in the directory
|
|
|
|
|
// that openDirectoryNearPathLimit opens inside dir. Even the root user can
|
|
|
|
|
// then neither list that directory nor read the file's info. It returns
|
|
|
|
|
// their two paths.
|
|
|
|
|
func makeEntriesTooLongToRead(t *testing.T, dir string) []string {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
deepest := openDirectoryNearPathLimit(t, dir)
|
|
|
|
|
dirName := strings.Repeat("0", 255)
|
|
|
|
|
|
|
|
|
|
err := deepest.Mkdir(dirName, StorageDirPerm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to make a directory too deep to list: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileName := strings.Repeat("1", 255)
|
|
|
|
|
|
|
|
|
|
err = root.WriteFile(filepath.Join(chain, fileName), []byte("unreadable"),
|
|
|
|
|
StorageFilePerm)
|
|
|
|
|
err = deepest.WriteFile(fileName, []byte("unreadable"), StorageFilePerm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to make a file too deep to read: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return []string{filepath.Join(deepest, name), filepath.Join(deepest, fileName)}
|
|
|
|
|
return []string{
|
|
|
|
|
filepath.Join(deepest.Name(), dirName),
|
|
|
|
|
filepath.Join(deepest.Name(), fileName),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// makeTempFileTooLongToRead makes a temp file in the directory that
|
|
|
|
|
// openDirectoryNearPathLimit opens inside dir. Even the root user then
|
|
|
|
|
// cannot read its info. It returns its path.
|
|
|
|
|
func makeTempFileTooLongToRead(t *testing.T, dir string) string {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
deepest := openDirectoryNearPathLimit(t, dir)
|
|
|
|
|
name := tempFilePrefix + strings.Repeat("1", 255-len(tempFilePrefix))
|
|
|
|
|
|
|
|
|
|
err := deepest.WriteFile(name, []byte("partial"), StorageFilePerm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to make a temp file too deep to read: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filepath.Join(deepest.Name(), name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestReconciliationGoesOnPastEntriesItCannotRead puts, in each storage
|
|
|
|
@@ -917,6 +948,31 @@ func TestReconciliationGoesOnPastEntriesItCannotRead(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestReconciliationWarnsOfTempFilesItCannotRead puts, in each storage
|
|
|
|
|
// directory, a temp file whose info the pass cannot read. The pass must
|
|
|
|
|
// log a warning naming each one's path and the error, as it does for any
|
|
|
|
|
// other file whose info it cannot read.
|
|
|
|
|
func TestReconciliationWarnsOfTempFilesItCannotRead(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
cache, _ := newEvictionTestCache(t, 1<<30)
|
|
|
|
|
|
|
|
|
|
unreadable := []string{
|
|
|
|
|
makeTempFileTooLongToRead(t, cache.variants.baseDir),
|
|
|
|
|
makeTempFileTooLongToRead(t, cache.srcContent.baseDir),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logs := reconcileAsAtStartup(t, cache)
|
|
|
|
|
|
|
|
|
|
for _, path := range unreadable {
|
|
|
|
|
assertWarningNames(t, logs, path)
|
|
|
|
|
|
|
|
|
|
if !strings.Contains(logs, path+": "+syscall.ENAMETOOLONG.Error()) {
|
|
|
|
|
t.Errorf("no log line gives the error reading %s", path)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestPeriodicReconciliationAdoptsFileThatAppearsAfterStartup proves
|
|
|
|
|
// reconciliation is not a one-shot startup-only pass: it must also run
|
|
|
|
|
// on the periodic ticker, so a variant file that lands on disk with no
|
|
|
|
|