X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/a78762353971ea3707bcf92960a12557d66fb9af..7ebe828a435dcaa1b5668b72adbaad495059f211:/services/keepstore/unix_volume.go diff --git a/services/keepstore/unix_volume.go b/services/keepstore/unix_volume.go index f076ccf184..dd62cf1319 100644 --- a/services/keepstore/unix_volume.go +++ b/services/keepstore/unix_volume.go @@ -321,7 +321,12 @@ func (v *UnixVolume) Status() *VolumeStatus { v.logger.WithError(err).Error("stat failed") return nil } - devnum := fi.Sys().(*syscall.Stat_t).Dev + // uint64() cast here supports GOOS=darwin where Dev is + // int32. If the device number is negative, the unsigned + // devnum won't be the real device number any more, but that's + // fine -- all we care about is getting the same number each + // time. + devnum := uint64(fi.Sys().(*syscall.Stat_t).Dev) var fs syscall.Statfs_t if err := syscall.Statfs(v.Root, &fs); err != nil { @@ -359,47 +364,55 @@ var blockFileRe = regexp.MustCompile(`^[0-9a-f]{32}$`) // e4de7a2810f5554cd39b36d8ddb132ff+67108864 1388701136 // func (v *UnixVolume) IndexTo(prefix string, w io.Writer) error { - var lastErr error rootdir, err := v.os.Open(v.Root) if err != nil { return err } - defer rootdir.Close() v.os.stats.TickOps("readdir") v.os.stats.Tick(&v.os.stats.ReaddirOps) - for { - names, err := rootdir.Readdirnames(1) - if err == io.EOF { - return lastErr - } else if err != nil { - return err - } - if !strings.HasPrefix(names[0], prefix) && !strings.HasPrefix(prefix, names[0]) { + subdirs, err := rootdir.Readdirnames(-1) + rootdir.Close() + if err != nil { + return err + } + for _, subdir := range subdirs { + if !strings.HasPrefix(subdir, prefix) && !strings.HasPrefix(prefix, subdir) { // prefix excludes all blocks stored in this dir continue } - if !blockDirRe.MatchString(names[0]) { + if !blockDirRe.MatchString(subdir) { continue } - blockdirpath := filepath.Join(v.Root, names[0]) - blockdir, err := v.os.Open(blockdirpath) - if err != nil { - v.logger.WithError(err).Errorf("error reading %q", blockdirpath) - lastErr = fmt.Errorf("error reading %q: %s", blockdirpath, err) - continue - } - v.os.stats.TickOps("readdir") - v.os.stats.Tick(&v.os.stats.ReaddirOps) - for { - fileInfo, err := blockdir.Readdir(1) - if err == io.EOF { + blockdirpath := filepath.Join(v.Root, subdir) + + var dirents []os.DirEntry + for attempt := 0; ; attempt++ { + v.os.stats.TickOps("readdir") + v.os.stats.Tick(&v.os.stats.ReaddirOps) + dirents, err = os.ReadDir(blockdirpath) + if err == nil { break + } else if attempt < 5 && strings.Contains(err.Error(), "errno 523") { + // EBADCOOKIE (NFS stopped accepting + // our readdirent cookie) -- retry a + // few times before giving up + v.logger.WithError(err).Printf("retry after error reading %s", blockdirpath) + continue + } else { + return err + } + } + + for _, dirent := range dirents { + fileInfo, err := dirent.Info() + if os.IsNotExist(err) { + // File disappeared between ReadDir() and now + continue } else if err != nil { - v.logger.WithError(err).Errorf("error reading %q", blockdirpath) - lastErr = fmt.Errorf("error reading %q: %s", blockdirpath, err) - break + v.logger.WithError(err).Errorf("error getting FileInfo for %q in %q", dirent.Name(), blockdirpath) + return err } - name := fileInfo[0].Name() + name := fileInfo.Name() if !strings.HasPrefix(name, prefix) { continue } @@ -408,16 +421,15 @@ func (v *UnixVolume) IndexTo(prefix string, w io.Writer) error { } _, err = fmt.Fprint(w, name, - "+", fileInfo[0].Size(), - " ", fileInfo[0].ModTime().UnixNano(), + "+", fileInfo.Size(), + " ", fileInfo.ModTime().UnixNano(), "\n") if err != nil { - blockdir.Close() return fmt.Errorf("error writing: %s", err) } } - blockdir.Close() } + return nil } // Trash trashes the block data from the unix storage