Mercurial > 510Connectbot
comparison app/src/main/java/ch/ethz/ssh2/AbstractSFTPClient.java @ 438:d29cce60f393
migrate from Eclipse to Android Studio
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 03 Dec 2015 11:23:55 -0800 |
parents | src/ch/ethz/ssh2/AbstractSFTPClient.java@071eccdff8ea |
children |
comparison
equal
deleted
inserted
replaced
437:208b31032318 | 438:d29cce60f393 |
---|---|
1 package ch.ethz.ssh2; | |
2 | |
3 import java.io.BufferedOutputStream; | |
4 import java.io.IOException; | |
5 import java.io.InputStream; | |
6 import java.io.OutputStream; | |
7 import java.net.SocketException; | |
8 import java.nio.charset.Charset; | |
9 import java.nio.charset.UnsupportedCharsetException; | |
10 import java.util.HashMap; | |
11 import java.util.Map; | |
12 | |
13 import ch.ethz.ssh2.channel.Channel; | |
14 import ch.ethz.ssh2.log.Logger; | |
15 import ch.ethz.ssh2.packets.TypesReader; | |
16 import ch.ethz.ssh2.packets.TypesWriter; | |
17 import ch.ethz.ssh2.sftp.AttribFlags; | |
18 import ch.ethz.ssh2.sftp.ErrorCodes; | |
19 import ch.ethz.ssh2.sftp.Packet; | |
20 import ch.ethz.ssh2.util.StringEncoder; | |
21 | |
22 /** | |
23 * @version $Id: AbstractSFTPClient.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $ | |
24 */ | |
25 public abstract class AbstractSFTPClient implements SFTPClient { | |
26 | |
27 private static final Logger log = Logger.getLogger(SFTPv3Client.class); | |
28 | |
29 private Session sess; | |
30 | |
31 private InputStream is; | |
32 private OutputStream os; | |
33 | |
34 private int next_request_id = 1000; | |
35 | |
36 private String charset; | |
37 | |
38 /** | |
39 * Parallel read requests maximum size. | |
40 */ | |
41 private static final int DEFAULT_MAX_PARALLELISM = 64; | |
42 | |
43 /** | |
44 * Parallel read requests. | |
45 */ | |
46 private int parallelism = DEFAULT_MAX_PARALLELISM; | |
47 | |
48 public void setRequestParallelism(int parallelism) { | |
49 this.parallelism = Math.min(parallelism, DEFAULT_MAX_PARALLELISM); | |
50 } | |
51 | |
52 /** | |
53 * Mapping request ID to request. | |
54 */ | |
55 private Map<Integer, OutstandingReadRequest> pendingReadQueue | |
56 = new HashMap<Integer, OutstandingReadRequest>(); | |
57 | |
58 /** | |
59 * Mapping request ID to request. | |
60 */ | |
61 private Map<Integer, OutstandingStatusRequest> pendingStatusQueue | |
62 = new HashMap<Integer, OutstandingStatusRequest>(); | |
63 | |
64 private PacketListener listener; | |
65 | |
66 protected AbstractSFTPClient(final Connection conn, final int version, final PacketListener listener) throws IOException { | |
67 this.listener = listener; | |
68 log.debug("Opening session and starting SFTP subsystem."); | |
69 sess = conn.openSession(); | |
70 sess.startSubSystem("sftp"); | |
71 is = sess.getStdout(); | |
72 os = new BufferedOutputStream(sess.getStdin(), 2048); | |
73 init(version); | |
74 } | |
75 | |
76 private void init(final int client_version) throws IOException { | |
77 // Send SSH_FXP_INIT with client version | |
78 TypesWriter tw = new TypesWriter(); | |
79 tw.writeUINT32(client_version); | |
80 sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes()); | |
81 /* Receive SSH_FXP_VERSION */ | |
82 log.debug("Waiting for SSH_FXP_VERSION..."); | |
83 TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */ | |
84 int t = tr.readByte(); | |
85 listener.read(Packet.forName(t)); | |
86 | |
87 if (t != Packet.SSH_FXP_VERSION) { | |
88 log.warning(String.format("The server did not send a SSH_FXP_VERSION but %d", t)); | |
89 throw new PacketTypeException(t); | |
90 } | |
91 | |
92 final int protocol_version = tr.readUINT32(); | |
93 log.debug("SSH_FXP_VERSION: protocol_version = " + protocol_version); | |
94 | |
95 if (protocol_version != client_version) { | |
96 throw new IOException(String.format("Server protocol version %d does not match %d", | |
97 protocol_version, client_version)); | |
98 } | |
99 | |
100 // Both parties should from then on adhere to particular version of the protocol | |
101 | |
102 // Read and save extensions (if any) for later use | |
103 while (tr.remain() != 0) { | |
104 String name = tr.readString(); | |
105 listener.read(name); | |
106 byte[] value = tr.readByteString(); | |
107 log.debug(String.format("SSH_FXP_VERSION: extension: %s = '%s'", name, StringEncoder.GetString(value))); | |
108 } | |
109 } | |
110 | |
111 /** | |
112 * Queries the channel state | |
113 * | |
114 * @return True if the underlying session is in open state | |
115 */ | |
116 public boolean isConnected() { | |
117 return sess.getState() == Channel.STATE_OPEN; | |
118 } | |
119 | |
120 /** | |
121 * Close this SFTP session. NEVER forget to call this method to free up | |
122 * resources - even if you got an exception from one of the other methods. | |
123 * Sometimes these other methods may throw an exception, saying that the | |
124 * underlying channel is closed (this can happen, e.g., if the other server | |
125 * sent a close message.) However, as long as you have not called the | |
126 * <code>close()</code> method, you are likely wasting resources. | |
127 */ | |
128 public void close() { | |
129 sess.close(); | |
130 } | |
131 | |
132 /** | |
133 * Set the charset used to convert between Java Unicode Strings and byte encodings | |
134 * used by the server for paths and file names. | |
135 * | |
136 * @param charset the name of the charset to be used or <code>null</code> to use UTF-8. | |
137 * @throws java.io.IOException | |
138 * @see #getCharset() | |
139 */ | |
140 public void setCharset(String charset) throws IOException { | |
141 if (charset == null) { | |
142 this.charset = null; | |
143 return; | |
144 } | |
145 | |
146 try { | |
147 Charset.forName(charset); | |
148 } | |
149 catch (UnsupportedCharsetException e) { | |
150 throw new IOException("This charset is not supported", e); | |
151 } | |
152 | |
153 this.charset = charset; | |
154 } | |
155 | |
156 /** | |
157 * The currently used charset for filename encoding/decoding. | |
158 * | |
159 * @return The name of the charset (<code>null</code> if UTF-8 is used). | |
160 * @see #setCharset(String) | |
161 */ | |
162 public String getCharset() { | |
163 return charset; | |
164 } | |
165 | |
166 public abstract SFTPFileHandle openFile(String fileName, int flags, SFTPFileAttributes attr) throws IOException; | |
167 | |
168 public void mkdir(String dirName, int posixPermissions) throws IOException { | |
169 int req_id = generateNextRequestID(); | |
170 TypesWriter tw = new TypesWriter(); | |
171 tw.writeString(dirName, this.getCharset()); | |
172 tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS); | |
173 tw.writeUINT32(posixPermissions); | |
174 sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes()); | |
175 expectStatusOKMessage(req_id); | |
176 } | |
177 | |
178 public void rm(String fileName) throws IOException { | |
179 int req_id = generateNextRequestID(); | |
180 TypesWriter tw = new TypesWriter(); | |
181 tw.writeString(fileName, this.getCharset()); | |
182 sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes()); | |
183 expectStatusOKMessage(req_id); | |
184 } | |
185 | |
186 public void rmdir(String dirName) throws IOException { | |
187 int req_id = generateNextRequestID(); | |
188 TypesWriter tw = new TypesWriter(); | |
189 tw.writeString(dirName, this.getCharset()); | |
190 sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes()); | |
191 expectStatusOKMessage(req_id); | |
192 } | |
193 | |
194 public void mv(String oldPath, String newPath) throws IOException { | |
195 int req_id = generateNextRequestID(); | |
196 TypesWriter tw = new TypesWriter(); | |
197 tw.writeString(oldPath, this.getCharset()); | |
198 tw.writeString(newPath, this.getCharset()); | |
199 sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes()); | |
200 expectStatusOKMessage(req_id); | |
201 } | |
202 | |
203 public String readLink(String path) throws IOException { | |
204 int req_id = generateNextRequestID(); | |
205 TypesWriter tw = new TypesWriter(); | |
206 tw.writeString(path, charset); | |
207 sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes()); | |
208 byte[] resp = receiveMessage(34000); | |
209 TypesReader tr = new TypesReader(resp); | |
210 int t = tr.readByte(); | |
211 listener.read(Packet.forName(t)); | |
212 int rep_id = tr.readUINT32(); | |
213 | |
214 if (rep_id != req_id) { | |
215 throw new RequestMismatchException(); | |
216 } | |
217 | |
218 if (t == Packet.SSH_FXP_NAME) { | |
219 int count = tr.readUINT32(); | |
220 | |
221 if (count != 1) { | |
222 throw new PacketTypeException(t); | |
223 } | |
224 | |
225 return tr.readString(charset); | |
226 } | |
227 | |
228 if (t != Packet.SSH_FXP_STATUS) { | |
229 throw new PacketTypeException(t); | |
230 } | |
231 | |
232 int errorCode = tr.readUINT32(); | |
233 String errorMessage = tr.readString(); | |
234 listener.read(errorMessage); | |
235 throw new SFTPException(errorMessage, errorCode); | |
236 } | |
237 | |
238 public void setstat(String path, SFTPFileAttributes attr) throws IOException { | |
239 int req_id = generateNextRequestID(); | |
240 TypesWriter tw = new TypesWriter(); | |
241 tw.writeString(path, charset); | |
242 tw.writeBytes(attr.toBytes()); | |
243 sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes()); | |
244 expectStatusOKMessage(req_id); | |
245 } | |
246 | |
247 public void fsetstat(SFTPFileHandle handle, SFTPFileAttributes attr) throws IOException { | |
248 int req_id = generateNextRequestID(); | |
249 TypesWriter tw = new TypesWriter(); | |
250 tw.writeString(handle.getHandle(), 0, handle.getHandle().length); | |
251 tw.writeBytes(attr.toBytes()); | |
252 sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes()); | |
253 expectStatusOKMessage(req_id); | |
254 } | |
255 | |
256 public void createSymlink(String src, String target) throws IOException { | |
257 int req_id = generateNextRequestID(); | |
258 TypesWriter tw = new TypesWriter(); | |
259 tw.writeString(src, charset); | |
260 tw.writeString(target, charset); | |
261 sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes()); | |
262 expectStatusOKMessage(req_id); | |
263 } | |
264 | |
265 public void createHardlink(String src, String target) throws IOException { | |
266 int req_id = generateNextRequestID(); | |
267 TypesWriter tw = new TypesWriter(); | |
268 tw.writeString("hardlink@openssh.com", charset); | |
269 tw.writeString(target, charset); | |
270 tw.writeString(src, charset); | |
271 sendMessage(Packet.SSH_FXP_EXTENDED, req_id, tw.getBytes()); | |
272 expectStatusOKMessage(req_id); | |
273 } | |
274 | |
275 public String canonicalPath(String path) throws IOException { | |
276 int req_id = generateNextRequestID(); | |
277 TypesWriter tw = new TypesWriter(); | |
278 tw.writeString(path, charset); | |
279 sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes()); | |
280 byte[] resp = receiveMessage(34000); | |
281 TypesReader tr = new TypesReader(resp); | |
282 int t = tr.readByte(); | |
283 listener.read(Packet.forName(t)); | |
284 int rep_id = tr.readUINT32(); | |
285 | |
286 if (rep_id != req_id) { | |
287 throw new RequestMismatchException(); | |
288 } | |
289 | |
290 if (t == Packet.SSH_FXP_NAME) { | |
291 int count = tr.readUINT32(); | |
292 | |
293 if (count != 1) { | |
294 throw new PacketFormatException("The server sent an invalid SSH_FXP_NAME packet."); | |
295 } | |
296 | |
297 final String name = tr.readString(charset); | |
298 listener.read(name); | |
299 return name; | |
300 } | |
301 | |
302 if (t != Packet.SSH_FXP_STATUS) { | |
303 throw new PacketTypeException(t); | |
304 } | |
305 | |
306 int errorCode = tr.readUINT32(); | |
307 String errorMessage = tr.readString(); | |
308 listener.read(errorMessage); | |
309 throw new SFTPException(errorMessage, errorCode); | |
310 } | |
311 | |
312 private void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException { | |
313 if (log.isDebugEnabled()) { | |
314 log.debug(String.format("Send message of type %d with request id %d", type, requestId)); | |
315 } | |
316 | |
317 listener.write(Packet.forName(type)); | |
318 int msglen = len + 1; | |
319 | |
320 if (type != Packet.SSH_FXP_INIT) { | |
321 msglen += 4; | |
322 } | |
323 | |
324 os.write(msglen >> 24); | |
325 os.write(msglen >> 16); | |
326 os.write(msglen >> 8); | |
327 os.write(msglen); | |
328 os.write(type); | |
329 | |
330 if (type != Packet.SSH_FXP_INIT) { | |
331 os.write(requestId >> 24); | |
332 os.write(requestId >> 16); | |
333 os.write(requestId >> 8); | |
334 os.write(requestId); | |
335 } | |
336 | |
337 os.write(msg, off, len); | |
338 os.flush(); | |
339 } | |
340 | |
341 protected void sendMessage(int type, int requestId, byte[] msg) throws IOException { | |
342 sendMessage(type, requestId, msg, 0, msg.length); | |
343 } | |
344 | |
345 private void readBytes(byte[] buff, int pos, int len) throws IOException { | |
346 while (len > 0) { | |
347 int count = is.read(buff, pos, len); | |
348 | |
349 if (count < 0) { | |
350 throw new SocketException("Unexpected end of stream."); | |
351 } | |
352 | |
353 len -= count; | |
354 pos += count; | |
355 } | |
356 } | |
357 | |
358 /** | |
359 * Read a message and guarantee that the <b>contents</b> is not larger than | |
360 * <code>maxlen</code> bytes. | |
361 * <p/> | |
362 * Note: receiveMessage(34000) actually means that the message may be up to 34004 | |
363 * bytes (the length attribute preceding the contents is 4 bytes). | |
364 * | |
365 * @param maxlen | |
366 * @return the message contents | |
367 * @throws IOException | |
368 */ | |
369 protected byte[] receiveMessage(int maxlen) throws IOException { | |
370 byte[] msglen = new byte[4]; | |
371 readBytes(msglen, 0, 4); | |
372 int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff)); | |
373 | |
374 if ((len > maxlen) || (len <= 0)) { | |
375 throw new PacketFormatException(String.format("Illegal SFTP packet length %d", len)); | |
376 } | |
377 | |
378 byte[] msg = new byte[len]; | |
379 readBytes(msg, 0, len); | |
380 return msg; | |
381 } | |
382 | |
383 protected int generateNextRequestID() { | |
384 synchronized (this) { | |
385 return next_request_id++; | |
386 } | |
387 } | |
388 | |
389 protected void closeHandle(byte[] handle) throws IOException { | |
390 int req_id = generateNextRequestID(); | |
391 TypesWriter tw = new TypesWriter(); | |
392 tw.writeString(handle, 0, handle.length); | |
393 sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes()); | |
394 expectStatusOKMessage(req_id); | |
395 } | |
396 | |
397 private void readStatus() throws IOException { | |
398 byte[] resp = receiveMessage(34000); | |
399 TypesReader tr = new TypesReader(resp); | |
400 int t = tr.readByte(); | |
401 listener.read(Packet.forName(t)); | |
402 // Search the pending queue | |
403 OutstandingStatusRequest status = pendingStatusQueue.remove(tr.readUINT32()); | |
404 | |
405 if (null == status) { | |
406 throw new RequestMismatchException(); | |
407 } | |
408 | |
409 // Evaluate the answer | |
410 if (t == Packet.SSH_FXP_STATUS) { | |
411 // In any case, stop sending more packets | |
412 int code = tr.readUINT32(); | |
413 | |
414 if (log.isDebugEnabled()) { | |
415 String[] desc = ErrorCodes.getDescription(code); | |
416 log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")"); | |
417 } | |
418 | |
419 if (code == ErrorCodes.SSH_FX_OK) { | |
420 return; | |
421 } | |
422 | |
423 String msg = tr.readString(); | |
424 listener.read(msg); | |
425 throw new SFTPException(msg, code); | |
426 } | |
427 | |
428 throw new PacketTypeException(t); | |
429 } | |
430 | |
431 private void readPendingReadStatus() throws IOException { | |
432 byte[] resp = receiveMessage(34000); | |
433 TypesReader tr = new TypesReader(resp); | |
434 int t = tr.readByte(); | |
435 listener.read(Packet.forName(t)); | |
436 // Search the pending queue | |
437 OutstandingReadRequest status = pendingReadQueue.remove(tr.readUINT32()); | |
438 | |
439 if (null == status) { | |
440 throw new RequestMismatchException(); | |
441 } | |
442 | |
443 // Evaluate the answer | |
444 if (t == Packet.SSH_FXP_STATUS) { | |
445 // In any case, stop sending more packets | |
446 int code = tr.readUINT32(); | |
447 | |
448 if (log.isDebugEnabled()) { | |
449 String[] desc = ErrorCodes.getDescription(code); | |
450 log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")"); | |
451 } | |
452 | |
453 if (code == ErrorCodes.SSH_FX_OK) { | |
454 return; | |
455 } | |
456 | |
457 if (code == ErrorCodes.SSH_FX_EOF) { | |
458 return; | |
459 } | |
460 | |
461 String msg = tr.readString(); | |
462 listener.read(msg); | |
463 throw new SFTPException(msg, code); | |
464 } | |
465 | |
466 throw new PacketTypeException(t); | |
467 } | |
468 | |
469 protected void expectStatusOKMessage(int id) throws IOException { | |
470 byte[] resp = receiveMessage(34000); | |
471 TypesReader tr = new TypesReader(resp); | |
472 int t = tr.readByte(); | |
473 listener.read(Packet.forName(t)); | |
474 int rep_id = tr.readUINT32(); | |
475 | |
476 if (rep_id != id) { | |
477 throw new RequestMismatchException(); | |
478 } | |
479 | |
480 if (t != Packet.SSH_FXP_STATUS) { | |
481 throw new PacketTypeException(t); | |
482 } | |
483 | |
484 int errorCode = tr.readUINT32(); | |
485 | |
486 if (errorCode == ErrorCodes.SSH_FX_OK) { | |
487 return; | |
488 } | |
489 | |
490 String errorMessage = tr.readString(); | |
491 listener.read(errorMessage); | |
492 throw new SFTPException(errorMessage, errorCode); | |
493 } | |
494 | |
495 public void closeFile(SFTPFileHandle handle) throws IOException { | |
496 while (!pendingReadQueue.isEmpty()) { | |
497 this.readPendingReadStatus(); | |
498 } | |
499 | |
500 while (!pendingStatusQueue.isEmpty()) { | |
501 this.readStatus(); | |
502 } | |
503 | |
504 closeHandle(handle.getHandle()); | |
505 } | |
506 | |
507 public int read(SFTPFileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException { | |
508 boolean errorOccured = false; | |
509 int remaining = len * parallelism; | |
510 //int clientOffset = dstoff; | |
511 long serverOffset = fileOffset; | |
512 | |
513 for (OutstandingReadRequest r : pendingReadQueue.values()) { | |
514 // Server offset should take pending requests into account. | |
515 serverOffset += r.len; | |
516 } | |
517 | |
518 while (true) { | |
519 // Stop if there was an error and no outstanding request | |
520 if ((pendingReadQueue.size() == 0) && errorOccured) { | |
521 break; | |
522 } | |
523 | |
524 // Send as many requests as we are allowed to | |
525 while (pendingReadQueue.size() < parallelism) { | |
526 if (errorOccured) { | |
527 break; | |
528 } | |
529 | |
530 // Send the next read request | |
531 OutstandingReadRequest req = new OutstandingReadRequest(); | |
532 req.req_id = generateNextRequestID(); | |
533 req.serverOffset = serverOffset; | |
534 req.len = (remaining > len) ? len : remaining; | |
535 req.buffer = dst; | |
536 req.dstOffset = dstoff; | |
537 serverOffset += req.len; | |
538 //clientOffset += req.len; | |
539 remaining -= req.len; | |
540 sendReadRequest(req.req_id, handle, req.serverOffset, req.len); | |
541 pendingReadQueue.put(req.req_id, req); | |
542 } | |
543 | |
544 if (pendingReadQueue.size() == 0) { | |
545 break; | |
546 } | |
547 | |
548 // Receive a single answer | |
549 byte[] resp = receiveMessage(34000); | |
550 TypesReader tr = new TypesReader(resp); | |
551 int t = tr.readByte(); | |
552 listener.read(Packet.forName(t)); | |
553 // Search the pending queue | |
554 OutstandingReadRequest req = pendingReadQueue.remove(tr.readUINT32()); | |
555 | |
556 if (null == req) { | |
557 throw new RequestMismatchException(); | |
558 } | |
559 | |
560 // Evaluate the answer | |
561 if (t == Packet.SSH_FXP_STATUS) { | |
562 /* In any case, stop sending more packets */ | |
563 int code = tr.readUINT32(); | |
564 String msg = tr.readString(); | |
565 listener.read(msg); | |
566 | |
567 if (log.isDebugEnabled()) { | |
568 String[] desc = ErrorCodes.getDescription(code); | |
569 log.debug("Got SSH_FXP_STATUS (" + req.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")"); | |
570 } | |
571 | |
572 // Flag to read all pending requests but don't send any more. | |
573 errorOccured = true; | |
574 | |
575 if (pendingReadQueue.isEmpty()) { | |
576 if (ErrorCodes.SSH_FX_EOF == code) { | |
577 return -1; | |
578 } | |
579 | |
580 throw new SFTPException(msg, code); | |
581 } | |
582 } | |
583 else if (t == Packet.SSH_FXP_DATA) { | |
584 // OK, collect data | |
585 int readLen = tr.readUINT32(); | |
586 | |
587 if ((readLen < 0) || (readLen > req.len)) { | |
588 throw new PacketFormatException("The server sent an invalid length field in a SSH_FXP_DATA packet."); | |
589 } | |
590 | |
591 if (log.isDebugEnabled()) { | |
592 log.debug("Got SSH_FXP_DATA (" + req.req_id + ") " + req.serverOffset + "/" + readLen | |
593 + " (requested: " + req.len + ")"); | |
594 } | |
595 | |
596 // Read bytes into buffer | |
597 tr.readBytes(req.buffer, req.dstOffset, readLen); | |
598 | |
599 if (readLen < req.len) { | |
600 /* Send this request packet again to request the remaing data in this slot. */ | |
601 req.req_id = generateNextRequestID(); | |
602 req.serverOffset += readLen; | |
603 req.len -= readLen; | |
604 log.debug("Requesting again: " + req.serverOffset + "/" + req.len); | |
605 sendReadRequest(req.req_id, handle, req.serverOffset, req.len); | |
606 pendingReadQueue.put(req.req_id, req); | |
607 } | |
608 | |
609 return readLen; | |
610 } | |
611 else { | |
612 throw new PacketTypeException(t); | |
613 } | |
614 } | |
615 | |
616 // Should never reach here. | |
617 throw new SFTPException("No EOF reached", -1); | |
618 } | |
619 | |
620 private void sendReadRequest(int id, SFTPFileHandle handle, long offset, int len) throws IOException { | |
621 TypesWriter tw = new TypesWriter(); | |
622 tw.writeString(handle.getHandle(), 0, handle.getHandle().length); | |
623 tw.writeUINT64(offset); | |
624 tw.writeUINT32(len); | |
625 sendMessage(Packet.SSH_FXP_READ, id, tw.getBytes()); | |
626 } | |
627 | |
628 public void write(SFTPFileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException { | |
629 while (len > 0) { | |
630 int writeRequestLen = len; | |
631 | |
632 if (writeRequestLen > 32768) { | |
633 writeRequestLen = 32768; | |
634 } | |
635 | |
636 // Send the next write request | |
637 OutstandingStatusRequest req = new OutstandingStatusRequest(); | |
638 req.req_id = generateNextRequestID(); | |
639 TypesWriter tw = new TypesWriter(); | |
640 tw.writeString(handle.getHandle(), 0, handle.getHandle().length); | |
641 tw.writeUINT64(fileOffset); | |
642 tw.writeString(src, srcoff, writeRequestLen); | |
643 sendMessage(Packet.SSH_FXP_WRITE, req.req_id, tw.getBytes()); | |
644 pendingStatusQueue.put(req.req_id, req); | |
645 | |
646 // Only read next status if parallelism reached | |
647 while (pendingStatusQueue.size() >= parallelism) { | |
648 this.readStatus(); | |
649 } | |
650 | |
651 fileOffset += writeRequestLen; | |
652 srcoff += writeRequestLen; | |
653 len -= writeRequestLen; | |
654 } | |
655 } | |
656 | |
657 | |
658 /** | |
659 * A read is divided into multiple requests sent sequentially before | |
660 * reading any status from the server | |
661 */ | |
662 private static class OutstandingReadRequest { | |
663 int req_id; | |
664 /** | |
665 * Read offset to request on server starting at the file offset for the first request. | |
666 */ | |
667 long serverOffset; | |
668 /** | |
669 * Length of requested data | |
670 */ | |
671 int len; | |
672 /** | |
673 * Offset in destination buffer | |
674 */ | |
675 int dstOffset; | |
676 /** | |
677 * Temporary buffer | |
678 */ | |
679 byte[] buffer; | |
680 } | |
681 | |
682 /** | |
683 * A read is divided into multiple requests sent sequentially before | |
684 * reading any status from the server | |
685 */ | |
686 private static final class OutstandingStatusRequest { | |
687 int req_id; | |
688 } | |
689 | |
690 } |