handle changes to placeholder state correctly

We assumed that `zfs recv -F FS` would basically replace FS inplace, leaving its children untouched.
That is in fact not the case, it only works if `zfs send -R` is set, which we don't do.

Thus, implement the required functionality manually.

This solves a `zfs recv` error that would occur when a filesystem previously created as placeholder on the receiving side becomes a non-placeholder filesystem (likely due to config change on the sending side):

  zfs send pool1/foo@1 | zfs recv -F pool1/bar
  cannot receive new filesystem stream:
  destination has snapshots (eg. pool1/bar)
  must destroy them to overwrite it
This commit is contained in:
Christian Schwarz
2019-03-13 18:33:20 +01:00
parent 1eb0f12a61
commit d50e553ebb
6 changed files with 196 additions and 116 deletions

View File

@ -108,7 +108,7 @@ type Filesystem struct {
receiver Receiver
Path string // compat
receiverFSExists bool // compat
receiverFS *pdu.Filesystem
promBytesReplicated prometheus.Counter // compat
}
@ -237,10 +237,10 @@ func (p *Planner) doPlanning(ctx context.Context) ([]*Filesystem, error) {
q := make([]*Filesystem, 0, len(sfss))
for _, fs := range sfss {
receiverFSExists := false
var receiverFS *pdu.Filesystem
for _, rfs := range rfss {
if rfs.Path == fs.Path {
receiverFSExists = true
receiverFS = rfs
}
}
@ -250,7 +250,7 @@ func (p *Planner) doPlanning(ctx context.Context) ([]*Filesystem, error) {
sender: p.sender,
receiver: p.receiver,
Path: fs.Path,
receiverFSExists: receiverFSExists,
receiverFS: receiverFS,
promBytesReplicated: ctr,
})
}
@ -278,7 +278,7 @@ func (fs *Filesystem) doPlanning(ctx context.Context) ([]*Step, error) {
}
var rfsvs []*pdu.FilesystemVersion
if fs.receiverFSExists {
if fs.receiverFS != nil && !fs.receiverFS.GetIsPlaceholder() {
rfsvsres, err := fs.receiver.ListFilesystemVersions(ctx, &pdu.ListFilesystemVersionsReq{Filesystem: fs.Path})
if err != nil {
log.WithError(err).Error("receiver error")
@ -301,7 +301,7 @@ func (fs *Filesystem) doPlanning(ctx context.Context) ([]*Step, error) {
log.WithField("problem", msg).Error("cannot resolve conflict")
}
}
if path == nil {
if len(path) == 0 {
return nil, conflict
}