diff --git a/cmd/distribyted/main.go b/cmd/distribyted/main.go index a23325b..c5bf435 100644 --- a/cmd/distribyted/main.go +++ b/cmd/distribyted/main.go @@ -148,9 +148,12 @@ func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error { return fmt.Errorf("error starting magnet database: %w", err) } - ts := torrent.NewService(cl, dbl, ss, c, conf.Torrent.AddTimeout) + ts := torrent.NewService(cl, dbl, ss, c, conf.Torrent.AddTimeout, conf.Torrent.ReadTimeout) - mh := fuse.NewHandler(fuseAllowOther || conf.Fuse.AllowOther, conf.Fuse.Path) + var mh *fuse.Handler + if conf.Fuse != nil { + mh = fuse.NewHandler(fuseAllowOther || conf.Fuse.AllowOther, conf.Fuse.Path) + } sigChan := make(chan os.Signal) signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) @@ -170,8 +173,10 @@ func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error { dbl.Close() log.Info().Msg("closing torrent client...") c.Close() - log.Info().Msg("unmounting fuse filesystem...") - mh.Unmount() + if mh != nil { + log.Info().Msg("unmounting fuse filesystem...") + mh.Unmount() + } log.Info().Msg("exiting") os.Exit(1) @@ -186,6 +191,10 @@ func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error { } go func() { + if mh == nil { + return + } + if err := mh.Mount(fss); err != nil { log.Info().Err(err).Msg("error mounting filesystems") } diff --git a/config/config.go b/config/config.go index f807424..18cc7db 100644 --- a/config/config.go +++ b/config/config.go @@ -31,6 +31,7 @@ func DefaultConfig() *Root { GlobalCacheSize: 2048, MetadataFolder: metadataFolder, AddTimeout: 60, + ReadTimeout: 120, }, Fuse: &FuseGlobal{ AllowOther: false, diff --git a/config/config_test.go b/config/config_test.go index 7a4a7fe..7e906cc 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -34,8 +34,20 @@ func TestDefaults(t *testing.T) { r := &Root{} dr := AddDefaults(r) require.NotNil(dr) - require.NotNil(dr.Fuse) + + // FUSE can be deactivated + require.Nil(dr.Fuse) require.NotNil(dr.HTTPGlobal) require.NotNil(dr.Log) require.NotNil(dr.Torrent) + + // Add defaults when fuse is set + r = &Root{ + Fuse: &FuseGlobal{}, + } + + dr = AddDefaults(r) + require.NotNil(dr.Fuse) + require.Equal(mountFolder, dr.Fuse.Path) + } diff --git a/config/model.go b/config/model.go index f36e8b1..1c57292 100644 --- a/config/model.go +++ b/config/model.go @@ -21,6 +21,7 @@ type Log struct { } type TorrentGlobal struct { + ReadTimeout int `yaml:"read_timeout,omitempty"` AddTimeout int `yaml:"add_timeout,omitempty"` GlobalCacheSize int64 `yaml:"global_cache_size,omitempty"` MetadataFolder string `yaml:"metadata_folder,omitempty"` @@ -70,6 +71,10 @@ func AddDefaults(r *Root) *Root { r.Torrent.AddTimeout = 60 } + if r.Torrent.ReadTimeout == 0 { + r.Torrent.ReadTimeout = 120 + } + if r.Torrent.GlobalCacheSize == 0 { r.Torrent.GlobalCacheSize = 2048 // 2GB } @@ -78,11 +83,10 @@ func AddDefaults(r *Root) *Root { r.Torrent.MetadataFolder = metadataFolder } - if r.Fuse == nil { - r.Fuse = &FuseGlobal{} - } - if r.Fuse.Path == "" { - r.Fuse.Path = mountFolder + if r.Fuse != nil { + if r.Fuse.Path == "" { + r.Fuse.Path = mountFolder + } } if r.HTTPGlobal == nil { diff --git a/fs/archive.go b/fs/archive.go new file mode 100644 index 0000000..043385e --- /dev/null +++ b/fs/archive.go @@ -0,0 +1,246 @@ +package fs + +import ( + "archive/zip" + "io" + "os" + "path/filepath" + "sync" + + "github.com/bodgit/sevenzip" + "github.com/distribyted/distribyted/iio" + "github.com/nwaples/rardecode/v2" +) + +var _ loader = &Zip{} + +type Zip struct { +} + +func (fs *Zip) getFiles(reader iio.Reader, size int64) (map[string]*ArchiveFile, error) { + zr, err := zip.NewReader(reader, size) + if err != nil { + return nil, err + } + + out := make(map[string]*ArchiveFile) + for _, f := range zr.File { + f := f + if f.FileInfo().IsDir() { + continue + } + + rf := func() (iio.Reader, error) { + zr, err := f.Open() + if err != nil { + return nil, err + } + + return iio.NewDiskTeeReader(zr) + } + + n := filepath.Join(string(os.PathSeparator), f.Name) + af := NewArchiveFile(rf, f.FileInfo().Size()) + + out[n] = af + } + + return out, nil +} + +var _ loader = &SevenZip{} + +type SevenZip struct { +} + +func (fs *SevenZip) getFiles(reader iio.Reader, size int64) (map[string]*ArchiveFile, error) { + r, err := sevenzip.NewReader(reader, size) + if err != nil { + return nil, err + } + + out := make(map[string]*ArchiveFile) + for _, f := range r.File { + f := f + if f.FileInfo().IsDir() { + continue + } + + rf := func() (iio.Reader, error) { + zr, err := f.Open() + if err != nil { + return nil, err + } + + return iio.NewDiskTeeReader(zr) + } + + af := NewArchiveFile(rf, f.FileInfo().Size()) + n := filepath.Join(string(os.PathSeparator), f.Name) + + out[n] = af + } + + return out, nil +} + +var _ loader = &Rar{} + +type Rar struct { +} + +func (fs *Rar) getFiles(reader iio.Reader, size int64) (map[string]*ArchiveFile, error) { + r, err := rardecode.NewReader(iio.NewSeekerWrapper(reader, size)) + if err != nil { + return nil, err + } + + out := make(map[string]*ArchiveFile) + for { + header, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + + rf := func() (iio.Reader, error) { + return iio.NewDiskTeeReader(r) + } + + n := filepath.Join(string(os.PathSeparator), header.Name) + + af := NewArchiveFile(rf, header.UnPackedSize) + + out[n] = af + } + + return out, nil +} + +type loader interface { + getFiles(r iio.Reader, size int64) (map[string]*ArchiveFile, error) +} + +var _ Filesystem = &archive{} + +type archive struct { + r iio.Reader + s *storage + + size int64 + once sync.Once + l loader +} + +func NewArchive(r iio.Reader, size int64, l loader) *archive { + return &archive{ + r: r, + s: newStorage(nil), + size: size, + l: l, + } +} + +func (fs *archive) loadOnce() error { + var errOut error + fs.once.Do(func() { + files, err := fs.l.getFiles(fs.r, fs.size) + if err != nil { + errOut = err + return + } + + for name, file := range files { + if err := fs.s.Add(file, name); err != nil { + errOut = err + return + } + } + }) + + return errOut +} + +func (fs *archive) Open(filename string) (File, error) { + if filename == string(os.PathSeparator) { + return &Dir{}, nil + } + + if err := fs.loadOnce(); err != nil { + return nil, err + } + + return fs.s.Get(filename) +} + +func (fs *archive) ReadDir(path string) (map[string]File, error) { + if err := fs.loadOnce(); err != nil { + return nil, err + } + + return fs.s.Children(path) +} + +var _ File = &ArchiveFile{} + +func NewArchiveFile(readerFunc func() (iio.Reader, error), len int64) *ArchiveFile { + return &ArchiveFile{ + readerFunc: readerFunc, + len: len, + } +} + +type ArchiveFile struct { + readerFunc func() (iio.Reader, error) + reader iio.Reader + len int64 +} + +func (d *ArchiveFile) load() error { + if d.reader != nil { + return nil + } + r, err := d.readerFunc() + if err != nil { + return err + } + + d.reader = r + + return nil +} + +func (d *ArchiveFile) Size() int64 { + return d.len +} + +func (d *ArchiveFile) IsDir() bool { + return false +} + +func (d *ArchiveFile) Close() (err error) { + if d.reader != nil { + err = d.reader.Close() + d.reader = nil + } + + return +} + +func (d *ArchiveFile) Read(p []byte) (n int, err error) { + if err := d.load(); err != nil { + return 0, err + } + + return d.reader.Read(p) +} + +func (d *ArchiveFile) ReadAt(p []byte, off int64) (n int, err error) { + if err := d.load(); err != nil { + return 0, err + } + + return d.reader.ReadAt(p, off) +} diff --git a/fs/zip_test.go b/fs/archive_test.go similarity index 96% rename from fs/zip_test.go rename to fs/archive_test.go index cbe7de1..b5a1ca5 100644 --- a/fs/zip_test.go +++ b/fs/archive_test.go @@ -18,7 +18,7 @@ func TestZipFilesystem(t *testing.T) { zReader, len := createTestZip(require) - zfs := NewZip(zReader, len) + zfs := NewArchive(zReader, len, &Zip{}) files, err := zfs.ReadDir("/path/to/test/file") require.NoError(err) diff --git a/fs/container.go b/fs/container.go index bdeca2a..3a839fe 100644 --- a/fs/container.go +++ b/fs/container.go @@ -20,5 +20,5 @@ func (fs *ContainerFs) Open(filename string) (File, error) { } func (fs *ContainerFs) ReadDir(path string) (map[string]File, error) { - return fs.s.Children(path), nil + return fs.s.Children(path) } diff --git a/fs/memory.go b/fs/memory.go index c9fc3e9..f2bcb0f 100644 --- a/fs/memory.go +++ b/fs/memory.go @@ -21,7 +21,7 @@ func (fs *Memory) Open(filename string) (File, error) { } func (fs *Memory) ReadDir(path string) (map[string]File, error) { - return fs.Storage.Children(path), nil + return fs.Storage.Children(path) } var _ File = &MemoryFile{} diff --git a/fs/storage.go b/fs/storage.go index ab630b9..e0134dc 100644 --- a/fs/storage.go +++ b/fs/storage.go @@ -12,7 +12,13 @@ type FsFactory func(f File) (Filesystem, error) var SupportedFactories = map[string]FsFactory{ ".zip": func(f File) (Filesystem, error) { - return NewZip(f, f.Size()), nil + return NewArchive(f, f.Size(), &Zip{}), nil + }, + ".rar": func(f File) (Filesystem, error) { + return NewArchive(f, f.Size(), &Rar{}), nil + }, + ".7z": func(f File) (Filesystem, error) { + return NewArchive(f, f.Size(), &SevenZip{}), nil }, } @@ -37,6 +43,8 @@ func (s *storage) Clear() { s.files = make(map[string]File) s.children = make(map[string]map[string]File) s.filesystems = make(map[string]Filesystem) + + s.Add(&Dir{}, "/") } func (s *storage) Has(path string) bool { @@ -116,20 +124,20 @@ func (s *storage) createParent(p string, f File) error { return nil } -func (s *storage) Children(path string) map[string]File { +func (s *storage) Children(path string) (map[string]File, error) { path = clean(path) - out, err := s.getDirFromFs(path) - if err == nil { - return out - } - l := make(map[string]File) for n, f := range s.children[path] { l[n] = f } - return l + if _, ok := s.children[path]; ok { + return l, nil + } + + return s.getDirFromFs(path) + } func (s *storage) Get(path string) (File, error) { diff --git a/fs/storage_test.go b/fs/storage_test.go index fff36e1..1038d5f 100644 --- a/fs/storage_test.go +++ b/fs/storage_test.go @@ -40,7 +40,8 @@ func TestStorage(t *testing.T) { require.Error(err) require.Nil(file) - files := s.Children("/path/to/dummy/") + files, err := s.Children("/path/to/dummy/") + require.NoError(err) require.Len(files, 2) require.Contains(files, "file.txt") require.Contains(files, "file2.txt") @@ -48,7 +49,8 @@ func TestStorage(t *testing.T) { err = s.Add(&Dummy{}, "/path/to/dummy/folder/file.txt") require.NoError(err) - files = s.Children("/path/to/dummy/") + files, err = s.Children("/path/to/dummy/") + require.NoError(err) require.Len(files, 3) require.Contains(files, "file.txt") require.Contains(files, "file2.txt") @@ -59,7 +61,8 @@ func TestStorage(t *testing.T) { require.True(s.Has("/path/file4.txt")) - files = s.Children("/") + files, err = s.Children("/") + require.NoError(err) require.Len(files, 1) err = s.Add(&Dummy{}, "/path/special_file.test") @@ -69,10 +72,12 @@ func TestStorage(t *testing.T) { require.NoError(err) require.Equal(&Dummy{}, file) - files = s.Children("/path/special_file.test") - require.Len(files, 0) + files, err = s.Children("/path/special_file.test") + require.Error(err) + require.Nil(files) - files = s.Children("/path/special_file.test/dir/here") + files, err = s.Children("/path/special_file.test/dir/here") + require.NoError(err) require.Len(files, 2) err = s.Add(&Dummy{}, "/path/to/__special__path/file3.txt") @@ -81,6 +86,8 @@ func TestStorage(t *testing.T) { file, err = s.Get("/path/to/__special__path/file3.txt") require.NoError(err) require.Equal(&Dummy{}, file) + + s.Clear() } func TestStorageWindowsPath(t *testing.T) { @@ -120,6 +127,28 @@ func TestStorageAddFs(t *testing.T) { require.Error(err) } +func TestSupportedFactories(t *testing.T) { + t.Parallel() + + require := require.New(t) + + require.Contains(SupportedFactories, ".zip") + require.Contains(SupportedFactories, ".rar") + require.Contains(SupportedFactories, ".7z") + + fs, err := SupportedFactories[".zip"](&Dummy{}) + require.NoError(err) + require.NotNil(fs) + + fs, err = SupportedFactories[".rar"](&Dummy{}) + require.NoError(err) + require.NotNil(fs) + + fs, err = SupportedFactories[".7z"](&Dummy{}) + require.NoError(err) + require.NotNil(fs) +} + var _ Filesystem = &DummyFs{} type DummyFs struct { diff --git a/fs/torrent.go b/fs/torrent.go index cc8c303..e66f796 100644 --- a/fs/torrent.go +++ b/fs/torrent.go @@ -1,25 +1,29 @@ package fs import ( + "context" + "io" "sync" + "time" "github.com/anacrolix/torrent" - "github.com/distribyted/distribyted/iio" ) var _ Filesystem = &Torrent{} type Torrent struct { - mu sync.Mutex - ts map[string]*torrent.Torrent - s *storage - loaded bool + mu sync.RWMutex + ts map[string]*torrent.Torrent + s *storage + loaded bool + readTimeout int } -func NewTorrent() *Torrent { +func NewTorrent(readTimeout int) *Torrent { return &Torrent{ - s: newStorage(SupportedFactories), - ts: make(map[string]*torrent.Torrent), + s: newStorage(SupportedFactories), + ts: make(map[string]*torrent.Torrent), + readTimeout: readTimeout, } } @@ -45,14 +49,17 @@ func (fs *Torrent) load() { if fs.loaded { return } - - fs.mu.Lock() - defer fs.mu.Unlock() + fs.mu.RLock() + defer fs.mu.RUnlock() for _, t := range fs.ts { <-t.GotInfo() for _, file := range t.Files() { - fs.s.Add(&torrentFile{readerFunc: file.NewReader, len: file.Length()}, file.Path()) + fs.s.Add(&torrentFile{ + reader: file.NewReader(), + len: file.Length(), + timeout: fs.readTimeout, + }, file.Path()) } } @@ -66,23 +73,18 @@ func (fs *Torrent) Open(filename string) (File, error) { func (fs *Torrent) ReadDir(path string) (map[string]File, error) { fs.load() - return fs.s.Children(path), nil + return fs.s.Children(path) } var _ File = &torrentFile{} type torrentFile struct { - readerFunc func() torrent.Reader - reader iio.Reader - len int64 -} + mu sync.Mutex -func (d *torrentFile) load() { - if d.reader != nil { - return - } + reader torrent.Reader + len int64 - d.reader = iio.NewReadAtWrapper(d.readerFunc()) + timeout int } func (d *torrentFile) Size() int64 { @@ -94,22 +96,58 @@ func (d *torrentFile) IsDir() bool { } func (d *torrentFile) Close() error { - var err error - if d.reader != nil { - err = d.reader.Close() - } - - d.reader = nil - - return err + return d.reader.Close() } func (d *torrentFile) Read(p []byte) (n int, err error) { - d.load() - return d.reader.Read(p) + ctx, cancel := context.WithCancel(context.Background()) + timer := time.AfterFunc( + time.Duration(d.timeout)*time.Second, + func() { + cancel() + }, + ) + + defer timer.Stop() + + return d.reader.ReadContext(ctx, p) } -func (d *torrentFile) ReadAt(p []byte, off int64) (n int, err error) { - d.load() - return d.reader.ReadAt(p, off) +func (d *torrentFile) ReadAt(p []byte, off int64) (int, error) { + d.mu.Lock() + defer d.mu.Unlock() + _, err := d.reader.Seek(off, io.SeekStart) + if err != nil { + return 0, err + } + i, err := d.readAtLeast(p, len(p)) + return i, err +} + +func (d *torrentFile) readAtLeast(buf []byte, min int) (n int, err error) { + if len(buf) < min { + return 0, io.ErrShortBuffer + } + for n < min && err == nil { + var nn int + + ctx, cancel := context.WithCancel(context.Background()) + timer := time.AfterFunc( + time.Duration(d.timeout)*time.Second, + func() { + cancel() + }, + ) + + nn, err = d.reader.ReadContext(ctx, buf[n:]) + n += nn + + timer.Stop() + } + if n >= min { + err = nil + } else if n > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return } diff --git a/fs/torrent_test.go b/fs/torrent_test.go index 96de518..a1c09a3 100644 --- a/fs/torrent_test.go +++ b/fs/torrent_test.go @@ -11,21 +11,33 @@ import ( const testMagnet = "magnet:?xt=urn:btih:a88fda5954e89178c372716a6a78b8180ed4dad3&dn=The+WIRED+CD+-+Rip.+Sample.+Mash.+Share&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fwired-cd.torrent" -func TestTorrentFilesystem(t *testing.T) { - t.Parallel() - - require := require.New(t) +var Cli *torrent.Client +func TestMain(m *testing.M) { cfg := torrent.NewDefaultClientConfig() cfg.DataDir = os.TempDir() client, err := torrent.NewClient(cfg) + if err != nil { + panic(err) + } + + Cli = client + + exitVal := m.Run() + + client.Close() + + os.Exit(exitVal) +} + +func TestTorrentFilesystem(t *testing.T) { + require := require.New(t) + + to, err := Cli.AddMagnet(testMagnet) require.NoError(err) - to, err := client.AddMagnet(testMagnet) - require.NoError(err) - - tfs := NewTorrent() + tfs := NewTorrent(600) tfs.AddTorrent(to) files, err := tfs.ReadDir("/") @@ -61,5 +73,39 @@ func TestTorrentFilesystem(t *testing.T) { require.NoError(err) require.Equal(10, n) + tfs.RemoveTorrent(to.InfoHash().String()) + files, err = tfs.ReadDir("/") + require.NoError(err) + require.Len(files, 0) + require.NoError(f.Close()) } + +func TestReadAtWrapper(t *testing.T) { + require := require.New(t) + + to, err := Cli.AddMagnet(testMagnet) + require.NoError(err) + + <-to.GotInfo() + torrFile := to.Files()[0] + + tf := torrentFile{ + reader: torrFile.NewReader(), + len: torrFile.Length(), + timeout: 500, + } + + defer tf.Close() + + toRead := make([]byte, 5) + n, err := tf.ReadAt(toRead, 6) + require.NoError(err) + require.Equal(5, n) + require.Equal([]byte{0x0, 0x0, 0x1f, 0x76, 0x54}, toRead) + + n, err = tf.ReadAt(toRead, 0) + require.NoError(err) + require.Equal(5, n) + require.Equal([]byte{0x49, 0x44, 0x33, 0x3, 0x0}, toRead) +} diff --git a/fs/zip.go b/fs/zip.go deleted file mode 100644 index d2234a8..0000000 --- a/fs/zip.go +++ /dev/null @@ -1,141 +0,0 @@ -package fs - -import ( - "archive/zip" - "os" - - "github.com/distribyted/distribyted/iio" -) - -var _ Filesystem = &Zip{} - -type Zip struct { - r iio.Reader - s *storage - size int64 - - loaded bool -} - -func NewZip(r iio.Reader, size int64) *Zip { - return &Zip{ - r: r, - size: size, - s: newStorage(nil), - } -} - -func (fs *Zip) load() error { - if fs.loaded { - return nil - } - - zr, err := zip.NewReader(fs.r, fs.size) - if err != nil { - return err - } - - for _, f := range zr.File { - f := f - if f.FileInfo().IsDir() { - continue - } - - err := fs.s.Add(newZipFile( - func() (iio.Reader, error) { - zr, err := f.Open() - if err != nil { - return nil, err - } - - return iio.NewDiskTeeReader(zr) - }, - f.FileInfo().Size(), - ), string(os.PathSeparator)+f.Name) - if err != nil { - return err - } - } - - fs.loaded = true - - return nil -} - -func (fs *Zip) Open(filename string) (File, error) { - if err := fs.load(); err != nil { - return nil, err - } - - return fs.s.Get(filename) -} - -func (fs *Zip) ReadDir(path string) (map[string]File, error) { - if err := fs.load(); err != nil { - return nil, err - } - - return fs.s.Children(path), nil -} - -var _ File = &zipFile{} - -func newZipFile(readerFunc func() (iio.Reader, error), len int64) *zipFile { - return &zipFile{ - readerFunc: readerFunc, - len: len, - } -} - -type zipFile struct { - readerFunc func() (iio.Reader, error) - reader iio.Reader - len int64 -} - -func (d *zipFile) load() error { - if d.reader != nil { - return nil - } - r, err := d.readerFunc() - if err != nil { - return err - } - - d.reader = r - - return nil -} - -func (d *zipFile) Size() int64 { - return d.len -} - -func (d *zipFile) IsDir() bool { - return false -} - -func (d *zipFile) Close() (err error) { - if d.reader != nil { - err = d.reader.Close() - d.reader = nil - } - - return -} - -func (d *zipFile) Read(p []byte) (n int, err error) { - if err := d.load(); err != nil { - return 0, err - } - - return d.reader.Read(p) -} - -func (d *zipFile) ReadAt(p []byte, off int64) (n int, err error) { - if err := d.load(); err != nil { - return 0, err - } - - return d.reader.ReadAt(p, off) -} diff --git a/fuse/mount.go b/fuse/mount.go index d2d6593..8a1f38c 100644 --- a/fuse/mount.go +++ b/fuse/mount.go @@ -9,29 +9,34 @@ import ( "github.com/billziss-gh/cgofuse/fuse" "github.com/distribyted/distribyted/fs" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) type FS struct { fuse.FileSystemBase fh *fileHandler + + log zerolog.Logger } func NewFS(fs fs.Filesystem) fuse.FileSystemInterface { + l := log.Logger.With().Str("component", "fuse").Logger() return &FS{ - fh: &fileHandler{fs: fs}, + fh: &fileHandler{fs: fs}, + log: l, } } func (fs *FS) Open(path string, flags int) (errc int, fh uint64) { fh, err := fs.fh.OpenHolder(path) - if err == os.ErrNotExist { - log.Debug().Str("path", path).Msg("file does not exists") + if os.IsNotExist(err) { + fs.log.Debug().Str("path", path).Msg("file does not exists") return -fuse.ENOENT, fhNone } if err != nil { - log.Error().Err(err).Str("path", path).Msg("error opening file") + fs.log.Error().Err(err).Str("path", path).Msg("error opening file") return -fuse.EIO, fhNone } @@ -42,20 +47,20 @@ func (fs *FS) Opendir(path string) (errc int, fh uint64) { return fs.Open(path, 0) } -func (cfs *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { +func (fs *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { if path == "/" { stat.Mode = fuse.S_IFDIR | 0555 return 0 } - file, err := cfs.fh.GetFile(path, fh) - if err == os.ErrNotExist { - log.Debug().Str("path", path).Msg("file does not exists") + file, err := fs.fh.GetFile(path, fh) + if os.IsNotExist(err) { + fs.log.Debug().Str("path", path).Msg("file does not exists") return -fuse.ENOENT } if err != nil { - log.Error().Err(err).Str("path", path).Msg("error getting holder when reading file attributes") + fs.log.Error().Err(err).Str("path", path).Msg("error getting holder when reading file attributes") return -fuse.EIO } @@ -71,13 +76,13 @@ func (cfs *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { func (fs *FS) Read(path string, dest []byte, off int64, fh uint64) int { file, err := fs.fh.GetFile(path, fh) - if err == os.ErrNotExist { - log.Error().Str("path", path).Msg("file not found on READ operation") + if os.IsNotExist(err) { + fs.log.Error().Err(err).Str("path", path).Msg("file not found on READ operation") return -fuse.ENOENT } if err != nil { - log.Error().Err(err).Str("path", path).Msg("error getting holder reading data from file") + fs.log.Error().Err(err).Str("path", path).Msg("error getting holder reading data from file") return -fuse.EIO } @@ -100,7 +105,7 @@ func (fs *FS) Read(path string, dest []byte, off int64, fh uint64) int { func (fs *FS) Release(path string, fh uint64) int { if err := fs.fh.Remove(fh); err != nil { - log.Error().Err(err).Str("path", path).Msg("error getting holder when releasing file") + fs.log.Error().Err(err).Str("path", path).Msg("error getting holder when releasing file") return -fuse.EIO } @@ -121,13 +126,13 @@ func (fs *FS) Readdir(path string, //TODO improve this function to make use of fh index if possible paths, err := fs.fh.ListDir(path) if err != nil { - log.Error().Str("path", path).Msg("error reading directory") - return -fuse.EIO + fs.log.Error().Err(err).Str("path", path).Msg("error reading directory") + return -fuse.ENOSYS } for _, p := range paths { if !fill(p, nil, 0) { - log.Error().Str("path", path).Msg("error adding directory") + fs.log.Error().Str("path", path).Msg("error adding directory") break } } @@ -141,25 +146,24 @@ var ErrHolderEmpty = errors.New("file holder is empty") var ErrBadHolderIndex = errors.New("holder index too big") type fileHandler struct { - mu sync.Mutex + mu sync.RWMutex opened []fs.File fs fs.Filesystem } func (fh *fileHandler) GetFile(path string, fhi uint64) (fs.File, error) { - fh.mu.Lock() - defer fh.mu.Unlock() + fh.mu.RLock() + defer fh.mu.RUnlock() if fhi == fhNone { return fh.lookupFile(path) } - return fh.get(fhi) } func (fh *fileHandler) ListDir(path string) ([]string, error) { - fh.mu.Lock() - defer fh.mu.Unlock() + fh.mu.RLock() + defer fh.mu.RUnlock() var out []string files, err := fh.fs.ReadDir(path) @@ -174,14 +178,14 @@ func (fh *fileHandler) ListDir(path string) ([]string, error) { } func (fh *fileHandler) OpenHolder(path string) (uint64, error) { - fh.mu.Lock() - defer fh.mu.Unlock() - file, err := fh.lookupFile(path) if err != nil { return fhNone, err } + fh.mu.Lock() + defer fh.mu.Unlock() + for i, old := range fh.opened { if old == nil { fh.opened[i] = file diff --git a/go.mod b/go.mod index f11491a..87c6bed 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/anacrolix/multiless v0.2.0 // indirect github.com/anacrolix/torrent v1.38.0 github.com/billziss-gh/cgofuse v1.5.0 + github.com/bodgit/sevenzip v1.1.1 github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/dgraph-io/badger/v3 v3.2103.2 github.com/elliotchance/orderedmap v1.4.0 // indirect @@ -21,6 +22,7 @@ require ( github.com/google/btree v1.0.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-colorable v0.1.11 + github.com/nwaples/rardecode/v2 v2.0.0-beta.2 github.com/pion/dtls/v2 v2.0.10 // indirect github.com/pion/mdns v0.0.5 // indirect github.com/pion/rtp v1.7.4 // indirect @@ -30,12 +32,13 @@ require ( github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect - golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 - golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect + golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 + golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect + golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) require ( @@ -43,7 +46,7 @@ require ( github.com/anacrolix/chansync v0.3.0 // indirect github.com/anacrolix/confluence v1.10.0 // indirect github.com/anacrolix/envpprof v1.1.1 // indirect - github.com/anacrolix/go-libutp v1.0.4 // indirect + github.com/anacrolix/go-libutp v1.0.5-0.20211117031120-2dac1c67ecc5 // indirect github.com/anacrolix/missinggo v1.3.0 // indirect github.com/anacrolix/missinggo/perf v1.0.0 // indirect github.com/anacrolix/mmsg v1.0.0 // indirect @@ -53,9 +56,12 @@ require ( github.com/anacrolix/utp v0.1.0 // indirect github.com/benbjohnson/immutable v0.3.0 // indirect github.com/bits-and-blooms/bitset v1.2.1 // indirect + github.com/bodgit/plumbing v1.1.0 // indirect + github.com/bodgit/windows v1.0.0 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/connesc/cipherio v0.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -72,6 +78,8 @@ require ( github.com/google/go-cmp v0.5.6 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/json-iterator/go v1.1.10 // indirect github.com/klauspost/compress v1.13.6 // indirect @@ -80,28 +88,29 @@ require ( github.com/modern-go/reflect2 v1.0.1 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/pion/datachannel v1.5.2 // indirect - github.com/pion/ice/v2 v2.1.13 // indirect - github.com/pion/interceptor v0.1.0 // indirect + github.com/pion/ice/v2 v2.1.14 // indirect + github.com/pion/interceptor v0.1.2 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/rtcp v1.2.8 // indirect + github.com/pion/rtcp v1.2.9 // indirect github.com/pion/sdp/v3 v3.0.4 // indirect github.com/pion/srtp/v2 v2.0.5 // indirect github.com/pion/stun v0.3.5 // indirect github.com/pion/transport v0.12.3 // indirect github.com/pion/turn/v2 v2.0.5 // indirect github.com/pion/udp v0.1.1 // indirect - github.com/pion/webrtc/v3 v3.1.9 // indirect + github.com/pion/webrtc/v3 v3.1.11 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect github.com/ugorji/go/codec v1.1.7 // indirect + github.com/ulikunitz/xz v0.5.10 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.23.0 // indirect + go4.org v0.0.0-20200411211856-f5505b9728dd // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/protobuf v1.27.1 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 4e96877..b25664e 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,29 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797 h1:yDf7ARQc637HoxDho7xjqdvO5ZA2Yb+xzv/fOnnvZzw= crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508 h1:fILCBBFnjnrQ0whVJlGhfv1E/QiaFDNtGFBObEVRnYg= crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= @@ -32,8 +48,8 @@ github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54g github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= github.com/anacrolix/envpprof v1.1.1 h1:sHQCyj7HtiSfaZAzL2rJrQdyS7odLqlwO6nhk/tG/j8= github.com/anacrolix/envpprof v1.1.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= -github.com/anacrolix/go-libutp v1.0.4 h1:95sv09MoNQbgEJqJLrotglFnVBAiMx1tyl6xMAmnAgg= -github.com/anacrolix/go-libutp v1.0.4/go.mod h1:8vSGX5g0b4eebsDBNVQHUXSCwYaN18Lnkse0hUW8/5w= +github.com/anacrolix/go-libutp v1.0.5-0.20211117031120-2dac1c67ecc5 h1:VIMEtC+qYhEEBZHMvtOQKiKy+SI+u+Su/MXY8K2nOKY= +github.com/anacrolix/go-libutp v1.0.5-0.20211117031120-2dac1c67ecc5/go.mod h1:8vSGX5g0b4eebsDBNVQHUXSCwYaN18Lnkse0hUW8/5w= github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.6.1-0.20200416071330-f58a030e6149/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= @@ -85,6 +101,12 @@ github.com/billziss-gh/cgofuse v1.5.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nC github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bodgit/plumbing v1.1.0 h1:lesbixvHgSBQFNMsrjdPNsm+EBk4vFFhxWl0+90vDY0= +github.com/bodgit/plumbing v1.1.0/go.mod h1:HvY/F2JCfHpm7AxnSMjhRl8QGDCmEvke8F9e3vbLRhY= +github.com/bodgit/sevenzip v1.1.1 h1:safhC8Y1T9j+05DbSndxOIsy0/l0O+VnfapzIxcoWic= +github.com/bodgit/sevenzip v1.1.1/go.mod h1:Kj7XgTvuiQY+eatey/j6VCtQy9yc8qgvdoHV05qm6SM= +github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= +github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= @@ -95,8 +117,13 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw= +github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -149,6 +176,8 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -177,10 +206,16 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -202,6 +237,7 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -219,9 +255,16 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -230,7 +273,12 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= @@ -239,11 +287,14 @@ github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -287,6 +338,8 @@ github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -308,8 +361,12 @@ github.com/pion/dtls/v2 v2.0.10 h1:wgys7gPR1NMbWjmjJ3CW7lkUGaun8djgH8nahpNLnxI= github.com/pion/dtls/v2 v2.0.10/go.mod h1:00OxfeCRWHShcqT9jx8pKKmBWuTt0NCZoVPCaC4VKvU= github.com/pion/ice/v2 v2.1.13 h1:/YNYcIw56LT/whwuzkTnrprcRnapj2ZNqUsR0W8elmo= github.com/pion/ice/v2 v2.1.13/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= +github.com/pion/ice/v2 v2.1.14 h1:nD9GZs3MiR1/dPa5EiMRMe8hLBG3/qqCdx/hTS2g8VE= +github.com/pion/ice/v2 v2.1.14/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= github.com/pion/interceptor v0.1.0 h1:SlXKaDlEvSl7cr4j8fJykzVz4UdH+7UDtcvx+u01wLU= github.com/pion/interceptor v0.1.0/go.mod h1:j5NIl3tJJPB3u8+Z2Xz8MZs/VV6rc+If9mXEKNuFmEM= +github.com/pion/interceptor v0.1.2 h1:1IfrJ+AQ0HhwxNl4hqh9hMvl1hBKiNhAAr7DrUHsC6s= +github.com/pion/interceptor v0.1.2/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= @@ -319,6 +376,8 @@ github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TB github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= github.com/pion/rtcp v1.2.8 h1:Cys8X6r0xxU65ESTmXkqr8eU1Q1Wx+lNkoZCUH4zD7E= github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U= +github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA= @@ -341,6 +400,8 @@ github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pion/webrtc/v3 v3.1.9 h1:Nyimo16me180ZBT35nV2YcrZvjOs1i4ktUi/UmXVQGg= github.com/pion/webrtc/v3 v3.1.9/go.mod h1:2b1+xQu7GP0vaMGPLbmEX+uB+Fvn2F4sDBp90ZrPBdI= +github.com/pion/webrtc/v3 v3.1.11 h1:8Q5BEsxvlDn3botM8U8n/Haln745FBa5TWgm8v2c2FA= +github.com/pion/webrtc/v3 v3.1.11/go.mod h1:h9pbP+CADYb/99s5rfjflEcBLgdVKm55Rm7heQ/gIvY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -366,6 +427,7 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -378,6 +440,7 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= @@ -416,6 +479,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= @@ -428,13 +493,20 @@ go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= +go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -442,11 +514,32 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -460,8 +553,15 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -477,8 +577,13 @@ golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0= golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= +golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -495,15 +600,23 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -522,22 +635,48 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -549,19 +688,42 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -601,4 +763,10 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/http/http.go b/http/http.go index 91fa838..1a777ad 100644 --- a/http/http.go +++ b/http/http.go @@ -18,6 +18,7 @@ func New(fc *filecache.Cache, ss *torrent.Stats, s *torrent.Service, ch *config. r := gin.New() r.Use(gin.Recovery()) r.Use(gin.ErrorLogger()) + r.Use(Logger()) r.GET("/assets/*filepath", func(c *gin.Context) { c.FileFromFS(c.Request.URL.Path, http.FS(distribyted.Assets)) @@ -63,3 +64,29 @@ func New(fc *filecache.Cache, ss *torrent.Stats, s *torrent.Service, ch *config. return nil } + +func Logger() gin.HandlerFunc { + l := log.Logger.With().Str("component", "http").Logger() + return func(c *gin.Context) { + path := c.Request.URL.Path + raw := c.Request.URL.RawQuery + c.Next() + if raw != "" { + path = path + "?" + raw + } + msg := c.Errors.String() + if msg == "" { + msg = "Request" + } + + s := c.Writer.Status() + switch { + case s >= 400 && s < 500: + l.Warn().Str("path", path).Int("status", s).Msg(msg) + case s >= 500: + l.Error().Str("path", path).Int("status", s).Msg(msg) + default: + l.Debug().Str("path", path).Int("status", s).Msg(msg) + } + } +} diff --git a/iio/wrapper.go b/iio/wrapper.go index 47be270..0eba023 100644 --- a/iio/wrapper.go +++ b/iio/wrapper.go @@ -5,40 +5,6 @@ import ( "sync" ) -type readAtWrapper struct { - mu sync.Mutex - - io.ReadSeeker - io.ReaderAt - io.Closer -} - -func NewReadAtWrapper(r io.ReadSeeker) Reader { - return &readAtWrapper{ReadSeeker: r} -} - -func (rw *readAtWrapper) ReadAt(p []byte, off int64) (int, error) { - rw.mu.Lock() - defer rw.mu.Unlock() - _, err := rw.Seek(off, io.SeekStart) - if err != nil { - return 0, err - } - - return io.ReadAtLeast(rw, p, len(p)) -} - -func (rw *readAtWrapper) Close() error { - rw.mu.Lock() - defer rw.mu.Unlock() - c, ok := rw.ReadSeeker.(io.Closer) - if !ok { - return nil - } - - return c.Close() -} - type seekerWrapper struct { mu sync.Mutex pos int64 diff --git a/iio/wrapper_test.go b/iio/wrapper_test.go index de22b7c..8c28e79 100644 --- a/iio/wrapper_test.go +++ b/iio/wrapper_test.go @@ -1,7 +1,6 @@ package iio_test import ( - "bytes" "io" "testing" @@ -12,28 +11,6 @@ import ( var testData []byte = []byte("Hello World") -func TestReadAtWrapper(t *testing.T) { - t.Parallel() - - require := require.New(t) - - br := bytes.NewReader(testData) - - r := iio.NewReadAtWrapper(br) - defer r.Close() - - toRead := make([]byte, 5) - n, err := r.ReadAt(toRead, 6) - require.NoError(err) - require.Equal(5, n) - require.Equal("World", string(toRead)) - - n, err = r.ReadAt(toRead, 0) - require.NoError(err) - require.Equal(5, n) - require.Equal("Hello", string(toRead)) -} - func TestSeekerWrapper(t *testing.T) { t.Parallel() diff --git a/templates/config_template.yaml b/templates/config_template.yaml index 7480779..eaca9cd 100644 --- a/templates/config_template.yaml +++ b/templates/config_template.yaml @@ -31,6 +31,10 @@ torrent: # Timeout in seconds when adding a magnet or a torrent. add_timeout: 60 + + # Timeout in seconds when reading any torrent content. Usefult when reading + # archived content from .rar, .zip or .7z. + read_timeout: 120 fuse: # Folder where fuse will mount torrent filesystem # For windows users: You can set here also a disk letter like X: or Z: diff --git a/torrent/loader/config.go b/torrent/loader/config.go index ea9f202..a49dcd2 100644 --- a/torrent/loader/config.go +++ b/torrent/loader/config.go @@ -17,6 +17,7 @@ func NewConfig(r []*config.Route) *Config { func (l *Config) ListMagnets() (map[string][]string, error) { out := make(map[string][]string) for _, r := range l.c { + out[r.Name] = make([]string, 0) for _, t := range r.Torrents { if t.MagnetURI == "" { continue @@ -32,6 +33,7 @@ func (l *Config) ListMagnets() (map[string][]string, error) { func (l *Config) ListTorrentPaths() (map[string][]string, error) { out := make(map[string][]string) for _, r := range l.c { + out[r.Name] = make([]string, 0) for _, t := range r.Torrents { if t.TorrentPath == "" { continue diff --git a/torrent/service.go b/torrent/service.go index c2b13ac..fcb4a0e 100644 --- a/torrent/service.go +++ b/torrent/service.go @@ -26,20 +26,20 @@ type Service struct { cfgLoader loader.Loader db loader.LoaderAdder - log zerolog.Logger - timeout int + log zerolog.Logger + addTimeout, readTimeout int } -func NewService(cfg loader.Loader, db loader.LoaderAdder, stats *Stats, c *torrent.Client, timeout int) *Service { +func NewService(cfg loader.Loader, db loader.LoaderAdder, stats *Stats, c *torrent.Client, addTimeout, readTimeout int) *Service { l := log.Logger.With().Str("component", "torrent-service").Logger() return &Service{ - log: l, - s: stats, - c: c, - fss: make(map[string]fs.Filesystem), - cfgLoader: cfg, - db: db, - timeout: timeout, + log: l, + s: stats, + c: c, + fss: make(map[string]fs.Filesystem), + cfgLoader: cfg, + db: db, + addTimeout: addTimeout, } } @@ -61,6 +61,7 @@ func (s *Service) load(l loader.Loader) error { return err } for r, ms := range list { + s.addRoute(r) for _, m := range ms { if err := s.addMagnet(r, m); err != nil { return err @@ -73,6 +74,7 @@ func (s *Service) load(l loader.Loader) error { return err } for r, ms := range list { + s.addRoute(r) for _, p := range ms { if err := s.addTorrentPath(r, p); err != nil { return err @@ -113,12 +115,25 @@ func (s *Service) addMagnet(r, m string) error { } +func (s *Service) addRoute(r string) { + s.s.AddRoute(r) + + // Add to filesystems + folder := path.Join("/", r) + s.mu.Lock() + defer s.mu.Unlock() + _, ok := s.fss[folder] + if !ok { + s.fss[folder] = fs.NewTorrent(s.readTimeout) + } +} + func (s *Service) addTorrent(r string, t *torrent.Torrent) error { // only get info if name is not available if t.Info() == nil { s.log.Info().Str("hash", t.InfoHash().String()).Msg("getting torrent info") select { - case <-time.After(time.Duration(s.timeout) * time.Second): + case <-time.After(time.Duration(s.addTimeout) * time.Second): s.log.Error().Str("hash", t.InfoHash().String()).Msg("timeout getting torrent info") return errors.New("timeout getting torrent info") case <-t.GotInfo(): @@ -134,10 +149,6 @@ func (s *Service) addTorrent(r string, t *torrent.Torrent) error { folder := path.Join("/", r) s.mu.Lock() defer s.mu.Unlock() - _, ok := s.fss[folder] - if !ok { - s.fss[folder] = fs.NewTorrent() - } tfs, ok := s.fss[folder].(*fs.Torrent) if !ok { diff --git a/torrent/stats.go b/torrent/stats.go index bba940b..88cfc3e 100644 --- a/torrent/stats.go +++ b/torrent/stats.go @@ -90,6 +90,13 @@ func NewStats() *Stats { } } +func (s *Stats) AddRoute(route string) { + _, ok := s.torrentsByRoute[route] + if !ok { + s.torrentsByRoute[route] = make(map[string]*torrent.Torrent) + } +} + func (s *Stats) Add(route string, t *torrent.Torrent) { s.mut.Lock() defer s.mut.Unlock() diff --git a/webdav/fs.go b/webdav/fs.go index 5327c51..125ca65 100644 --- a/webdav/fs.go +++ b/webdav/fs.go @@ -31,17 +31,9 @@ func (wd *WebDAV) OpenFile(ctx context.Context, name string, flag int, perm os.F return nil, err } - var dirContent []os.FileInfo - if f.IsDir() { - dir, err := wd.listDir(p) - if err != nil { - return nil, err - } - - dirContent = dir - } - - wdf := newFile(filepath.Base(p), f, dirContent) + wdf := newFile(filepath.Base(p), f, func() ([]os.FileInfo, error) { + return wd.listDir(p) + }) return wdf, nil } @@ -92,29 +84,39 @@ type webDAVFile struct { fi os.FileInfo - mu sync.Mutex - // dirPos and pos are protected by mu. - dirPos int + mudp sync.Mutex + dirPos int + + mup sync.Mutex pos int64 + dirFunc func() ([]os.FileInfo, error) dirContent []os.FileInfo } -func newFile(name string, f fs.File, dir []os.FileInfo) *webDAVFile { +func newFile(name string, f fs.File, df func() ([]os.FileInfo, error)) *webDAVFile { return &webDAVFile{ - fi: newFileInfo(name, f.Size(), f.IsDir()), - dirContent: dir, - Reader: f, + fi: newFileInfo(name, f.Size(), f.IsDir()), + dirFunc: df, + Reader: f, } } func (wdf *webDAVFile) Readdir(count int) ([]os.FileInfo, error) { - wdf.mu.Lock() - defer wdf.mu.Unlock() + wdf.mudp.Lock() + defer wdf.mudp.Unlock() if !wdf.fi.IsDir() { return nil, os.ErrInvalid } + if wdf.dirContent == nil { + dc, err := wdf.dirFunc() + if err != nil { + return nil, err + } + wdf.dirContent = dc + } + old := wdf.dirPos if old >= len(wdf.dirContent) { // The os.File Readdir docs say that at the end of a directory, @@ -142,8 +144,8 @@ func (wdf *webDAVFile) Stat() (os.FileInfo, error) { } func (wdf *webDAVFile) Read(p []byte) (int, error) { - wdf.mu.Lock() - defer wdf.mu.Unlock() + wdf.mup.Lock() + defer wdf.mup.Unlock() n, err := wdf.Reader.ReadAt(p, wdf.pos) wdf.pos += int64(n) @@ -152,8 +154,8 @@ func (wdf *webDAVFile) Read(p []byte) (int, error) { } func (wdf *webDAVFile) Seek(offset int64, whence int) (int64, error) { - wdf.mu.Lock() - defer wdf.mu.Unlock() + wdf.mup.Lock() + defer wdf.mup.Unlock() switch whence { case io.SeekStart: diff --git a/webdav/fs_test.go b/webdav/fs_test.go index 0394ee5..d5e413e 100644 --- a/webdav/fs_test.go +++ b/webdav/fs_test.go @@ -8,6 +8,7 @@ import ( "github.com/distribyted/distribyted/fs" "github.com/stretchr/testify/require" + "golang.org/x/net/webdav" ) func TestWebDAVFilesystem(t *testing.T) { @@ -53,4 +54,27 @@ func TestWebDAVFilesystem(t *testing.T) { require.NoError(err) require.Equal(4, nn) require.Equal([]byte("test"), br) + + fInfo, err := wfs.Stat(context.Background(), "/folder/file.txt") + require.NoError(err) + require.Equal("/folder/file.txt", fInfo.Name()) + require.Equal(false, fInfo.IsDir()) + require.Equal(int64(18), fInfo.Size()) +} + +func TestErrNotImplemented(t *testing.T) { + t.Parallel() + + require := require.New(t) + + mfs := fs.NewMemory() + mf := fs.NewMemoryFile([]byte("test file content.")) + err := mfs.Storage.Add(mf, "/folder/file.txt") + require.NoError(err) + + wfs := newFS(mfs) + + require.ErrorIs(wfs.Mkdir(context.Background(), "test", 0), webdav.ErrNotImplemented) + require.ErrorIs(wfs.RemoveAll(context.Background(), "test"), webdav.ErrNotImplemented) + require.ErrorIs(wfs.Rename(context.Background(), "test", "newTest"), webdav.ErrNotImplemented) } diff --git a/webdav/handler.go b/webdav/handler.go index 3649992..47cb151 100644 --- a/webdav/handler.go +++ b/webdav/handler.go @@ -1,14 +1,23 @@ package webdav import ( + "net/http" + "github.com/distribyted/distribyted/fs" + "github.com/rs/zerolog/log" "golang.org/x/net/webdav" ) func newHandler(fs fs.Filesystem) *webdav.Handler { + l := log.Logger.With().Str("component", "webDAV").Logger() return &webdav.Handler{ Prefix: "/", FileSystem: newFS(fs), LockSystem: webdav.NewMemLS(), + Logger: func(req *http.Request, err error) { + if err != nil { + l.Error().Err(err).Str("path", req.RequestURI).Msg("webDAV error") + } + }, } }