You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2012/07/16 16:10:40 UTC

svn commit: r1362045 - in /commons/proper/vfs/trunk: ./ core/src/main/java/org/apache/commons/vfs2/ core/src/main/java/org/apache/commons/vfs2/cache/ core/src/main/java/org/apache/commons/vfs2/impl/ core/src/main/java/org/apache/commons/vfs2/provider/ ...

Author: ggregory
Date: Mon Jul 16 14:10:39 2012
New Revision: 1362045

URL: http://svn.apache.org/viewvc?rev=1362045&view=rev
Log:
[VFS-405] Get/set the file permissions.
[VFS-415] Update VFS requirement to Java 1.6.

Added:
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java   (with props)
    commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java   (with props)
Removed:
    commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/local/test/PermissionsTests.java
Modified:
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/FileObject.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/Resources.properties
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/cache/OnCallRefreshFileObject.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/DecoratedFileObject.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/SynchronizedFileObject.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java
    commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileSystem.java
    commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/local/test/LocalProviderTestCase.java
    commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/sftp/test/SftpProviderTestCase.java
    commons/proper/vfs/trunk/pom.xml
    commons/proper/vfs/trunk/src/changes/changes.xml

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/FileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/FileObject.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/FileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/FileObject.java Mon Jul 16 14:10:39 2012
@@ -193,6 +193,48 @@ public interface FileObject extends Comp
         throws FileSystemException;
 
     /**
+     * Sets the owner's (or everybody's) write permission
+     * 
+     * @param executable
+     *            True to allow read access, false to disallow
+     * @param ownerOnly
+     *            If <code>true</code>, the permission applies only to the owner; otherwise, it applies to everybody.
+     * @return true if the operation succeeded
+     * @throws FileSystemException
+     *             On error determining if this file exists.
+     * @since 2.1
+     */
+    boolean setExecutable(boolean executable, boolean ownerOnly) throws FileSystemException;
+
+    /**
+     * Sets the owner's (or everybody's) read permission
+     * 
+     * @param readable
+     *            True to allow read access, false to disallow
+     * @param ownerOnly
+     *            If <code>true</code>, the permission applies only to the owner; otherwise, it applies to everybody.
+     * @return true if the operation succeeded
+     * @throws FileSystemException
+     *             On error determining if this file exists.
+     * @since 2.1
+     */
+    boolean setReadable(boolean readable, boolean ownerOnly) throws FileSystemException;
+
+    /**
+     * Sets the owner's (or everybody's) write permission
+     * 
+     * @param writable
+     *            True to allow read access, false to disallow
+     * @param ownerOnly
+     *            If <code>true</code>, the permission applies only to the owner; otherwise, it applies to everybody.
+     * @return true if the operation succeeded
+     * @throws FileSystemException
+     *             On error determining if this file exists.
+     * @since 2.1
+     */
+    boolean setWritable(boolean writable, boolean ownerOnly) throws FileSystemException;
+
+    /**
      * Finds a file, relative to this file.  Equivalent to calling
      * <code>resolveFile( path, NameScope.FILE_SYSTEM )</code>.
      *

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/Resources.properties
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/Resources.properties?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/Resources.properties (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/Resources.properties Mon Jul 16 14:10:39 2012
@@ -57,6 +57,9 @@ vfs.provider/check-is-executable.error=C
 vfs.provider/check-is-hidden.error=Could not determine if file "{0}" is hidden.
 vfs.provider/check-is-writeable.error=Could not determine if file "{0}" is writeable.
 vfs.provider/check-is-readable.error=Could not determine if file "{0}" is readable.
+vfs.provider/set-executable.error=Could not set the executable flag of file "{0}".
+vfs.provider/set-writeable.error=Could not set the writeable flag of file "{0}".
+vfs.provider/set-readable.error=Could not set the readable flag of file "{0}".
 vfs.provider/get-url.error=Could not create URL for "{0}".
 vfs.provider/resync.error=Could not resync "{0}".
 vfs.provider/close.error=Could not close "{0}".

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/cache/OnCallRefreshFileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/cache/OnCallRefreshFileObject.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/cache/OnCallRefreshFileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/cache/OnCallRefreshFileObject.java Mon Jul 16 14:10:39 2012
@@ -157,6 +157,27 @@ public class OnCallRefreshFileObject ext
     }
 
     @Override
+    public boolean setExecutable(boolean executable, boolean ownerOnly) throws FileSystemException
+    {
+        refresh();
+        return super.setExecutable(executable, ownerOnly);
+    }
+
+    @Override
+    public boolean setReadable(boolean readable, boolean ownerOnly) throws FileSystemException
+    {
+        refresh();
+        return super.setReadable(readable, ownerOnly);
+    }
+
+    @Override
+    public boolean setWritable(boolean writable, boolean ownerOnly) throws FileSystemException
+    {
+        refresh();
+        return super.setWritable(writable, ownerOnly);
+    }
+
+    @Override
     public void moveTo(FileObject destFile) throws FileSystemException
     {
         refresh();

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/DecoratedFileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/DecoratedFileObject.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/DecoratedFileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/DecoratedFileObject.java Mon Jul 16 14:10:39 2012
@@ -159,11 +159,29 @@ public class DecoratedFileObject impleme
         return decoratedFileObject.isReadable();
     }
 
+    @Override
+    public boolean setReadable(boolean readable, boolean ownerOnly) throws FileSystemException
+    {
+        return decoratedFileObject.setReadable(readable, ownerOnly);
+    }
+
     public boolean isWriteable() throws FileSystemException
     {
         return decoratedFileObject.isWriteable();
     }
 
+    @Override
+    public boolean setWritable(boolean writable, boolean ownerOnly) throws FileSystemException
+    {
+        return decoratedFileObject.setWritable(writable, ownerOnly);
+    }
+
+    @Override
+    public boolean setExecutable(boolean executable, boolean ownerOnly) throws FileSystemException
+    {
+        return decoratedFileObject.setExecutable(executable, ownerOnly);
+    }
+
     public Iterator<FileObject> iterator()
     {
         return decoratedFileObject.iterator();

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/SynchronizedFileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/SynchronizedFileObject.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/SynchronizedFileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/impl/SynchronizedFileObject.java Mon Jul 16 14:10:39 2012
@@ -181,6 +181,38 @@ public class SynchronizedFileObject exte
     }
 
     @Override
+    public boolean isExecutable() throws FileSystemException
+    {
+        synchronized (this) {
+            return super.isExecutable();
+        }
+    }
+
+    @Override
+    public boolean setReadable(boolean readable, boolean ownerOnly) throws FileSystemException
+    {
+        synchronized (this) {
+            return super.setReadable(readable, ownerOnly);
+        }
+    }
+
+    @Override
+    public boolean setWritable(boolean writable, boolean ownerOnly) throws FileSystemException
+    {
+        synchronized (this) {
+            return super.setWritable(writable, ownerOnly);
+        }
+    }
+
+    @Override
+    public boolean setExecutable(boolean executable, boolean ownerOnly) throws FileSystemException
+    {
+        synchronized (this) {
+            return super.setExecutable(executable, ownerOnly);
+        }
+    }
+
+    @Override
     public void moveTo(FileObject destFile) throws FileSystemException
     {
         synchronized (this)

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java Mon Jul 16 14:10:39 2012
@@ -176,6 +176,17 @@ public abstract class AbstractFileObject
     }
 
     /**
+     * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * 
+     * @see {@link #setReadable(boolean, boolean)}
+     * @since 2.1
+     */
+    protected boolean doSetReadable(boolean readable, boolean ownerOnly) throws Exception
+    {
+        return false;
+    }
+
+    /**
      * Determines if this file can be written to.  Is only called if
      * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
      * <p/>
@@ -189,6 +200,28 @@ public abstract class AbstractFileObject
     }
 
     /**
+     * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * 
+     * @see {@link #setWritable(boolean, boolean)}
+     * @since 2.1
+     */
+    protected boolean doSetWritable(boolean writable, boolean ownerOnly) throws Exception
+    {
+        return false;
+    }
+
+    /**
+     * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * 
+     * @see {@link #setExecutable(boolean, boolean)}
+     * @since 2.1
+     */
+    protected boolean doSetExecutable(boolean writable, boolean ownerOnly) throws Exception
+    {
+        return false;
+    }
+
+    /**
      * Lists the children of this file.  Is only called if {@link #doGetType}
      * returns {@link FileType#FOLDER}.  The return value of this method
      * is cached, so the implementation can be expensive.<br />
@@ -620,6 +653,26 @@ public abstract class AbstractFileObject
         }
     }
 
+    @Override
+    public boolean setReadable(boolean readable, boolean ownerOnly) throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                return doSetReadable(readable, ownerOnly);
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/set-readable.error", name, exc);
+        }
+    }
+
     /**
      * Determines if this file can be written to.
      * @return true if the file can be written to, false otherwise.
@@ -649,6 +702,46 @@ public abstract class AbstractFileObject
         }
     }
 
+    @Override
+    public boolean setWritable(boolean readable, boolean ownerOnly) throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                return doSetWritable(readable, ownerOnly);
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/set-writeable.error", name, exc);
+        }
+    }
+
+    @Override
+    public boolean setExecutable(boolean readable, boolean ownerOnly) throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                return doSetExecutable(readable, ownerOnly);
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/set-executable.error", name, exc);
+        }
+    }
+
     /**
      * Returns an iterator over a set of all FileObject in this file object.
      *

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java Mon Jul 16 14:10:39 2012
@@ -161,6 +161,12 @@ public class LocalFile extends AbstractF
         return file.canWrite();
     }
 
+    @Override
+    protected boolean doSetWritable(boolean writable, boolean ownerOnly) throws Exception
+    {
+        return file.setWritable(writable, ownerOnly);
+    }
+
     /**
      * Determines if this file is hidden.
      */
