~bzr-pqm/bzr/bzr.dev

2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
1
# Copyright (C) 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
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
16
17
"""Implementation of Transport that prevents access to locations above a set
18
root.
19
"""
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
20
from urlparse import urlparse
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
21
22
from bzrlib import errors, urlutils
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
23
from bzrlib.transport import (
24
    get_transport,
25
    register_transport,
26
    Server,
27
    Transport,
28
    unregister_transport,
29
    )
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
30
from bzrlib.transport.decorator import TransportDecorator, DecoratorServer
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
31
from bzrlib.transport.memory import MemoryTransport
32
33
34
class ChrootServer(Server):
2379.2.3 by Robert Collins
Review feedback.
35
    """User space 'chroot' facility.
36
    
37
    The server's get_url returns the url for a chroot transport mapped to the
38
    backing transport. The url is of the form chroot-xxx:/// so parent
39
    directories of the backing transport are not visible. The chroot url will
40
    not allow '..' sequences to result in requests to the chroot affecting
41
    directories outside the backing transport.
42
    """
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
43
44
    def __init__(self, backing_transport):
45
        self.backing_transport = backing_transport
46
47
    def _factory(self, url):
48
        assert url.startswith(self.scheme)
49
        return ChrootTransport(self, url)
50
51
    def get_url(self):
52
        return self.scheme
53
54
    def setUp(self):
55
        self.scheme = 'chroot-%d:///' % id(self)
56
        register_transport(self.scheme, self._factory)
57
58
    def tearDown(self):
59
        unregister_transport(self.scheme, self._factory)
60
61
62
class ChrootTransport(Transport):
2379.2.3 by Robert Collins
Review feedback.
63
    """A ChrootTransport.
64
65
    Please see ChrootServer for details.
66
    """
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
67
68
    def __init__(self, server, base):
69
        self.server = server
70
        if not base.endswith('/'):
71
            base += '/'
72
        Transport.__init__(self, base)
73
        self.base_path = self.base[len(self.server.scheme)-1:]
74
        self.scheme = self.server.scheme
75
76
    def _call(self, methodname, relpath, *args):
77
        method = getattr(self.server.backing_transport, methodname)
78
        return method(self._safe_relpath(relpath), *args)
79
80
    def _safe_relpath(self, relpath):
81
        safe_relpath = self._combine_paths(self.base_path, relpath)
82
        assert safe_relpath.startswith('/')
83
        return safe_relpath[1:]
84
85
    # Transport methods
2018.5.54 by Andrew Bennetts
Fix ChrootTransportDecorator's abspath method to be consistent with its clone
86
    def abspath(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
87
        return self.scheme + self._safe_relpath(relpath)
2018.5.54 by Andrew Bennetts
Fix ChrootTransportDecorator's abspath method to be consistent with its clone
88
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
89
    def append_file(self, relpath, f, mode=None):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
90
        return self._call('append_file', relpath, f, mode)
91
2671.3.3 by Robert Collins
Add mode parameter to Transport.open_file_stream.
92
    def _can_roundtrip_unix_modebits(self):
93
        return self.server.backing_transport._can_roundtrip_unix_modebits()
94
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
95
    def clone(self, relpath):
96
        return ChrootTransport(self.server, self.abspath(relpath))
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
97
98
    def delete(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
99
        return self._call('delete', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
100
101
    def delete_tree(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
102
        return self._call('delete_tree', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
103
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
104
    def external_url(self):
105
        """See bzrlib.transport.Transport.external_url."""
106
        # Chroots, like MemoryTransport depend on in-process
107
        # state and thus the base cannot simply be handed out.
108
        # See the base class docstring for more details and
109
        # possible directions. For now we return the chrooted
110
        # url. 
111
        return self.server.backing_transport.external_url()
112
2164.2.15 by Vincent Ladeuil
Http redirections are not followed by default. Do not use hints
113
    def get(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
114
        return self._call('get', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
115
116
    def has(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
117
        return self._call('has', relpath)
118
119
    def iter_files_recursive(self):
120
        backing_transport = self.server.backing_transport.clone(
121
            self._safe_relpath('.'))
122
        return backing_transport.iter_files_recursive()
123
124
    def listable(self):
125
        return self.server.backing_transport.listable()
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
126
127
    def list_dir(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
128
        return self._call('list_dir', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
129
130
    def lock_read(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
131
        return self._call('lock_read', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
132
133
    def lock_write(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
134
        return self._call('lock_write', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
135
136
    def mkdir(self, relpath, mode=None):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
137
        return self._call('mkdir', relpath, mode)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
138
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
139
    def open_write_stream(self, relpath, mode=None):
140
        return self._call('open_write_stream', relpath, mode)
2671.3.2 by Robert Collins
Start open_file_stream logic.
141
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
142
    def put_file(self, relpath, f, mode=None):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
143
        return self._call('put_file', relpath, f, mode)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
144
145
    def rename(self, rel_from, rel_to):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
146
        return self._call('rename', rel_from, self._safe_relpath(rel_to))
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
147
148
    def rmdir(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
149
        return self._call('rmdir', relpath)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
150
151
    def stat(self, relpath):
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
152
        return self._call('stat', relpath)
153
154
155
class TestingChrootServer(ChrootServer):
156
157
    def __init__(self):
2379.2.2 by Robert Collins
Further test-usability for chroots.
158
        """TestingChrootServer is not usable until setUp is called."""
159
160
    def setUp(self, backing_server=None):
161
        """Setup the Chroot on backing_server."""
162
        if backing_server is not None:
163
            self.backing_transport = get_transport(backing_server.get_url())
164
        else:
165
            self.backing_transport = get_transport('.')
166
        ChrootServer.setUp(self)
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
167
168
169
def get_test_permutations():
170
    """Return the permutations to be used in testing."""
2018.5.104 by Andrew Bennetts
Completely rework chrooted transports.
171
    return [(ChrootTransport, TestingChrootServer),
2070.5.1 by Andrew Bennetts
Add ChrootTransportDecorator.
172
            ]