13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Implementation of Transport that prevents access to locations above a set
21
from bzrlib import errors, urlutils
22
from bzrlib.transport.decorator import TransportDecorator, DecoratorServer
25
class ChrootTransportDecorator(TransportDecorator):
26
"""A decorator that can convert any transport to be chrooted.
28
This is requested via the 'chrooted+' prefix to get_transport().
31
def __init__(self, url, _decorated=None, chroot=None):
32
super(ChrootTransportDecorator, self).__init__(url,
33
_decorated=_decorated)
35
self.chroot_url = self._decorated.base
37
self.chroot_url = chroot
40
def _get_url_prefix(self):
41
"""Chroot transport decorators are invoked via 'chroot+'"""
44
def _ensure_relpath_is_child(self, relpath):
45
abspath = self.abspath(relpath)
46
chroot_base = self._get_url_prefix() + self.chroot_url
47
real_relpath = urlutils.relative_url(chroot_base, abspath)
48
if real_relpath == '..' or real_relpath.startswith('../'):
49
raise errors.PathNotChild(relpath, self.chroot_url)
52
def append_file(self, relpath, f, mode=None):
53
self._ensure_relpath_is_child(relpath)
54
return TransportDecorator.append_file(self, relpath, f, mode=mode)
56
def append_bytes(self, relpath, bytes, mode=None):
57
self._ensure_relpath_is_child(relpath)
58
return TransportDecorator.append_bytes(self, relpath, bytes, mode=mode)
60
def clone(self, offset=None):
61
self._ensure_relpath_is_child(offset)
62
return TransportDecorator.clone(self, offset)
64
def delete(self, relpath):
65
self._ensure_relpath_is_child(relpath)
66
return TransportDecorator.delete(self, relpath)
68
def delete_tree(self, relpath):
69
self._ensure_relpath_is_child(relpath)
70
return TransportDecorator.delete_tree(self, relpath)
72
def get(self, relpath):
73
self._ensure_relpath_is_child(relpath)
74
return TransportDecorator.get(self, relpath)
76
def get_bytes(self, relpath):
77
self._ensure_relpath_is_child(relpath)
78
return TransportDecorator.get_bytes(self, relpath)
80
def get(self, relpath):
81
self._ensure_relpath_is_child(relpath)
82
return TransportDecorator.get(self, relpath)
84
def has(self, relpath):
85
self._ensure_relpath_is_child(relpath)
86
return TransportDecorator.has(self, relpath)
88
def list_dir(self, relpath):
89
self._ensure_relpath_is_child(relpath)
90
return TransportDecorator.list_dir(self, relpath)
92
def lock_read(self, relpath):
93
self._ensure_relpath_is_child(relpath)
94
return TransportDecorator.lock_read(self, relpath)
96
def lock_write(self, relpath):
97
self._ensure_relpath_is_child(relpath)
98
return TransportDecorator.lock_write(self, relpath)
100
def mkdir(self, relpath, mode=None):
101
self._ensure_relpath_is_child(relpath)
102
return TransportDecorator.mkdir(self, relpath, mode=mode)
104
def put_bytes(self, relpath, bytes, mode=None):
105
self._ensure_relpath_is_child(relpath)
106
return TransportDecorator.put_bytes(self, relpath, bytes, mode=mode)
108
def put_file(self, relpath, f, mode=None):
109
self._ensure_relpath_is_child(relpath)
110
return TransportDecorator.put_file(self, relpath, f, mode=mode)
112
def rename(self, rel_from, rel_to):
113
self._ensure_relpath_is_child(rel_from)
114
self._ensure_relpath_is_child(rel_to)
115
return TransportDecorator.rename(self, rel_from, rel_to)
117
def rmdir(self, relpath):
118
self._ensure_relpath_is_child(relpath)
119
return TransportDecorator.rmdir(self, relpath)
121
def stat(self, relpath):
122
self._ensure_relpath_is_child(relpath)
123
return TransportDecorator.stat(self, relpath)
126
class ChrootServer(DecoratorServer):
127
"""Server for the ReadonlyTransportDecorator for testing with."""
129
def get_decorator_class(self):
130
return ChrootTransportDecorator
21
from bzrlib.transport import (
27
class ChrootServer(pathfilter.PathFilteringServer):
28
"""User space 'chroot' facility.
30
The server's get_url returns the url for a chroot transport mapped to the
31
backing transport. The url is of the form chroot-xxx:/// so parent
32
directories of the backing transport are not visible. The chroot url will
33
not allow '..' sequences to result in requests to the chroot affecting
34
directories outside the backing transport.
36
PathFilteringServer does all the path sanitation needed to enforce a
37
chroot, so this is a simple subclass of PathFilteringServer that ignores
41
def __init__(self, backing_transport):
42
pathfilter.PathFilteringServer.__init__(self, backing_transport, None)
44
def _factory(self, url):
45
return ChrootTransport(self, url)
47
def start_server(self):
48
self.scheme = 'chroot-%d:///' % id(self)
49
register_transport(self.scheme, self._factory)
52
class ChrootTransport(pathfilter.PathFilteringTransport):
55
Please see ChrootServer for details.
58
def _filter(self, relpath):
59
# A simplified version of PathFilteringTransport's _filter that omits
60
# the call to self.server.filter_func.
61
return self._relpath_from_server_root(relpath)
133
64
def get_test_permutations():
134
65
"""Return the permutations to be used in testing."""
135
return [(ChrootTransportDecorator, ChrootServer),
66
from bzrlib.tests import test_server
67
return [(ChrootTransport, test_server.TestingChrootServer)]