@@ -188,6 +194,18 @@ public class LocalFile extends AbstractF
         return file.canRead();
     }
 
+    @Override
+    protected boolean doSetReadable(boolean readable, boolean ownerOnly) throws Exception
+    {
+        return file.setReadable(readable, ownerOnly);
+    }
+
+    @Override
+    protected boolean doSetExecutable(boolean executable, boolean ownerOnly) throws Exception
+    {
+        return file.setExecutable(executable, ownerOnly);
+    }
+
     /**
      * Gets the last modified time of this file.
      */

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileObject.java Mon Jul 16 14:10:39 2012
@@ -20,9 +20,11 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Vector;
 
+import com.jcraft.jsch.JSchException;
 import org.apache.commons.vfs2.FileNotFoundException;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystemException;
@@ -33,10 +35,7 @@ import org.apache.commons.vfs2.VFS;
 import org.apache.commons.vfs2.provider.AbstractFileName;
 import org.apache.commons.vfs2.provider.AbstractFileObject;
 import org.apache.commons.vfs2.provider.UriParser;
-import org.apache.commons.vfs2.util.FileObjectUtils;
-import org.apache.commons.vfs2.util.MonitorInputStream;
-import org.apache.commons.vfs2.util.MonitorOutputStream;
-import org.apache.commons.vfs2.util.RandomAccessMode;
+import org.apache.commons.vfs2.util.*;
 
 import com.jcraft.jsch.ChannelSftp;
 import com.jcraft.jsch.ChannelSftp.LsEntry;
