]> source.dussan.org Git - jgit.git/commitdiff
TransportSftp: eliminate dependency on Jsch 82/131882/8
authorThomas Wolf <thomas.wolf@paranor.ch>
Mon, 17 Sep 2018 18:36:36 +0000 (20:36 +0200)
committerMatthias Sohn <matthias.sohn@sap.com>
Tue, 13 Nov 2018 18:46:00 +0000 (10:46 -0800)
Introduce an FtpChannel abstraction, which can be obtained from a
RemoteSession. In JSchSession, wrap a JSch ChannelSftp as such an
FtpChannel. The JSch-specific SftpException is also mapped to a
generic FtpException. Rewrite TransportSftp to use only the new
abstraction layer.

This makes it possible to provide alternate ssh/sftp implementations.

Bug: 520927
Change-Id: I379026f7d4122f34931df909a28e73c02cd8a1da
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
org.eclipse.jgit/.settings/.api_filters
org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java

index 1b56ecd2edda6ccee59b6b8d2ded0a1833292ef4..9e8cecbd7f13d0a60aeaf6b3476f814a0fb50aea 100644 (file)
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/transport/RemoteSession.java" type="org.eclipse.jgit.transport.RemoteSession">
+        <filter id="404000815">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.RemoteSession"/>
+                <message_argument value="getFtpChannel()"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java
new file mode 100644 (file)
index 0000000..162a1dc
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An interface providing FTP operations over a {@link RemoteSession}. All
+ * operations are supposed to throw {@link FtpException} for remote file system
+ * errors and other IOExceptions on connection errors.
+ *
+ * @since 5.2
+ */
+public interface FtpChannel {
+
+       /**
+        * An {@link Exception} for reporting SFTP errors.
+        */
+       static class FtpException extends IOException {
+
+               private static final long serialVersionUID = 7176525179280330876L;
+
+               public static final int OK = 0;
+
+               public static final int EOF = 1;
+
+               public static final int NO_SUCH_FILE = 2;
+
+               public static final int NO_PERMISSION = 3;
+
+               public static final int UNSPECIFIED_FAILURE = 4;
+
+               public static final int PROTOCOL_ERROR = 5;
+
+               public static final int UNSUPPORTED = 8;
+
+               private final int status;
+
+               public FtpException(String message, int status) {
+                       super(message);
+                       this.status = status;
+               }
+
+               public FtpException(String message, int status, Throwable cause) {
+                       super(message, cause);
+                       this.status = status;
+               }
+
+               public int getStatus() {
+                       return status;
+               }
+       }
+
+       /**
+        * Connects the {@link FtpChannel} to the remote end.
+        *
+        * @param timeout
+        *            for establishing the FTP connection
+        * @param unit
+        *            of the {@code timeout}
+        * @throws IOException
+        */
+       void connect(int timeout, TimeUnit unit) throws IOException;
+
+       /**
+        * Disconnects and {@link FtpChannel}.
+        */
+       void disconnect();
+
+       /**
+        * @return whether the {@link FtpChannel} is connected
+        */
+       boolean isConnected();
+
+       /**
+        * Changes the current remote directory.
+        *
+        * @param path
+        *            target directory
+        * @throws IOException
+        *             if the operation could not be performed remotely
+        */
+       void cd(String path) throws IOException;
+
+       /**
+        * @return the current remote directory path
+        * @throws IOException
+        */
+       String pwd() throws IOException;
+
+       /**
+        * Simplified remote directory entry.
+        */
+       interface DirEntry {
+               String getFilename();
+
+               long getModifiedTime();
+
+               boolean isDirectory();
+       }
+
+       /**
+        * Lists contents of a remote directory
+        *
+        * @param path
+        *            of the directory to list
+        * @return the directory entries
+        * @throws IOException
+        */
+       Collection<DirEntry> ls(String path) throws IOException;
+
+       /**
+        * Deletes a directory on the remote file system. The directory must be
+        * empty.
+        *
+        * @param path
+        *            to delete
+        * @throws IOException
+        */
+       void rmdir(String path) throws IOException;
+
+       /**
+        * Creates a directory on the remote file system.
+        *
+        * @param path
+        *            to create
+        * @throws IOException
+        */
+       void mkdir(String path) throws IOException;
+
+       /**
+        * Obtain an {@link InputStream} to read the contents of a remote file.
+        *
+        * @param path
+        *            of the file to read
+        *
+        * @return the stream to read from
+        * @throws IOException
+        */
+       InputStream get(String path) throws IOException;
+
+       /**
+        * Obtain an {@link OutputStream} to write to a remote file. If the file
+        * exists already, it will be overwritten.
+        *
+        * @param path
+        *            of the file to read
+        *
+        * @return the stream to read from
+        * @throws IOException
+        */
+       OutputStream put(String path) throws IOException;
+
+       /**
+        * Deletes a file on the remote file system.
+        *
+        * @param path
+        *            to delete
+        * @throws IOException
+        */
+       void rm(String path) throws IOException;
+
+       /**
+        * Renames a file on the remote file system.
+        *
+        * @param from
+        *            original name of the file
+        * @param to
+        *            new name of the file
+        * @throws IOException
+        */
+       void rename(String from, String to) throws IOException;
+
+}
index e3ef83234309df5f4517e5742dd4e4e5a944caf5..a7797d98f736a14277a298c52f72c88c7a509dce 100644 (file)
@@ -52,6 +52,11 @@ import java.io.BufferedOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
@@ -59,8 +64,10 @@ import org.eclipse.jgit.util.io.IsolatedOutputStream;
 
 import com.jcraft.jsch.Channel;
 import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.ChannelSftp;
 import com.jcraft.jsch.JSchException;
 import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
 
 /**
  * Run remote commands using Jsch.
@@ -109,11 +116,23 @@ public class JschSession implements RemoteSession {
         * @return a channel suitable for Sftp operations.
         * @throws com.jcraft.jsch.JSchException
         *             on problems getting the channel.
+        * @deprecated since 5.2; use {@link #getFtpChannel()} instead
         */
