-
Notifications
You must be signed in to change notification settings - Fork 75
/
Copy pathsftp.pyx
469 lines (381 loc) · 14.9 KB
/
sftp.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# This file is part of ssh2-python.
# Copyright (C) 2017-2020 Panos Kittenis
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation, version 2.1.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""
SFTP channel class and related SFTP flags.
File types
------------
:var LIBSSH2_SFTP_S_IFMT: Type of file mask
:var LIBSSH2_SFTP_S_IFIFO: Named pipe (fifo)
:var LIBSSH2_SFTP_S_IFCHR: Character special (character device)
:var LIBSSH2_SFTP_S_IFDIR: Directory
:var LIBSSH2_SFTP_S_IFBLK: Block special (block device)
:var LIBSSH2_SFTP_S_IFREG: Regular file
:var LIBSSH2_SFTP_S_IFLNK: Symbolic link
:var LIBSSH2_SFTP_S_IFSOCK: Socket
File transfer flags
--------------------
:var LIBSSH2_FXF_READ: File read flag
:var LIBSSH2_FXF_WRITE: File write flag
:var LIBSSH2_FXF_APPEND: File append flag
:var LIBSSH2_FXF_CREAT: File create flag
:var LIBSSH2_FXF_TRUNC: File truncate flag
:var LIBSSH2_FXF_EXCL: Exclusive file flag
File Attributes
----------------
These flags need to be set on `SFTPAttributes.attrs` when changing any of their
associated attributes via `setstat`.
:var LIBSSH2_SFTP_ATTR_SIZE: Size attribute flag
:var LIBSSH2_SFTP_ATTR_UIDGID: UID and GID attribute flag
:var LIBSSH2_SFTP_ATTR_PERMISSIONS: Permissions attribute flag
:var LIBSSH2_SFTP_ATTR_ACMODTIME: File access/created/modified time attribute
flag
:var LIBSSH2_SFTP_ATTR_EXTENDED: Extended attributes flag
File mode masks
-----------------
Owner masks
_____________
:var LIBSSH2_SFTP_S_IRWXU: Read/write/execute
:var LIBSSH2_SFTP_S_IRUSR: Read
:var LIBSSH2_SFTP_S_IWUSR: Write
:var LIBSSH2_SFTP_S_IXUSR: Execute
Group masks
____________
:var LIBSSH2_SFTP_S_IRWXG: Read/write/execute
:var LIBSSH2_SFTP_S_IRGRP: Read
:var LIBSSH2_SFTP_S_IWUSR: Write
:var LIBSSH2_SFTP_S_IXUSR: Execute
Other masks
____________
:var LIBSSH2_SFTP_S_IRWXO: Read/write/execute
:var LIBSSH2_SFTP_S_IROTH: Read
:var LIBSSH2_SFTP_S_IWOTH: Write
:var LIBSSH2_SFTP_S_IXOTH: Execute
Generic mode masks
___________________
:var LIBSSH2_SFTP_ST_RDONLY: Read only
:var LIBSSH2_SFTP_ST_NOSUID: No suid
"""
from libc.stdlib cimport malloc, free
from .session cimport Session
from .channel cimport Channel, PyChannel
from .utils cimport to_bytes, to_str_len, handle_error_codes
from .sftp_handle cimport SFTPHandle, PySFTPHandle, SFTPAttributes, SFTPStatVFS
from . cimport c_ssh2
from . cimport c_sftp
# File types
# Type of file mask
LIBSSH2_SFTP_S_IFMT = c_sftp.LIBSSH2_SFTP_S_IFMT
# named pipe (fifo)
LIBSSH2_SFTP_S_IFIFO = c_sftp.LIBSSH2_SFTP_S_IFIFO
# character special
LIBSSH2_SFTP_S_IFCHR = c_sftp.LIBSSH2_SFTP_S_IFCHR
# directory
LIBSSH2_SFTP_S_IFDIR = c_sftp.LIBSSH2_SFTP_S_IFDIR
# block special (block device)
LIBSSH2_SFTP_S_IFBLK = c_sftp.LIBSSH2_SFTP_S_IFBLK
# regular
LIBSSH2_SFTP_S_IFREG = c_sftp.LIBSSH2_SFTP_S_IFREG
# symbolic link
LIBSSH2_SFTP_S_IFLNK = c_sftp.LIBSSH2_SFTP_S_IFLNK
# socket
LIBSSH2_SFTP_S_IFSOCK = c_sftp.LIBSSH2_SFTP_S_IFSOCK
# Attributes
LIBSSH2_SFTP_ATTR_SIZE = c_sftp.LIBSSH2_SFTP_ATTR_SIZE
LIBSSH2_SFTP_ATTR_UIDGID = c_sftp.LIBSSH2_SFTP_ATTR_UIDGID
LIBSSH2_SFTP_ATTR_PERMISSIONS = c_sftp.LIBSSH2_SFTP_ATTR_PERMISSIONS
LIBSSH2_SFTP_ATTR_ACMODTIME = c_sftp.LIBSSH2_SFTP_ATTR_ACMODTIME
LIBSSH2_SFTP_ATTR_EXTENDED = c_sftp.LIBSSH2_SFTP_ATTR_EXTENDED
# File Transfer Flags
LIBSSH2_FXF_READ = c_sftp.LIBSSH2_FXF_READ
LIBSSH2_FXF_WRITE = c_sftp.LIBSSH2_FXF_WRITE
LIBSSH2_FXF_APPEND = c_sftp.LIBSSH2_FXF_APPEND
LIBSSH2_FXF_CREAT = c_sftp.LIBSSH2_FXF_CREAT
LIBSSH2_FXF_TRUNC = c_sftp.LIBSSH2_FXF_TRUNC
LIBSSH2_FXF_EXCL = c_sftp.LIBSSH2_FXF_EXCL
# File mode masks
# Read, write, execute/search by owner
LIBSSH2_SFTP_S_IRWXU = c_sftp.LIBSSH2_SFTP_S_IRWXU
LIBSSH2_SFTP_S_IRUSR = c_sftp.LIBSSH2_SFTP_S_IRUSR
LIBSSH2_SFTP_S_IWUSR = c_sftp.LIBSSH2_SFTP_S_IWUSR
LIBSSH2_SFTP_S_IXUSR = c_sftp.LIBSSH2_SFTP_S_IXUSR
# Read, write, execute/search by group
LIBSSH2_SFTP_S_IRWXG = c_sftp.LIBSSH2_SFTP_S_IRWXG
LIBSSH2_SFTP_S_IRGRP = c_sftp.LIBSSH2_SFTP_S_IRGRP
LIBSSH2_SFTP_S_IWGRP = c_sftp.LIBSSH2_SFTP_S_IWGRP
LIBSSH2_SFTP_S_IXGRP = c_sftp.LIBSSH2_SFTP_S_IXGRP
# Read, write, execute/search by others
LIBSSH2_SFTP_S_IRWXO = c_sftp.LIBSSH2_SFTP_S_IRWXO
LIBSSH2_SFTP_S_IROTH = c_sftp.LIBSSH2_SFTP_S_IROTH
LIBSSH2_SFTP_S_IWOTH = c_sftp.LIBSSH2_SFTP_S_IWOTH
LIBSSH2_SFTP_S_IXOTH = c_sftp.LIBSSH2_SFTP_S_IXOTH
# Read only
LIBSSH2_SFTP_ST_RDONLY = c_sftp.LIBSSH2_SFTP_ST_RDONLY
# No suid
LIBSSH2_SFTP_ST_NOSUID = c_sftp.LIBSSH2_SFTP_ST_NOSUID
cdef object PySFTP(c_sftp.LIBSSH2_SFTP *sftp, Session session):
cdef SFTP _sftp = SFTP.__new__(SFTP, session)
_sftp._sftp = sftp
return _sftp
cdef class SFTP:
"""SFTP session.
:param session: Session that initiated SFTP.
:type session: :py:class:`ssh2.session.Session` pointer"""
def __cinit__(self, session):
self._sftp = NULL
self._session = session
def __dealloc__(self):
if self._session is not None and self._session._session is not NULL and self._sftp is not NULL:
with nogil:
c_sftp.libssh2_sftp_shutdown(self._sftp)
self._sftp = NULL
@property
def session(self):
"""Originating session."""
return self._session
def get_channel(self):
"""Get new channel from the SFTP session"""
cdef c_ssh2.LIBSSH2_CHANNEL *_channel
with nogil:
_channel = c_sftp.libssh2_sftp_get_channel(self._sftp)
if _channel is NULL:
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
self._session._session))
return PyChannel(_channel, self._session)
def open_ex(self, const char *filename,
unsigned int filename_len,
unsigned long flags,
long mode, int open_type):
cdef c_sftp.LIBSSH2_SFTP_HANDLE *_handle
cdef SFTPHandle handle
with nogil:
_handle = c_sftp.libssh2_sftp_open_ex(
self._sftp, filename, filename_len, flags,
mode, open_type)
if _handle is NULL:
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
self._session._session))
handle = PySFTPHandle(_handle, self)
return handle
def open(self, filename not None,
unsigned long flags,
long mode):
"""Open file handle for file name.
:param filename: Name of file to open.
:type filename: str
:param flags: One or more LIBSSH2_FXF_* flags.
Eg for reading flags is ``LIBSSH2_FXF_READ``,
for writing ``LIBSSH2_FXF_WRITE``,
for both ``LIBSSH2_FXF_READ`` | ``LIBSSH2_FXF_WRITE``.
:type flags: int
:param mode: File permissions mode. ``LIBSSH2_SFTP_S_IRUSR`` for
reading.
For writing one or more ``LIBSSH2_SFTP_S_*`` flags.
Eg, for 664 permission mask (read/write owner/group, read other),
mode is
``LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | \``
``LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IWGRP | \``
``LIBSSH2_SFTP_S_IROTH``
:type mode: int
:rtype: :py:class:`ssh2.sftp.SFTPHandle` or `None`
:raises: :py:class:`ssh2.exceptions.SFTPHandleError` on errors opening
file.
""" # noqa: W605
cdef c_sftp.LIBSSH2_SFTP_HANDLE *_handle
cdef bytes b_filename = to_bytes(filename)
cdef char *_filename = b_filename
with nogil:
_handle = c_sftp.libssh2_sftp_open(
self._sftp, _filename, flags, mode)
if _handle is NULL:
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
self._session._session))
return PySFTPHandle(_handle, self)
def opendir(self, path not None):
"""Open handle to directory path.
:param path: Path of directory
:type path: str
:rtype: :py:class:`ssh2.sftp.SFTPHandle` or `None`
:raises: :py:class:`ssh2.exceptions.SFTPHandleError` on errors opening
directory.
"""
cdef c_sftp.LIBSSH2_SFTP_HANDLE *_handle
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
with nogil:
_handle = c_sftp.libssh2_sftp_opendir(self._sftp, _path)
if _handle is NULL:
return handle_error_codes(c_ssh2.libssh2_session_last_errno(
self._session._session))
return PySFTPHandle(_handle, self)
def rename_ex(self, const char *source_filename,
unsigned int source_filename_len,
const char *dest_filename,
unsigned int dest_filename_len,
long flags):
cdef int rc
with nogil:
rc = c_sftp.libssh2_sftp_rename_ex(
self._sftp, source_filename, source_filename_len,
dest_filename, dest_filename_len, flags)
return handle_error_codes(rc)
def rename(self, source_filename not None, dest_filename not None):
"""Rename file.
:param source_filename: Old name of file.
:type source_filename: str
:param dest_filename: New name of file.
:type dest_filename: str"""
cdef int rc
cdef bytes b_source_filename = to_bytes(source_filename)
cdef bytes b_dest_filename = to_bytes(dest_filename)
cdef char *_source_filename = b_source_filename
cdef char *_dest_filename = b_dest_filename
with nogil:
rc = c_sftp.libssh2_sftp_rename(
self._sftp, _source_filename, _dest_filename)
return handle_error_codes(rc)
def unlink(self, filename not None):
"""Delete/unlink file.
:param filename: Name of file to delete/unlink.
:type filename: str"""
cdef int rc
cdef bytes b_filename = to_bytes(filename)
cdef char *_filename = b_filename
with nogil:
rc = c_sftp.libssh2_sftp_unlink(self._sftp, _filename)
return handle_error_codes(rc)
def statvfs(self, path):
"""Get file system statistics from path.
:rtype: `ssh2.sftp.SFTPStatVFS` or int of error code"""
cdef SFTPStatVFS vfs = SFTPStatVFS(self)
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
cdef size_t path_len = len(b_path)
with nogil:
rc = c_sftp.libssh2_sftp_statvfs(
self._sftp, _path, path_len, vfs._ptr)
return handle_error_codes(rc) if rc != 0 else vfs
def mkdir(self, path not None, long mode):
"""Make directory.
:param path: Path of directory to create.
:type path: str
:param mode: Permissions mode of new directory.
:type mode: int
:rtype: int
:raises: Appropriate exception from :py:mod:`ssh2.exceptions` on errors.
"""
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
with nogil:
rc = c_sftp.libssh2_sftp_mkdir(self._sftp, _path, mode)
return handle_error_codes(rc)
def rmdir(self, path not None):
"""Remove directory.
:param path: Directory path to remove.
:type path: str
:rtype: int"""
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
with nogil:
rc = c_sftp.libssh2_sftp_rmdir(self._sftp, _path)
return handle_error_codes(rc)
def stat(self, path not None):
"""Stat file.
:param path: Path of file to stat.
:type path: str
:rtype: :py:class:`ssh2.sftp_handle.SFTPAttributes` or
LIBSSH2_ERROR_EAGAIN"""
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
cdef SFTPAttributes attrs = SFTPAttributes()
with nogil:
rc = c_sftp.libssh2_sftp_stat(
self._sftp, _path, attrs._attrs)
return handle_error_codes(rc) if rc != 0 else attrs
def lstat(self, path not None):
"""Link stat a file."""
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
cdef SFTPAttributes attrs = SFTPAttributes()
with nogil:
rc = c_sftp.libssh2_sftp_lstat(
self._sftp, _path, attrs._attrs)
return handle_error_codes(rc) if rc != 0 else attrs
def setstat(self, path not None, SFTPAttributes attrs):
"""Set file attributes.
:param path: File path.
:type path: str
:param attrs: File attributes to set.
:type attrs: :py:class:`ssh2.sftp_handle.SFTPAttributes`
:rtype: int"""
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
with nogil:
rc = c_sftp.libssh2_sftp_setstat(
self._sftp, _path, attrs._attrs)
return handle_error_codes(rc)
def symlink(self, path not None, target not None):
"""Create symlink.
:param path: Source file path.
:type path: str
:param target: Target file path.
:type target: str
:rtype: int"""
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
cdef bytes b_target = to_bytes(target)
cdef char *_target = b_target
with nogil:
rc = c_sftp.libssh2_sftp_symlink(self._sftp, _path, _target)
return handle_error_codes(rc)
def realpath(self, path not None, size_t max_len=256):
"""Get real path for path.
:param: Path name to get real path for.
:type param: str
:param max_len: Max size of returned real path.
:type max_len: int
:raises: :py:class:`ssh2.exceptions.SFTPHandleError` on errors getting
real path.
:raises: :py:class:`ssh2.exceptions.SFTPBufferTooSmall` on max_len less
than real path length."""
cdef char *_target = <char *>malloc(sizeof(char)*max_len)
if _target is NULL:
raise MemoryError
cdef int rc
cdef bytes b_path = to_bytes(path)
cdef char *_path = b_path
try:
with nogil:
rc = c_sftp.libssh2_sftp_realpath(
self._sftp, _path, _target, max_len)
if rc < 0:
with gil:
return handle_error_codes(rc)
return to_str_len(_target, rc)
finally:
free(_target)
def last_error(self):
"""Get last error code from SFTP channel.
:rtype: int"""
cdef unsigned long rc
with nogil:
rc = c_sftp.libssh2_sftp_last_error(self._sftp)
return rc