@@ -240,19 +239,23 @@ public class SftpFileObject extends Abst
     @Override
     protected boolean doSetLastModifiedTime(final long modtime) throws Exception
     {
+        int newMTime = (int) (modtime / MOD_TIME_FACTOR);
+        attrs.setACMODTIME(attrs.getATime(), newMTime);
+        flushStat();
+        return true;
+    }
+
+    private void flushStat() throws IOException, SftpException
+    {
         final ChannelSftp channel = fileSystem.getChannel();
         try
         {
-            int newMTime = (int) (modtime / MOD_TIME_FACTOR);
-
-            attrs.setACMODTIME(attrs.getATime(), newMTime);
             channel.setStat(relPath, attrs);
         }
         finally
         {
             fileSystem.putChannel(channel);
         }
-        return true;
     }
 
     /**
@@ -298,6 +301,103 @@ public class SftpFileObject extends Abst
     }
 
     /**
+     * Returns the POSIX type permissions of the file.
+     * 
+     * @boolean true if user and group ID should be checked (needed for some access rights checks)
+     * @return A PosixPermission object
+     * @throws Exception If an error occurs
+     * @since 2.1
+     */
+    protected PosixPermissions getPermissions(boolean checkIds) throws Exception
+    {
+        statSelf();
+        boolean isInGroup = false;
+        if (checkIds)
+        {
+            for (int groupId : fileSystem.getGroupsIds())
+            {
+                if (groupId == attrs.getGId())
+                {
+                    isInGroup = true;
+                    break;
+                }
+            }
+        }
+        final boolean isOwner = checkIds ?  attrs.getUId() == fileSystem.getUId() : false;
+        final PosixPermissions permissions = new PosixPermissions(attrs.getPermissions(), isOwner, isInGroup);
+
+        return permissions;
+    }
+
+    @Override
+    protected boolean doIsReadable() throws Exception
+    {
+        return getPermissions(true).isReadable();
+    }
+
+    @Override
+    protected boolean doSetReadable(boolean readable, boolean ownerOnly) throws Exception
+    {
+        final PosixPermissions permissions = getPermissions(false);
+        int newPermissions = permissions.makeReadable(readable, ownerOnly);
+        if (newPermissions == permissions.getPermissions())
+        {
+            return true;
+        }
+
+        attrs.setPERMISSIONS(newPermissions);
+        flushStat();
+
+        return true;
+    }
+
+    @Override
+    protected boolean doIsWriteable() throws Exception
+    {
+        return getPermissions(true).isWritable();
+    }
+
+    @Override
+    protected boolean doSetWritable(boolean writable, boolean ownerOnly) throws Exception
+    {
+        final PosixPermissions permissions = getPermissions(false);
+        int newPermissions = permissions.makeWritable(writable, ownerOnly);
+        if (newPermissions == permissions.getPermissions())
+        {
+            return true;
+        }
+
+        attrs.setPERMISSIONS(newPermissions);
+        flushStat();
+
+        return true;
+    }
+
+    @Override
+    protected boolean doIsExecutable() throws Exception
+    {
+        final PosixPermissions permissions = getPermissions(true);
+        return permissions.isExecutable();
+    }
+
+
+    @Override
+    protected boolean doSetExecutable(boolean executable, boolean ownerOnly) throws Exception
+    {
+        final PosixPermissions permissions = getPermissions(false);
+        int newPermissions = permissions.makeExecutable(executable, ownerOnly);
+        if (newPermissions == permissions.getPermissions())
+        {
+            return true;
+        }
+
+        attrs.setPERMISSIONS(newPermissions);
+        flushStat();
+
+        return true;
+    }
+
+    /**
      * Lists the children of this file.
      */
     @Override

Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileSystem.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileSystem.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileSystem.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/sftp/SftpFileSystem.java Mon Jul 16 14:10:39 2012
@@ -17,8 +17,11 @@
 package org.apache.commons.vfs2.provider.sftp;
 
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
 import java.util.Collection;
 
+import com.jcraft.jsch.*;
 import org.apache.commons.vfs2.Capability;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystem;
@@ -30,11 +33,6 @@ import org.apache.commons.vfs2.provider.
 import org.apache.commons.vfs2.provider.GenericFileName;
 import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
 
-import com.jcraft.jsch.ChannelSftp;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-import com.jcraft.jsch.SftpException;
-
 /**
  * Represents the files on an SFTP server.
  */
@@ -48,6 +46,14 @@ public class SftpFileSystem
     private Session session;
     // private final JSch jSch;
     private ChannelSftp idleChannel;
