~launchpad-pqm/launchpad/devel

8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
3
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
4
"""An SFTP server that backs on to a special kind of Bazaar Transport.
5
6
The Bazaar Transport is special in two ways:
7
8
 1. It implements two methods `writeChunk` and `local_realPath` (see the
9
    `FatLocalTransport` class for a description of these)
10
 2. Every transport method returns Deferreds and does not block.
11
12
We call such a transport a "Twisted Transport".
13
"""
14
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
15
__metaclass__ = type
7483.2.27 by Jonathan Lange
Log the close of an SFTP session.
16
__all__ = [
10548.1.1 by Jonathan Lange
Move twistedsupport to lp.services
17
    'avatar_to_sftp_server',
7483.2.27 by Jonathan Lange
Log the close of an SFTP session.
18
    'TransportSFTPServer',
19
    ]
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
20
21
9949.3.1 by Aaron Bentley
Handle MemoryStat eccentricities in Launchpad itself.
22
from copy import copy
6125.4.8 by Jonathan Lange
Test error handling in sftp server
23
import errno
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
24
import os
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
25
import stat
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
26
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
27
from bzrlib import (
28
    errors as bzr_errors,
29
    osutils,
30
    urlutils,
31
    )
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
32
from bzrlib.transport.local import LocalTransport
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
33
from twisted.conch.interfaces import (
34
    ISFTPFile,
35
    ISFTPServer,
36
    )
37
from twisted.conch.ls import lsLine
6125.4.4 by Jonathan Lange
Get PermissionDenied errors converted into failures correctly
38
from twisted.conch.ssh import filetransfer
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
39
from twisted.internet import defer
40
from twisted.python import util
6125.7.24 by Jonathan Lange
Merge from twisted-sync-transport.
41
from zope.interface import implements
42
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
43
from lp.codehosting.vfs import (
44
    AsyncLaunchpadTransport,
45
    LaunchpadServer,
46
    )
14612.2.1 by William Grant
format-imports on lib/. So many imports.
47
from lp.services.config import config
11086.2.1 by James Westby
Move FileIsADirectory from codehosting.sftp to services.sshserver.
48
from lp.services.sshserver.sftp import FileIsADirectory
10548.1.1 by Jonathan Lange
Move twistedsupport to lp.services
49
from lp.services.twistedsupport import gatherResults
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
50
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
51
52
class FatLocalTransport(LocalTransport):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
53
    """A Bazaar transport that also implements writeChunk and local_realPath.
54
55
    We need these so that we can implement SFTP over a Bazaar transport.
56
    """
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
57
7213.10.6 by Jonathan Lange
Fix up a lot of the SFTP tests by unbreaking the underlying transport's
58
    def clone(self, offset=None):
59
        if offset is None:
60
            abspath = self.base
61
        else:
62
            abspath = self.abspath(offset)
63
        return FatLocalTransport(abspath)
64
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
65
    def writeChunk(self, name, offset, data):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
66
        """Write a chunk of data to file `name` at `offset`."""
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
67
        abspath = self._abspath(name)
68
        osutils.check_legal_path(abspath)
6125.4.8 by Jonathan Lange
Test error handling in sftp server
69
        try:
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
70
            chunk_file = os.open(abspath, os.O_CREAT | os.O_WRONLY)
71
        except OSError, e:
6125.4.8 by Jonathan Lange
Test error handling in sftp server
72
            if e.errno != errno.EISDIR:
73
                raise
74
            raise FileIsADirectory(name)
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
75
        os.lseek(chunk_file, offset, 0)
76
        os.write(chunk_file, data)
77
        os.close(chunk_file)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
78
79
    def local_realPath(self, path):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
80
        """Return the absolute path to `path`."""
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
81
        abspath = self._abspath(path)
6956.1.2 by Michael Hudson
fix realpath
82
        return urlutils.escape(os.path.realpath(abspath))
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
83
84
6125.4.8 by Jonathan Lange
Test error handling in sftp server
85
def with_sftp_error(func):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
86
    """Decorator used to translate Bazaar errors into SFTP errors.
87
88
    This assumes that the function being decorated returns a Deferred.
89
90
    See `TransportSFTPServer.translateError` for the details of the
91
    translation.
92
    """
6125.4.8 by Jonathan Lange
Test error handling in sftp server
93
    def decorator(*args, **kwargs):
94
        deferred = func(*args, **kwargs)
6125.4.10 by Jonathan Lange
Customize error when mkdir fails
95
        return deferred.addErrback(TransportSFTPServer.translateError,
96
                                   func.__name__)
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
97
    return util.mergeFunctionMetadata(func, decorator)
