~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

Update non_atomic_put to have a create_parent_dir flag

Show diffs side-by-side

added added

removed removed

Lines of Context:
581
581
                self._pump(f, fout)
582
582
            except (IOError, paramiko.SSHException), e:
583
583
                self._translate_io_exception(e, tmp_abspath)
 
584
            # XXX: This doesn't truly help like we would like it to.
 
585
            #      The problem is that openssh strips sticky bits. So while we
 
586
            #      can properly set group write permission, we lose the group
 
587
            #      sticky bit. So it is probably best to stop chmodding, and
 
588
            #      just tell users that they need to set the umask correctly.
 
589
            #      The attr.st_mode = mode, in _sftp_open_exclusive
 
590
            #      will handle when the user wants the final mode to be more 
 
591
            #      restrictive. And then we avoid a round trip. Unless 
 
592
            #      paramiko decides to expose an async chmod()
 
593
 
 
594
            # This is designed to chmod() right before we close.
 
595
            # Because we set_pipelined() earlier, theoretically we might 
 
596
            # avoid the round trip for fout.close()
584
597
            if mode is not None:
585
598
                self._sftp.chmod(tmp_abspath, mode)
586
599
            fout.close()
604
617
            # raise the original with its traceback if we can.
605
618
            raise
606
619
 
607
 
    def non_atomic_put(self, relpath, f, mode=None):
 
620
    def non_atomic_put(self, relpath, f, mode=None, create_parent_dir=False):
608
621
        """Copy the file-like object into the target location.
609
622
 
610
623
        This function is not strictly safe to use. It is only meant to
616
629
        :param f:       File-like object.
617
630
        :param mode:    Possible access permissions for new file.
618
631
                        None means do not set remote permissions.
 
632
        :param create_parent_dir: If we cannot create the target file because
 
633
                        the parent directory does not exist, go ahead and
 
634
                        create it, and then try again.
619
635
        """
620
636
        abspath = self._remote_path(relpath)
621
 
        path = self._sftp._adjust_cwd(abspath)
622
 
        attr = SFTPAttributes()
623
 
        if mode is not None:
624
 
            attr.st_mode = mode
625
 
        omode = (SFTP_FLAG_WRITE | SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC)
 
637
 
 
638
        # TODO: jam 20060816 paramiko doesn't publicly expose a way to
 
639
        #       set the file mode at create time. If it does, use it.
 
640
        #       But for now, we just chmod later anyway.
626
641
 
627
642
        fout = None
628
 
        try:
629
 
            t, msg = self._sftp._request(CMD_OPEN, path, omode, attr)
630
 
            if t != CMD_HANDLE:
631
 
                raise TransportError('Expected an SFTP handle')
632
 
            handle = msg.get_string()
633
 
            fout = SFTPFile(self._sftp, handle, 'wb', -1)
634
 
        except (paramiko.SSHException, IOError), e:
635
 
            self._translate_io_exception(e, abspath, ': unable to open',
636
 
                failure_exc=FileExists)
637
 
 
638
 
        try:
639
 
            fout.set_pipelined(True)
640
 
            self._pump(f, fout)
641
 
        except (IOError, paramiko.SSHException), e:
642
 
            self._translate_io_exception(e, tmp_abspath)
643
 
        # XXX: This doesn't truly help like we would like it to.
644
 
        #      The problem is that openssh strips sticky bits. So while we
645
 
        #      can properly set group write permission, we lose the group
646
 
        #      sticky bit. So it is probably best to stop chmodding, and
647
 
        #      just tell users that they need to set the umask correctly.
648
 
        #      The attr.st_mode = mode, will handle when the user wants the
649
 
        #      final mode to be more restrictive. And then we avoid a round 
650
 
        #      trip. Unless paramiko decides to expose an async chmod()
651
 
        #
652
 
        #      This is designed to chmod() right before we close.
653
 
        #      Because we set_pipelined() earlier, theoretically we might avoid
654
 
        #      The round trip for fout.close()
655
 
        if mode is not None:
656
 
            self._sftp.chmod(tmp_abspath, mode)
657
 
        fout.close()
 
643
        def _open_and_write_file():
 
644
            """Try to open the target file, raise error on failure"""
 
645
            try:
 
646
                fout = self._sftp.file(abspath, mode='wb')
 
647
                fout.set_pipelined(True)
 
648
                self._pump(f, fout)
 
649
            except (paramiko.SSHException, IOError), e:
 
650
                self._translate_io_exception(e, abspath, ': unable to open')
 
651
 
 
652
            # This is designed to chmod() right before we close.
 
653
            # Because we set_pipelined() earlier, theoretically we might 
 
654
            # avoid the round trip for fout.close()
 
655
            if mode is not None:
 
656
                self._sftp.chmod(tmp_abspath, mode)
 
657
            fout.close()
 
658
 
 
659
        if not create_parent_dir:
 
660
            _open_and_write_file()
 
661
            return
 
662
 
 
663
        # Try error handling to create the parent directory if we need to
 
664
        try:
 
665
            _open_and_write_file()
 
666
        except NoSuchFile:
 
667
            # Try to create the parent directory, and then go back to
 
668
            # writing the file
 
669
            parent_dir = os.path.dirname(abspath)
 
670
            try:
 
671
                self._sftp.mkdir(parent_dir)
 
672
            except (paramiko.SSHException, IOError), e:
 
673
                self._translate_io_exception(e, abspath, ': unable to open')
 
674
            _open_and_write_file()
658
675
 
659
676
    def iter_files_recursive(self):
660
677
        """Walk the relative paths of all files in this transport."""
1024
1041
        :param abspath: The remote absolute path where the file should be opened
1025
1042
        :param mode: The mode permissions bits for the new file
1026
1043
        """
 
1044
        # TODO: jam 20060816 Paramiko >= 1.6.2 (probably earlier) supports
 
1045
        #       using the 'x' flag to indicate SFTP_FLAG_EXCL.
 
1046
        #       However, there is no way to set the permission mode at open 
 
1047
        #       time using the sftp_client.file() functionality.
1027
1048
        path = self._sftp._adjust_cwd(abspath)
1028
1049
        # mutter('sftp abspath %s => %s', abspath, path)
1029
1050
        attr = SFTPAttributes()