~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
384
384
            # on to the next
385
385
            cur_coalesced = cur_coalesced_stack.next()
386
386
 
387
 
    def put(self, relpath, f, mode=None):
 
387
    def put_file(self, relpath, f, mode=None):
388
388
        """
389
 
        Copy the file-like or string object into the location.
 
389
        Copy the file-like object into the location.
390
390
 
391
391
        :param relpath: Location to put the contents, relative to base.
392
 
        :param f:       File-like or string object.
 
392
        :param f:       File-like object.
393
393
        :param mode: The final mode for the file
394
394
        """
395
395
        final_path = self._remote_path(relpath)
407
407
                self._pump(f, fout)
408
408
            except (IOError, paramiko.SSHException), e:
409
409
                self._translate_io_exception(e, tmp_abspath)
 
410
            # XXX: This doesn't truly help like we would like it to.
 
411
            #      The problem is that openssh strips sticky bits. So while we
 
412
            #      can properly set group write permission, we lose the group
 
413
            #      sticky bit. So it is probably best to stop chmodding, and
 
414
            #      just tell users that they need to set the umask correctly.
 
415
            #      The attr.st_mode = mode, in _sftp_open_exclusive
 
416
            #      will handle when the user wants the final mode to be more 
 
417
            #      restrictive. And then we avoid a round trip. Unless 
 
418
            #      paramiko decides to expose an async chmod()
 
419
 
 
420
            # This is designed to chmod() right before we close.
 
421
            # Because we set_pipelined() earlier, theoretically we might 
 
422
            # avoid the round trip for fout.close()
410
423
            if mode is not None:
411
424
                self._sftp.chmod(tmp_abspath, mode)
412
425
            fout.close()
430
443
            # raise the original with its traceback if we can.
431
444
            raise
432
445
 
 
446
    def _put_non_atomic_helper(self, relpath, writer, mode=None,
 
447
                               create_parent_dir=False,
 
448
                               dir_mode=None):
 
449
        abspath = self._remote_path(relpath)
 
450
 
 
451
        # TODO: jam 20060816 paramiko doesn't publicly expose a way to
 
452
        #       set the file mode at create time. If it does, use it.
 
453
        #       But for now, we just chmod later anyway.
 
454
 
 
455
        def _open_and_write_file():
 
456
            """Try to open the target file, raise error on failure"""
 
457
            fout = None
 
458
            try:
 
459
                try:
 
460
                    fout = self._sftp.file(abspath, mode='wb')
 
461
                    fout.set_pipelined(True)
 
462
                    writer(fout)
 
463
                except (paramiko.SSHException, IOError), e:
 
464
                    self._translate_io_exception(e, abspath,
 
465
                                                 ': unable to open')
 
466
 
 
467
                # This is designed to chmod() right before we close.
 
468
                # Because we set_pipelined() earlier, theoretically we might 
 
469
                # avoid the round trip for fout.close()
 
470
                if mode is not None:
 
471
                    self._sftp.chmod(abspath, mode)
 
472
            finally:
 
473
                if fout is not None:
 
474
                    fout.close()
 
475
 
 
476
        if not create_parent_dir:
 
477
            _open_and_write_file()
 
478
            return
 
479
 
 
480
        # Try error handling to create the parent directory if we need to
 
481
        try:
 
482
            _open_and_write_file()
 
483
        except NoSuchFile:
 
484
            # Try to create the parent directory, and then go back to
 
485
            # writing the file
 
486
            parent_dir = os.path.dirname(abspath)
 
487
            self._mkdir(parent_dir, dir_mode)
 
488
            _open_and_write_file()
 
489
 
 
490
    def put_file_non_atomic(self, relpath, f, mode=None,
 
491
                            create_parent_dir=False,
 
492
                            dir_mode=None):
 
493
        """Copy the file-like object into the target location.
 
494
 
 
495
        This function is not strictly safe to use. It is only meant to
 
496
        be used when you already know that the target does not exist.
 
497
        It is not safe, because it will open and truncate the remote
 
498
        file. So there may be a time when the file has invalid contents.
 
499
 
 
500
        :param relpath: The remote location to put the contents.
 
501
        :param f:       File-like object.
 
502
        :param mode:    Possible access permissions for new file.
 
503
                        None means do not set remote permissions.
 
504
        :param create_parent_dir: If we cannot create the target file because
 
505
                        the parent directory does not exist, go ahead and
 
506
                        create it, and then try again.
 
507
        """
 
