Package paramiko :: Module sftp_file
[frames] | no frames]

Source Code for Module paramiko.sftp_file

  1  # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com> 
  2  # 
  3  # This file is part of paramiko. 
  4  # 
  5  # Paramiko is free software; you can redistribute it and/or modify it under the 
  6  # terms of the GNU Lesser General Public License as published by the Free 
  7  # Software Foundation; either version 2.1 of the License, or (at your option) 
  8  # any later version. 
  9  # 
 10  # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 
 11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 13  # details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public License 
 16  # along with Paramiko; if not, write to the Free Software Foundation, Inc., 
 17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
 18   
 19  """ 
 20  L{SFTPFile} 
 21  """ 
 22   
 23  from binascii import hexlify 
 24  import socket 
 25  import threading 
 26  import time 
 27   
 28  from paramiko.common import * 
 29  from paramiko.sftp import * 
 30  from paramiko.file import BufferedFile 
 31  from paramiko.sftp_attr import SFTPAttributes 
 32   
 33   
34 -class SFTPFile (BufferedFile):
35 """ 36 Proxy object for a file on the remote server, in client mode SFTP. 37 """ 38 39 # Some sftp servers will choke if you send read/write requests larger than 40 # this size. 41 MAX_REQUEST_SIZE = 32768 42
43 - def __init__(self, sftp, handle, mode='r', bufsize=-1):
44 BufferedFile.__init__(self) 45 self.sftp = sftp 46 self.handle = handle 47 BufferedFile._set_mode(self, mode, bufsize) 48 self.pipelined = False 49 self._prefetching = False 50 self._prefetch_done = False 51 self._prefetch_data = {} 52 self._prefetch_reads = [] 53 self._saved_exception = None
54
55 - def __del__(self):
56 self._close(async=True)
57
58 - def close(self):
59 self._close(async=False)
60
61 - def _close(self, async=False):
62 # We allow double-close without signaling an error, because real 63 # Python file objects do. However, we must protect against actually 64 # sending multiple CMD_CLOSE packets, because after we close our 65 # handle, the same handle may be re-allocated by the server, and we 66 # may end up mysteriously closing some random other file. (This is 67 # especially important because we unconditionally call close() from 68 # __del__.) 69 if self._closed: 70 return 71 self.sftp._log(DEBUG, 'close(%s)' % hexlify(self.handle)) 72 if self.pipelined: 73 self.sftp._finish_responses(self) 74 BufferedFile.close(self) 75 try: 76 if async: 77 # GC'd file handle could be called from an arbitrary thread -- don't wait for a response 78 self.sftp._async_request(type(None), CMD_CLOSE, self.handle) 79 else: 80 self.sftp._request(CMD_CLOSE, self.handle) 81 except EOFError: 82 # may have outlived the Transport connection 83 pass 84 except (IOError, socket.error): 85 # may have outlived the Transport connection 86 pass
87
88 - def _data_in_prefetch_requests(self, offset, size):
89 k = [i for i in self._prefetch_reads if i[0] <= offset] 90 if len(k) == 0: 91 return False 92 k.sort(lambda x, y: cmp(x[0], y[0])) 93 buf_offset, buf_size = k[-1] 94 if buf_offset + buf_size <= offset: 95 # prefetch request ends before this one begins 96 return False 97 if buf_offset + buf_size >= offset + size: 98 # inclusive 99 return True 100 # well, we have part of the request. see if another chunk has the rest. 101 return self._data_in_prefetch_requests(buf_offset + buf_size, offset + size - buf_offset - buf_size)
102
103 - def _data_in_prefetch_buffers(self, offset):
104 """ 105 if a block of data is present in the prefetch buffers, at the given 106 offset, return the offset of the relevant prefetch buffer. otherwise, 107 return None. this guarantees nothing about the number of bytes 108 collected in the prefetch buffer so far. 109 """ 110 k = [i for i in self._prefetch_data.keys() if i <= offset] 111 if len(k) == 0: 112 return None 113 index = max(k) 114 buf_offset = offset - index 115 if buf_offset >= len(self._prefetch_data[index]): 116 # it's not here 117 return None 118 return index
119
120 - def _read_prefetch(self, size):
121 """ 122 read data out of the prefetch buffer, if possible. if the data isn't 123 in the buffer, return None. otherwise, behaves like a normal read. 124 """ 125 # while not closed, and haven't fetched past the current position, and haven't reached EOF... 126 while True: 127 offset = self._data_in_prefetch_buffers(self._realpos) 128 if offset is not None: 129 break 130 if self._prefetch_done or self._closed: 131 break 132 self.sftp._read_response() 133 self._check_exception() 134 if offset is None: 135 self._prefetching = False 136 return None 137 prefetch = self._prefetch_data[offset] 138 del self._prefetch_data[offset] 139 140 buf_offset = self._realpos - offset 141 if buf_offset > 0: 142 self._prefetch_data[offset] = prefetch[:buf_offset] 143 prefetch = prefetch[buf_offset:] 144 if size < len(prefetch): 145 self._prefetch_data[self._realpos + size] = prefetch[size:] 146 prefetch = prefetch[:size] 147 return prefetch
148
149 - def _read(self, size):
150 size = min(size, self.MAX_REQUEST_SIZE) 151 if self._prefetching: 152 data = self._read_prefetch(size) 153 if data is not None: 154 return data 155 t, msg = self.sftp._request(CMD_READ, self.handle, long(self._realpos), int(size)) 156 if t != CMD_DATA: 157 raise SFTPError('Expected data') 158 return msg.get_string()
159
160 - def _write(self, data):
161 # may write less than requested if it would exceed max packet size 162 chunk = min(len(data), self.MAX_REQUEST_SIZE) 163 req = self.sftp._async_request(type(None), CMD_WRITE, self.handle, long(self._realpos), str(data[:chunk])) 164 if not self.pipelined or self.sftp.sock.recv_ready(): 165 t, msg = self.sftp._read_response(req) 166 if t != CMD_STATUS: 167 raise SFTPError('Expected status') 168 # convert_status already called 169 return chunk
170
171 - def settimeout(self, timeout):
172 """ 173 Set a timeout on read/write operations on the underlying socket or 174 ssh L{Channel}. 175 176 @see: L{Channel.settimeout} 177 @param timeout: seconds to wait for a pending read/write operation 178 before raising C{socket.timeout}, or C{None} for no timeout 179 @type timeout: float 180 """ 181 self.sftp.sock.settimeout(timeout)
182
183 - def gettimeout(self):
184 """ 185 Returns the timeout in seconds (as a float) associated with the socket 186 or ssh L{Channel} used for this file. 187 188 @see: L{Channel.gettimeout} 189 @rtype: float 190 """ 191 return self.sftp.sock.gettimeout()
192
193 - def setblocking(self, blocking):
194 """ 195 Set blocking or non-blocking mode on the underiying socket or ssh 196 L{Channel}. 197 198 @see: L{Channel.setblocking} 199 @param blocking: 0 to set non-blocking mode; non-0 to set blocking 200 mode. 201 @type blocking: int 202 """ 203 self.sftp.sock.setblocking(blocking)
204
205 - def seek(self, offset, whence=0):
206 self.flush() 207 if whence == self.SEEK_SET: 208 self._realpos = self._pos = offset 209 elif whence == self.SEEK_CUR: 210 self._pos += offset 211 self._realpos = self._pos 212 else: 213 self._realpos = self._pos = self._get_size() + offset 214 self._rbuffer = ''
215
216 - def stat(self):
217 """ 218 Retrieve information about this file from the remote system. This is 219 exactly like L{SFTP.stat}, except that it operates on an already-open 220 file. 221 222 @return: an object containing attributes about this file. 223 @rtype: SFTPAttributes 224 """ 225 t, msg = self.sftp._request(CMD_FSTAT, self.handle) 226 if t != CMD_ATTRS: 227 raise SFTPError('Expected attributes') 228 return SFTPAttributes._from_msg(msg)
229
230 - def chmod(self, mode):
231 """ 232 Change the mode (permissions) of this file. The permissions are 233 unix-style and identical to those used by python's C{os.chmod} 234 function. 235 236 @param mode: new permissions 237 @type mode: int 238 """ 239 self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode)) 240 attr = SFTPAttributes() 241 attr.st_mode = mode 242 self.sftp._request(CMD_FSETSTAT, self.handle, attr)
243
244 - def chown(self, uid, gid):
245 """ 246 Change the owner (C{uid}) and group (C{gid}) of this file. As with 247 python's C{os.chown} function, you must pass both arguments, so if you 248 only want to change one, use L{stat} first to retrieve the current 249 owner and group. 250 251 @param uid: new owner's uid 252 @type uid: int 253 @param gid: new group id 254 @type gid: int 255 """ 256 self.sftp._log(DEBUG, 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid)) 257 attr = SFTPAttributes() 258 attr.st_uid, attr.st_gid = uid, gid 259 self.sftp._request(CMD_FSETSTAT, self.handle, attr)
260
261 - def utime(self, times):
262 """ 263 Set the access and modified times of this file. If 264 C{times} is C{None}, then the file's access and modified times are set 265 to the current time. Otherwise, C{times} must be a 2-tuple of numbers, 266 of the form C{(atime, mtime)}, which is used to set the access and 267 modified times, respectively. This bizarre API is mimicked from python 268 for the sake of consistency -- I apologize. 269 270 @param times: C{None} or a tuple of (access time, modified time) in 271 standard internet epoch time (seconds since 01 January 1970 GMT) 272 @type times: tuple(int) 273 """ 274 if times is None: 275 times = (time.time(), time.time()) 276 self.sftp._log(DEBUG, 'utime(%s, %r)' % (hexlify(self.handle), times)) 277 attr = SFTPAttributes() 278 attr.st_atime, attr.st_mtime = times 279 self.sftp._request(CMD_FSETSTAT, self.handle, attr)
280
281 - def truncate(self, size):
282 """ 283 Change the size of this file. This usually extends 284 or shrinks the size of the file, just like the C{truncate()} method on 285 python file objects. 286 287 @param size: the new size of the file 288 @type size: int or long 289 """ 290 self.sftp._log(DEBUG, 'truncate(%s, %r)' % (hexlify(self.handle), size)) 291 attr = SFTPAttributes() 292 attr.st_size = size 293 self.sftp._request(CMD_FSETSTAT, self.handle, attr)
294
295 - def check(self, hash_algorithm, offset=0, length=0, block_size=0):
296 """ 297 Ask the server for a hash of a section of this file. This can be used 298 to verify a successful upload or download, or for various rsync-like 299 operations. 300 301 The file is hashed from C{offset}, for C{length} bytes. If C{length} 302 is 0, the remainder of the file is hashed. Thus, if both C{offset} 303 and C{length} are zero, the entire file is hashed. 304 305 Normally, C{block_size} will be 0 (the default), and this method will 306 return a byte string representing the requested hash (for example, a 307 string of length 16 for MD5, or 20 for SHA-1). If a non-zero 308 C{block_size} is given, each chunk of the file (from C{offset} to 309 C{offset + length}) of C{block_size} bytes is computed as a separate 310 hash. The hash results are all concatenated and returned as a single 311 string. 312 313 For example, C{check('sha1', 0, 1024, 512)} will return a string of 314 length 40. The first 20 bytes will be the SHA-1 of the first 512 bytes 315 of the file, and the last 20 bytes will be the SHA-1 of the next 512 316 bytes. 317 318 @param hash_algorithm: the name of the hash algorithm to use (normally 319 C{"sha1"} or C{"md5"}) 320 @type hash_algorithm: str 321 @param offset: offset into the file to begin hashing (0 means to start 322 from the beginning) 323 @type offset: int or long 324 @param length: number of bytes to hash (0 means continue to the end of 325 the file) 326 @type length: int or long 327 @param block_size: number of bytes to hash per result (must not be less 328 than 256; 0 means to compute only one hash of the entire segment) 329 @type block_size: int 330 @return: string of bytes representing the hash of each block, 331 concatenated together 332 @rtype: str 333 334 @note: Many (most?) servers don't support this extension yet. 335 336 @raise IOError: if the server doesn't support the "check-file" 337 extension, or possibly doesn't support the hash algorithm 338 requested 339 340 @since: 1.4 341 """ 342 t, msg = self.sftp._request(CMD_EXTENDED, 'check-file', self.handle, 343 hash_algorithm, long(offset), long(length), block_size) 344 ext = msg.get_string() 345 alg = msg.get_string() 346 data = msg.get_remainder() 347 return data
348
349 - def set_pipelined(self, pipelined=True):
350 """ 351 Turn on/off the pipelining of write operations to this file. When 352 pipelining is on, paramiko won't wait for the server response after 353 each write operation. Instead, they're collected as they come in. 354 At the first non-write operation (including L{close}), all remaining 355 server responses are collected. This means that if there was an error 356 with one of your later writes, an exception might be thrown from 357 within L{close} instead of L{write}. 358 359 By default, files are I{not} pipelined. 360 361 @param pipelined: C{True} if pipelining should be turned on for this 362 file; C{False} otherwise 363 @type pipelined: bool 364 365 @since: 1.5 366 """ 367 self.pipelined = pipelined
368
369 - def prefetch(self):
370 """ 371 Pre-fetch the remaining contents of this file in anticipation of 372 future L{read} calls. If reading the entire file, pre-fetching can 373 dramatically improve the download speed by avoiding roundtrip latency. 374 The file's contents are incrementally buffered in a background thread. 375 376 The prefetched data is stored in a buffer until read via the L{read} 377 method. Once data has been read, it's removed from the buffer. The 378 data may be read in a random order (using L{seek}); chunks of the 379 buffer that haven't been read will continue to be buffered. 380 381 @since: 1.5.1 382 """ 383 size = self.stat().st_size 384 # queue up async reads for the rest of the file 385 chunks = [] 386 n = self._realpos 387 while n < size: 388 chunk = min(self.MAX_REQUEST_SIZE, size - n) 389 chunks.append((n, chunk)) 390 n += chunk 391 if len(chunks) > 0: 392 self._start_prefetch(chunks)
393
394 - def readv(self, chunks):
395 """ 396 Read a set of blocks from the file by (offset, length). This is more 397 efficient than doing a series of L{seek} and L{read} calls, since the 398 prefetch machinery is used to retrieve all the requested blocks at 399 once. 400 401 @param chunks: a list of (offset, length) tuples indicating which 402 sections of the file to read 403 @type chunks: list(tuple(long, int)) 404 @return: a list of blocks read, in the same order as in C{chunks} 405 @rtype: list(str) 406 407 @since: 1.5.4 408 """ 409 self.sftp._log(DEBUG, 'readv(%s, %r)' % (hexlify(self.handle), chunks)) 410 411 read_chunks = [] 412 for offset, size in chunks: 413 # don't fetch data that's already in the prefetch buffer 414 if self._data_in_prefetch_buffers(offset) or self._data_in_prefetch_requests(offset, size): 415 continue 416 417 # break up anything larger than the max read size 418 while size > 0: 419 chunk_size = min(size, self.MAX_REQUEST_SIZE) 420 read_chunks.append((offset, chunk_size)) 421 offset += chunk_size 422 size -= chunk_size 423 424 self._start_prefetch(read_chunks) 425 # now we can just devolve to a bunch of read()s :) 426 for x in chunks: 427 self.seek(x[0]) 428 yield self.read(x[1])
429 430 431 ### internals... 432 433
434 - def _get_size(self):
435 try: 436 return self.stat().st_size 437 except: 438 return 0
439
440 - def _start_prefetch(self, chunks):
441 self._prefetching = True 442 self._prefetch_done = False 443 self._prefetch_reads.extend(chunks) 444 445 t = threading.Thread(target=self._prefetch_thread, args=(chunks,)) 446 t.setDaemon(True) 447 t.start()
448
449 - def _prefetch_thread(self, chunks):
450 # do these read requests in a temporary thread because there may be 451 # a lot of them, so it may block. 452 for offset, length in chunks: 453 self.sftp._async_request(self, CMD_READ, self.handle, long(offset), int(length))
454
455 - def _async_response(self, t, msg):
456 if t == CMD_STATUS: 457 # save exception and re-raise it on next file operation 458 try: 459 self.sftp._convert_status(msg) 460 except Exception, x: 461 self._saved_exception = x 462 return 463 if t != CMD_DATA: 464 raise SFTPError('Expected data') 465 data = msg.get_string() 466 offset, length = self._prefetch_reads.pop(0) 467 self._prefetch_data[offset] = data 468 if len(self._prefetch_reads) == 0: 469 self._prefetch_done = True
470
471 - def _check_exception(self):
472 "if there's a saved exception, raise & clear it" 473 if self._saved_exception is not None: 474 x = self._saved_exception 475 self._saved_exception = None 476 raise x
477