+       @Deprecated
        public Channel getSftpChannel() throws JSchException {
                return sock.openChannel("sftp"); //$NON-NLS-1$
        }
 
+       /**
+        * {@inheritDoc}
+        *
+        * @since 5.2
+        */
+       @Override
+       public FtpChannel getFtpChannel() {
+               return new JschFtpChannel();
+       }
+
        /**
         * Implementation of Process for running a single command using Jsch.
         * <p>
@@ -233,4 +252,127 @@ public class JschSession implements RemoteSession {
                        return exitValue();
                }
        }
+
+       private class JschFtpChannel implements FtpChannel {
+
+               private ChannelSftp ftp;
+
+               @Override
+               public void connect(int timeout, TimeUnit unit) throws IOException {
+                       try {
+                               ftp = (ChannelSftp) sock.openChannel("sftp"); //$NON-NLS-1$
+                               ftp.connect((int) unit.toMillis(timeout));
+                       } catch (JSchException e) {
+                               ftp = null;
+                               throw new IOException(e.getLocalizedMessage(), e);
+                       }
+               }
+
+               @Override
+               public void disconnect() {
+                       ftp.disconnect();
+                       ftp = null;
+               }
+
+               private <T> T map(Callable<T> op) throws IOException {
+                       try {
+                               return op.call();
+                       } catch (Exception e) {
+                               if (e instanceof SftpException) {
+                                       throw new FtpChannel.FtpException(e.getLocalizedMessage(),
+                                                       ((SftpException) e).id, e);
+                               }
+                               throw new IOException(e.getLocalizedMessage(), e);
+                       }
+               }
+
+               @Override
+               public boolean isConnected() {
+                       return ftp != null && sock.isConnected();
+               }
+
+               @Override
+               public void cd(String path) throws IOException {
+                       map(() -> {
+                               ftp.cd(path);
+                               return null;
+                       });
+               }
+
+               @Override
+               public String pwd() throws IOException {
+                       return map(() -> ftp.pwd());
+               }
+
+               @Override
+               public Collection<DirEntry> ls(String path) throws IOException {
+                       return map(() -> {
+                               List<DirEntry> result = new ArrayList<>();
+                               for (Object e : ftp.ls(path)) {
+                                       ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) e;
+                                       result.add(new DirEntry() {
+
+                                               @Override
+                                               public String getFilename() {
+                                                       return entry.getFilename();
+                                               }
+
+                                               @Override
+                                               public long getModifiedTime() {
+                                                       return entry.getAttrs().getMTime();
+                                               }
+
+                                               @Override
+                                               public boolean isDirectory() {
+                                                       return entry.getAttrs().isDir();
+                                               }
+                                       });
+                               }
+                               return result;
+                       });
+               }
+
+               @Override
+               public void rmdir(String path) throws IOException {
+                       map(() -> {
+                               ftp.rm(path);
+                               return null;
+                       });
+               }
+
+               @Override
+               public void mkdir(String path) throws IOException {
+                       map(() -> {
+                               ftp.mkdir(path);
+                               return null;
+                       });
+               }
+
+               @Override
+               public InputStream get(String path) throws IOException {
+                       return map(() -> ftp.get(path));
+               }
+
+               @Override
+               public OutputStream put(String path) throws IOException {
+                       return map(() -> ftp.put(path));
+               }
+
+               @Override
+               public void rm(String path) throws IOException {
+                       map(() -> {
+                               ftp.rm(path);
+                               return null;
+                       });
+               }
+
+               @Override
+               public void rename(String from, String to) throws IOException {
+                       map(() -> {
+                               ftp.rename(from, to);
+                               return null;
+                       });
+               }
+
+       }
 }
index 525c895f450d770d7c3fa93a3e8f943d0251f0ea..e2109c2c5b588464682105dc62f34c7f017b1bf9 100644 (file)
@@ -78,10 +78,21 @@ public interface RemoteSession {
         *             a TransportException may be thrown (a subclass of
         *             java.io.IOException).
         */