+    /**
+     * Cache for the user ID (-1 when not set)
+     */
+    private int uid = -1;
+    /**
+     * Cache for the user groups ids (null when not set)
+     */
+    private int[] groupsIds;
 
     protected SftpFileSystem(final GenericFileName rootName,
                              final Session session,
@@ -78,42 +84,7 @@ public class SftpFileSystem
      */
     protected ChannelSftp getChannel() throws IOException
     {
-        if (this.session == null || !this.session.isConnected())
-        {
-            doCloseCommunicationLink();
-
-            // channel closed. e.g. by freeUnusedResources, but now we need it again
-            Session session;
-            UserAuthenticationData authData = null;
-            try
-            {
-                final GenericFileName rootName = (GenericFileName) getRootName();
-
-                authData = UserAuthenticatorUtils.authenticate(getFileSystemOptions(),
-                    SftpFileProvider.AUTHENTICATOR_TYPES);
-
-                session = SftpClientFactory.createConnection(
-                    rootName.getHostName(),
-                    rootName.getPort(),
-                    UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME,
-                        UserAuthenticatorUtils.toChar(rootName.getUserName())),
-                    UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD,
-                        UserAuthenticatorUtils.toChar(rootName.getPassword())),
-                    getFileSystemOptions());
-            }
-            catch (final Exception e)
-            {
-                throw new FileSystemException("vfs.provider.sftp/connect.error",
-                    getRootName(),
-                    e);
-            }
-            finally
-            {
-                UserAuthenticatorUtils.cleanup(authData);
-            }
-
-            this.session = session;
-        }
+        ensureSession();
 
         try
         {
@@ -157,6 +128,49 @@ public class SftpFileSystem
     }
 
     /**
+     * Ensures that the session link is established
+     */
+    private void ensureSession() throws FileSystemException
+    {
+        if (this.session == null || !this.session.isConnected())
+        {
+            doCloseCommunicationLink();
+
+            // channel closed. e.g. by freeUnusedResources, but now we need it again
+            Session session;
+            UserAuthenticationData authData = null;
+            try
+            {
+                final GenericFileName rootName = (GenericFileName) getRootName();
+
+                authData = UserAuthenticatorUtils.authenticate(getFileSystemOptions(),
+                        SftpFileProvider.AUTHENTICATOR_TYPES);
+
+                session = SftpClientFactory.createConnection(
+                        rootName.getHostName(),
+                        rootName.getPort(),
+                        UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME,
+                                UserAuthenticatorUtils.toChar(rootName.getUserName())),
+                        UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD,
+                                UserAuthenticatorUtils.toChar(rootName.getPassword())),
+                        getFileSystemOptions());
+            }
+            catch (final Exception e)
+            {
+                throw new FileSystemException("vfs.provider.sftp/connect.error",
+                    getRootName(),
+                    e);
+            }
+            finally
+            {
+                UserAuthenticatorUtils.cleanup(authData);
+            }
+
+            this.session = session;
+        }
+    }
+
+    /**
      * Returns a channel to the pool.
      */
     protected void putChannel(final ChannelSftp channel)
@@ -205,4 +219,90 @@ public class SftpFileSystem
     {
         return LAST_MOD_TIME_ACCURACY;
     }
+
+    /**
+     * Get the (numeric) group ids
+     * @return
+     * @throws JSchException If a problem occurs while retrieving the groups ID
+     */
+    public int[] getGroupsIds() throws JSchException, IOException
+    {
+        if (groupsIds == null)
+        {
+
+            StringBuilder output = new StringBuilder();
+            int code = executeCommand("id -G", output);
+            if (code != 0)
+                throw new JSchException("Could not get the groups id of the current user (error code: " + code  + ")");
+
+            // Retrieve the different groups
+            final String[] groups = output.toString().trim().split("\\s+");
+
+            int [] groupsIds = new int [groups.length];
+            for(int i = 0; i < groups.length; i++)
+                groupsIds[i] = Integer.parseInt(groups[i]);
+
+            this.groupsIds = groupsIds;
+
+        }
+        return groupsIds;
+    }
+
+    /**
+     * Get the (numeric) group ids
+     * @return The numeric user ID
+     * @throws JSchException
+     * @throws IOException
+     */
+    public int getUId() throws JSchException, IOException
+    {
+        if (uid < 0)
+        {
+
+            StringBuilder output = new StringBuilder();
+            int code = executeCommand("id -u", output);
+            if (code != 0)
+                throw new JSchException("Could not get the user id of the current user (error code: " + code  + ")");
+            uid = Integer.parseInt(output.toString().trim());
+        }
+        return uid;
+    }
+
+    /**
+     * Execute a command and returns the (standard) output through a StringBuilder
+     * @param command The command
+     * @param output The output
+     * @return The exit code of the command
+     * @throws JSchException
+     * @throws IOException
+     */
+    private int executeCommand(String command, StringBuilder output) throws JSchException, IOException
+    {
+        ensureSession();
+        ChannelExec channel = (ChannelExec) session.openChannel("exec");
+
+        channel.setCommand(command);
+        channel.setInputStream(null);
+        final InputStreamReader stream = new InputStreamReader(channel.getInputStream());
+        channel.setErrStream(System.err, true);
+        channel.connect();
+
+        // Read the stream
+        char[] buffer = new char[128];
+        int read;
+        while ((read = stream.read(buffer, 0, buffer.length)) >= 0)
+        {
+            output.append(buffer, 0, read);
+        }
+        stream.close();
+
+        // Wait until the command finishes (should not be long since we read the output stream)
+        while (!channel.isClosed()) {
+            try{Thread.sleep(100);} catch(Exception ee){}
+        }
+
+        channel.disconnect();
+
+        return channel.getExitStatus();
+    }
 }

