1
# Copyright (C) 2006-2010 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
-- i.e. methods that operate directly on files and directories, rather than
21
higher-level concepts like branches and revisions.
23
These methods, plus 'hello' and 'get_bundle', are version 1 of the smart server
24
protocol, as implemented in bzr 0.11 and later.
27
from __future__ import absolute_import
31
from bzrlib import errors
32
from bzrlib import urlutils
33
from bzrlib.smart import request
36
def _deserialise_optional_mode(mode):
37
# XXX: FIXME this should be on the protocol object. Later protocol versions
38
# might serialise modes differently.
46
"""Is the VFS enabled ?
48
the VFS is disabled when the BZR_NO_SMART_VFS environment variable is set.
50
:return: True if it is enabled.
52
return not 'BZR_NO_SMART_VFS' in os.environ
55
class VfsRequest(request.SmartServerRequest):
56
"""Base class for VFS requests.
58
VFS requests are disabled if vfs_enabled() returns False.
61
def _check_enabled(self):
63
raise errors.DisabledMethod(self.__class__.__name__)
65
def translate_client_path(self, relpath):
66
# VFS requests are made with escaped paths so the escaping done in
67
# SmartServerRequest.translate_client_path leads to double escaping.
68
# Remove it here -- the fact that the result is still escaped means
69
# that the str() will not fail on valid input.
70
x = request.SmartServerRequest.translate_client_path(self, relpath)
71
return str(urlutils.unescape(x))
74
class HasRequest(VfsRequest):
76
def do(self, relpath):
77
relpath = self.translate_client_path(relpath)
78
r = self._backing_transport.has(relpath) and 'yes' or 'no'
79
return request.SuccessfulSmartServerResponse((r,))
82
class GetRequest(VfsRequest):
84
def do(self, relpath):
85
relpath = self.translate_client_path(relpath)
86
backing_bytes = self._backing_transport.get_bytes(relpath)
87
return request.SuccessfulSmartServerResponse(('ok',), backing_bytes)
90
class AppendRequest(VfsRequest):
92
def do(self, relpath, mode):
93
relpath = self.translate_client_path(relpath)
94
self._relpath = relpath
95
self._mode = _deserialise_optional_mode(mode)
97
def do_body(self, body_bytes):
98
old_length = self._backing_transport.append_bytes(
99
self._relpath, body_bytes, self._mode)
100
return request.SuccessfulSmartServerResponse(('appended', '%d' % old_length))
103
class DeleteRequest(VfsRequest):
105
def do(self, relpath):
106
relpath = self.translate_client_path(relpath)
107
self._backing_transport.delete(relpath)
108
return request.SuccessfulSmartServerResponse(('ok', ))
111
class IterFilesRecursiveRequest(VfsRequest):
113
def do(self, relpath):
114
if not relpath.endswith('/'):
116
relpath = self.translate_client_path(relpath)
117
transport = self._backing_transport.clone(relpath)
118
filenames = transport.iter_files_recursive()
119
return request.SuccessfulSmartServerResponse(('names',) + tuple(filenames))
122
class ListDirRequest(VfsRequest):
124
def do(self, relpath):
125
if not relpath.endswith('/'):
127
relpath = self.translate_client_path(relpath)
128
filenames = self._backing_transport.list_dir(relpath)
129
return request.SuccessfulSmartServerResponse(('names',) + tuple(filenames))
132
class MkdirRequest(VfsRequest):
134
def do(self, relpath, mode):
135
relpath = self.translate_client_path(relpath)
136
self._backing_transport.mkdir(relpath,
137
_deserialise_optional_mode(mode))
138
return request.SuccessfulSmartServerResponse(('ok',))
141
class MoveRequest(VfsRequest):
143
def do(self, rel_from, rel_to):
144
rel_from = self.translate_client_path(rel_from)
145
rel_to = self.translate_client_path(rel_to)
146
self._backing_transport.move(rel_from, rel_to)
147
return request.SuccessfulSmartServerResponse(('ok',))
150
class PutRequest(VfsRequest):
152
def do(self, relpath, mode):
153
relpath = self.translate_client_path(relpath)
154
self._relpath = relpath
155
self._mode = _deserialise_optional_mode(mode)
157
def do_body(self, body_bytes):
158
self._backing_transport.put_bytes(self._relpath, body_bytes, self._mode)
159
return request.SuccessfulSmartServerResponse(('ok',))
162
class PutNonAtomicRequest(VfsRequest):
164
def do(self, relpath, mode, create_parent, dir_mode):
165
relpath = self.translate_client_path(relpath)
166
self._relpath = relpath
167
self._dir_mode = _deserialise_optional_mode(dir_mode)
168
self._mode = _deserialise_optional_mode(mode)
169
# a boolean would be nicer XXX
170
self._create_parent = (create_parent == 'T')
172
def do_body(self, body_bytes):
173
self._backing_transport.put_bytes_non_atomic(self._relpath,
176
create_parent_dir=self._create_parent,
177
dir_mode=self._dir_mode)
178
return request.SuccessfulSmartServerResponse(('ok',))
181
class ReadvRequest(VfsRequest):
183
def do(self, relpath):
184
relpath = self.translate_client_path(relpath)
185
self._relpath = relpath
187
def do_body(self, body_bytes):
188
"""accept offsets for a readv request."""
189
offsets = self._deserialise_offsets(body_bytes)
190
backing_bytes = ''.join(bytes for offset, bytes in
191
self._backing_transport.readv(self._relpath, offsets))
192
return request.SuccessfulSmartServerResponse(('readv',), backing_bytes)
194
def _deserialise_offsets(self, text):
195
# XXX: FIXME this should be on the protocol object.
197
for line in text.split('\n'):
200
start, length = line.split(',')
201
offsets.append((int(start), int(length)))
205
class RenameRequest(VfsRequest):
207
def do(self, rel_from, rel_to):
208
rel_from = self.translate_client_path(rel_from)
209
rel_to = self.translate_client_path(rel_to)
210
self._backing_transport.rename(rel_from, rel_to)
211
return request.SuccessfulSmartServerResponse(('ok', ))
214
class RmdirRequest(VfsRequest):
216
def do(self, relpath):
217
relpath = self.translate_client_path(relpath)
218
self._backing_transport.rmdir(relpath)
219
return request.SuccessfulSmartServerResponse(('ok', ))
222
class StatRequest(VfsRequest):
224
def do(self, relpath):
225
if not relpath.endswith('/'):
227
relpath = self.translate_client_path(relpath)
228
stat = self._backing_transport.stat(relpath)
229
return request.SuccessfulSmartServerResponse(
230
('stat', str(stat.st_size), oct(stat.st_mode)))