From 1428678b58235790c79847149ec1b8a111c42687 Mon Sep 17 00:00:00 2001 From: adminer Date: Fri, 24 Jul 2026 17:32:03 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20internal/endpoint/endpoint.go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/endpoint/endpoint.go | 45 ++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/internal/endpoint/endpoint.go b/internal/endpoint/endpoint.go index 70c2c06..ba2e4dc 100644 --- a/internal/endpoint/endpoint.go +++ b/internal/endpoint/endpoint.go @@ -636,6 +636,24 @@ func (f subroot) UserSpecifiedDatasets() zfs.UserSpecifiedDatasetsSet { } +// senderPrefixDatasetPath returns the configured literal sender-side prefix +// (e.g. "hdd_9XG1J9J1") to strip from incoming filesystem paths, or nil if +// ZREPL_STRIP_SENDER_PREFIX is unset/empty. Using a literal string (rather +// than a component count) lets us losslessly reconstruct the sender's +// original path later in ListFilesystems, which is required for the +// sender/receiver filesystem-matching in the replication planner to work. +func senderPrefixDatasetPath() *zfs.DatasetPath { + s := envconst.String("ZREPL_STRIP_SENDER_PREFIX", "") + if s == "" { + return nil + } + dp, err := zfs.NewDatasetPath(s) + if err != nil || dp.Length() == 0 { + return nil + } + return dp +} + func (f subroot) MapToLocal(fs string) (*zfs.DatasetPath, error) { p, err := zfs.NewDatasetPath(fs) if err != nil { @@ -644,9 +662,10 @@ func (f subroot) MapToLocal(fs string) (*zfs.DatasetPath, error) { if p.Length() == 0 { return nil, errors.Errorf("cannot map empty filesystem") } - // drop N leading components of the sender's path (e.g. pool name) - if n := envconst.Int("ZREPL_STRIP_SENDER_PREFIX_COMPS", 0); n > 0 { - p.TrimNPrefixComps(n) + // PATCH: strip a configured literal prefix (e.g. the source zpool name) + // from the *sender's* path before appending it under root_fs. + if prefix := senderPrefixDatasetPath(); prefix != nil && p.HasPrefix(prefix) { + p.TrimPrefix(prefix) } c := f.localRoot.Copy() c.Extend(p) @@ -654,8 +673,6 @@ func (f subroot) MapToLocal(fs string) (*zfs.DatasetPath, error) { } - - func (s *Receiver) ListFilesystems(ctx context.Context, req *pdu.ListFilesystemReq) (*pdu.ListFilesystemRes, error) { defer trace.WithSpanFromStackUpdateCtx(&ctx)() @@ -696,12 +713,28 @@ func (s *Receiver) ListFilesystems(ctx context.Context, req *pdu.ListFilesystemR a.TrimPrefix(root) + // PATCH: re-prepend the literal sender prefix that MapToLocal strips, + // so that the Path we report here matches what the sender reports + // for the same filesystem (see replication_logic.go's Path-based + // sender/receiver matching). Without this, the planner can never + // find a matching receiverFS and will treat every cycle as if the + // filesystem doesn't exist yet on the receiver. + reportPath := a.ToString() + if prefix := senderPrefixDatasetPath(); prefix != nil { + if reportPath == "" { + reportPath = prefix.ToString() + } else { + reportPath = prefix.ToString() + "/" + reportPath + } + } + fs := &pdu.Filesystem{ - Path: a.ToString(), + Path: reportPath, IsPlaceholder: ph.IsPlaceholder, ResumeToken: token, } fss = append(fss, fs) + } if len(fss) == 0 { getLogger(ctx).Debug("no filesystems found")