6125.4.8 by Jonathan Lange
Test error handling in sftp server
98
99
7013.5.4 by Michael Hudson
move DirectoryListing out of the openDirectory method
100
class DirectoryListing:
101
    """Class to satisfy openDirectory return interface.
102
103
    openDirectory returns an iterator -- with a `close` method.  Hence
104
    this class.
105
    """
106
107
    def __init__(self, entries):
108
        self.iter = iter(entries)
109
110
    def __iter__(self):
111
        return self
112
113
    def next(self):
114
        return self.iter.next()
115
116
    def close(self):
117
        # I can't believe we had to implement a whole class just to
118
        # have this do-nothing method (abentley).
119
        pass
120
121
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
122
class TransportSFTPFile:
123
    """An implementation of `ISFTPFile` that backs onto a Bazaar transport.
124
125
    The transport must be a Twisted Transport.
126
    """
127
128
    implements(ISFTPFile)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
129
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
130
    def __init__(self, transport, name, flags, server):
6382.6.14 by Jonathan Lange
Some fixes from review.
131
        self._unescaped_relpath = name
132
        self._escaped_path = urlutils.escape(self._unescaped_relpath)
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
133
        self._flags = flags
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
134
        self.transport = transport
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
135
        self._written = False
136
        self._server = server
137
138
    def _shouldAppend(self):
139
        """Is this file opened append?"""
140
        return bool(self._flags & filetransfer.FXF_APPEND)
141
142
    def _shouldCreate(self):
143
        """Should we create a file?"""
144
        # The Twisted VFS adapter creates a file when any of these flags are
145
        # set. It's possible that we only need to check for FXF_CREAT.
146
        create_mask = (
147
            filetransfer.FXF_WRITE | filetransfer.FXF_APPEND |
148
            filetransfer.FXF_CREAT)
149
        return bool(self._flags & create_mask)
150
151
    def _shouldTruncate(self):
152
        """Should we truncate the file?"""
153
        return (bool(self._flags & filetransfer.FXF_TRUNC)
154
                and not self._written)
155
156
    def _shouldWrite(self):
157
        """Is this file opened writable?"""
158
        write_mask = (filetransfer.FXF_WRITE | filetransfer.FXF_APPEND)
159
        return bool(self._flags & write_mask)
160
161
    def _truncateFile(self):
162
        """Truncate this file."""
163
        self._written = True
6382.6.11 by Jonathan Lange
Handle paths that need escaping in SFTPFile.
164
        return self.transport.put_bytes(self._escaped_path, '')
6125.7.42 by Jonathan Lange
Give a name to creating an empty file.
165
6125.4.8 by Jonathan Lange
Test error handling in sftp server
166
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
167
    def writeChunk(self, offset, data):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
168
        """See `ISFTPFile`."""
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
169
        if not self._shouldWrite():
170
            raise filetransfer.SFTPError(
171
                filetransfer.FX_PERMISSION_DENIED,
6382.6.14 by Jonathan Lange
Some fixes from review.
172
                "%r was opened read-only." % self._unescaped_relpath)
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
173
        if self._shouldTruncate():
174
            deferred = self._truncateFile()
175
        else:
176
            deferred = defer.succeed(None)
177
        self._written = True
178
        if self._shouldAppend():
179
            deferred.addCallback(
180
                lambda ignored:
6382.6.11 by Jonathan Lange
Handle paths that need escaping in SFTPFile.
181
                self.transport.append_bytes(self._escaped_path, data))
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
182
        else:
183
            deferred.addCallback(
184
                lambda ignored:
6382.6.11 by Jonathan Lange
Handle paths that need escaping in SFTPFile.
185
                self.transport.writeChunk(self._escaped_path, offset, data))
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
186
        return deferred
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
187
6125.4.8 by Jonathan Lange
Test error handling in sftp server
188
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
189
    def readChunk(self, offset, length):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
190
        """See `ISFTPFile`."""
6382.6.11 by Jonathan Lange
Handle paths that need escaping in SFTPFile.
191
        deferred = self.transport.readv(
192
            self._escaped_path, [(offset, length)])
6125.7.11 by Jonathan Lange
Create and register an adapter from LaunchpadAvatar to an SFTP server
193
        def get_first_chunk(read_things):
194
            return read_things.next()[1]
6956.1.4 by Michael Hudson
rearrange, add docstring
195
        def handle_short_read(failure):
196
            """Handle short reads by reading what was available.
197
198
            Doing things this way around, by trying to read all the data
199
            requested and then handling the short read error, might be a bit
200
            inefficient, but the bzrlib sftp transport doesn't read past the
201
            end of files, so we don't need to worry too much about performance
202
            here.
203
            """
