diff --git a/cmd/config_mapfilter.go b/cmd/config_mapfilter.go index b39cc51..1a30f00 100644 --- a/cmd/config_mapfilter.go +++ b/cmd/config_mapfilter.go @@ -14,7 +14,7 @@ type DatasetMapFilter struct { // if set, only valid filter entries can be added using Add() // and Map() will always return an error - filterOnly bool + filterMode bool } type datasetMapFilterEntry struct { @@ -25,17 +25,17 @@ type datasetMapFilterEntry struct { subtreeMatch bool } -func NewDatasetMapFilter(capacity int, filterOnly bool) *DatasetMapFilter { +func NewDatasetMapFilter(capacity int, filterMode bool) *DatasetMapFilter { return &DatasetMapFilter{ entries: make([]datasetMapFilterEntry, 0, capacity), - filterOnly: filterOnly, + filterMode: filterMode, } } func (m *DatasetMapFilter) Add(pathPattern, mapping string) (err error) { - if m.filterOnly { - if _, err = parseDatasetFilterResult(mapping); err != nil { + if m.filterMode { + if _, err = m.parseDatasetFilterResult(mapping); err != nil { return } } @@ -103,7 +103,7 @@ func (m DatasetMapFilter) mostSpecificPrefixMapping(path *zfs.DatasetPath) (idx func (m DatasetMapFilter) Map(source *zfs.DatasetPath) (target *zfs.DatasetPath, err error) { - if m.filterOnly { + if m.filterMode { err = fmt.Errorf("using a filter for mapping simply does not work") return } @@ -115,6 +115,11 @@ func (m DatasetMapFilter) Map(source *zfs.DatasetPath) (target *zfs.DatasetPath, } me := m.entries[mi] + if strings.HasPrefix("!", me.mapping) { + // reject mapping + return nil, nil + } + target, err = zfs.NewDatasetPath(me.mapping) if err != nil { err = fmt.Errorf("mapping target is not a dataset path: %s", err) @@ -135,13 +140,19 @@ func (m DatasetMapFilter) Map(source *zfs.DatasetPath) (target *zfs.DatasetPath, } func (m DatasetMapFilter) Filter(p *zfs.DatasetPath) (pass bool, err error) { + + if !m.filterMode { + err = fmt.Errorf("using a mapping as a filter does not work") + return + } + mi, hasMapping := m.mostSpecificPrefixMapping(p) if !hasMapping { pass = false return } me := m.entries[mi] - pass, err = parseDatasetFilterResult(me.mapping) + pass, err = m.parseDatasetFilterResult(me.mapping) return } @@ -149,7 +160,7 @@ func (m DatasetMapFilter) Filter(p *zfs.DatasetPath) (pass bool, err error) { // The new filter allows excactly those paths that were not forbidden by the mapping. func (m DatasetMapFilter) InvertedFilter() (inv *DatasetMapFilter, err error) { - if m.filterOnly { + if m.filterMode { err = errors.Errorf("can only invert mappings") return } @@ -172,28 +183,47 @@ func (m DatasetMapFilter) InvertedFilter() (inv *DatasetMapFilter, err error) { return inv, nil } +// Creates a new DatasetMapFilter in filter mode from a mapping +// All accepting mapping results are mapped to accepting filter results +// All rejecting mapping results are mapped to rejecting filter results +func (m DatasetMapFilter) AsFilter() (f *DatasetMapFilter) { + + f = &DatasetMapFilter{ + make([]datasetMapFilterEntry, len(m.entries)), + true, + } + + for i, e := range m.entries { + var newe datasetMapFilterEntry = e + if strings.HasPrefix(newe.mapping, "!") { + newe.mapping = MapFilterResultOmit + } else { + newe.mapping = MapFilterResultOk + } + f.entries[i] = newe + } + + return f +} + const ( MapFilterResultOk string = "ok" - MapFilterResultOmit string = "omit" + MapFilterResultOmit string = "!" ) // Parse a dataset filter result -func parseDatasetFilterResult(result string) (pass bool, err error) { +func (m DatasetMapFilter) parseDatasetFilterResult(result string) (pass bool, err error) { l := strings.ToLower(result) - switch strings.ToLower(l) { - case MapFilterResultOk: - pass = true - return - case MapFilterResultOmit: - return - default: - err = fmt.Errorf("'%s' is not a valid filter result", result) - return + if l == MapFilterResultOk { + return true, nil } - return + if l == MapFilterResultOmit { + return false, nil + } + return false, fmt.Errorf("'%s' is not a valid filter result", result) } -func parseDatasetMapFilter(mi interface{}, filterOnly bool) (f *DatasetMapFilter, err error) { +func parseDatasetMapFilter(mi interface{}, filterMode bool) (f *DatasetMapFilter, err error) { var m map[string]string if err = mapstructure.Decode(mi, &m); err != nil { @@ -201,7 +231,7 @@ func parseDatasetMapFilter(mi interface{}, filterOnly bool) (f *DatasetMapFilter return } - f = NewDatasetMapFilter(len(m), filterOnly) + f = NewDatasetMapFilter(len(m), filterMode) for pathPattern, mapping := range m { if err = f.Add(pathPattern, mapping); err != nil { err = fmt.Errorf("invalid mapping entry ['%s':'%s']: %s", pathPattern, mapping, err) diff --git a/cmd/sampleconf/localbackup/host1.yml b/cmd/sampleconf/localbackup/host1.yml index f88aaea..34c73dc 100644 --- a/cmd/sampleconf/localbackup/host1.yml +++ b/cmd/sampleconf/localbackup/host1.yml @@ -7,6 +7,7 @@ jobs: mapping: { "zroot/var/db<": "storage/backups/local/zroot/var/db", "zroot/usr/home<": "storage/backups/local/zroot/usr/home", + "zroot/var/tmp": "!", #don't backup /tmp } snapshot_prefix: zrepl_ interval: 10m diff --git a/cmd/sampleconf/pullbackup/productionhost.yml b/cmd/sampleconf/pullbackup/productionhost.yml index 1d080a0..9d232b7 100644 --- a/cmd/sampleconf/pullbackup/productionhost.yml +++ b/cmd/sampleconf/pullbackup/productionhost.yml @@ -29,8 +29,9 @@ jobs: # snapshot these filesystems every 10m with zrepl_ as prefix datasets: { - "zroot/var/db<": ok, - "zroot/usr/home<": omit, + "zroot/var/db<": "ok", + "zroot/usr/home<": "ok", + "zroot/var/tmp": "!", #don't backup /tmp } snapshot_prefix: zrepl_ interval: 10m diff --git a/cmd/sampleconf/pushbackup/productionhost.yml b/cmd/sampleconf/pushbackup/productionhost.yml index 58e962e..d38ce53 100644 --- a/cmd/sampleconf/pushbackup/productionhost.yml +++ b/cmd/sampleconf/pushbackup/productionhost.yml @@ -12,8 +12,8 @@ jobs: # snapshot these datsets every 10m with zrepl_ as prefix datasets: { - "zroot/var/db<": ok, - "zroot/usr/home<": ok, + "zroot/var/db<": "ok", + "zroot/usr/home<": "!", } snapshot_prefix: zrepl_ interval: 10m diff --git a/cmd/test.go b/cmd/test.go index 632a317..a7f328c 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -72,7 +72,7 @@ func doTestDatasetMapFilter(cmd *cobra.Command, args []string) { os.Exit(1) } - if mf.filterOnly { + if mf.filterMode{ pass, err := mf.Filter(ip) if err != nil { log.Printf("error evaluating filter: %s", err)