508
        def writer(fout):
 
509
            self._pump(f, fout)
 
510
        self._put_non_atomic_helper(relpath, writer, mode=mode,
 
511
                                    create_parent_dir=create_parent_dir,
 
512
                                    dir_mode=dir_mode)
 
513
 
 
514
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
 
515
                             create_parent_dir=False,
 
516
                             dir_mode=None):
 
517
        def writer(fout):
 
518
            fout.write(bytes)
 
519
        self._put_non_atomic_helper(relpath, writer, mode=mode,
 
520
                                    create_parent_dir=create_parent_dir,
 
521
                                    dir_mode=dir_mode)
 
522
 
433
523
    def iter_files_recursive(self):
434
524
        """Walk the relative paths of all files in this transport."""
435
525
        queue = list(self.list_dir('.'))
442
532
            else:
443
533
                yield relpath
444
534
 
 
535
    def _mkdir(self, abspath, mode=None):
 
536
        if mode is None:
 
537
            local_mode = 0777
 
538
        else:
 
539
            local_mode = mode
 
540
        try:
 
541
            self._sftp.mkdir(abspath, local_mode)
 
542
            if mode is not None:
 
543
                self._sftp.chmod(abspath, mode=mode)
 
544
        except (paramiko.SSHException, IOError), e:
 
545
            self._translate_io_exception(e, abspath, ': unable to mkdir',
 
546
                failure_exc=FileExists)
 
547
 
445
548
    def mkdir(self, relpath, mode=None):
446
549
        """Create a directory at the given path."""
447
 
        path = self._remote_path(relpath)
448
 
        try:
449
 
            # In the paramiko documentation, it says that passing a mode flag 
450
 
            # will filtered against the server umask.
451
 
            # StubSFTPServer does not do this, which would be nice, because it is
452
 
            # what we really want :)
453
 
            # However, real servers do use umask, so we really should do it that way
454
 
            self._sftp.mkdir(path)
455
 
            if mode is not None:
456
 
                self._sftp.chmod(path, mode=mode)
457
 
        except (paramiko.SSHException, IOError), e:
458
 
            self._translate_io_exception(e, path, ': unable to mkdir',
459
 
                failure_exc=FileExists)
 
550
        self._mkdir(self._remote_path(relpath), mode=mode)
460
551
 
461
552
    def _translate_io_exception(self, e, path, more_info='', 
462
553
                                failure_exc=PathError):
474
565
        """
475
566
        # paramiko seems to generate detailless errors.
476
567
        self._translate_error(e, path, raise_generic=False)
477
 
        if hasattr(e, 'args'):
 
568
        if getattr(e, 'args', None) is not None:
478
569
            if (e.args == ('No such file or directory',) or
479
570
                e.args == ('No such file',)):
480
571
                raise NoSuchFile(path, str(e) + more_info)
484
575
            if (e.args == ('Failure',)):
485
576
                raise failure_exc(path, str(e) + more_info)
486
577
            mutter('Raising exception with args %s', e.args)
487
 
        if hasattr(e, 'errno'):
 
578
        if getattr(e, 'errno', None) is not None:
488
579
            mutter('Raising exception with errno %s', e.errno)
489
580
        raise e
490
581
 
491
 
    def append(self, relpath, f, mode=None):
 
582
    def append_file(self, relpath, f, mode=None):
492
583
        """
493
584
        Append the text in the file-like object into the final
494
585
        location.
660
751
        :param abspath: The remote absolute path where the file should be opened
661
752
        :param mode: The mode permissions bits for the new file
662
753
        """
 
754
        # TODO: jam 20060816 Paramiko >= 1.6.2 (probably earlier) supports
 
755
        #       using the 'x' flag to indicate SFTP_FLAG_EXCL.
 
756
        #       However, there is no way to set the permission mode at open 
 
757
        #       time using the sftp_client.file() functionality.
663
758
        path = self._sftp._adjust_cwd(abspath)
664
759
        # mutter('sftp abspath %s => %s', abspath, path)
665
760
        attr = SFTPAttributes()
677
772
            self._translate_io_exception(e, abspath, ': unable to open',
678
773
                failure_exc=FileExists)
679
774
 
 
775
    def _can_roundtrip_unix_modebits(self):
 
776
        if sys.platform == 'win32':
 
777
            # anyone else?
 
778
            return False
 
779
        else:
 
780
            return True
680
781
 
681
782
# ------------- server test implementation --------------
682
783
import threading