204
            failure.trap(bzr_errors.ShortReadvError)
205
            return self.readChunk(failure.value.offset, failure.value.actual)
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
206
        deferred.addCallback(get_first_chunk)
6956.1.4 by Michael Hudson
rearrange, add docstring
207
        return deferred.addErrback(handle_short_read)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
208
209
    def setAttrs(self, attrs):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
210
        """See `ISFTPFile`.
211
212
        The Transport interface does not allow setting any attributes.
213
        """
6125.5.2 by Michael Hudson
mumble
214
        # XXX 2008-05-09 JonathanLange: This should at least raise an error,
215
        # not do nothing silently.
6382.6.14 by Jonathan Lange
Some fixes from review.
216
        return self._server.setAttrs(self._unescaped_relpath, attrs)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
217
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
218
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
219
    def getAttrs(self):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
220
        """See `ISFTPFile`."""
6382.6.14 by Jonathan Lange
Some fixes from review.
221
        return self._server.getAttrs(self._unescaped_relpath, False)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
222
223
    def close(self):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
224
        """See `ISFTPFile`."""
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
225
        if self._written or not self._shouldCreate():
226
            return defer.succeed(None)
227
228
        if self._shouldTruncate():
229
            return self._truncateFile()
230
6382.6.11 by Jonathan Lange
Handle paths that need escaping in SFTPFile.
231
        deferred = self.transport.has(self._escaped_path)
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
232
        def maybe_create_file(already_exists):
233
            if not already_exists:
234
                return self._truncateFile()
235
        return deferred.addCallback(maybe_create_file)
236
237
238
def _get_transport_for_dir(directory):
239
    url = urlutils.local_path_to_url(directory)
240
    return FatLocalTransport(url)
241
242
243
def avatar_to_sftp_server(avatar):
6406.3.7 by Jonathan Lange
Be clear about when we want the username and when we want the user id.
244
    user_id = avatar.user_id
9590.1.15 by Michael Hudson
sftp fixes
245
    branch_transport = _get_transport_for_dir(
7732.1.3 by Jonathan Lange
Get rid of the branchesdest config variable.
246
        config.codehosting.mirrored_branches_root)
9590.1.48 by Michael Hudson
a start at combining the puller and filesystem endpoints
247
    server = LaunchpadServer(
248
        avatar.codehosting_proxy, user_id, branch_transport)
10197.5.7 by Michael Hudson
fix some more stuff
249
    server.start_server()
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
250
    transport = AsyncLaunchpadTransport(server, server.get_url())
251
    return TransportSFTPServer(transport)
6125.7.24 by Jonathan Lange
Merge from twisted-sync-transport.
252
253
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
254
class TransportSFTPServer:
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
255
    """An implementation of `ISFTPServer` that backs onto a Bazaar transport.
256
257
    The transport must be a Twisted Transport.
258
    """
259
6125.7.24 by Jonathan Lange
Merge from twisted-sync-transport.
260
    implements(ISFTPServer)
261
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
262
    def __init__(self, transport):
263
        self.transport = transport
264
265
    def extendedRequest(self, extendedName, extendedData):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
266
        """See `ISFTPServer`."""
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
267
        raise NotImplementedError
268
269
    def makeLink(self, src, dest):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
270
        """See `ISFTPServer`."""
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
271
        raise NotImplementedError()
272
7013.5.7 by Michael Hudson
docstrings are nice
273
    def _stat_files_in_list(self, file_list, escaped_dir_path):
7013.5.10 by Michael Hudson
expand docstring
274
        """Stat the a list of files.
7013.5.7 by Michael Hudson
docstrings are nice
275
7013.5.10 by Michael Hudson
expand docstring
276
        :param file_list: The list of escaped file names.
277
        :param escaped_dir_path: The escaped path of the directory containing
278
            the files.
7013.5.7 by Michael Hudson
docstrings are nice
279
        :return: A Deferred which will be called back with the list of all the
280
            stat results.
281
        """
7013.5.5 by Michael Hudson
move things around a bit
282
        deferreds = []
283
        for filename in file_list:
7013.5.7 by Michael Hudson
docstrings are nice
284
            escaped_file_path = os.path.join(escaped_dir_path, filename)
7013.5.5 by Michael Hudson
move things around a bit
285
            deferreds.append(
286
                self.transport.stat(escaped_file_path))
287
        return gatherResults(deferreds)
288
7013.5.7 by Michael Hudson
docstrings are nice
289
    def _format_directory_entries(self, stat_results, filenames):