Added: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java?rev=1362045&view=auto
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java (added)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java Mon Jul 16 14:10:39 2012
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.vfs2.util;
+
+import org.apache.commons.vfs2.FileSystemException;
+
+import java.io.IOException;
+import java.util.EnumMap;
+import java.util.Map;
+
+
+/**
+ * UNIX permissions.
+ * 
+ * @since 2.1
+ */
+public class PosixPermissions
+{
+
+
+    static public enum Type
+    {
+        // User rights
+        UserReadable(00400),
+        UserWritable(00200),
+        UserExecutable(00100),
+
+        // Group rights
+        GroupReadable(00040),
+        GroupWritable(00020),
+        GroupExecutable(00010),
+
+        // Other rights
+        OtherReadable(00004),
+        OtherWritable(00002),
+        OtherExecutable(00001);
+
+        final private int mask;
+
+        /**
+         * Initialise with the mask
+         */
+        private Type(int mask)
+        {
+            this.mask = mask;
+        }
+
+        /**
+         * Return the mask for this permission
+         */
+        public int getMask()
+        {
+            return mask;
+        }
+
+    }
+
+    /**
+     * Current permissions
+     */
+    int permissions;
+
+    /**
+     * If the user is the owner of the file
+     */
+    boolean isOwner;
+
+    /**
+     * If one user group is the group of the file
+     */
+    boolean isInGroup;
+
+
+    /**
+     * Creates a new PosixPermissions object
+     * @param permissions The permissions
+     * @param isOwner true if the user is the owner of the file
+     * @param isInGroup true if the user is a group owner of the file
+     */
+    public PosixPermissions(int permissions, boolean isOwner, boolean isInGroup)
+    {
+        this.permissions = permissions;
+        this.isOwner = isOwner;
+        this.isInGroup = isInGroup;
+    }
+
+    public int getPermissions()
+    {
+        return permissions;
+    }
+
+
+    /**
+     * Computes new permission from old ones
+     *
+     * @param values The permissions to set
+     * @return The new permission
+     */
+    private int computeNewPermissions(Map<Type, Boolean> values)
+    {
+        int old = this.permissions;
+        for (Map.Entry<Type, Boolean> entry : values.entrySet())
+        {
+            final Type type = entry.getKey();
+            if (entry.getValue())
+            {
+                old |= type.getMask();
+            } else
+            {
+                old &= ~type.getMask();
+            }
+        }
+
+        return old;
+    }
+
+
+    /**
+     * Test whether the bit corresponding to the permission is set
+     */
+    private boolean get(Type type)
+    {
+        return (type.getMask() & permissions) != 0;
+    }
+
+    /**
+     * Check if whether the user can read the file
+     *
+     * @return true if the user can read
+     */
+    public boolean isReadable()
+    {
+        if (isOwner)
+        {
+            return get(Type.UserReadable);
+        }
+        if (isInGroup)
+        {
+            return get(Type.GroupReadable);
+        }
+        return get(Type.OtherReadable);
+    }
+
+
+    public Integer makeReadable(boolean readable, boolean ownerOnly)
+    {
+        EnumMap<Type, Boolean> map = new EnumMap<Type, Boolean>(Type.class);
+        map.put(Type.UserReadable, readable);
+        if (!ownerOnly)
+        {
+            map.put(Type.GroupReadable, readable);
+            map.put(Type.OtherReadable, readable);
+        }
+
+        return this.computeNewPermissions(map);
+    }
+
+    public boolean isWritable()
+    {
+        if (isOwner)
+        {
+            return get(Type.UserWritable);
+        }
+        if (isInGroup)
+        {
+            return get(Type.GroupWritable);
+        }
+        return get(Type.OtherWritable);
+    }
+
+    public Integer makeWritable(boolean writable, boolean ownerOnly)
+    {
+        EnumMap<Type, Boolean> map = new EnumMap<Type, Boolean>(Type.class);
+        map.put(Type.UserWritable, writable);
+        if (!ownerOnly)
+        {
+            map.put(Type.GroupWritable, writable);
+            map.put(Type.OtherWritable, writable);
+        }
+
+        return this.computeNewPermissions(map);
+    }
+
+    public boolean isExecutable()
+    {
+        if (isOwner)
+        {
+            return get(Type.UserExecutable);
+        }
+        if (isInGroup)
+        {
+            return get(Type.GroupExecutable);
+        }
+        return get(Type.OtherExecutable);
+    }
+
+    public int makeExecutable(boolean executable, boolean ownerOnly)
+    {
+        EnumMap<Type, Boolean> map = new EnumMap<Type, Boolean>(Type.class);
+        map.put(Type.UserExecutable, executable);
+        if (!ownerOnly)
+        {
+            map.put(Type.GroupExecutable, executable);
+            map.put(Type.OtherExecutable, executable);
+        }
+
+        return this.computeNewPermissions(map);
+    }
+}

Propchange: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/util/PosixPermissions.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/local/test/LocalProviderTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/local/test/LocalProviderTestCase.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/local/test/LocalProviderTestCase.java (original)
+++ commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/local/test/LocalProviderTestCase.java Mon Jul 16 14:10:39 2012
@@ -24,6 +24,7 @@ import org.apache.commons.AbstractVfsTes
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystemManager;
 import org.apache.commons.vfs2.test.AbstractProviderTestConfig;
+import org.apache.commons.vfs2.test.PermissionsTests;
 import org.apache.commons.vfs2.test.ProviderTestConfig;
 import org.apache.commons.vfs2.test.ProviderTestSuite;
 

