1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.sftp;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.Vector;
25
26 import org.apache.commons.vfs2.FileNotFoundException;
27 import org.apache.commons.vfs2.FileObject;
28 import org.apache.commons.vfs2.FileSystemException;
29 import org.apache.commons.vfs2.FileType;
30 import org.apache.commons.vfs2.NameScope;
31 import org.apache.commons.vfs2.RandomAccessContent;
32 import org.apache.commons.vfs2.VFS;
33 import org.apache.commons.vfs2.provider.AbstractFileName;
34 import org.apache.commons.vfs2.provider.AbstractFileObject;
35 import org.apache.commons.vfs2.provider.UriParser;
36 import org.apache.commons.vfs2.util.FileObjectUtils;
37 import org.apache.commons.vfs2.util.MonitorInputStream;
38 import org.apache.commons.vfs2.util.MonitorOutputStream;
39 import org.apache.commons.vfs2.util.PosixPermissions;
40 import org.apache.commons.vfs2.util.RandomAccessMode;
41
42 import com.jcraft.jsch.ChannelSftp;
43 import com.jcraft.jsch.ChannelSftp.LsEntry;
44 import com.jcraft.jsch.SftpATTRS;
45 import com.jcraft.jsch.SftpException;
46
47
48
49
50 public class SftpFileObject extends AbstractFileObject<SftpFileSystem> {
51
52
53
54
55 private class SftpInputStream extends MonitorInputStream {
56 private final ChannelSftp channel;
57
58 public SftpInputStream(final ChannelSftp channel, final InputStream in) {
59 super(in);
60 this.channel = channel;
61 }
62
63 public SftpInputStream(final ChannelSftp channel, final InputStream in, final int bufferSize) {
64 super(in, bufferSize);
65 this.channel = channel;
66 }
67
68
69
70
71 @Override
72 protected void onClose() throws IOException {
73 getAbstractFileSystem().putChannel(channel);
74 }
75 }
76
77
78
79
80 private class SftpOutputStream extends MonitorOutputStream {
81 private final ChannelSftp channel;
82
83 public SftpOutputStream(final ChannelSftp channel, final OutputStream out) {
84 super(out);
85 this.channel = channel;
86 }
87
88
89
90
91 @Override
92 protected void onClose() throws IOException {
93 getAbstractFileSystem().putChannel(channel);
94 }
95 }
96 private static final long MOD_TIME_FACTOR = 1000L;
97
98 private SftpATTRS attrs;
99
100 private final String relPath;
101
102 protected SftpFileObject(final AbstractFileName name, final SftpFileSystem fileSystem) throws FileSystemException {
103 super(name, fileSystem);
104 relPath = UriParser.decode(fileSystem.getRootName().getRelativeName(name));
105 }
106
107
108
109
110 @Override
111 protected void doCreateFolder() throws Exception {
112 final ChannelSftp channel = getAbstractFileSystem().getChannel();
113 try {
114 channel.mkdir(relPath);
115 } finally {
116 getAbstractFileSystem().putChannel(channel);
117 }
118 }
119
120
121
122
123 @Override
124 protected void doDelete() throws Exception {
125 final ChannelSftp channel = getAbstractFileSystem().getChannel();
126 try {
127 if (isFile()) {
128 channel.rm(relPath);
129 } else {
130 channel.rmdir(relPath);
131 }
132 } finally {
133 getAbstractFileSystem().putChannel(channel);
134 }
135 }
136
137
138 @Override
139 protected synchronized void doDetach() throws Exception {
140 attrs = null;
141 }
142
143
144
145
146 @Override
147 protected synchronized long doGetContentSize() throws Exception {
148 if (attrs == null || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_SIZE) == 0) {
149 throw new FileSystemException("vfs.provider.sftp/unknown-size.error");
150 }
151 return attrs.getSize();
152 }
153
154
155
156
157 @SuppressWarnings("resource")
158 @Override
159 protected InputStream doGetInputStream(final int bufferSize) throws Exception {
160
161 synchronized (getAbstractFileSystem()) {
162 final ChannelSftp channel = getAbstractFileSystem().getChannel();
163 try {
164
165
166
167
168
169
170
171
172
173
174
175 final InputStream inputStream;
176 try {
177
178
179 if (!getType().hasContent()) {
180 throw new FileSystemException("vfs.provider/read-not-file.error", getName());
181 }
182
183 inputStream = channel.get(relPath);
184 } catch (final SftpException e) {
185 if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
186 throw new FileNotFoundException(getName());
187 }
188
189 throw new FileSystemException(e);
190 }
191
192 return new SftpInputStream(channel, inputStream, bufferSize);
193
194 } finally {
195
196 }
197 }
198 }
199
200 @Override
201 protected synchronized long doGetLastModifiedTime() throws Exception {
202 if (attrs == null || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) == 0) {
203 throw new FileSystemException("vfs.provider.sftp/unknown-modtime.error");
204 }
205 return attrs.getMTime() * MOD_TIME_FACTOR;
206 }
207
208
209
210
211 @Override
212 protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
213
214
215
216
217
218
219 final ChannelSftp channel = getAbstractFileSystem().getChannel();
220 return new SftpOutputStream(channel, channel.put(relPath, bAppend ? ChannelSftp.APPEND : ChannelSftp.OVERWRITE));
221 }
222
223 @Override
224 protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
225 return new SftpRandomAccessContent(this, mode);
226 }
227
228
229
230
231 @Override
232 protected synchronized FileType doGetType() throws Exception {
233 if (attrs == null) {
234 statSelf();
235 }
236
237 if (attrs == null) {
238 return FileType.IMAGINARY;
239 }
240
241 if ((attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) == 0) {
242 throw new FileSystemException("vfs.provider.sftp/unknown-permissions.error");
243 }
244 if (attrs.isDir()) {
245 return FileType.FOLDER;
246 }
247 return FileType.FILE;
248 }
249
250 @Override
251 protected boolean doIsExecutable() throws Exception {
252 return getPermissions(true).isExecutable();
253 }
254
255 @Override
256 protected boolean doIsReadable() throws Exception {
257 return getPermissions(true).isReadable();
258 }
259
260 @Override
261 protected boolean doIsWriteable() throws Exception {
262 return getPermissions(true).isWritable();
263 }
264
265
266
267
268 @Override
269 protected String[] doListChildren() throws Exception {
270
271 return null;
272 }
273
274
275
276
277 @Override
278 protected FileObject[] doListChildrenResolved() throws Exception {
279
280 if (this.isFile()) {
281 return null;
282 }
283
284 Vector<?> vector = null;
285 final ChannelSftp channel = getAbstractFileSystem().getChannel();
286
287 try {
288
289 vector = channel.ls(relPath);
290 } catch (final SftpException e) {
291 String workingDirectory = null;
292 try {
293 if (relPath != null) {
294 workingDirectory = channel.pwd();
295 channel.cd(relPath);
296 }
297 } catch (final SftpException ex) {
298
299 return null;
300 }
301
302 SftpException lsEx = null;
303 try {
304 vector = channel.ls(".");
305 } catch (final SftpException ex) {
306 lsEx = ex;
307 } finally {
308 try {
309 if (relPath != null) {
310 channel.cd(workingDirectory);
311 }
312 } catch (final SftpException xe) {
313 throw new FileSystemException("vfs.provider.sftp/change-work-directory-back.error",
314 workingDirectory, lsEx);
315 }
316 }
317
318 if (lsEx != null) {
319 throw lsEx;
320 }
321 } finally {
322 getAbstractFileSystem().putChannel(channel);
323 }
324 FileSystemException.requireNonNull(vector, "vfs.provider.sftp/list-children.error");
325
326
327 final ArrayList<FileObject> children = new ArrayList<>();
328 for (@SuppressWarnings("unchecked")
329 final Iterator<LsEntry> iterator = (Iterator<LsEntry>) vector.iterator(); iterator.hasNext();) {
330 final LsEntry stat = iterator.next();
331
332 String name = stat.getFilename();
333 if (VFS.isUriStyle() && stat.getAttrs().isDir() && name.charAt(name.length() - 1) != '/') {
334 name = name + "/";
335 }
336
337 if (name.equals(".") || name.equals("..") || name.equals("./") || name.equals("../")) {
338 continue;
339 }
340
341 final FileObject fo = getFileSystem().resolveFile(getFileSystem().getFileSystemManager()
342 .resolveName(getName(), UriParser.encode(name), NameScope.CHILD));
343
344 ((SftpFileObject) FileObjectUtils.getAbstractFileObject(fo)).setStat(stat.getAttrs());
345
346 children.add(fo);
347 }
348
349 return children.toArray(FileObject.EMPTY_ARRAY);
350 }
351
352
353
354
355 @Override
356 protected void doRename(final FileObject newFile) throws Exception {
357 final ChannelSftp channel = getAbstractFileSystem().getChannel();
358 try {
359 final SftpFileObjectrg/apache/commons/vfs2/provider/sftp/SftpFileObject.html#SftpFileObject">SftpFileObject newSftpFileObject = (SftpFileObject) FileObjectUtils.getAbstractFileObject(newFile);
360 channel.rename(relPath, newSftpFileObject.relPath);
361 } finally {
362 getAbstractFileSystem().putChannel(channel);
363 }
364 }
365
366 @Override
367 protected synchronized boolean doSetExecutable(final boolean executable, final boolean ownerOnly) throws Exception {
368 final PosixPermissions permissions = getPermissions(false);
369 final int newPermissions = permissions.makeExecutable(executable, ownerOnly);
370 if (newPermissions == permissions.getPermissions()) {
371 return true;
372 }
373
374 attrs.setPERMISSIONS(newPermissions);
375 flushStat();
376
377 return true;
378 }
379
380
381
382
383
384
385
386
387 @Override
388 protected synchronized boolean doSetLastModifiedTime(final long modtime) throws Exception {
389 final int newMTime = (int) (modtime / MOD_TIME_FACTOR);
390 attrs.setACMODTIME(attrs.getATime(), newMTime);
391 flushStat();
392 return true;
393 }
394
395 @Override
396 protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception {
397 final PosixPermissions permissions = getPermissions(false);
398 final int newPermissions = permissions.makeReadable(readable, ownerOnly);
399 if (newPermissions == permissions.getPermissions()) {
400 return true;
401 }
402
403 attrs.setPERMISSIONS(newPermissions);
404 flushStat();
405
406 return true;
407 }
408
409 @Override
410 protected synchronized boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception {
411 final PosixPermissions permissions = getPermissions(false);
412 final int newPermissions = permissions.makeWritable(writable, ownerOnly);
413 if (newPermissions == permissions.getPermissions()) {
414 return true;
415 }
416
417 attrs.setPERMISSIONS(newPermissions);
418 flushStat();
419
420 return true;
421 }
422
423 private synchronized void flushStat() throws IOException, SftpException {
424 final ChannelSftp channel = getAbstractFileSystem().getChannel();
425 try {
426 channel.setStat(relPath, attrs);
427 } finally {
428 getAbstractFileSystem().putChannel(channel);
429 }
430 }
431
432
433
434
435
436 InputStream getInputStream(final long filePointer) throws IOException {
437 final ChannelSftp channel = getAbstractFileSystem().getChannel();
438
439
440 try {
441 return new SftpInputStream(channel, channel.get(getName().getPathDecoded(), null, filePointer));
442 } catch (final SftpException e) {
443 getAbstractFileSystem().putChannel(channel);
444 throw new FileSystemException(e);
445 }
446 }
447
448
449
450
451
452
453
454
455
456 protected synchronized PosixPermissions getPermissions(final boolean checkIds) throws Exception {
457 statSelf();
458 boolean isInGroup = false;
459 if (checkIds) {
460 if(getAbstractFileSystem().isExecDisabled()) {
461
462
463
464 return new UserIsOwnerPosixPermissions(attrs.getPermissions());
465 }
466
467 for (final int groupId : getAbstractFileSystem().getGroupsIds()) {
468 if (groupId == attrs.getGId()) {
469 isInGroup = true;
470 break;
471 }
472 }
473 }
474 final boolean isOwner = checkIds && attrs.getUId() == getAbstractFileSystem().getUId();
475 return new PosixPermissions(attrs.getPermissions(), isOwner, isInGroup);
476 }
477
478
479
480
481 @Override
482 protected void onChange() throws Exception {
483 statSelf();
484 }
485
486
487
488
489 private synchronized void setStat(final SftpATTRS attrs) {
490 this.attrs = attrs;
491 }
492
493
494
495
496
497
498 private synchronized void statSelf() throws IOException {
499 ChannelSftp channelSftp = null;
500 try {
501 channelSftp = getAbstractFileSystem().getChannel();
502 setStat(channelSftp.stat(relPath));
503 } catch (final SftpException e) {
504 try {
505
506 if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) {
507 channelSftp.disconnect();
508 channelSftp = getAbstractFileSystem().getChannel();
509 setStat(channelSftp.stat(relPath));
510 } else {
511
512 attrs = null;
513 }
514 } catch (final SftpException innerEx) {
515
516
517
518
519
520
521
522
523 attrs = null;
524 }
525 } finally {
526 if (channelSftp != null) {
527 getAbstractFileSystem().putChannel(channelSftp);
528 }
529 }
530 }
531
532 }