package zfs import ( "fmt" "github.com/pkg/errors" "strconv" ) const ReplicationCursorBookmarkName = "zrepl_replication_cursor" // may return nil for both values, indicating there is no cursor func ZFSGetReplicationCursor(fs *DatasetPath) (*FilesystemVersion, error) { versions, err := ZFSListFilesystemVersions(fs, nil) if err != nil { return nil, err } for _, v := range versions { if v.Type == Bookmark && v.Name == ReplicationCursorBookmarkName { return &v, nil } } return nil, nil } func ZFSSetReplicationCursor(fs *DatasetPath, snapname string) (guid uint64, err error) { snapPath := fmt.Sprintf("%s@%s", fs.ToString(), snapname) propsSnap, err := zfsGet(snapPath, []string{"createtxg", "guid"}, sourceAny) if err != nil { return 0, err } snapGuid, err := strconv.ParseUint(propsSnap.Get("guid"), 10, 64) bookmarkPath := fmt.Sprintf("%s#%s", fs.ToString(), ReplicationCursorBookmarkName) propsBookmark, err := zfsGet(bookmarkPath, []string{"createtxg"}, sourceAny) _, bookmarkNotExistErr := err.(*DatasetDoesNotExist) if err != nil && !bookmarkNotExistErr { return 0, err } if err == nil { bookmarkTxg, err := strconv.ParseUint(propsBookmark.Get("createtxg"), 10, 64) if err != nil { return 0, errors.Wrap(err, "cannot parse bookmark createtxg") } snapTxg, err := strconv.ParseUint(propsSnap.Get("createtxg"), 10, 64) if err != nil { return 0, errors.Wrap(err, "cannot parse snapshot createtxg") } if snapTxg < bookmarkTxg { return 0, errors.New("replication cursor can only be advanced, not set back") } if err := ZFSDestroy(bookmarkPath); err != nil { // FIXME make safer by using new temporary bookmark, then rename, possible with channel programs return 0, err } } if err := ZFSBookmark(fs, snapname, ReplicationCursorBookmarkName); err != nil { return 0, err } return snapGuid, nil }