Modified: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/sftp/test/SftpProviderTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/sftp/test/SftpProviderTestCase.java?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/sftp/test/SftpProviderTestCase.java (original)
+++ commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/provider/sftp/test/SftpProviderTestCase.java Mon Jul 16 14:10:39 2012
@@ -16,13 +16,14 @@
  */
 package org.apache.commons.vfs2.provider.sftp.test;
 
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
 import java.net.InetSocketAddress;
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.TreeMap;
 
+import com.jcraft.jsch.SftpATTRS;
 import junit.framework.Test;
 
 import org.apache.commons.AbstractVfsTestCase;
@@ -30,6 +31,7 @@ import org.apache.commons.vfs2.FileObjec
 import org.apache.commons.vfs2.FileSystemManager;
 import org.apache.commons.vfs2.FileSystemOptions;
 import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
+import org.apache.commons.vfs2.test.PermissionsTests;
 import org.apache.commons.vfs2.provider.sftp.SftpFileProvider;
 import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
 import org.apache.commons.vfs2.provider.sftp.TrustEveryoneUserInfo;
@@ -40,15 +42,11 @@ import org.apache.ftpserver.ftplet.FtpEx
 import org.apache.sshd.SshServer;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
+import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.SecurityUtils;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.FileSystemFactory;
-import org.apache.sshd.server.FileSystemView;
-import org.apache.sshd.server.ForwardingFilter;
-import org.apache.sshd.server.PasswordAuthenticator;
-import org.apache.sshd.server.PublickeyAuthenticator;
-import org.apache.sshd.server.SshFile;
+import org.apache.sshd.server.*;
 import org.apache.sshd.server.auth.UserAuthNone;
+import org.apache.sshd.server.command.ScpCommandFactory;
 import org.apache.sshd.server.filesystem.NativeSshFile;
 import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
@@ -62,7 +60,6 @@ import com.jcraft.jsch.TestIdentityRepos
  * <p>
  * Starts and stops an embedded Apache SSHd (MINA) server.
  * </p>
- *
  */
 public class SftpProviderTestCase extends AbstractProviderTestConfig
 {
@@ -185,7 +182,21 @@ public class SftpProviderTestCase extend
             Server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(tmpDir + "/key.ser"));
         }
         List<NamedFactory<Command>> list = new ArrayList<NamedFactory<Command>>(1);
-        list.add(new SftpSubsystem.Factory());
+        list.add(new NamedFactory<Command>()
+        {
+
+            @Override
+            public String getName()
+            {
+                return "sftp";
+            }
+
+            @Override
+            public Command create()
+            {
+                return new MySftpSubsystem();
+            }
+        });
         Server.setSubsystemFactories(list);
         Server.setPasswordAuthenticator(new PasswordAuthenticator()
         {
@@ -224,6 +235,8 @@ public class SftpProviderTestCase extend
                 return true;
             }
         });
+        // Allows the execution of commands
+        Server.setCommandFactory(new ScpCommandFactory(new TestCommandFactory()));
         // HACK Start
         // How do we really do simple user to directory matching?
         Server.setFileSystemFactory(new TestFileSystemFactory());
@@ -234,6 +247,7 @@ public class SftpProviderTestCase extend
         // Do this after we start the server to simplify this set up code.
         Server.getUserAuthFactories().add(new UserAuthNone.Factory());
         // HACK End
+
     }
 
     /**
@@ -241,7 +255,7 @@ public class SftpProviderTestCase extend
      */
     public static Test suite() throws Exception
     {
-        return new ProviderTestSuite(new SftpProviderTestCase())
+        final ProviderTestSuite suite = new ProviderTestSuite(new SftpProviderTestCase())
         {
             @Override
             protected void setUp() throws Exception
@@ -260,6 +274,13 @@ public class SftpProviderTestCase extend
                 super.tearDown();
             }
         };
