diff --git a/cmd/config_mapfilter.go b/cmd/config_mapfilter.go index 65546b7..b39e593 100644 --- a/cmd/config_mapfilter.go +++ b/cmd/config_mapfilter.go @@ -17,7 +17,7 @@ type DatasetMapFilter struct { } type datasetMapFilterEntry struct { - path zfs.DatasetPath + path *zfs.DatasetPath // the mapping. since this datastructure acts as both mapping and filter // we have to convert it to the desired rep dynamically mapping string @@ -50,12 +50,10 @@ func (m *DatasetMapFilter) Add(pathPattern, mapping string) (err error) { return } - var path zfs.DatasetPath pathStr := strings.TrimSuffix(pathPattern, SUBTREE_PATTERN) - path, err = zfs.NewDatasetPath(pathStr) + path, err := zfs.NewDatasetPath(pathStr) if err != nil { - err = fmt.Errorf("pattern is not a dataset path: %s", err) - return + return fmt.Errorf("pattern is not a dataset path: %s", err) } entry := datasetMapFilterEntry{ @@ -71,7 +69,7 @@ func (m *DatasetMapFilter) Add(pathPattern, mapping string) (err error) { // find the most specific prefix mapping we have // // longer prefix wins over shorter prefix, direct wins over glob -func (m DatasetMapFilter) mostSpecificPrefixMapping(path zfs.DatasetPath) (idx int, found bool) { +func (m DatasetMapFilter) mostSpecificPrefixMapping(path *zfs.DatasetPath) (idx int, found bool) { lcp, lcp_entry_idx := -1, -1 direct_idx := -1 for e := range m.entries { @@ -103,7 +101,7 @@ func (m DatasetMapFilter) mostSpecificPrefixMapping(path zfs.DatasetPath) (idx i return } -func (m DatasetMapFilter) Map(source zfs.DatasetPath) (target zfs.DatasetPath, err error) { +func (m DatasetMapFilter) Map(source *zfs.DatasetPath) (target *zfs.DatasetPath, err error) { if m.filterOnly { err = fmt.Errorf("using a filter for mapping simply does not work") @@ -136,7 +134,7 @@ func (m DatasetMapFilter) Map(source zfs.DatasetPath) (target zfs.DatasetPath, e return } -func (m DatasetMapFilter) Filter(p zfs.DatasetPath) (pass bool, err error) { +func (m DatasetMapFilter) Filter(p *zfs.DatasetPath) (pass bool, err error) { mi, hasMapping := m.mostSpecificPrefixMapping(p) if !hasMapping { pass = false diff --git a/cmd/handler.go b/cmd/handler.go index b4b39b1..9ff0eb2 100644 --- a/cmd/handler.go +++ b/cmd/handler.go @@ -8,7 +8,7 @@ import ( ) type DatasetMapping interface { - Map(source zfs.DatasetPath) (target zfs.DatasetPath, err error) + Map(source *zfs.DatasetPath) (target *zfs.DatasetPath, err error) } type Handler struct { @@ -17,7 +17,7 @@ type Handler struct { SinkMappingFunc func(clientIdentity string) (mapping DatasetMapping, err error) } -func (h Handler) HandleFilesystemRequest(r rpc.FilesystemRequest) (roots []zfs.DatasetPath, err error) { +func (h Handler) HandleFilesystemRequest(r rpc.FilesystemRequest) (roots []*zfs.DatasetPath, err error) { h.Logger.Printf("handling fsr: %#v", r) @@ -120,7 +120,7 @@ func (h Handler) HandlePullMeRequest(r rpc.PullMeRequest, clientIdentity string, return } -func (h Handler) pullACLCheck(p zfs.DatasetPath) (err error) { +func (h Handler) pullACLCheck(p *zfs.DatasetPath) (err error) { var allowed bool allowed, err = h.PullACL.Filter(p) if err != nil { diff --git a/cmd/replication.go b/cmd/replication.go index a7cb054..41efb98 100644 --- a/cmd/replication.go +++ b/cmd/replication.go @@ -143,7 +143,7 @@ func cmdRun(cmd *cobra.Command, args []string) { type localPullACL struct{} -func (a localPullACL) Filter(p zfs.DatasetPath) (pass bool, err error) { +func (a localPullACL) Filter(p *zfs.DatasetPath) (pass bool, err error) { return true, nil } @@ -250,15 +250,15 @@ func doPull(pull PullContext) (err error) { log := pull.Log fsr := rpc.FilesystemRequest{} - var remoteFilesystems []zfs.DatasetPath + var remoteFilesystems []*zfs.DatasetPath if remoteFilesystems, err = remote.FilesystemRequest(fsr); err != nil { return } // build mapping (local->RemoteLocalMapping) + traversal datastructure type RemoteLocalMapping struct { - Remote zfs.DatasetPath - Local zfs.DatasetPath + Remote *zfs.DatasetPath + Local *zfs.DatasetPath } replMapping := make(map[string]RemoteLocalMapping, len(remoteFilesystems)) localTraversal := zfs.NewDatasetPathForest() @@ -267,7 +267,7 @@ func doPull(pull PullContext) (err error) { log.Printf("mapping using %#v\n", pull.Mapping) for fs := range remoteFilesystems { var err error - var localFs zfs.DatasetPath + var localFs *zfs.DatasetPath localFs, err = pull.Mapping.Map(remoteFilesystems[fs]) if err != nil { if err != NoMatchError { diff --git a/rpc/rpc.go b/rpc/rpc.go index 667f04c..df7ba9b 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -14,7 +14,7 @@ import ( ) type RPCRequester interface { - FilesystemRequest(r FilesystemRequest) (roots []zfs.DatasetPath, err error) + FilesystemRequest(r FilesystemRequest) (roots []*zfs.DatasetPath, err error) FilesystemVersionsRequest(r FilesystemVersionsRequest) (versions []zfs.FilesystemVersion, err error) InitialTransferRequest(r InitialTransferRequest) (io.Reader, error) IncrementalTransferRequest(r IncrementalTransferRequest) (io.Reader, error) @@ -24,7 +24,7 @@ type RPCRequester interface { } type RPCHandler interface { - HandleFilesystemRequest(r FilesystemRequest) (roots []zfs.DatasetPath, err error) + HandleFilesystemRequest(r FilesystemRequest) (roots []*zfs.DatasetPath, err error) // returned versions ordered by birthtime, oldest first HandleFilesystemVersionsRequest(r FilesystemVersionsRequest) (versions []zfs.FilesystemVersion, err error) @@ -451,13 +451,13 @@ func (c ByteStreamRPC) ProtocolVersionRequest() (err error) { return c.sendRequestReceiveHeader(b, ROK) } -func (c ByteStreamRPC) FilesystemRequest(r FilesystemRequest) (roots []zfs.DatasetPath, err error) { +func (c ByteStreamRPC) FilesystemRequest(r FilesystemRequest) (roots []*zfs.DatasetPath, err error) { if err = c.sendRequestReceiveHeader(r, RFilesystems); err != nil { return } - roots = make([]zfs.DatasetPath, 0) + roots = make([]*zfs.DatasetPath, 0) if err = readChunkedJSON(c.conn, &roots); err != nil { return @@ -520,7 +520,7 @@ func ConnectLocalRPC(handler RPCHandler) RPCRequester { return LocalRPC{handler} } -func (c LocalRPC) FilesystemRequest(r FilesystemRequest) (roots []zfs.DatasetPath, err error) { +func (c LocalRPC) FilesystemRequest(r FilesystemRequest) (roots []*zfs.DatasetPath, err error) { return c.handler.HandleFilesystemRequest(r) } diff --git a/rpc/structs.go b/rpc/structs.go index fdd6908..2a4c7a8 100644 --- a/rpc/structs.go +++ b/rpc/structs.go @@ -1,7 +1,14 @@ package rpc -import "io" -import "github.com/zrepl/zrepl/zfs" +import ( + "encoding/json" + "io" + + "github.com/zrepl/zrepl/zfs" +) + +var _ json.Marshaler = &zfs.DatasetPath{} +var _ json.Unmarshaler = &zfs.DatasetPath{} type RequestId [16]byte type RequestType uint8 @@ -26,11 +33,11 @@ type FilesystemRequest struct { } type FilesystemVersionsRequest struct { - Filesystem zfs.DatasetPath + Filesystem *zfs.DatasetPath } type InitialTransferRequest struct { - Filesystem zfs.DatasetPath + Filesystem *zfs.DatasetPath FilesystemVersion zfs.FilesystemVersion } @@ -39,7 +46,7 @@ func (r InitialTransferRequest) Respond(snapshotReader io.Reader) { } type IncrementalTransferRequest struct { - Filesystem zfs.DatasetPath + Filesystem *zfs.DatasetPath From zfs.FilesystemVersion To zfs.FilesystemVersion } diff --git a/zfs/datasetpath_visitor.go b/zfs/datasetpath_visitor.go index dd1857d..b8c8e05 100644 --- a/zfs/datasetpath_visitor.go +++ b/zfs/datasetpath_visitor.go @@ -10,7 +10,7 @@ func NewDatasetPathForest() *DatasetPathForest { } } -func (f *DatasetPathForest) Add(p DatasetPath) { +func (f *DatasetPathForest) Add(p *DatasetPath) { if len(p.comps) <= 0 { panic("dataset path too short. must have length > 0") } @@ -30,7 +30,7 @@ func (f *DatasetPathForest) Add(p DatasetPath) { } type DatasetPathVisit struct { - Path DatasetPath + Path *DatasetPath // If true, the dataset referenced by Path was not in the list of datasets to traverse FilledIn bool } @@ -93,7 +93,7 @@ func (t *datasetPathTree) WalkTopDown(parent []string, visitor DatasetPathsVisit this := append(parent, t.Component) thisVisit := DatasetPathVisit{ - DatasetPath{this}, + &DatasetPath{this}, t.FilledIn, } visitChildTree := visitor(thisVisit) diff --git a/zfs/datasetpath_visitor_test.go b/zfs/datasetpath_visitor_test.go index 4b4d5d1..7e348f2 100644 --- a/zfs/datasetpath_visitor_test.go +++ b/zfs/datasetpath_visitor_test.go @@ -34,7 +34,7 @@ func makeVisitRecorder() (v DatasetPathsVisitor, rec *visitRecorder) { return } -func buildForest(paths []DatasetPath) (f *DatasetPathForest) { +func buildForest(paths []*DatasetPath) (f *DatasetPathForest) { f = NewDatasetPathForest() for _, p := range paths { f.Add(p) @@ -44,7 +44,7 @@ func buildForest(paths []DatasetPath) (f *DatasetPathForest) { func TestDatasetPathForestWalkTopDown(t *testing.T) { - paths := []DatasetPath{ + paths := []*DatasetPath{ toDatasetPath("pool1"), toDatasetPath("pool1/foo/bar"), toDatasetPath("pool1/foo/bar/looloo"), @@ -70,7 +70,7 @@ func TestDatasetPathForestWalkTopDown(t *testing.T) { func TestDatasetPathWalkTopDownWorksUnordered(t *testing.T) { - paths := []DatasetPath{ + paths := []*DatasetPath{ toDatasetPath("pool1"), toDatasetPath("pool1/foo/bar/looloo"), toDatasetPath("pool1/foo/bar"), diff --git a/zfs/diff.go b/zfs/diff.go index d6bfd77..07786c1 100644 --- a/zfs/diff.go +++ b/zfs/diff.go @@ -208,13 +208,13 @@ func ZFSListFilesystemState() (localState map[string]FilesystemState, err error) // move. // // TODO better solution available? -func PlaceholderPropertyValue(p DatasetPath) string { +func PlaceholderPropertyValue(p *DatasetPath) string { ps := []byte(p.ToString()) sum := sha512.Sum512_256(ps) return hex.EncodeToString(sum[:]) } -func IsPlaceholder(p DatasetPath, placeholderPropertyValue string) (isPlaceholder bool, err error) { +func IsPlaceholder(p *DatasetPath, placeholderPropertyValue string) (isPlaceholder bool, err error) { expected := PlaceholderPropertyValue(p) isPlaceholder = expected == placeholderPropertyValue if !isPlaceholder { @@ -223,7 +223,7 @@ func IsPlaceholder(p DatasetPath, placeholderPropertyValue string) (isPlaceholde return } -func ZFSCreatePlaceholderFilesystem(p DatasetPath) (err error) { +func ZFSCreatePlaceholderFilesystem(p *DatasetPath) (err error) { v := PlaceholderPropertyValue(p) cmd := exec.Command(ZFS_BINARY, "create", "-o", fmt.Sprintf("%s=%s", ZREPL_PLACEHOLDER_PROPERTY_NAME, v), diff --git a/zfs/mapping.go b/zfs/mapping.go index 889166e..ca80318 100644 --- a/zfs/mapping.go +++ b/zfs/mapping.go @@ -3,10 +3,10 @@ package zfs import "fmt" type DatasetFilter interface { - Filter(p DatasetPath) (pass bool, err error) + Filter(p *DatasetPath) (pass bool, err error) } -func ZFSListMapping(filter DatasetFilter) (datasets []DatasetPath, err error) { +func ZFSListMapping(filter DatasetFilter) (datasets []*DatasetPath, err error) { if filter == nil { panic("filter must not be nil") @@ -15,11 +15,11 @@ func ZFSListMapping(filter DatasetFilter) (datasets []DatasetPath, err error) { var lines [][]string lines, err = ZFSList([]string{"name"}, "-r", "-t", "filesystem,volume") - datasets = make([]DatasetPath, 0, len(lines)) + datasets = make([]*DatasetPath, 0, len(lines)) for _, line := range lines { - var path DatasetPath + var path *DatasetPath if path, err = NewDatasetPath(line[0]); err != nil { return } diff --git a/zfs/versions.go b/zfs/versions.go index 91e45e9..ec5593d 100644 --- a/zfs/versions.go +++ b/zfs/versions.go @@ -45,7 +45,7 @@ type FilesystemVersion struct { Creation time.Time } -func (v FilesystemVersion) ToAbsPath(p DatasetPath) string { +func (v FilesystemVersion) ToAbsPath(p *DatasetPath) string { var b bytes.Buffer b.WriteString(p.ToString()) b.WriteString(v.Type.DelimiterChar()) @@ -57,7 +57,7 @@ type FilesystemVersionFilter interface { Filter(fsv FilesystemVersion) (accept bool, err error) } -func ZFSListFilesystemVersions(fs DatasetPath, filter FilesystemVersionFilter) (res []FilesystemVersion, err error) { +func ZFSListFilesystemVersions(fs *DatasetPath, filter FilesystemVersionFilter) (res []FilesystemVersion, err error) { var fieldLines [][]string fieldLines, err = ZFSList( []string{"name", "guid", "createtxg", "creation"}, @@ -125,7 +125,7 @@ func ZFSListFilesystemVersions(fs DatasetPath, filter FilesystemVersionFilter) ( return } -func ZFSDestroyFilesystemVersion(filesystem DatasetPath, version FilesystemVersion) (err error) { +func ZFSDestroyFilesystemVersion(filesystem *DatasetPath, version FilesystemVersion) (err error) { datasetPath := version.ToAbsPath(filesystem) diff --git a/zfs/zfs.go b/zfs/zfs.go index 55047e9..2b8005a 100644 --- a/zfs/zfs.go +++ b/zfs/zfs.go @@ -3,31 +3,33 @@ package zfs import ( "bufio" "bytes" + "encoding/json" "errors" "fmt" - "github.com/zrepl/zrepl/util" "io" "os/exec" "strings" + + "github.com/zrepl/zrepl/util" ) type DatasetPath struct { comps []string } -func (p DatasetPath) ToString() string { +func (p *DatasetPath) ToString() string { return strings.Join(p.comps, "/") } -func (p DatasetPath) Empty() bool { +func (p *DatasetPath) Empty() bool { return len(p.comps) == 0 } -func (p *DatasetPath) Extend(extend DatasetPath) { +func (p *DatasetPath) Extend(extend *DatasetPath) { p.comps = append(p.comps, extend.comps...) } -func (p DatasetPath) HasPrefix(prefix DatasetPath) bool { +func (p *DatasetPath) HasPrefix(prefix *DatasetPath) bool { if len(prefix.comps) > len(p.comps) { return false } @@ -39,7 +41,7 @@ func (p DatasetPath) HasPrefix(prefix DatasetPath) bool { return true } -func (p *DatasetPath) TrimPrefix(prefix DatasetPath) { +func (p *DatasetPath) TrimPrefix(prefix *DatasetPath) { if !p.HasPrefix(prefix) { return } @@ -64,7 +66,7 @@ func (p *DatasetPath) TrimNPrefixComps(n int) { } -func (p DatasetPath) Equal(q DatasetPath) bool { +func (p DatasetPath) Equal(q *DatasetPath) bool { if len(p.comps) != len(q.comps) { return false } @@ -76,17 +78,28 @@ func (p DatasetPath) Equal(q DatasetPath) bool { return true } -func (p DatasetPath) Length() int { +func (p *DatasetPath) Length() int { return len(p.comps) } -func (p DatasetPath) Copy() (c DatasetPath) { +func (p *DatasetPath) Copy() (c *DatasetPath) { + c = &DatasetPath{} c.comps = make([]string, len(p.comps)) copy(c.comps, p.comps) return } -func NewDatasetPath(s string) (p DatasetPath, err error) { +func (p *DatasetPath) MarshalJSON() ([]byte, error) { + return json.Marshal(p.comps) +} + +func (p *DatasetPath) UnmarshalJSON(b []byte) error { + p.comps = make([]string, 0) + return json.Unmarshal(b, &p.comps) +} + +func NewDatasetPath(s string) (p *DatasetPath, err error) { + p = &DatasetPath{} if s == "" { p.comps = make([]string, 0) return p, nil // the empty dataset path @@ -104,7 +117,7 @@ func NewDatasetPath(s string) (p DatasetPath, err error) { return } -func toDatasetPath(s string) DatasetPath { +func toDatasetPath(s string) *DatasetPath { p, err := NewDatasetPath(s) if err != nil { panic(err) @@ -172,7 +185,7 @@ func ZFSList(properties []string, zfsArgs ...string) (res [][]string, err error) return } -func ZFSSend(fs DatasetPath, from, to *FilesystemVersion) (stream io.Reader, err error) { +func ZFSSend(fs *DatasetPath, from, to *FilesystemVersion) (stream io.Reader, err error) { args := make([]string, 0) args = append(args, "send") @@ -188,7 +201,7 @@ func ZFSSend(fs DatasetPath, from, to *FilesystemVersion) (stream io.Reader, err return } -func ZFSRecv(fs DatasetPath, stream io.Reader, additionalArgs ...string) (err error) { +func ZFSRecv(fs *DatasetPath, stream io.Reader, additionalArgs ...string) (err error) { args := make([]string, 0) args = append(args, "recv") @@ -226,7 +239,7 @@ func ZFSRecv(fs DatasetPath, stream io.Reader, additionalArgs ...string) (err er return nil } -func ZFSSet(fs DatasetPath, prop, val string) (err error) { +func ZFSSet(fs *DatasetPath, prop, val string) (err error) { if strings.ContainsRune(prop, '=') { panic("prop contains rune '=' which is the delimiter between property name and value") @@ -273,7 +286,7 @@ func ZFSDestroy(dataset string) (err error) { } -func ZFSSnapshot(fs DatasetPath, name string, recursive bool) (err error) { +func ZFSSnapshot(fs *DatasetPath, name string, recursive bool) (err error) { snapname := fmt.Sprintf("%s@%s", fs.ToString(), name) cmd := exec.Command(ZFS_BINARY, "snapshot", snapname)