1
# Copyright (C) 2006, 2007 Canonical Ltd
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""VFS operations for the smart server.
19
This module defines the smart server methods that are low-level file operations
20
higher-level concepts like branches and revisions.
22
These methods, plus 'hello' and 'get_bundle', are version 1 of the smart server
23
protocol, as implemented in bzr 0.11 and later.
28
from bzrlib import errors
29
from bzrlib import urlutils
30
from bzrlib.smart import request
33
def _deserialise_optional_mode(mode):
34
# XXX: FIXME this should be on the protocol object. Later protocol versions
35
# might serialise modes differently.
43
"""Is the VFS enabled ?
45
the VFS is disabled when the BZR_NO_SMART_VFS environment variable is set.
47
:return: True if it is enabled.
49
return not 'BZR_NO_SMART_VFS' in os.environ
52
class VfsRequest(request.SmartServerRequest):
53
"""Base class for VFS requests.
55
VFS requests are disabled if vfs_enabled() returns False.
58
def _check_enabled(self):
60
raise errors.DisabledMethod(self.__class__.__name__)
62
def translate_client_path(self, relpath):
63
# VFS requests are made with escaped paths so the escaping done in
64
# SmartServerRequest.translate_client_path leads to double escaping.
65
# Remove it here -- the fact that the result is still escaped means
66
# that the str() will not fail on valid input.
67
x = request.SmartServerRequest.translate_client_path(self, relpath)
68
return str(urlutils.unescape(x))
71
class HasRequest(VfsRequest):
73
def do(self, relpath):
74
relpath = self.translate_client_path(relpath)
75
r = self._backing_transport.has(relpath) and 'yes' or 'no'
76
return request.SuccessfulSmartServerResponse((r,))
79
class GetRequest(VfsRequest):
81
def do(self, relpath):
82
relpath = self.translate_client_path(relpath)
83
backing_bytes = self._backing_transport.get_bytes(relpath)
84
return request.SuccessfulSmartServerResponse(('ok',), backing_bytes)
87
class AppendRequest(VfsRequest):
89
def do(self, relpath, mode):
90
relpath = self.translate_client_path(relpath)
91
self._relpath = relpath
92
self._mode = _deserialise_optional_mode(mode)
94
def do_body(self, body_bytes):
95
old_length = self._backing_transport.append_bytes(
96
self._relpath, body_bytes, self._mode)
97
return request.SuccessfulSmartServerResponse(('appended', '%d' % old_length))
100
class DeleteRequest(VfsRequest):
102
def do(self, relpath):
103
relpath = self.translate_client_path(relpath)
104
self._backing_transport.delete(relpath)
105
return request.SuccessfulSmartServerResponse(('ok', ))
108
class IterFilesRecursiveRequest(VfsRequest):
110
def do(self, relpath):
111
if not relpath.endswith('/'):
113
relpath = self.translate_client_path(relpath)
114
transport = self._backing_transport.clone(relpath)
115
filenames = transport.iter_files_recursive()
116
return request.SuccessfulSmartServerResponse(('names',) + tuple(filenames))
119
class ListDirRequest(VfsRequest):
121
def do(self, relpath):
122
if not relpath.endswith('/'):
124
relpath = self.translate_client_path(relpath)
125
filenames = self._backing_transport.list_dir(relpath)
126
return request.SuccessfulSmartServerResponse(('names',) + tuple(filenames))
129
class MkdirRequest(VfsRequest):
131
def do(self, relpath, mode):
132
relpath = self.translate_client_path(relpath)
133
self._backing_transport.mkdir(relpath,
134
_deserialise_optional_mode(mode))
135
return request.SuccessfulSmartServerResponse(('ok',))
138
class MoveRequest(VfsRequest):
140
def do(self, rel_from, rel_to):
141
rel_from = self.translate_client_path(rel_from)
142
rel_to = self.translate_client_path(rel_to)
143
self._backing_transport.move(rel_from, rel_to)
144
return request.SuccessfulSmartServerResponse(('ok',))
147
class PutRequest(VfsRequest):
149
def do(self, relpath, mode):
150
relpath = self.translate_client_path(relpath)
151
self._relpath = relpath
152
self._mode = _deserialise_optional_mode(mode)
154
def do_body(self, body_bytes):
155
self._backing_transport.put_bytes(self._relpath, body_bytes, self._mode)
156
return request.SuccessfulSmartServerResponse(('ok',))
159
class PutNonAtomicRequest(VfsRequest):
161
def do(self, relpath, mode, create_parent, dir_mode):
162
relpath = self.translate_client_path(relpath)
163
self._relpath = relpath
164
self._dir_mode = _deserialise_optional_mode(dir_mode)
165
self._mode = _deserialise_optional_mode(mode)
166
# a boolean would be nicer XXX
167
self._create_parent = (create_parent == 'T')
169
def do_body(self, body_bytes):
170
self._backing_transport.put_bytes_non_atomic(self._relpath,
173
create_parent_dir=self._create_parent,
174
dir_mode=self._dir_mode)
175
return request.SuccessfulSmartServerResponse(('ok',))
178
class ReadvRequest(VfsRequest):
180
def do(self, relpath):
181
relpath = self.translate_client_path(relpath)
182
self._relpath = relpath
184
def do_body(self, body_bytes):
185
"""accept offsets for a readv request."""
186
offsets = self._deserialise_offsets(body_bytes)
187
backing_bytes = ''.join(bytes for offset, bytes in
188
self._backing_transport.readv(self._relpath, offsets))
189
return request.SuccessfulSmartServerResponse(('readv',), backing_bytes)
191
def _deserialise_offsets(self, text):
192
# XXX: FIXME this should be on the protocol object.
194
for line in text.split('\n'):
197
start, length = line.split(',')
198
offsets.append((int(start), int(length)))
202
class RenameRequest(VfsRequest):
204
def do(self, rel_from, rel_to):
205
rel_from = self.translate_client_path(rel_from)
206
rel_to = self.translate_client_path(rel_to)
207
self._backing_transport.rename(rel_from, rel_to)
208
return request.SuccessfulSmartServerResponse(('ok', ))
211
class RmdirRequest(VfsRequest):
213
def do(self, relpath):
214
relpath = self.translate_client_path(relpath)
215
self._backing_transport.rmdir(relpath)
216
return request.SuccessfulSmartServerResponse(('ok', ))
219
class StatRequest(VfsRequest):
221
def do(self, relpath):
222
if not relpath.endswith('/'):
224
relpath = self.translate_client_path(relpath)
225
stat = self._backing_transport.stat(relpath)
226
return request.SuccessfulSmartServerResponse(
227
('stat', str(stat.st_size), oct(stat.st_mode)))