+
+
+        // VFS-405: set/get permissions
+        suite.addTests(PermissionsTests.class);
+
+        return suite;
+
     }
 
     /**
@@ -311,4 +332,246 @@ public class SftpProviderTestCase extend
     {
         manager.addProvider("sftp", new SftpFileProvider());
     }
+
+
+    /**
+     * The command factory for the SSH server:
+     * Handles two commands: id -u and id -G
+     */
+    private static class TestCommandFactory extends ScpCommandFactory
+    {
+        public Command createCommand(final String command)
+        {
+            return new Command()
+            {
+                public ExitCallback callback = null;
+                public PrintStream out = null;
+                public PrintStream err = null;
+
+                @Override
+                public void setInputStream(InputStream in)
+                {
+                }
+
+                @Override
+                public void setOutputStream(OutputStream out)
+                {
+                    this.out = new PrintStream(out);
+                }
+
+                @Override
+                public void setErrorStream(OutputStream err)
+                {
+                    this.err = new PrintStream(err);
+                }
+
+                @Override
+                public void setExitCallback(ExitCallback callback)
+                {
+                    this.callback = callback;
+
+                }
+
+                @Override
+                public void start(Environment env) throws IOException
+                {
+                    int code = 0;
+                    if (command.equals("id -G") || command.equals("id -u"))
+                    {
+                        out.println(0);
+                    } else
+                    {
+                        if (err != null)
+                        {
+                            err.format("Unknown command %s%n", command);
+                        }
+                        code = -1;
+                    }
+                    if (out != null)
+                    {
+                        out.flush();
+                    }
+                    if (err != null)
+                    {
+                        err.flush();
+                    }
+                    callback.onExit(code);
+                }
+
+                @Override
+                public void destroy()
+                {
+                }
+            };
+        }
+    }
+
+
+    private static class SftpAttrs
+    {
+        int flags = 0;
+        private int uid;
+        long size = 0;
+        private int gid;
+        private int atime;
+        private int permissions;
+        private int mtime;
+        private String[] extended;
+
+        private SftpAttrs(Buffer buf)
+        {
+            int flags = 0;
+            flags = buf.getInt();
+
+            if ((flags & SftpATTRS.SSH_FILEXFER_ATTR_SIZE) != 0)
+            {
+                size = buf.getLong();
+            }
+            if ((flags & SftpATTRS.SSH_FILEXFER_ATTR_UIDGID) != 0)
+            {
+                uid = buf.getInt();
+                gid = buf.getInt();
+            }
+            if ((flags & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) != 0)
+            {
+                permissions = buf.getInt();
+            }
+            if ((flags & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) != 0)
+            {
+                atime = buf.getInt();
+            }
+            if ((flags & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) != 0)
+            {
+                mtime = buf.getInt();
+            }
+
+        }
+    }
+
+    private static class MySftpSubsystem extends SftpSubsystem
+    {
+        TreeMap<String, Integer> permissions = new TreeMap<String, Integer>();
+        private int _version;
+
+        @Override
+        protected void process(Buffer buffer) throws IOException
+        {
+            int rpos = buffer.rpos();
+            int length = buffer.getInt();
+            int type = buffer.getByte();
+            int id = buffer.getInt();
+
+            switch (type)
+            {
+                case SSH_FXP_SETSTAT:
+                case SSH_FXP_FSETSTAT:
+                {
+                    // Get the path
+                    String path = buffer.getString();
+                    // Get the permission
+                    SftpAttrs attrs = new SftpAttrs(buffer);
+                    permissions.put(path, attrs.permissions);
+//                    System.err.format("Setting [%s] permission to %o%n", path, attrs.permissions);
+                    break;
+                }
+
+                case SSH_FXP_REMOVE:
+                {
+                    // Remove cached attributes
+                    String path = buffer.getString();
+                    permissions.remove(path);
+//                    System.err.format("Removing [%s] permission cache%n", path);
+                    break;
+                }
+
+                case SSH_FXP_INIT:
+                {
+                    // Just grab the version here
+                    this._version = id;
+                    break;
+                }
+            }
+
+            buffer.rpos(rpos);
+            super.process(buffer);
+
+        }
+
+        protected void writeAttrs(Buffer buffer, SshFile file, int flags) throws IOException
+        {
+            if (!file.doesExist()) {
+                throw new FileNotFoundException(file.getAbsolutePath());
+            }
+
+
+            int p = 0;
+
+            final Integer cached = permissions.get(file.getAbsolutePath());
+            if (cached != null)
+            {
+                // Use cached permissions
+//                System.err.format("Using cached [%s] permission of %o%n", file.getAbsolutePath(), cached);
+                p |= cached;
+            } else
+            {
+                // Use permissions from Java file
+                if (file.isReadable())
+                {
+                    p |= S_IRUSR;
+                }
+                if (file.isWritable())
+                {
+                    p |= S_IWUSR;
+                }
+                if (file.isExecutable())
+                {
+                    p |= S_IXUSR;
+                }
+            }
+
+            if (_version >= 4)
+            {
+                long size = file.getSize();
+//                String username = session.getUsername();
+                long lastModif = file.getLastModified();
+                if (file.isFile())
+                {
+                    buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS);
+                    buffer.putByte((byte) SSH_FILEXFER_TYPE_REGULAR);
+                    buffer.putInt(p);
+                } else if (file.isDirectory()) {
+                    buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS);
+                    buffer.putByte((byte) SSH_FILEXFER_TYPE_DIRECTORY);
+                    buffer.putInt(p);
+                } else {
+                    buffer.putInt(0);
+                    buffer.putByte((byte) SSH_FILEXFER_TYPE_UNKNOWN);
+                }
+            } else {
+                if (file.isFile()) {
+                    p |= 0100000;
+                }
+                if (file.isDirectory()) {
+                    p |= 0040000;
+                }
+
+
+                if (file.isFile()) {
+                    buffer.putInt(SSH_FILEXFER_ATTR_SIZE| SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+                    buffer.putLong(file.getSize());
+                    buffer.putInt(p);
+                    buffer.putInt(file.getLastModified()/1000);
+                    buffer.putInt(file.getLastModified()/1000);
+                } else if (file.isDirectory()) {
+                    buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+                    buffer.putInt(p);
+                    buffer.putInt(file.getLastModified()/1000);
+                    buffer.putInt(file.getLastModified()/1000);
+                } else {
+                    buffer.putInt(0);
+                }
+            }
+        }
+
+    }
 }

