diff --git a/internal/daemon/filters/entry.go b/internal/daemon/filters/entry.go index da2a48c4..6410d93d 100644 --- a/internal/daemon/filters/entry.go +++ b/internal/daemon/filters/entry.go @@ -68,3 +68,10 @@ func (self *filterItem) CompatCompare(b *filterItem) int { } return cmp.Compare(self.path.Length(), b.path.Length()) } + +func (self *filterItem) RecursiveDataset() *zfs.DatasetPath { + if !self.recursive || !self.mapping { + return nil + } + return self.path +} diff --git a/internal/daemon/filters/fsmapfilter.go b/internal/daemon/filters/fsmapfilter.go index c90200a5..90d7cc4b 100644 --- a/internal/daemon/filters/fsmapfilter.go +++ b/internal/daemon/filters/fsmapfilter.go @@ -152,3 +152,10 @@ func (self *DatasetFilter) UserSpecifiedDatasets() zfs.UserSpecifiedDatasetsSet func (self *DatasetFilter) AsFilter() endpoint.FSFilter { return self } func (self *DatasetFilter) Empty() bool { return len(self.entries) == 0 } + +func (self *DatasetFilter) SingleRecursiveDataset() *zfs.DatasetPath { + if len(self.entries) != 1 { + return nil + } + return self.entries[0].RecursiveDataset() +} diff --git a/internal/endpoint/endpoint.go b/internal/endpoint/endpoint.go index ce8fb0f4..000acec3 100644 --- a/internal/endpoint/endpoint.go +++ b/internal/endpoint/endpoint.go @@ -96,6 +96,10 @@ func (s *Sender) filterCheckFS(fs string) (*zfs.DatasetPath, error) { func (s *Sender) ListFilesystems(ctx context.Context) (*pdu.ListFilesystemRes, error, ) { + if root := s.FSFilter.SingleRecursiveDataset(); root != nil { + return listFilesystemsRecursive(ctx, root, zfs.PlaceholderPropertyName) + } + fss, err := zfs.ZFSListMapping(ctx, s.FSFilter) if err != nil { return nil, err @@ -710,17 +714,25 @@ func (f subroot) MapToLocal(fs string) (*zfs.DatasetPath, error) { return c, nil } +func (f subroot) SingleRecursiveDataset() *zfs.DatasetPath { + return f.localRoot +} + const receiveResumeToken = "receive_resume_token" func (s *Receiver) ListFilesystems(ctx context.Context) (*pdu.ListFilesystemRes, error, ) { - root := s.clientRootFromCtx(ctx) + return listFilesystemsRecursive(ctx, s.clientRootFromCtx(ctx), + zfs.PlaceholderPropertyName, receiveResumeToken) +} + +func listFilesystemsRecursive(ctx context.Context, root *zfs.DatasetPath, + props ...string, +) (*pdu.ListFilesystemRes, error) { rootStr := root.ToString() fsProps, err := zfs.ZFSGetRecursive(ctx, rootStr, -1, - []string{"filesystem"}, - []string{zfs.PlaceholderPropertyName, receiveResumeToken}, - zfs.SourceAny) + []string{"filesystem"}, props, zfs.SourceAny) if err != nil { var errNotExist *zfs.DatasetDoesNotExist if errors.As(err, &errNotExist) { diff --git a/internal/zfs/mapping.go b/internal/zfs/mapping.go index 32eab4e4..4abb0e6d 100644 --- a/internal/zfs/mapping.go +++ b/internal/zfs/mapping.go @@ -14,13 +14,14 @@ type DatasetFilter interface { // The caller owns the returned set. // Implementations should return a copy. UserSpecifiedDatasets() UserSpecifiedDatasetsSet + SingleRecursiveDataset() *DatasetPath } // A set of dataset names that the user specified in the configuration file. type UserSpecifiedDatasetsSet map[string]bool // Returns a DatasetFilter that does not filter (passes all paths) -func NoFilter() DatasetFilter { +func NoFilter() noFilter { return noFilter{} } @@ -36,6 +37,8 @@ func (noFilter) UserSpecifiedDatasets() UserSpecifiedDatasetsSet { return nil } func (noFilter) Empty() bool { return true } +func (noFilter) SingleRecursiveDataset() *DatasetPath { return nil } + func ZFSListMapping(ctx context.Context, filter DatasetFilter, ) ([]*DatasetPath, error) { res, err := ZFSListMappingProperties(ctx, filter, nil)