1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 from binascii import hexlify
21 import errno
22 import os
23 import stat
24 import threading
25 import time
26 import weakref
27 from paramiko import util
28 from paramiko.channel import Channel
29 from paramiko.message import Message
30 from paramiko.common import INFO, DEBUG, o777
31 from paramiko.py3compat import bytestring, b, u, long, string_types, bytes_types
32 from paramiko.sftp import BaseSFTP, CMD_OPENDIR, CMD_HANDLE, SFTPError, CMD_READDIR, \
33 CMD_NAME, CMD_CLOSE, SFTP_FLAG_READ, SFTP_FLAG_WRITE, SFTP_FLAG_CREATE, \
34 SFTP_FLAG_TRUNC, SFTP_FLAG_APPEND, SFTP_FLAG_EXCL, CMD_OPEN, CMD_REMOVE, \
35 CMD_RENAME, CMD_MKDIR, CMD_RMDIR, CMD_STAT, CMD_ATTRS, CMD_LSTAT, \
36 CMD_SYMLINK, CMD_SETSTAT, CMD_READLINK, CMD_REALPATH, CMD_STATUS, SFTP_OK, \
37 SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED
38
39 from paramiko.sftp_attr import SFTPAttributes
40 from paramiko.ssh_exception import SSHException
41 from paramiko.sftp_file import SFTPFile
42
43
45 """
46 decode a string as ascii or utf8 if possible (as required by the sftp
47 protocol). if neither works, just return a byte string because the server
48 probably doesn't know the filename's encoding.
49 """
50 try:
51 return s.encode('ascii')
52 except (UnicodeError, AttributeError):
53 try:
54 return s.decode('utf-8')
55 except UnicodeError:
56 return s
57
58 b_slash = b'/'
59
60
62 """
63 SFTP client object.
64
65 Used to open an SFTP session across an open SSH `.Transport` and perform
66 remote file operations.
67 """
69 """
70 Create an SFTP client from an existing `.Channel`. The channel
71 should already have requested the ``"sftp"`` subsystem.
72
73 An alternate way to create an SFTP client context is by using
74 `from_transport`.
75
76 :param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem
77
78 :raises SSHException: if there's an exception while negotiating
79 sftp
80 """
81 BaseSFTP.__init__(self)
82 self.sock = sock
83 self.ultra_debug = False
84 self.request_number = 1
85
86 self._lock = threading.Lock()
87 self._cwd = None
88
89 self._expecting = weakref.WeakValueDictionary()
90 if type(sock) is Channel:
91
92 transport = self.sock.get_transport()
93 self.logger = util.get_logger(transport.get_log_channel() + '.sftp')
94 self.ultra_debug = transport.get_hexdump()
95 try:
96 server_version = self._send_version()
97 except EOFError:
98 raise SSHException('EOF during negotiation')
99 self._log(INFO, 'Opened sftp connection (server version %d)' % server_version)
100
102 """
103 Create an SFTP client channel from an open `.Transport`.
104
105 :param .Transport t: an open `.Transport` which is already authenticated
106 :return:
107 a new `.SFTPClient` object, referring to an sftp session (channel)
108 across the transport
109 """
110 chan = t.open_session()
111 if chan is None:
112 return None
113 chan.invoke_subsystem('sftp')
114 return cls(chan)
115 from_transport = classmethod(from_transport)
116
117 - def _log(self, level, msg, *args):
118 if isinstance(msg, list):
119 for m in msg:
120 self._log(level, m, *args)
121 else:
122
123 msg = msg.replace('%','%%')
124 super(SFTPClient, self)._log(level, "[chan %s] " + msg, *([self.sock.get_name()] + list(args)))
125
127 """
128 Close the SFTP session and its underlying channel.
129
130 .. versionadded:: 1.4
131 """
132 self._log(INFO, 'sftp session closed.')
133 self.sock.close()
134
136 """
137 Return the underlying `.Channel` object for this SFTP session. This
138 might be useful for doing things like setting a timeout on the channel.
139
140 .. versionadded:: 1.7.1
141 """
142 return self.sock
143
145 """
146 Return a list containing the names of the entries in the given ``path``.
147
148 The list is in arbitrary order. It does not include the special
149 entries ``'.'`` and ``'..'`` even if they are present in the folder.
150 This method is meant to mirror ``os.listdir`` as closely as possible.
151 For a list of full `.SFTPAttributes` objects, see `listdir_attr`.
152
153 :param str path: path to list (defaults to ``'.'``)
154 """
155 return [f.filename for f in self.listdir_attr(path)]
156
158 """
159 Return a list containing `.SFTPAttributes` objects corresponding to
160 files in the given ``path``. The list is in arbitrary order. It does
161 not include the special entries ``'.'`` and ``'..'`` even if they are
162 present in the folder.
163
164 The returned `.SFTPAttributes` objects will each have an additional
165 field: ``longname``, which may contain a formatted string of the file's
166 attributes, in unix format. The content of this string will probably
167 depend on the SFTP server implementation.
168
169 :param str path: path to list (defaults to ``'.'``)
170 :return: list of `.SFTPAttributes` objects
171
172 .. versionadded:: 1.2
173 """
174 path = self._adjust_cwd(path)
175 self._log(DEBUG, 'listdir(%r)' % path)
176 t, msg = self._request(CMD_OPENDIR, path)
177 if t != CMD_HANDLE:
178 raise SFTPError('Expected handle')
179 handle = msg.get_binary()
180 filelist = []
181 while True:
182 try:
183 t, msg = self._request(CMD_READDIR, handle)
184 except EOFError:
185
186 break
187 if t != CMD_NAME:
188 raise SFTPError('Expected name response')
189 count = msg.get_int()
190 for i in range(count):
191 filename = msg.get_text()
192 longname = msg.get_text()
193 attr = SFTPAttributes._from_msg(msg, filename, longname)
194 if (filename != '.') and (filename != '..'):
195 filelist.append(attr)
196 self._request(CMD_CLOSE, handle)
197 return filelist
198
199 - def open(self, filename, mode='r', bufsize=-1):
200 """
201 Open a file on the remote server. The arguments are the same as for
202 Python's built-in `python:file` (aka `python:open`). A file-like
203 object is returned, which closely mimics the behavior of a normal
204 Python file object, including the ability to be used as a context
205 manager.
206
207 The mode indicates how the file is to be opened: ``'r'`` for reading,
208 ``'w'`` for writing (truncating an existing file), ``'a'`` for
209 appending, ``'r+'`` for reading/writing, ``'w+'`` for reading/writing
210 (truncating an existing file), ``'a+'`` for reading/appending. The
211 Python ``'b'`` flag is ignored, since SSH treats all files as binary.
212 The ``'U'`` flag is supported in a compatible way.
213
214 Since 1.5.2, an ``'x'`` flag indicates that the operation should only
215 succeed if the file was created and did not previously exist. This has
216 no direct mapping to Python's file flags, but is commonly known as the
217 ``O_EXCL`` flag in posix.
218
219 The file will be buffered in standard Python style by default, but
220 can be altered with the ``bufsize`` parameter. ``0`` turns off
221 buffering, ``1`` uses line buffering, and any number greater than 1
222 (``>1``) uses that specific buffer size.
223
224 :param str filename: name of the file to open
225 :param str mode: mode (Python-style) to open in
226 :param int bufsize: desired buffering (-1 = default buffer size)
227 :return: an `.SFTPFile` object representing the open file
228
229 :raises IOError: if the file could not be opened.
230 """
231 filename = self._adjust_cwd(filename)
232 self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
233 imode = 0
234 if ('r' in mode) or ('+' in mode):
235 imode |= SFTP_FLAG_READ
236 if ('w' in mode) or ('+' in mode) or ('a' in mode):
237 imode |= SFTP_FLAG_WRITE
238 if 'w' in mode:
239 imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC
240 if 'a' in mode:
241 imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND
242 if 'x' in mode:
243 imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL
244 attrblock = SFTPAttributes()
245 t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
246 if t != CMD_HANDLE:
247 raise SFTPError('Expected handle')
248 handle = msg.get_binary()
249 self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle)))
250 return SFTPFile(self, handle, mode, bufsize)
251
252
253 file = open
254
256 """
257 Remove the file at the given path. This only works on files; for
258 removing folders (directories), use `rmdir`.
259
260 :param str path: path (absolute or relative) of the file to remove
261
262 :raises IOError: if the path refers to a folder (directory)
263 """
264 path = self._adjust_cwd(path)
265 self._log(DEBUG, 'remove(%r)' % path)
266 self._request(CMD_REMOVE, path)
267
268 unlink = remove
269
270 - def rename(self, oldpath, newpath):
271 """
272 Rename a file or folder from ``oldpath`` to ``newpath``.
273
274 :param str oldpath: existing name of the file or folder
275 :param str newpath: new name for the file or folder
276
277 :raises IOError: if ``newpath`` is a folder, or something else goes
278 wrong
279 """
280 oldpath = self._adjust_cwd(oldpath)
281 newpath = self._adjust_cwd(newpath)
282 self._log(DEBUG, 'rename(%r, %r)' % (oldpath, newpath))
283 self._request(CMD_RENAME, oldpath, newpath)
284
286 """
287 Create a folder (directory) named ``path`` with numeric mode ``mode``.
288 The default mode is 0777 (octal). On some systems, mode is ignored.
289 Where it is used, the current umask value is first masked out.
290
291 :param str path: name of the folder to create
292 :param int mode: permissions (posix-style) for the newly-created folder
293 """
294 path = self._adjust_cwd(path)
295 self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
296 attr = SFTPAttributes()
297 attr.st_mode = mode
298 self._request(CMD_MKDIR, path, attr)
299
301 """
302 Remove the folder named ``path``.
303
304 :param str path: name of the folder to remove
305 """
306 path = self._adjust_cwd(path)
307 self._log(DEBUG, 'rmdir(%r)' % path)
308 self._request(CMD_RMDIR, path)
309
310 - def stat(self, path):
311 """
312 Retrieve information about a file on the remote system. The return
313 value is an object whose attributes correspond to the attributes of
314 Python's ``stat`` structure as returned by ``os.stat``, except that it
315 contains fewer fields. An SFTP server may return as much or as little
316 info as it wants, so the results may vary from server to server.
317
318 Unlike a Python `python:stat` object, the result may not be accessed as
319 a tuple. This is mostly due to the author's slack factor.
320
321 The fields supported are: ``st_mode``, ``st_size``, ``st_uid``,
322 ``st_gid``, ``st_atime``, and ``st_mtime``.
323
324 :param str path: the filename to stat
325 :return:
326 an `.SFTPAttributes` object containing attributes about the given
327 file
328 """
329 path = self._adjust_cwd(path)
330 self._log(DEBUG, 'stat(%r)' % path)
331 t, msg = self._request(CMD_STAT, path)
332 if t != CMD_ATTRS:
333 raise SFTPError('Expected attributes')
334 return SFTPAttributes._from_msg(msg)
335
337 """
338 Retrieve information about a file on the remote system, without
339 following symbolic links (shortcuts). This otherwise behaves exactly
340 the same as `stat`.
341
342 :param str path: the filename to stat
343 :return:
344 an `.SFTPAttributes` object containing attributes about the given
345 file
346 """
347 path = self._adjust_cwd(path)
348 self._log(DEBUG, 'lstat(%r)' % path)
349 t, msg = self._request(CMD_LSTAT, path)
350 if t != CMD_ATTRS:
351 raise SFTPError('Expected attributes')
352 return SFTPAttributes._from_msg(msg)
353
355 """
356 Create a symbolic link (shortcut) of the ``source`` path at
357 ``destination``.
358
359 :param str source: path of the original file
360 :param str dest: path of the newly created symlink
361 """
362 dest = self._adjust_cwd(dest)
363 self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
364 source = bytestring(source)
365 self._request(CMD_SYMLINK, source, dest)
366
367 - def chmod(self, path, mode):
368 """
369 Change the mode (permissions) of a file. The permissions are
370 unix-style and identical to those used by Python's `os.chmod`
371 function.
372
373 :param str path: path of the file to change the permissions of
374 :param int mode: new permissions
375 """
376 path = self._adjust_cwd(path)
377 self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
378 attr = SFTPAttributes()
379 attr.st_mode = mode
380 self._request(CMD_SETSTAT, path, attr)
381
382 - def chown(self, path, uid, gid):
383 """
384 Change the owner (``uid``) and group (``gid``) of a file. As with
385 Python's `os.chown` function, you must pass both arguments, so if you
386 only want to change one, use `stat` first to retrieve the current
387 owner and group.
388
389 :param str path: path of the file to change the owner and group of
390 :param int uid: new owner's uid
391 :param int gid: new group id
392 """
393 path = self._adjust_cwd(path)
394 self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
395 attr = SFTPAttributes()
396 attr.st_uid, attr.st_gid = uid, gid
397 self._request(CMD_SETSTAT, path, attr)
398
399 - def utime(self, path, times):
400 """
401 Set the access and modified times of the file specified by ``path``. If
402 ``times`` is ``None``, then the file's access and modified times are set
403 to the current time. Otherwise, ``times`` must be a 2-tuple of numbers,
404 of the form ``(atime, mtime)``, which is used to set the access and
405 modified times, respectively. This bizarre API is mimicked from Python
406 for the sake of consistency -- I apologize.
407
408 :param str path: path of the file to modify
409 :param tuple times:
410 ``None`` or a tuple of (access time, modified time) in standard
411 internet epoch time (seconds since 01 January 1970 GMT)
412 """
413 path = self._adjust_cwd(path)
414 if times is None:
415 times = (time.time(), time.time())
416 self._log(DEBUG, 'utime(%r, %r)' % (path, times))
417 attr = SFTPAttributes()
418 attr.st_atime, attr.st_mtime = times
419 self._request(CMD_SETSTAT, path, attr)
420
422 """
423 Change the size of the file specified by ``path``. This usually
424 extends or shrinks the size of the file, just like the `~file.truncate`
425 method on Python file objects.
426
427 :param str path: path of the file to modify
428 :param size: the new size of the file
429 :type size: int or long
430 """
431 path = self._adjust_cwd(path)
432 self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
433 attr = SFTPAttributes()
434 attr.st_size = size
435 self._request(CMD_SETSTAT, path, attr)
436
438 """
439 Return the target of a symbolic link (shortcut). You can use
440 `symlink` to create these. The result may be either an absolute or
441 relative pathname.
442
443 :param str path: path of the symbolic link file
444 :return: target path, as a `str`
445 """
446 path = self._adjust_cwd(path)
447 self._log(DEBUG, 'readlink(%r)' % path)
448 t, msg = self._request(CMD_READLINK, path)
449 if t != CMD_NAME:
450 raise SFTPError('Expected name response')
451 count = msg.get_int()
452 if count == 0:
453 return None
454 if count != 1:
455 raise SFTPError('Readlink returned %d results' % count)
456 return _to_unicode(msg.get_string())
457
459 """
460 Return the normalized path (on the server) of a given path. This
461 can be used to quickly resolve symbolic links or determine what the
462 server is considering to be the "current folder" (by passing ``'.'``
463 as ``path``).
464
465 :param str path: path to be normalized
466 :return: normalized form of the given path (as a `str`)
467
468 :raises IOError: if the path can't be resolved on the server
469 """
470 path = self._adjust_cwd(path)
471 self._log(DEBUG, 'normalize(%r)' % path)
472 t, msg = self._request(CMD_REALPATH, path)
473 if t != CMD_NAME:
474 raise SFTPError('Expected name response')
475 count = msg.get_int()
476 if count != 1:
477 raise SFTPError('Realpath returned %d results' % count)
478 return msg.get_text()
479
480 - def chdir(self, path=None):
481 """
482 Change the "current directory" of this SFTP session. Since SFTP
483 doesn't really have the concept of a current working directory, this is
484 emulated by Paramiko. Once you use this method to set a working
485 directory, all operations on this `.SFTPClient` object will be relative
486 to that path. You can pass in ``None`` to stop using a current working
487 directory.
488
489 :param str path: new current working directory
490
491 :raises IOError: if the requested path doesn't exist on the server
492
493 .. versionadded:: 1.4
494 """
495 if path is None:
496 self._cwd = None
497 return
498 if not stat.S_ISDIR(self.stat(path).st_mode):
499 raise SFTPError(errno.ENOTDIR, "%s: %s" % (os.strerror(errno.ENOTDIR), path))
500 self._cwd = b(self.normalize(path))
501
503 """
504 Return the "current working directory" for this SFTP session, as
505 emulated by Paramiko. If no directory has been set with `chdir`,
506 this method will return ``None``.
507
508 .. versionadded:: 1.4
509 """
510 return self._cwd and u(self._cwd)
511
512 - def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
513 """
514 Copy the contents of an open file object (``fl``) to the SFTP server as
515 ``remotepath``. Any exception raised by operations will be passed
516 through.
517
518 The SFTP operations use pipelining for speed.
519
520 :param file fl: opened file or file-like object to copy
521 :param str remotepath: the destination path on the SFTP server
522 :param int file_size:
523 optional size parameter passed to callback. If none is specified,
524 size defaults to 0
525 :param callable callback:
526 optional callback function (form: ``func(int, int)``) that accepts
527 the bytes transferred so far and the total bytes to be transferred
528 (since 1.7.4)
529 :param bool confirm:
530 whether to do a stat() on the file afterwards to confirm the file
531 size (since 1.7.7)
532
533 :return:
534 an `.SFTPAttributes` object containing attributes about the given
535 file.
536
537 .. versionadded:: 1.10
538 """
539 with self.file(remotepath, 'wb') as fr:
540 fr.set_pipelined(True)
541 size = 0
542 while True:
543 data = fl.read(32768)
544 fr.write(data)
545 size += len(data)
546 if callback is not None:
547 callback(size, file_size)
548 if len(data) == 0:
549 break
550 if confirm:
551 s = self.stat(remotepath)
552 if s.st_size != size:
553 raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
554 else:
555 s = SFTPAttributes()
556 return s
557
558 - def put(self, localpath, remotepath, callback=None, confirm=True):
559 """
560 Copy a local file (``localpath``) to the SFTP server as ``remotepath``.
561 Any exception raised by operations will be passed through. This
562 method is primarily provided as a convenience.
563
564 The SFTP operations use pipelining for speed.
565
566 :param str localpath: the local file to copy
567 :param str remotepath: the destination path on the SFTP server. Note
568 that the filename should be included. Only specifying a directory
569 may result in an error.
570 :param callable callback:
571 optional callback function (form: ``func(int, int)``) that accepts
572 the bytes transferred so far and the total bytes to be transferred
573 :param bool confirm:
574 whether to do a stat() on the file afterwards to confirm the file
575 size
576
577 :return: an `.SFTPAttributes` object containing attributes about the given file
578
579 .. versionadded:: 1.4
580 .. versionchanged:: 1.7.4
581 ``callback`` and rich attribute return value added.
582 .. versionchanged:: 1.7.7
583 ``confirm`` param added.
584 """
585 file_size = os.stat(localpath).st_size
586 with open(localpath, 'rb') as fl:
587 return self.putfo(fl, remotepath, file_size, callback, confirm)
588
589 - def getfo(self, remotepath, fl, callback=None):
590 """
591 Copy a remote file (``remotepath``) from the SFTP server and write to
592 an open file or file-like object, ``fl``. Any exception raised by
593 operations will be passed through. This method is primarily provided
594 as a convenience.
595
596 :param object remotepath: opened file or file-like object to copy to
597 :param str fl:
598 the destination path on the local host or open file object
599 :param callable callback:
600 optional callback function (form: ``func(int, int)``) that accepts
601 the bytes transferred so far and the total bytes to be transferred
602 :return: the `number <int>` of bytes written to the opened file object
603
604 .. versionadded:: 1.10
605 """
606 with self.open(remotepath, 'rb') as fr:
607 file_size = self.stat(remotepath).st_size
608 fr.prefetch()
609 size = 0
610 while True:
611 data = fr.read(32768)
612 fl.write(data)
613 size += len(data)
614 if callback is not None:
615 callback(size, file_size)
616 if len(data) == 0:
617 break
618 return size
619
620 - def get(self, remotepath, localpath, callback=None):
621 """
622 Copy a remote file (``remotepath``) from the SFTP server to the local
623 host as ``localpath``. Any exception raised by operations will be
624 passed through. This method is primarily provided as a convenience.
625
626 :param str remotepath: the remote file to copy
627 :param str localpath: the destination path on the local host
628 :param callable callback:
629 optional callback function (form: ``func(int, int)``) that accepts
630 the bytes transferred so far and the total bytes to be transferred
631
632 .. versionadded:: 1.4
633 .. versionchanged:: 1.7.4
634 Added the ``callback`` param
635 """
636 file_size = self.stat(remotepath).st_size
637 with open(localpath, 'wb') as fl:
638 size = self.getfo(remotepath, fl, callback)
639 s = os.stat(localpath)
640 if s.st_size != size:
641 raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))
642
643
644
646 num = self._async_request(type(None), t, *arg)
647 return self._read_response(num)
648
650
651 self._lock.acquire()
652 try:
653 msg = Message()
654 msg.add_int(self.request_number)
655 for item in arg:
656 if isinstance(item, long):
657 msg.add_int64(item)
658 elif isinstance(item, int):
659 msg.add_int(item)
660 elif isinstance(item, (string_types, bytes_types)):
661 msg.add_string(item)
662 elif isinstance(item, SFTPAttributes):
663 item._pack(msg)
664 else:
665 raise Exception('unknown type for %r type %r' % (item, type(item)))
666 num = self.request_number
667 self._expecting[num] = fileobj
668 self._send_packet(t, msg)
669 self.request_number += 1
670 finally:
671 self._lock.release()
672 return num
673
675 while True:
676 try:
677 t, data = self._read_packet()
678 except EOFError as e:
679 raise SSHException('Server connection dropped: %s' % str(e))
680 msg = Message(data)
681 num = msg.get_int()
682 if num not in self._expecting:
683
684 self._log(DEBUG, 'Unexpected response #%d' % (num,))
685 if waitfor is None:
686
687 break
688 continue
689 fileobj = self._expecting[num]
690 del self._expecting[num]
691 if num == waitfor:
692
693 if t == CMD_STATUS:
694 self._convert_status(msg)
695 return t, msg
696 if fileobj is not type(None):
697 fileobj._async_response(t, msg, num)
698 if waitfor is None:
699
700 break
701 return None, None
702
704 while fileobj in self._expecting.values():
705 self._read_response()
706 fileobj._check_exception()
707
725
727 """
728 Return an adjusted path if we're emulating a "current working
729 directory" for the server.
730 """
731 path = b(path)
732 if self._cwd is None:
733 return path
734 if len(path) and path[0:1] == b_slash:
735
736 return path
737 if self._cwd == b_slash:
738 return self._cwd + path
739 return self._cwd + b_slash + path
740
741
742 -class SFTP(SFTPClient):
743 """
744 An alias for `.SFTPClient` for backwards compatability.
745 """
746 pass
747