/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011, 2016, 2017 Canonical Ltd
 
1
# Copyright (C) 2005-2011, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
35
35
import time
36
36
import warnings
37
37
 
38
 
from .. import (
 
38
from bzrlib import (
39
39
    config,
40
40
    debug,
41
41
    errors,
42
42
    urlutils,
43
43
    )
44
 
from ..errors import (FileExists,
 
44
from bzrlib.errors import (FileExists,
45
45
                           NoSuchFile,
46
46
                           TransportError,
47
47
                           LockError,
48
48
                           PathError,
49
49
                           ParamikoNotPresent,
50
50
                           )
51
 
from ..osutils import fancy_rename
52
 
from ..sixish import (
53
 
    zip,
54
 
    )
55
 
from ..trace import mutter, warning
56
 
from ..transport import (
 
51
from bzrlib.osutils import fancy_rename
 
52
from bzrlib.trace import mutter, warning
 
53
from bzrlib.transport import (
57
54
    FileFileStream,
58
55
    _file_streams,
59
56
    ssh,
75
72
 
76
73
try:
77
74
    import paramiko
78
 
except ImportError as e:
 
75
except ImportError, e:
79
76
    raise ParamikoNotPresent(e)
80
77
else:
81
78
    from paramiko.sftp import (SFTP_FLAG_WRITE, SFTP_FLAG_CREATE,
85
82
    from paramiko.sftp_file import SFTPFile
86
83
 
87
84
 
88
 
# GZ 2017-05-25: Some dark hackery to monkeypatch out issues with paramiko's
89
 
# Python 3 compatibility code. Replace broken b() and asbytes() code.
90
 
try:
91
 
    from paramiko.py3compat import b as _bad
92
 
    from paramiko.common import asbytes as _bad_asbytes
93
 
except ImportError:
94
 
    pass
95
 
else:
96
 
    def _b_for_broken_paramiko(s, encoding='utf8'):
97
 
        """Hacked b() that does not raise TypeError."""
98
 
        # https://github.com/paramiko/paramiko/issues/967
99
 
        if not isinstance(s, bytes):
100
 
            encode = getattr(s, 'encode', None)
101
 
            if encode is not None:
102
 
                return encode(encoding)
103
 
            # Would like to pass buffer objects along, but have to realise.
104
 
            tobytes = getattr(s, 'tobytes', None)
105
 
            if tobytes is not None:
106
 
                return tobytes()
107
 
        return s
108
 
 
109
 
    def _asbytes_for_broken_paramiko(s):
110
 
        """Hacked asbytes() that does not raise Exception."""
111
 
        # https://github.com/paramiko/paramiko/issues/968
112
 
        if not isinstance(s, bytes):
113
 
            encode = getattr(s, 'encode', None)
114
 
            if encode is not None:
115
 
                return encode('utf8')
116
 
            asbytes = getattr(s, 'asbytes', None)
117
 
            if asbytes is not None:
118
 
                return asbytes()
119
 
        return s
120
 
 
121
 
    _bad.__code__ = _b_for_broken_paramiko.__code__
122
 
    _bad_asbytes.__code__ = _asbytes_for_broken_paramiko.__code__
 
85
_paramiko_version = getattr(paramiko, '__version_info__', (0, 0, 0))
 
86
# don't use prefetch unless paramiko version >= 1.5.5 (there were bugs earlier)
 
87
_default_do_prefetch = (_paramiko_version >= (1, 5, 5))
123
88
 
124
89
 
125
90
class SFTPLock(object):
220
185
        """
221
186
        requests = self._get_requests()
222
187
        offset_iter = iter(self.original_offsets)
223
 
        cur_offset, cur_size = next(offset_iter)
 
188
        cur_offset, cur_size = offset_iter.next()
224
189
        # paramiko .readv() yields strings that are in the order of the requests
225
190
        # So we track the current request to know where the next data is
226
191
        # being returned from.
237
202
        # short readv.
238
203
        data_stream = itertools.chain(fp.readv(requests),
239
204
                                      itertools.repeat(None))
240
 
        for (start, length), data in zip(requests, data_stream):
 
205
        for (start, length), data in itertools.izip(requests, data_stream):
241
206
            if data is None:
242
207
                if cur_coalesced is not None:
243
208
                    raise errors.ShortReadvError(self.relpath,
294
259
                    input_start += cur_size
295
260
                    # Yield the requested data
296
261
                    yield cur_offset, cur_data
297
 
                    cur_offset, cur_size = next(offset_iter)
 
262
                    cur_offset, cur_size = offset_iter.next()
298
263
                # at this point, we've consumed as much of buffered as we can,
299
264
                # so break off the portion that we consumed
300
265
                if buffered_offset == len(buffered_data):
314
279
        if data_chunks:
315
280
            if 'sftp' in debug.debug_flags:
316
281
                mutter('SFTP readv left with %d out-of-order bytes',
317
 
                    sum(len(x[1]) for x in data_chunks))
 
282
                    sum(map(lambda x: len(x[1]), data_chunks)))
318
283
            # We've processed all the readv data, at this point, anything we
319
284
            # couldn't process is in data_chunks. This doesn't happen often, so
320
285
            # this code path isn't optimized
343
308
                        ' We expected %d bytes, but only found %d'
344
309
                        % (cur_size, len(data)))
345
310
                yield cur_offset, data
346
 
                cur_offset, cur_size = next(offset_iter)
 
311
                cur_offset, cur_size = offset_iter.next()
347
312
 
348
313
 
349
314
class SFTPTransport(ConnectedTransport):
350
315
    """Transport implementation for SFTP access."""
351
316
 
 
317
    _do_prefetch = _default_do_prefetch
352
318
    # TODO: jam 20060717 Conceivably these could be configurable, either
353
319
    #       by auto-tuning at run-time, or by a configuration (per host??)
354
320
    #       but the performance curve is pretty flat, so just going with
444
410
        try:
445
411
            path = self._remote_path(relpath)
446
412
            f = self._get_sftp().file(path, mode='rb')
447
 
            size = f.stat().st_size
448
 
            if getattr(f, 'prefetch', None) is not None:
449
 
                f.prefetch(size)
 
413
            if self._do_prefetch and (getattr(f, 'prefetch', None) is not None):
 
414
                f.prefetch()
450
415
            return f
451
 
        except (IOError, paramiko.SSHException) as e:
 
416
        except (IOError, paramiko.SSHException), e:
452
417
            self._translate_io_exception(e, path, ': error retrieving',
453
418
                failure_exc=errors.ReadError)
454
419
 
455
420
    def get_bytes(self, relpath):
456
421
        # reimplement this here so that we can report how many bytes came back
457
 
        with self.get(relpath) as f:
 
422
        f = self.get(relpath)
 
423
        try:
458
424
            bytes = f.read()
459
425
            self._report_activity(len(bytes), 'read')
460
426
            return bytes
 
427
        finally:
 
428
            f.close()
461
429
 
462
430
    def _readv(self, relpath, offsets):
463
431
        """See Transport.readv()"""
476
444
            if 'sftp' in debug.debug_flags:
477
445
                mutter('seek and read %s offsets', len(offsets))
478
446
            return self._seek_and_read(fp, offsets, relpath)
479
 
        except (IOError, paramiko.SSHException) as e:
 
447
        except (IOError, paramiko.SSHException), e:
480
448
            self._translate_io_exception(e, path, ': error retrieving')
481
449
 
482
450
    def recommended_page_size(self):
511
479
    def _put(self, abspath, f, mode=None):
512
480
        """Helper function so both put() and copy_abspaths can reuse the code"""
513
481
        tmp_abspath = '%s.tmp.%.9f.%d.%d' % (abspath, time.time(),
514
 
                        os.getpid(), random.randint(0, 0x7FFFFFFF))
 
482
                        os.getpid(), random.randint(0,0x7FFFFFFF))
515
483
        fout = self._sftp_open_exclusive(tmp_abspath, mode=mode)
516
484
        closed = False
517
485
        try:
518
486
            try:
519
487
                fout.set_pipelined(True)
520
488
                length = self._pump(f, fout)
521
 
            except (IOError, paramiko.SSHException) as e:
 
489
            except (IOError, paramiko.SSHException), e:
522
490
                self._translate_io_exception(e, tmp_abspath)
523
491
            # XXX: This doesn't truly help like we would like it to.
524
492
            #      The problem is that openssh strips sticky bits. So while we
539
507
            closed = True
540
508
            self._rename_and_overwrite(tmp_abspath, abspath)
541
509
            return length
542
 
        except Exception as e:
 
510
        except Exception, e:
543
511
            # If we fail, try to clean up the temporary file
544
512
            # before we throw the exception
545
513
            # but don't let another exception mess things up
574
542
                    fout = self._get_sftp().file(abspath, mode='wb')
575
543
                    fout.set_pipelined(True)
576
544
                    writer(fout)
577
 
                except (paramiko.SSHException, IOError) as e:
 
545
                except (paramiko.SSHException, IOError), e:
578
546
                    self._translate_io_exception(e, abspath,
579
547
                                                 ': unable to open')
580
548
 
653
621
 
654
622
    def _mkdir(self, abspath, mode=None):
655
623
        if mode is None:
656
 
            local_mode = 0o777
 
624
            local_mode = 0777
657
625
        else:
658
626
            local_mode = mode
659
627
        try:
667
635
                # the sgid bit is set, report a warning to the user
668
636
                # with the umask fix.
669
637
                stat = self._get_sftp().lstat(abspath)
670
 
                mode = mode & 0o777 # can't set special bits anyway
671
 
                if mode != stat.st_mode & 0o777:
672
 
                    if stat.st_mode & 0o6000:
 
638
                mode = mode & 0777 # can't set special bits anyway
 
639
                if mode != stat.st_mode & 0777:
 
640
                    if stat.st_mode & 06000:
673
641
                        warning('About to chmod %s over sftp, which will result'
674
642
                                ' in its suid or sgid bits being cleared.  If'
675
643
                                ' you want to preserve those bits, change your '
676
644
                                ' environment on the server to use umask 0%03o.'
677
 
                                % (abspath, 0o777 - mode))
 
645
                                % (abspath, 0777 - mode))
678
646
                    self._get_sftp().chmod(abspath, mode=mode)
679
 
        except (paramiko.SSHException, IOError) as e:
 
647
        except (paramiko.SSHException, IOError), e:
680
648
            self._translate_io_exception(e, abspath, ': unable to mkdir',
681
649
                failure_exc=FileExists)
682
650
 
700
668
        try:
701
669
            handle = self._get_sftp().file(abspath, mode='wb')
702
670
            handle.set_pipelined(True)
703
 
        except (paramiko.SSHException, IOError) as e:
 
671
        except (paramiko.SSHException, IOError), e:
704
672
            self._translate_io_exception(e, abspath,
705
673
                                         ': unable to open')
706
674
        _file_streams[self.abspath(relpath)] = handle
758
726
            result = fout.tell()
759
727
            self._pump(f, fout)
760
728
            return result
761
 
        except (IOError, paramiko.SSHException) as e:
 
729
        except (IOError, paramiko.SSHException), e:
762
730
            self._translate_io_exception(e, relpath, ': unable to append')
763
731
 
764
732
    def rename(self, rel_from, rel_to):
766
734
        try:
767
735
            self._get_sftp().rename(self._remote_path(rel_from),
768
736
                              self._remote_path(rel_to))
769
 
        except (IOError, paramiko.SSHException) as e:
 
737
        except (IOError, paramiko.SSHException), e:
770
738
            self._translate_io_exception(e, rel_from,
771
739
                    ': unable to rename to %r' % (rel_to))
772
740
 
780
748
            fancy_rename(abs_from, abs_to,
781
749
                         rename_func=sftp.rename,
782
750
                         unlink_func=sftp.remove)
783
 
        except (IOError, paramiko.SSHException) as e:
 
751
        except (IOError, paramiko.SSHException), e:
784
752
            self._translate_io_exception(e, abs_from,
785
753
                                         ': unable to rename to %r' % (abs_to))
786
754
 
795
763
        path = self._remote_path(relpath)
796
764
        try:
797
765
            self._get_sftp().remove(path)
798
 
        except (IOError, paramiko.SSHException) as e:
 
766
        except (IOError, paramiko.SSHException), e:
799
767
            self._translate_io_exception(e, path, ': unable to delete')
800
768
 
801
769
    def external_url(self):
802
 
        """See breezy.transport.Transport.external_url."""
 
770
        """See bzrlib.transport.Transport.external_url."""
803
771
        # the external path for SFTP is the base
804
772
        return self.base
805
773
 
819
787
        try:
820
788
            entries = self._get_sftp().listdir(path)
821
789
            self._report_activity(sum(map(len, entries)), 'read')
822
 
        except (IOError, paramiko.SSHException) as e:
 
790
        except (IOError, paramiko.SSHException), e:
823
791
            self._translate_io_exception(e, path, ': failed to list_dir')
824
792
        return [urlutils.escape(entry) for entry in entries]
825
793
 
828
796
        path = self._remote_path(relpath)
829
797
        try:
830
798
            return self._get_sftp().rmdir(path)
831
 
        except (IOError, paramiko.SSHException) as e:
 
799
        except (IOError, paramiko.SSHException), e:
832
800
            self._translate_io_exception(e, path, ': failed to rmdir')
833
801
 
834
802
    def stat(self, relpath):
836
804
        path = self._remote_path(relpath)
837
805
        try:
838
806
            return self._get_sftp().lstat(path)
839
 
        except (IOError, paramiko.SSHException) as e:
 
807
        except (IOError, paramiko.SSHException), e:
840
808
            self._translate_io_exception(e, path, ': unable to stat')
841
809
 
842
810
    def readlink(self, relpath):
844
812
        path = self._remote_path(relpath)
845
813
        try:
846
814
            return self._get_sftp().readlink(path)
847
 
        except (IOError, paramiko.SSHException) as e:
 
815
        except (IOError, paramiko.SSHException), e:
848
816
            self._translate_io_exception(e, path, ': unable to readlink')
849
817
 
850
818
    def symlink(self, source, link_name):
857
825
                    '%r: unable to create symlink to %r' % (link_name, source),
858
826
                    sftp_retval
859
827
                )
860
 
        except (IOError, paramiko.SSHException) as e:
 
828
        except (IOError, paramiko.SSHException), e:
861
829
            self._translate_io_exception(e, link_name,
862
830
                                         ': unable to create symlink to %r' % (source))
863
831
 
872
840
                self.path = path
873
841
            def unlock(self):
874
842
                pass
875
 
            def __exit__(self, exc_type, exc_val, exc_tb):
876
 
                return False
877
 
            def __enter__(self):
878
 
                pass
879
843
        return BogusLock(relpath)
880
844
 
881
845
    def lock_write(self, relpath):
922
886
                raise TransportError('Expected an SFTP handle')
923
887
            handle = msg.get_string()
924
888
            return SFTPFile(self._get_sftp(), handle, 'wb', -1)
925
 
        except (paramiko.SSHException, IOError) as e:
 
889
        except (paramiko.SSHException, IOError), e:
926
890
            self._translate_io_exception(e, abspath, ': unable to open',
927
891
                failure_exc=FileExists)
928
892
 
936
900
 
937
901
def get_test_permutations():
938
902
    """Return the permutations to be used in testing."""
939
 
    from ..tests import stub_sftp
 
903
    from bzrlib.tests import stub_sftp
940
904
    return [(SFTPTransport, stub_sftp.SFTPAbsoluteServer),
941
905
            (SFTPTransport, stub_sftp.SFTPHomeDirServer),
942
906
            (SFTPTransport, stub_sftp.SFTPSiblingAbsoluteServer),