290
        """Produce entries suitable for returning from `openDirectory`.
291
292
        :param stat_results: A list of the results of calling `stat` on each
293
            file in filenames.
294
        :param filenames: The list of filenames to produce entries for.
295
        :return: An iterator of ``(shortname, longname, attributes)``.
296
        """
7013.5.5 by Michael Hudson
move things around a bit
297
        for stat_result, filename in zip(stat_results, filenames):
298
            shortname = urlutils.unescape(filename).encode('utf-8')
9949.3.1 by Aaron Bentley
Handle MemoryStat eccentricities in Launchpad itself.
299
            stat_result = copy(stat_result)
300
            for attribute in ['st_uid', 'st_gid', 'st_mtime', 'st_nlink']:
301
                if getattr(stat_result, attribute, None) is None:
302
                    setattr(stat_result, attribute, 0)
7013.5.5 by Michael Hudson
move things around a bit
303
            longname = lsLine(shortname, stat_result)
304
            attr_dict = self._translate_stat(stat_result)
305
            yield (shortname, longname, attr_dict)
306
6125.4.8 by Jonathan Lange
Test error handling in sftp server
307
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
308
    def openDirectory(self, path):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
309
        """See `ISFTPServer`."""
7013.5.2 by Michael Hudson
show a longline too
310
        escaped_path = urlutils.escape(path)
311
        deferred = self.transport.list_dir(escaped_path)
7013.5.7 by Michael Hudson
docstrings are nice
312
        def produce_entries_from_file_list(file_list):
7013.5.9 by Michael Hudson
lint!
313
            stats_deferred = self._stat_files_in_list(file_list, escaped_path)
314
            stats_deferred.addCallback(
7013.5.7 by Michael Hudson
docstrings are nice
315
                self._format_directory_entries, file_list)
7013.5.9 by Michael Hudson
lint!
316
            return stats_deferred
7013.5.7 by Michael Hudson
docstrings are nice
317
        return deferred.addCallback(
318
            produce_entries_from_file_list).addCallback(DirectoryListing)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
319
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
320
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
321
    def openFile(self, path, flags, attrs):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
322
        """See `ISFTPServer`."""
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
323
        directory = os.path.dirname(path)
324
        deferred = self.transport.stat(directory)
325
        def open_file(stat_result):
326
            if stat.S_ISDIR(stat_result.st_mode):
327
                return TransportSFTPFile(self.transport, path, flags, self)
328
            else:
329
                raise filetransfer.SFTPError(
330
                    filetransfer.FX_NO_SUCH_FILE, directory)
331
        return deferred.addCallback(open_file)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
332
333
    def readLink(self, path):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
334
        """See `ISFTPServer`."""
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
335
        raise NotImplementedError()
336
337
    def realPath(self, relpath):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
338
        """See `ISFTPServer`."""
6956.1.2 by Michael Hudson
fix realpath
339
        deferred = self.transport.local_realPath(urlutils.escape(relpath))
6956.1.3 by Michael Hudson
utf-8 is the default file system encoding in higher sftp versions, at least
340
        def unescape_path(path):
6956.1.2 by Michael Hudson
fix realpath
341
            unescaped_path = urlutils.unescape(path)
6956.1.3 by Michael Hudson
utf-8 is the default file system encoding in higher sftp versions, at least
342
            return unescaped_path.encode('utf-8')
343
        return deferred.addCallback(unescape_path)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
344
345
    def setAttrs(self, path, attrs):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
346
        """See `ISFTPServer`.
347
348
        This just delegates to TransportSFTPFile's implementation.
349
        """
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
350
        return defer.succeed(None)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
351
7013.5.2 by Michael Hudson
show a longline too
352
    def _translate_stat(self, stat_val):
7013.5.7 by Michael Hudson
docstrings are nice
353
        """Translate the stat result `stat_val` into an attributes dict.
354
355
        This is very like conch.ssh.unix.SFTPServerForUnixConchUser._getAttrs,
356
        but (a) that is private and (b) we use getattr() to access the
357
        attributes as not all the Bazaar transports return full stat results.
358
        """
7013.5.2 by Michael Hudson
show a longline too
359
        return {
360
            'size': getattr(stat_val, 'st_size', 0),
361
            'uid': getattr(stat_val, 'st_uid', 0),
362
            'gid': getattr(stat_val, 'st_gid', 0),
363
            'permissions': getattr(stat_val, 'st_mode', 0),
364
            'atime': getattr(stat_val, 'st_atime', 0),
365
            'mtime': getattr(stat_val, 'st_mtime', 0),
366
        }
367
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
368
    @with_sftp_error