-       public Process exec(String commandName, int timeout) throws IOException;
+       Process exec(String commandName, int timeout) throws IOException;
+
+       /**
+        * Obtain an {@link FtpChannel} for performing FTP operations over this
+        * {@link RemoteSession}. The default implementation returns {@code null}.
+        *
+        * @return the {@link FtpChannel}
+        * @since 5.2
+        */
+       default FtpChannel getFtpChannel() {
+               return null;
+       }
 
        /**
         * Disconnect the remote session
         */
-       public void disconnect();
+       void disconnect();
 }
index e040e0c1cc46a5a1ba384b0fe0722b52f152f4db..5cf986718f7150c303eabd146a1a63c2f267867e 100644 (file)
@@ -53,13 +53,14 @@ import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.TransportException;
@@ -73,12 +74,6 @@ import org.eclipse.jgit.lib.Ref.Storage;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.SymbolicRef;
 
-import com.jcraft.jsch.Channel;
-import com.jcraft.jsch.ChannelSftp;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.SftpATTRS;
-import com.jcraft.jsch.SftpException;
-
 /**
  * Transport over the non-Git aware SFTP (SSH based FTP) protocol.
  * <p>
@@ -158,24 +153,16 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                return r;
        }
 
-       ChannelSftp newSftp() throws TransportException {
-               final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0;
-               try {
-                       // @TODO: Fix so that this operation is generic and casting to
-                       // JschSession is no longer necessary.
-                       final Channel channel = ((JschSession) getSession())
-                                       .getSftpChannel();
-                       channel.connect(tms);
-                       return (ChannelSftp) channel;
-               } catch (JSchException je) {
-                       throw new TransportException(uri, je.getMessage(), je);
-               }
+       FtpChannel newSftp() throws IOException {
+               FtpChannel channel = getSession().getFtpChannel();
+               channel.connect(getTimeout(), TimeUnit.SECONDS);
+               return channel;
        }
 
        class SftpObjectDB extends WalkRemoteObjectDatabase {
                private final String objectsPath;
 
-               private ChannelSftp ftp;
+               private FtpChannel ftp;
 
                SftpObjectDB(String path) throws TransportException {
                        if (path.startsWith("/~")) //$NON-NLS-1$
@@ -187,13 +174,13 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                                ftp.cd(path);
                                ftp.cd("objects"); //$NON-NLS-1$
                                objectsPath = ftp.pwd();
-                       } catch (TransportException err) {
-                               close();
-                               throw err;
-                       } catch (SftpException je) {
+                       } catch (FtpChannel.FtpException f) {
                                throw new TransportException(MessageFormat.format(
                                                JGitText.get().cannotEnterObjectsPath, path,
-                                               je.getMessage()), je);
+                                               f.getMessage()), f);
+                       } catch (IOException ioe) {
+                               close();
+                               throw new TransportException(uri, ioe.getMessage(), ioe);
                        }
                }
 
@@ -204,13 +191,13 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                                ftp.cd(parent.objectsPath);
                                ftp.cd(p);
                                objectsPath = ftp.pwd();
-                       } catch (TransportException err) {
-                               close();
-                               throw err;
-                       } catch (SftpException je) {
+                       } catch (FtpChannel.FtpException f) {
                                throw new TransportException(MessageFormat.format(
                                                JGitText.get().cannotEnterPathFromParent, p,
-                                               parent.objectsPath, je.getMessage()), je);
+                                               parent.objectsPath, f.getMessage()), f);
+                       } catch (IOException ioe) {
+                               close();
+                               throw new TransportException(uri, ioe.getMessage(), ioe);
                        }
                }
 
@@ -238,41 +225,32 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                Collection<String> getPackNames() throws IOException {
                        final List<String> packs = new ArrayList<>();
                        try {
-                               @SuppressWarnings("unchecked")
-                               final Collection<ChannelSftp.LsEntry> list = ftp.ls("pack"); //$NON-NLS-1$
-                               final HashMap<String, ChannelSftp.LsEntry> files;
-                               final HashMap<String, Integer> mtimes;
-
-                               files = new HashMap<>();
-                               mtimes = new HashMap<>();
-
-                               for (ChannelSftp.LsEntry ent : list)
-                                       files.put(ent.getFilename(), ent);
-                               for (ChannelSftp.LsEntry ent : list) {
-                                       final String n = ent.getFilename();
-                                       if (!n.startsWith("pack-") || !n.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
+                               Collection<FtpChannel.DirEntry> list = ftp.ls("pack"); //$NON-NLS-1$
+                               Set<String> files = list.stream()
+                                               .map(FtpChannel.DirEntry::getFilename)
+                                               .collect(Collectors.toSet());
+                               HashMap<String, Long> mtimes = new HashMap<>();
+
+                               for (FtpChannel.DirEntry ent : list) {
+                                       String n = ent.getFilename();
+                                       if (!n.startsWith("pack-") || !n.endsWith(".pack")) { //$NON-NLS-1$ //$NON-NLS-2$
                                                continue;
-
-                                       final String in = n.substring(0, n.length() - 5) + ".idx"; //$NON-NLS-1$
-                                       if (!files.containsKey(in))
+                                       }
+                                       String in = n.substring(0, n.length() - 5) + ".idx"; //$NON-NLS-1$
+                                       if (!files.contains(in)) {
                                                continue;
-
-                                       mtimes.put(n, Integer.valueOf(ent.getAttrs().getMTime()));
+                                       }
+                                       mtimes.put(n, Long.valueOf(ent.getModifiedTime()));
                                        packs.add(n);
                                }
 
-                               Collections.sort(packs, new Comparator<String>() {
-                                       @Override
-                                       public int compare(String o1, String o2) {
-                                               return mtimes.get(o2).intValue()
-                                                               - mtimes.get(o1).intValue();
-                                       }
-                               });
-                       } catch (SftpException je) {
+                               Collections.sort(packs,
+                                               (o1, o2) -> mtimes.get(o2).compareTo(mtimes.get(o1)));
+                       } catch (FtpChannel.FtpException f) {
                                throw new TransportException(
                                                MessageFormat.format(JGitText.get().cannotListPackPath,
-                                                               objectsPath, je.getMessage()),
-                                               je);
+                                                               objectsPath, f.getMessage()),
+                                               f);
                        }
                        return packs;
                }
@@ -280,14 +258,14 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                @Override
                FileStream open(String path) throws IOException {
                        try {
-                               final SftpATTRS a = ftp.lstat(path);
-                               return new FileStream(ftp.get(path), a.getSize());
-                       } catch (SftpException je) {
-                               if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
+                               return new FileStream(ftp.get(path));
+                       } catch (FtpChannel.FtpException f) {
+                               if (f.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
                                        throw new FileNotFoundException(path);
+                               }
                                throw new TransportException(MessageFormat.format(
                                                JGitText.get().cannotGetObjectsPath, objectsPath, path,
-                                               je.getMessage()), je);
+                                               f.getMessage()), f);
                        }
                }
 
@@ -295,12 +273,15 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                void deleteFile(String path) throws IOException {
                        try {
                                ftp.rm(path);
-                       } catch (SftpException je) {
-                               if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
+                       } catch (FileNotFoundException e) {
+                               return;
+                       } catch (FtpChannel.FtpException f) {
+                               if (f.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
                                        return;
+                               }
                                throw new TransportException(MessageFormat.format(
                                                JGitText.get().cannotDeleteObjectsPath, objectsPath,
-                                               path, je.getMessage()), je);
+                                               path, f.getMessage()), f);
                        }
 
                        // Prune any now empty directories.
@@ -312,7 +293,7 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                                        dir = dir.substring(0, s);
                                        ftp.rmdir(dir);
                                        s = dir.lastIndexOf('/');
-                               } catch (SftpException je) {
+                               } catch (IOException je) {
                                        // If we cannot delete it, leave it alone. It may have
                                        // entries still in it, or maybe we lack write access on
                                        // the parent. Either way it isn't a fatal error.
@@ -325,22 +306,29 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                @Override
                OutputStream writeFile(String path, ProgressMonitor monitor,
                                String monitorTask) throws IOException {
+                       Throwable err = null;
                        try {
                                return ftp.put(path);
-                       } catch (SftpException je) {
-                               if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                       } catch (FileNotFoundException e) {
+                               mkdir_p(path);
+                       } catch (FtpChannel.FtpException je) {
+                               if (je.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
                                        mkdir_p(path);
-                                       try {
-                                               return ftp.put(path);
-                                       } catch (SftpException je2) {
-                                               je = je2;
-                                       }
+                               } else {
+                                       err = je;
                                }
-
-                               throw new TransportException(MessageFormat.format(
-                                               JGitText.get().cannotWriteObjectsPath, objectsPath,
-                                               path, je.getMessage()), je);
                        }
+                       if (err == null) {
+                               try {
+                                       return ftp.put(path);
+                               } catch (IOException e) {
+                                       err = e;
+                               }
+                       }
+                       throw new TransportException(
+                                       MessageFormat.format(JGitText.get().cannotWriteObjectsPath,
+                                                       objectsPath, path, err.getMessage()),
+                                       err);
                }
 
                @Override
@@ -350,15 +338,15 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                                super.writeFile(lock, data);
                                try {
                                        ftp.rename(lock, path);
-                               } catch (SftpException je) {
+                               } catch (IOException e) {
                                        throw new TransportException(MessageFormat.format(
                                                        JGitText.get().cannotWriteObjectsPath, objectsPath,
-                                                       path, je.getMessage()), je);
+                                                       path, e.getMessage()), e);
                                }
                        } catch (IOException err) {
                                try {
                                        ftp.rm(lock);
-                               } catch (SftpException e) {
+                               } catch (IOException e) {
                                        // Ignore deletion failure, we are already
                                        // failing anyway.
                                }
@@ -372,23 +360,30 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                                return;
 
                        path = path.substring(0, s);
+                       Throwable err = null;
                        try {
                                ftp.mkdir(path);
-                       } catch (SftpException je) {
-                               if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                               return;
+                       } catch (FileNotFoundException f) {
+                               mkdir_p(path);
+                       } catch (FtpChannel.FtpException je) {
+                               if (je.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) {
                                        mkdir_p(path);
-                                       try {
-                                               ftp.mkdir(path);
-                                               return;
-                                       } catch (SftpException je2) {
-                                               je = je2;
-                                       }
+                               } else {
+                                       err = je;
                                }
-
-                               throw new TransportException(MessageFormat.format(
-                                               JGitText.get().cannotMkdirObjectPath, objectsPath, path,
-                                               je.getMessage()), je);
                        }
+                       if (err == null) {
+                               try {
+                                       ftp.mkdir(path);
+                                       return;
+                               } catch (IOException e) {
+                                       err = e;
+                               }
+                       }
+                       throw new TransportException(MessageFormat.format(
+                                               JGitText.get().cannotMkdirObjectPath, objectsPath, path,
+                                       err.getMessage()), err);
                }
 
                Map<String, Ref> readAdvertisedRefs() throws TransportException {
@@ -399,28 +394,28 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                        return avail;
                }
 
-               @SuppressWarnings("unchecked")
                private void readLooseRefs(TreeMap<String, Ref> avail, String dir,
                                String prefix) throws TransportException {
-                       final Collection<ChannelSftp.LsEntry> list;
+                       final Collection<FtpChannel.DirEntry> list;
                        try {
                                list = ftp.ls(dir);
-                       } catch (SftpException je) {
+                       } catch (IOException e) {
                                throw new TransportException(MessageFormat.format(
                                                JGitText.get().cannotListObjectsPath, objectsPath, dir,
-                                               je.getMessage()), je);
+                                               e.getMessage()), e);
                        }
 
-                       for (ChannelSftp.LsEntry ent : list) {
-                               final String n = ent.getFilename();
+                       for (FtpChannel.DirEntry ent : list) {
+                               String n = ent.getFilename();
                                if (".".equals(n) || "..".equals(n)) //$NON-NLS-1$ //$NON-NLS-2$
                                        continue;
 
-                               final String nPath = dir + "/" + n; //$NON-NLS-1$
-                               if (ent.getAttrs().isDir())
+                               String nPath = dir + "/" + n; //$NON-NLS-1$
+                               if (ent.isDirectory()) {
                                        readLooseRefs(avail, nPath, prefix + n + "/"); //$NON-NLS-1$
-                               else
+                               } else {
                                        readRef(avail, nPath, prefix + n);
+                               }
                        }
                }
 
@@ -437,10 +432,10 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                                                err.getMessage()), err);
                        }
 
-                       if (line == null)
+                       if (line == null) {
                                throw new TransportException(
                                                MessageFormat.format(JGitText.get().emptyRef, name));
-
+                       }
                        if (line.startsWith("ref: ")) { //$NON-NLS-1$
                                final String target = line.substring("ref: ".length()); //$NON-NLS-1$
                                Ref r = avail.get(target);
@@ -465,8 +460,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                }
 
                private Storage loose(Ref r) {
-                       if (r != null && r.getStorage() == Storage.PACKED)
+                       if (r != null && r.getStorage() == Storage.PACKED) {
                                return Storage.LOOSE_PACKED;
+                       }
                        return Storage.LOOSE;
                }
 
@@ -474,8 +470,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
                void close() {
                        if (ftp != null) {
                                try {
-                                       if (ftp.isConnected())
+                                       if (ftp.isConnected()) {
                                                ftp.disconnect();
+                                       }
                                } finally {
                                        ftp = null;
                                }