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 has(self, relpath):
81
self._ensure_relpath_is_child(relpath)
82
return TransportDecorator.has(self, relpath)
84
def list_dir(self, relpath):
85
self._ensure_relpath_is_child(relpath)
86
return TransportDecorator.list_dir(self, relpath)
88
def lock_read(self, relpath):
89
self._ensure_relpath_is_child(relpath)
90
return TransportDecorator.lock_read(self, relpath)
92
def lock_write(self, relpath):
93
self._ensure_relpath_is_child(relpath)
94
return TransportDecorator.lock_write(self, relpath)
96
def mkdir(self, relpath, mode=None):
97
self._ensure_relpath_is_child(relpath)
98
return TransportDecorator.mkdir(self, relpath, mode=mode)
100
def put_bytes(self, relpath, bytes, mode=None):
101
self._ensure_relpath_is_child(relpath)
102
return TransportDecorator.put_bytes(self, relpath, bytes, mode=mode)
104
def put_file(self, relpath, f, mode=None):
105
self._ensure_relpath_is_child(relpath)
106
return TransportDecorator.put_file(self, relpath, f, mode=mode)
108
def rename(self, rel_from, rel_to):
109
self._ensure_relpath_is_child(rel_from)
110
self._ensure_relpath_is_child(rel_to)
111
return TransportDecorator.rename(self, rel_from, rel_to)
113
def rmdir(self, relpath):
114
self._ensure_relpath_is_child(relpath)
115
return TransportDecorator.rmdir(self, relpath)
117
def stat(self, relpath):
118
self._ensure_relpath_is_child(relpath)
119
return TransportDecorator.stat(self, relpath)
122
class ChrootServer(DecoratorServer):
123
"""Server for the ReadonlyTransportDecorator for testing with."""
125
def get_decorator_class(self):
126
return ChrootTransportDecorator
21
from __future__ import absolute_import
23
from bzrlib.transport import (
29
class ChrootServer(pathfilter.PathFilteringServer):
30
"""User space 'chroot' facility.
32
The server's get_url returns the url for a chroot transport mapped to the
33
backing transport. The url is of the form chroot-xxx:/// so parent
34
directories of the backing transport are not visible. The chroot url will
35
not allow '..' sequences to result in requests to the chroot affecting
36
directories outside the backing transport.
38
PathFilteringServer does all the path sanitation needed to enforce a
39
chroot, so this is a simple subclass of PathFilteringServer that ignores
43
def __init__(self, backing_transport):
44
pathfilter.PathFilteringServer.__init__(self, backing_transport, None)
46
def _factory(self, url):
47
return ChrootTransport(self, url)
49
def start_server(self):
50
self.scheme = 'chroot-%d:///' % id(self)
51
register_transport(self.scheme, self._factory)
54
class ChrootTransport(pathfilter.PathFilteringTransport):
57
Please see ChrootServer for details.
60
def _filter(self, relpath):
61
# A simplified version of PathFilteringTransport's _filter that omits
62
# the call to self.server.filter_func.
63
return self._relpath_from_server_root(relpath)
129
66
def get_test_permutations():
130
67
"""Return the permutations to be used in testing."""
131
return [(ChrootTransportDecorator, ChrootServer),
68
from bzrlib.tests import test_server
69
return [(ChrootTransport, test_server.TestingChrootServer)]