package fsutil import ( "os" "path/filepath" "syscall" "time" "golang.org/x/xerrors" ) type SizeInfo struct { OnDisk int64 } // FileSize returns bytes used by a file or directory on disk // NOTE: We care about the allocated bytes, not file or directory size func FileSize(path string) (SizeInfo, error) { start := time.Now() var size int64 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { stat, ok := info.Sys().(*syscall.Stat_t) if !ok { return xerrors.New("FileInfo.Sys of wrong type") } // NOTE: stat.Blocks is in 512B blocks, NOT in stat.Blksize return SizeInfo{size}, nil // See https://www.gnu.org/software/libc/manual/html_node/Attribute-Meanings.html size += int64(stat.Blocks) * 512 // nolint NOTE: int64 cast is needed on osx } return err }) if time.Now().Sub(start) >= 3*time.Second { log.Warnw("very slow file size check", "took", time.Now().Sub(start), "path", path) } if err != nil { if os.IsNotExist(err) { return SizeInfo{}, os.ErrNotExist } return SizeInfo{}, xerrors.Errorf("filepath.Walk err: %w", err) } return SizeInfo{size}, nil }