Added: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java?rev=1362045&view=auto
==============================================================================
--- commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java (added)
+++ commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java Mon Jul 16 14:10:39 2012
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.vfs2.test;
+
+import junit.framework.Assert;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.apache.commons.vfs2.Capability;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.Selectors;
+import org.apache.commons.vfs2.provider.local.LocalFileSystem;
+
+import java.io.OutputStream;
+
+/**
+ * Additional naming tests for local file system.
+ * 
+ * @since 2.1
+ */
+public class PermissionsTests extends AbstractProviderTestCase
+{
+
+    /**
+     * Returns the capabilities required by the tests of this test case.
+     */
+    @Override
+    protected Capability[] getRequiredCaps()
+    {
+        return new Capability[]
+                {
+                        Capability.CREATE,
+                        Capability.DELETE,
+                        Capability.READ_CONTENT,
+                        Capability.WRITE_CONTENT,
+                };
+    }
+
+    /**
+     * Tests for the execution permission.
+     */
+    public void testExecutable() throws Exception
+    {
+        final FileObject file = createTestFile();
+
+        // On Windows, all files are executable
+        if (isWindows())
+        {
+            Assert.assertTrue(file.isExecutable());
+
+        } else
+        {
+            // Set the executable flag
+            file.setExecutable(true, true);
+            Assert.assertTrue(file.isExecutable());
+
+            // Set the executable flag
+            file.setExecutable(true, false);
+            Assert.assertTrue(file.isExecutable());
+
+            // Clear the executable flag
+            file.setExecutable(false, true);
+            Assert.assertFalse(file.isExecutable());
+        }
+    }
+
+    /**
+     * Returns true if the filesystem is a Windows file system
+     */
+    private boolean isWindows()
+    {
+        return SystemUtils.IS_OS_WINDOWS && this.getFileSystem() instanceof LocalFileSystem;
+    }
+
+
+    /**
+     * Tests for the writeable permission
+     */
+    public void testWriteable() throws Exception
+    {
+        final FileObject file = createTestFile();
+
+        // Set the executable flag
+        file.setWritable(true, true);
+        Assert.assertTrue(file.isWriteable());
+
+        // Set the executable flag
+        file.setWritable(true, false);
+        Assert.assertTrue(file.isWriteable());
+
+        // Clear the executable flag
+        file.setWritable(false, true);
+        Assert.assertFalse(file.isWriteable());
+    }
+
+
+    /**
+     * Tests for the readable permission
+     */
+    public void testReadable() throws Exception
+    {
+        final FileObject file = createTestFile();
+
+        if (isWindows())
+        {
+            // On Windows, all owned files are readable
+            Assert.assertTrue(file.isReadable());
+        }
+        else
+        {
+            // Set the executable flag
+            System.err.println(file.setReadable(true, true));
+            Assert.assertTrue(file.isReadable());
+
+            // Set the executable flag
+            file.setReadable(true, false);
+            Assert.assertTrue(file.isReadable());
+
+            // Clear the executable flag
+            file.setReadable(false, true);
+            Assert.assertFalse(file.isReadable());
+        }
+    }
+
+
+    private FileObject createTestFile() throws Exception
+    {
+        // Get the scratch folder
+        FileObject scratchFolder = getWriteFolder();
+        assertNotNull(scratchFolder);
+
+        // Make sure the test folder is empty
+        scratchFolder.delete(Selectors.EXCLUDE_SELF);
+        scratchFolder.createFolder();
+
+        // Create direct child of the test folder
+        final FileObject file = scratchFolder.resolveFile("file1.txt");
+        assertTrue(!file.exists());
+
+        // Create the source file
+        final String content = "Here is some sample content for the file.  Blah Blah Blah.";
+
+        final OutputStream os = file.getContent().getOutputStream();
+        try
+        {
+            os.write(content.getBytes("utf-8"));
+        }
+        finally
+        {
+            os.close();
+        }
+        return file;
+    }
+
+
+}
\ No newline at end of file

Propchange: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/vfs/trunk/core/src/test/java/org/apache/commons/vfs2/test/PermissionsTests.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: commons/proper/vfs/trunk/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/pom.xml?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/pom.xml (original)
+++ commons/proper/vfs/trunk/pom.xml Mon Jul 16 14:10:39 2012
@@ -128,8 +128,8 @@
     <commons.binary.suffix />
     <commons.jira.id>VFS</commons.jira.id>
     <commons.jira.pid>12310495</commons.jira.pid>
-    <maven.compile.source>1.5</maven.compile.source>
-    <maven.compile.target>1.5</maven.compile.target>
+    <maven.compile.source>1.6</maven.compile.source>
+    <maven.compile.target>1.6</maven.compile.target>
     <commons.release.name>commons-vfs-${commons.release.version}</commons.release.name>
     <vfs.parent.dir>${basedir}</vfs.parent.dir>
   </properties>
@@ -153,13 +153,11 @@
         </plugin>
         <!-- Use 2.11 to run individual tests, 2.12 seems to no longer support this feature.  -->
         <!-- See http://jira.codehaus.org/browse/SUREFIRE-827 -->
-        <!-- 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
           <version>2.11</version>
         </plugin>
-         -->
       </plugins>
     </pluginManagement>
     <plugins>

Modified: commons/proper/vfs/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/src/changes/changes.xml?rev=1362045&r1=1362044&r2=1362045&view=diff
==============================================================================
--- commons/proper/vfs/trunk/src/changes/changes.xml (original)
+++ commons/proper/vfs/trunk/src/changes/changes.xml Mon Jul 16 14:10:39 2012
@@ -23,6 +23,12 @@
 
   <body>
     <release version="2.1" date="TBD" description="New features and bug fix release.">
+      <action issue="VFS-405" dev="ggregory" type="add" due-to="dwaszak">
+        Get/set the file permissions.
+      </action>
+      <action issue="VFS-415" dev="ggregory" type="add" due-to="ggregory">
+        Update VFS requirement to Java 1.6.
+      </action>
       <action issue="VFS-426" dev="ggregory" type="add" due-to="daniel.bergholm">
         HTTP URL query string not part of cache key.
       </action>