~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/remote.py

Fix tracebacks caused by 'Permission denied' errors from a smart
        server (bug 278673),
        and refactor client-side smart error handling. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
    config,
29
29
    debug,
30
30
    errors,
 
31
    remote,
31
32
    trace,
32
33
    transport,
33
34
    urlutils,
143
144
        elif resp == ('no', ):
144
145
            return False
145
146
        else:
146
 
            self._translate_error(resp)
147
 
        raise errors.UnexpectedSmartServerResponse(resp)
 
147
            raise errors.UnexpectedSmartServerResponse(resp)
148
148
 
149
149
    def get_smart_client(self):
150
150
        return self._get_connection()
161
161
        return self._combine_paths(self._path, relpath)
162
162
 
163
163
    def _call(self, method, *args):
164
 
        try:
165
 
            resp = self._call2(method, *args)
166
 
        except errors.ErrorFromSmartServer, err:
167
 
            self._translate_error(err.error_tuple)
168
 
        self._translate_error(resp)
 
164
        resp = self._call2(method, *args)
 
165
        self._ensure_ok(resp)
169
166
 
170
167
    def _call2(self, method, *args):
171
168
        """Call a method on the remote server."""
172
169
        try:
173
170
            return self._client.call(method, *args)
174
171
        except errors.ErrorFromSmartServer, err:
175
 
            self._translate_error(err.error_tuple)
 
172
            self._translate_error(err)
176
173
 
177
174
    def _call_with_body_bytes(self, method, args, body):
178
175
        """Call a method on the remote server with body bytes."""
179
176
        try:
180
177
            return self._client.call_with_body_bytes(method, args, body)
181
178
        except errors.ErrorFromSmartServer, err:
182
 
            self._translate_error(err.error_tuple)
 
179
            self._translate_error(err)
183
180
 
184
181
    def has(self, relpath):
185
182
        """Indicate whether a remote file of the given name exists or not.
192
189
        elif resp == ('no', ):
193
190
            return False
194
191
        else:
195
 
            self._translate_error(resp)
 
192
            raise errors.UnexpectedSmartServerResponse(resp)
196
193
 
197
194
    def get(self, relpath):
198
195
        """Return file-like object reading the contents of a remote file.
206
203
        try:
207
204
            resp, response_handler = self._client.call_expecting_body('get', remote)
208
205
        except errors.ErrorFromSmartServer, err:
209
 
            self._translate_error(err.error_tuple, relpath)
 
206
            self._translate_error(err, relpath)
210
207
        if resp != ('ok', ):
211
208
            response_handler.cancel_read_body()
212
209
            raise errors.UnexpectedSmartServerResponse(resp)
221
218
    def mkdir(self, relpath, mode=None):
222
219
        resp = self._call2('mkdir', self._remote_path(relpath),
223
220
            self._serialise_optional_mode(mode))
224
 
        self._translate_error(resp)
225
221
 
226
222
    def open_write_stream(self, relpath, mode=None):
227
223
        """See Transport.open_write_stream."""
243
239
        resp = self._call_with_body_bytes('put',
244
240
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
245
241
            upload_contents)
246
 
        self._translate_error(resp)
 
242
        self._ensure_ok(resp)
247
243
        return len(upload_contents)
248
244
 
249
245
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
260
256
            (self._remote_path(relpath), self._serialise_optional_mode(mode),
261
257
             create_parent_str, self._serialise_optional_mode(dir_mode)),
262
258
            bytes)
263
 
        self._translate_error(resp)
 
259
        self._ensure_ok(resp)
264
260
 
265
261
    def put_file(self, relpath, upload_file, mode=None):
266
262
        # its not ideal to seek back, but currently put_non_atomic_file depends
290
286
            bytes)
291
287
        if resp[0] == 'appended':
292
288
            return int(resp[1])
293
 
        self._translate_error(resp)
 
289
        raise errors.UnexpectedSmartServerResponse(resp)
294
290
 
295
291
    def delete(self, relpath):
296
292
        resp = self._call2('delete', self._remote_path(relpath))
297
 
        self._translate_error(resp)
 
293
        self._ensure_ok(resp)
298
294
 
299
295
    def external_url(self):
300
296
        """See bzrlib.transport.Transport.external_url."""
322
318
                [(c.start, c.length) for c in coalesced])
323
319
            resp, response_handler = result
324
320
        except errors.ErrorFromSmartServer, err:
