diff options
| author | Jeff Darcy <jdarcy@redhat.com> | 2013-02-07 13:57:42 -0500 | 
|---|---|---|
| committer | Anand Avati <avati@redhat.com> | 2013-02-17 12:04:48 -0800 | 
| commit | fcc230c99dd7318c2bee54beaa152b5a8c66f186 (patch) | |
| tree | b8734e799d7610be989b692ef0021100fc847f9f /extras | |
| parent | 614529c59123d3f2a20a6ee9a99d362a7d35e5b1 (diff) | |
features: add a directory-protection translator
This is useful to find all calls that remove a file from the protected
directory, including renames and internal calls.  Such calls will cause
a stack trace to be logged.  There's a filter script to add the needed
translators, and then the new functionality can be invoked with one of
the following commands.
	setfattr -n trusted.glusterfs.protect -v log $dir
	setfattr -n trusted.glusterfs.protect -v reject $dir
	setfattr -n trusted.glusterfs.protect -v anything_else $dir
The first logs calls, but still allows them.  The second rejects them
with EPERM.  The third turns off protection for that directory.
Change-Id: Iee4baaf8e837106be2b4099542cb7dcaae40428c
BUG: 888072
Signed-off-by: Jeff Darcy <jdarcy@redhat.com>
Reviewed-on: http://review.gluster.org/4496
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Anand Avati <avati@redhat.com>
Diffstat (limited to 'extras')
| -rwxr-xr-x | extras/prot_filter.py | 144 | 
1 files changed, 144 insertions, 0 deletions
diff --git a/extras/prot_filter.py b/extras/prot_filter.py new file mode 100755 index 000000000..7dccacf15 --- /dev/null +++ b/extras/prot_filter.py @@ -0,0 +1,144 @@ +#!/usr/bin/python + +""" +  Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com> +  This file is part of GlusterFS. + +  This file is licensed to you under your choice of the GNU Lesser +  General Public License, version 3 or any later version (LGPLv3 or +  later), or the GNU General Public License, version 2 (GPLv2), in all +  cases as published by the Free Software Foundation. +""" + +""" +  INSTRUCTIONS +  Put this in /usr/lib64/glusterfs/$version/filter to have it run automatically, +  or else you'll have to run it by hand every time you change the volume +  configuration.  Give it a list of volume names on which to enable the +  protection functionality; it will deliberately ignore client volfiles for +  other volumes, and all server volfiles.  It *will* include internal client +  volfiles such as those used for NFS or rebalance/self-heal; this is a +  deliberate choice so that it will catch deletions from those sources as well. +""" + +volume_list = [ "jdtest" ] + +import copy +import string +import sys +import types + +class Translator: +    def __init__ (self, name): +        self.name = name +        self.xl_type = "" +        self.opts = {} +        self.subvols = [] +        self.dumped = False +    def __repr__ (self): +        return "<Translator %s>" % self.name + +def load (path): +    # If it's a string, open it; otherwise, assume it's already a +    # file-like object (most notably from urllib*). +    if type(path) in types.StringTypes: +        fp = file(path,"r") +    else: +        fp = path +    all_xlators = {} +    xlator = None +    last_xlator = None +    while True: +        text = fp.readline() +        if text == "": +            break +        text = text.split() +        if not len(text): +            continue +        if text[0] == "volume": +            if xlator: +                raise RuntimeError, "nested volume definition" +            xlator = Translator(text[1]) +            continue +        if not xlator: +            raise RuntimeError, "text outside volume definition" +        if text[0] == "type": +            xlator.xl_type = text[1] +            continue +        if text[0] == "option": +            xlator.opts[text[1]] = string.join(text[2:]) +            continue +        if text[0] == "subvolumes": +            for sv in text[1:]: +                xlator.subvols.append(all_xlators[sv]) +            continue +        if text[0] == "end-volume": +            all_xlators[xlator.name] = xlator +            last_xlator = xlator +            xlator = None +            continue +        raise RuntimeError, "unrecognized keyword %s" % text[0] +    if xlator: +        raise RuntimeError, "unclosed volume definition" +    return all_xlators, last_xlator + +def generate (graph, last, stream=sys.stdout): +    for sv in last.subvols: +        if not sv.dumped: +            generate(graph,sv,stream) +            print >> stream, "" +            sv.dumped = True +    print >> stream, "volume %s" % last.name +    print >> stream, "    type %s" % last.xl_type +    for k, v in last.opts.iteritems(): +        print >> stream, "    option %s %s" % (k, v) +    if last.subvols: +        print >> stream, "    subvolumes %s" % string.join( +            [ sv.name for sv in last.subvols ]) +    print >> stream, "end-volume" + +def push_filter (graph, old_xl, filt_type, opts={}): +    new_type = "-" + filt_type.split("/")[1] +    old_type = "-" + old_xl.xl_type.split("/")[1] +    pos = old_xl.name.find(old_type) +    if pos >= 0: +        new_name = old_xl.name +        old_name = new_name[:pos] + new_type + new_name[len(old_type)+pos:] +    else: +        new_name = old_xl.name + old_type +        old_name = old_xl.name + new_type +    new_xl = Translator(new_name) +    new_xl.xl_type = old_xl.xl_type +    new_xl.opts = old_xl.opts +    new_xl.subvols = old_xl.subvols +    graph[new_xl.name] = new_xl +    old_xl.name = old_name +    old_xl.xl_type = filt_type +    old_xl.opts = opts +    old_xl.subvols = [new_xl] +    graph[old_xl.name] = old_xl + +if __name__ == "__main__": +    path = sys.argv[1] +    # Alow an override for debugging. +    for extra in sys.argv[2:]: +        volume_list.append(extra) +    graph, last = load(path) +    for v in volume_list: +        if graph.has_key(v): +            break +    else: +        print "No configured volumes found - aborting." +        sys.exit(0) +    for v in graph.values(): +        if v.xl_type == "cluster/distribute": +            push_filter(graph,v,"features/prot_dht") +        elif v.xl_type == "protocol/client": +            push_filter(graph,v,"features/prot_client") +    # We push debug/trace so that every fop gets a real frame, because DHT +    # gets confused if STACK_WIND_TAIL causes certain fops to be invoked +    # from anything other than a direct child. +    for v in graph.values(): +        if v.xl_type == "features/prot_client": +            push_filter(graph,v,"debug/trace") +    generate(graph,last,stream=open(path,"w"))  | 