6125.4.6 by Jonathan Lange
Fix getAttr tests
369
    def getAttrs(self, path, followLinks):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
370
        """See `ISFTPServer`.
371
372
        This just delegates to TransportSFTPFile's implementation.
373
        """
6382.6.8 by Jonathan Lange
Ensure that server tests pass when we use path segments that require url
374
        deferred = self.transport.stat(urlutils.escape(path))
7013.5.2 by Michael Hudson
show a longline too
375
        return deferred.addCallback(self._translate_stat)
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
376
377
    def gotVersion(self, otherVersion, extensionData):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
378
        """See `ISFTPServer`."""
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
379
        return {}
380
6125.4.8 by Jonathan Lange
Test error handling in sftp server
381
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
382
    def makeDirectory(self, path, attrs):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
383
        """See `ISFTPServer`."""
6382.6.8 by Jonathan Lange
Ensure that server tests pass when we use path segments that require url
384
        return self.transport.mkdir(
385
            urlutils.escape(path), attrs['permissions'])
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
386
6125.4.8 by Jonathan Lange
Test error handling in sftp server
387
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
388
    def removeDirectory(self, path):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
389
        """See `ISFTPServer`."""
6382.6.8 by Jonathan Lange
Ensure that server tests pass when we use path segments that require url
390
        return self.transport.rmdir(urlutils.escape(path))
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
391
6125.4.8 by Jonathan Lange
Test error handling in sftp server
392
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
393
    def removeFile(self, path):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
394
        """See `ISFTPServer`."""
6382.6.8 by Jonathan Lange
Ensure that server tests pass when we use path segments that require url
395
        return self.transport.delete(urlutils.escape(path))
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
396
6125.4.8 by Jonathan Lange
Test error handling in sftp server
397
    @with_sftp_error
6125.4.2 by Aaron Bentley
Implement a bzrlib transport to SFTP server adapter (with tests).
398
    def renameFile(self, oldpath, newpath):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
399
        """See `ISFTPServer`."""
6382.6.8 by Jonathan Lange
Ensure that server tests pass when we use path segments that require url
400
        return self.transport.rename(
401
            urlutils.escape(oldpath), urlutils.escape(newpath))
6125.4.4 by Jonathan Lange
Get PermissionDenied errors converted into failures correctly
402
6125.4.7 by Jonathan Lange
Get tests passing with AsyncTransport
403
    @staticmethod
6125.4.10 by Jonathan Lange
Customize error when mkdir fails
404
    def translateError(failure, func_name):
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
405
        """Translate Bazaar errors to `filetransfer.SFTPError` instances."""
6125.4.5 by Jonathan Lange
Handle permission denied, file exists, no such file, misc
406
        types_to_codes = {
407
            bzr_errors.PermissionDenied: filetransfer.FX_PERMISSION_DENIED,
6382.6.1 by Jonathan Lange
Merge revisions for reverting sftp fix, but don't actually revert it.
408
            bzr_errors.TransportNotPossible:
409
                filetransfer.FX_PERMISSION_DENIED,
6125.4.5 by Jonathan Lange
Handle permission denied, file exists, no such file, misc
410
            bzr_errors.NoSuchFile: filetransfer.FX_NO_SUCH_FILE,
411
            bzr_errors.FileExists: filetransfer.FX_FILE_ALREADY_EXISTS,
6125.4.9 by Jonathan Lange
Add translation of Directory Not Empty
412
            bzr_errors.DirectoryNotEmpty: filetransfer.FX_FAILURE,
9790.2.1 by Michael Hudson
test and fix
413
            bzr_errors.TransportError: filetransfer.FX_FAILURE,
6125.4.8 by Jonathan Lange
Test error handling in sftp server
414
            FileIsADirectory: filetransfer.FX_FILE_IS_A_DIRECTORY,
6125.4.5 by Jonathan Lange
Handle permission denied, file exists, no such file, misc
415
            }
6125.5.1 by Michael Hudson
documentation and properly Twisted tests.
416
        # Bazaar expects makeDirectory to fail with exactly the string "mkdir
417
        # failed".
6125.4.10 by Jonathan Lange
Customize error when mkdir fails
418
        names_to_messages = {
419
            'makeDirectory': 'mkdir failed',
420
            }
6125.4.5 by Jonathan Lange
Handle permission denied, file exists, no such file, misc
421
        try:
422
            sftp_code = types_to_codes[failure.type]
423
        except KeyError:
424
            failure.raiseException()
6125.4.10 by Jonathan Lange
Customize error when mkdir fails
425
        message = names_to_messages.get(func_name, failure.getErrorMessage())
426
        raise filetransfer.SFTPError(sftp_code, message)