325
 
            self._translate_error(err.error_tuple)
 
321
            self._translate_error(err)
326
322
 
327
323
        if resp[0] != 'readv':
328
324
            # This should raise an exception
381
377
    def rmdir(self, relpath):
382
378
        resp = self._call('rmdir', self._remote_path(relpath))
383
379
 
384
 
    def _translate_error(self, resp, orig_path=None):
385
 
        """Raise an exception from a response"""
386
 
        if resp is None:
387
 
            what = None
388
 
        else:
389
 
            what = resp[0]
390
 
        if what == 'ok':
391
 
            return
392
 
        elif what == 'NoSuchFile':
393
 
            if orig_path is not None:
394
 
                error_path = orig_path
395
 
            else:
396
 
                error_path = resp[1]
397
 
            raise errors.NoSuchFile(error_path)
398
 
        elif what == 'error':
399
 
            raise errors.SmartProtocolError(unicode(resp[1]))
400
 
        elif what == 'FileExists':
401
 
            raise errors.FileExists(resp[1])
402
 
        elif what == 'DirectoryNotEmpty':
403
 
            raise errors.DirectoryNotEmpty(resp[1])
404
 
        elif what == 'ShortReadvError':
405
 
            raise errors.ShortReadvError(resp[1], int(resp[2]),
406
 
                                         int(resp[3]), int(resp[4]))
407
 
        elif what in ('UnicodeEncodeError', 'UnicodeDecodeError'):
408
 
            encoding = str(resp[1]) # encoding must always be a string
409
 
            val = resp[2]
410
 
            start = int(resp[3])
411
 
            end = int(resp[4])
412
 
            reason = str(resp[5]) # reason must always be a string
413
 
            if val.startswith('u:'):
414
 
                val = val[2:].decode('utf-8')
415
 
            elif val.startswith('s:'):
416
 
                val = val[2:].decode('base64')
417
 
            if what == 'UnicodeDecodeError':
418
 
                raise UnicodeDecodeError(encoding, val, start, end, reason)
419
 
            elif what == 'UnicodeEncodeError':
420
 
                raise UnicodeEncodeError(encoding, val, start, end, reason)
421
 
        elif what == "ReadOnlyError":
422
 
            raise errors.TransportNotPossible('readonly transport')
423
 
        elif what == "ReadError":
424
 
            if orig_path is not None:
425
 
                error_path = orig_path
426
 
            else:
427
 
                error_path = resp[1]
428
 
            raise errors.ReadError(error_path)
429
 
        elif what == "PermissionDenied":
430
 
            if orig_path is not None:
431
 
                error_path = orig_path
432
 
            else:
433
 
                error_path = resp[1]
434
 
            raise errors.PermissionDenied(error_path)
435
 
        else:
436
 
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
 
380
    def _ensure_ok(self, resp):
 
381
        if resp[0] != 'ok':
 
382
            raise errors.UnexpectedSmartServerResponse(resp)
 
383
        
 
384
    def _translate_error(self, err, orig_path=None):
 
385
        remote._translate_error(err, path=orig_path)
437
386
 
438
387
    def disconnect(self):
439
388
        self.get_smart_medium().disconnect()
442
391
        resp = self._call2('stat', self._remote_path(relpath))
443
392
        if resp[0] == 'stat':
444
393
            return _SmartStat(int(resp[1]), int(resp[2], 8))
445
 
        else:
446
 
            self._translate_error(resp)
 
394
        raise errors.UnexpectedSmartServerResponse(resp)
447
395
 
448
396
    ## def lock_read(self, relpath):
449
397
    ##     """Lock the given file for shared (read) access.
465
413
        resp = self._call2('list_dir', self._remote_path(relpath))
466
414
        if resp[0] == 'names':
467
415
            return [name.encode('ascii') for name in resp[1:]]
468
 
        else:
469
 
            self._translate_error(resp)
 
416
        raise errors.UnexpectedSmartServerResponse(resp)
470
417
 
471
418
    def iter_files_recursive(self):
472
419
        resp = self._call2('iter_files_recursive', self._remote_path(''))
473
420
        if resp[0] == 'names':
474
421
            return resp[1:]
475
 
        else:
476
 
            self._translate_error(resp)
 
422
        raise errors.UnexpectedSmartServerResponse(resp)
477
423
 
478
424
 
479
425
class RemoteTCPTransport(RemoteTransport):