diff -uprN a/daemon/gvfsbackendftp.c b/daemon/gvfsbackendftp.c
--- a/daemon/gvfsbackendftp.c	2010-04-12 09:19:41.000000000 +0200
+++ b/daemon/gvfsbackendftp.c	2010-09-01 21:25:35.000000000 +0200
@@ -109,7 +109,7 @@ gvfs_backend_ftp_determine_features (GVf
   if (!g_vfs_ftp_task_send_and_check (task, 0, NULL, NULL, &reply, "FEAT"))
     return FALSE;
 
-  task->backend->features = 0;
+  task->backend->features = G_VFS_FTP_FEATURES_DEFAULT;
   for (i = 1; reply[i]; i++)
     {
       char *feature = reply[i];
@@ -376,7 +376,7 @@ do_mount (GVfsBackend *backend,
     {
       g_vfs_ftp_task_clear_error (&task);
       g_vfs_backend_ftp_use_workaround (ftp, G_VFS_FTP_WORKAROUND_FEAT_AFTER_LOGIN);
-      ftp->features = 0;
+      ftp->features = G_VFS_FTP_FEATURES_DEFAULT;
     }
 
   addr = G_NETWORK_ADDRESS (ftp->addr);
@@ -1004,6 +1004,80 @@ do_query_info (GVfsBackend *backend,
   g_vfs_ftp_file_free (file);
 }
 
+static gboolean
+try_query_settable_attributes (GVfsBackend *backend,
+			       GVfsJobQueryAttributes *job,
+			       const char *filename)
+{
+  GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+  GFileAttributeInfoList *list;
+
+  if ( !(ftp->features & (1 << G_VFS_FTP_FEATURE_CHMOD)) )
+    {    
+    g_vfs_job_failed (G_VFS_JOB (job),
+      G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation unsupported"));
+    return FALSE;
+    }    
+
+  list = g_file_attribute_info_list_new ();
+
+  g_file_attribute_info_list_add (list,
+				  G_FILE_ATTRIBUTE_UNIX_MODE,
+				  G_FILE_ATTRIBUTE_TYPE_UINT32,
+				  G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
+				  G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+  
+  g_vfs_job_query_attributes_set_list (job, list);
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+  g_file_attribute_info_list_unref (list);
+  
+  return TRUE;
+}
+
+static void
+do_set_attribute (GVfsBackend *backend,
+                  GVfsJobSetAttribute *job,
+                  const char *filename,
+                  const char *attribute,
+                  GFileAttributeType type,
+                  gpointer value_p,
+                  GFileQueryInfoFlags flags)
+{
+  GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+  GVfsFtpTask task = G_VFS_FTP_TASK_INIT (ftp, G_VFS_JOB (job));
+  GVfsFtpFile *file;
+  guint res;
+
+  if ( !(ftp->features & (1 << G_VFS_FTP_FEATURE_CHMOD))
+    || strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) != 0 )
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                        _("Operation unsupported"));
+      return;
+    }
+
+  file = g_vfs_ftp_file_new_from_gvfs (ftp, filename);
+  res = g_vfs_ftp_task_send (&task, 0, "SITE CHMOD %04o %s",
+    (unsigned)((*(guint32 *)value_p) & 0777), g_vfs_ftp_file_get_ftp_path (file) );
+
+  if ( 2 == G_VFS_FTP_RESPONSE_GROUP(res))
+    {
+    g_vfs_ftp_dir_cache_purge_file( ftp->dir_cache, file);
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+  else 
+    {
+    if ( 502 == res) /* command not implemented */
+      ftp->features &= ~(1 << G_VFS_FTP_FEATURE_CHMOD);
+
+    g_vfs_job_failed (G_VFS_JOB (job),
+      G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, _("Operation failed") );
+    }
+
+  g_vfs_ftp_file_free (file);
+}
+
 static void
 do_enumerate (GVfsBackend *backend,
               GVfsJobEnumerate *job,
@@ -1527,6 +1601,8 @@ g_vfs_backend_ftp_class_init (GVfsBacken
   backend_class->delete = do_delete;
   backend_class->make_directory = do_make_directory;
   backend_class->move = do_move;
+  backend_class->try_query_settable_attributes = try_query_settable_attributes;
+  backend_class->set_attribute = do_set_attribute;
   backend_class->pull = do_pull;
 }
 
diff -uprN a/daemon/gvfsbackendftp.h b/daemon/gvfsbackendftp.h
--- a/daemon/gvfsbackendftp.h	2009-06-20 12:49:36.000000000 +0200
+++ b/daemon/gvfsbackendftp.h	2010-09-01 21:17:18.000000000 +0200
@@ -36,9 +36,10 @@ typedef enum {
   G_VFS_FTP_FEATURE_TVFS,
   G_VFS_FTP_FEATURE_EPRT,
   G_VFS_FTP_FEATURE_EPSV,
-  G_VFS_FTP_FEATURE_UTF8
+  G_VFS_FTP_FEATURE_UTF8,
+  G_VFS_FTP_FEATURE_CHMOD
 } GVfsFtpFeature;
-#define G_VFS_FTP_FEATURES_DEFAULT (0)
+#define G_VFS_FTP_FEATURES_DEFAULT (1<<G_VFS_FTP_FEATURE_CHMOD)
 
 typedef enum {
   G_VFS_FTP_SYSTEM_UNKNOWN = 0,
diff -uprN a/daemon/gvfsftpdircache.c b/daemon/gvfsftpdircache.c
--- a/daemon/gvfsftpdircache.c	2010-02-22 10:57:39.000000000 +0100
+++ b/daemon/gvfsftpdircache.c	2010-08-31 14:40:18.000000000 +0200
@@ -20,6 +20,9 @@
  * Author: Benjamin Otte <otte@gnome.org>
  */
 
+#include <stdio.h>
+#include <sys/stat.h>
+
 #include <config.h>
 
 #include <glib/gi18n.h>
@@ -599,6 +602,98 @@ g_vfs_ftp_dir_cache_funcs_process (GInpu
         g_file_info_set_is_hidden (info, result.fe_fnlen > 0 &&
                                          result.fe_fname[0] == '.');
 
+      /* If unix format then parse the attributes */
+      if ( 'U' == state.lstyle)
+        {
+        char file_mode[10], uid[64], gid[64];
+
+        /* POSIX ls -l form: mode, links, owner, group */
+        if ( 3 == sscanf( line, "%10c %*u %63s %63s", file_mode, uid, gid))
+          {
+          GFileType file_type;
+          guint32 mode;
+
+          /* File type */
+          switch ( file_mode[0])
+            {
+          case '-': /* Regular file */
+            mode = S_IFREG;
+            file_type = G_FILE_TYPE_REGULAR;
+            break;
+          case 'b': /* Block special file */
+            mode = S_IFBLK;
+            file_type = G_FILE_TYPE_SPECIAL;
+            break;
+          case 'c': /* Character special */
+            mode = S_IFCHR;
+            file_type = G_FILE_TYPE_SPECIAL;
+            break;
+          case 'd': /* Directory */
+            mode = S_IFDIR;
+            file_type = G_FILE_TYPE_DIRECTORY;
+            break;
+          case 'l': /* Symbolic link */
+            mode = S_IFLNK;
+            file_type = G_FILE_TYPE_SYMBOLIC_LINK;
+            break;
+          case 'p': /* FIFO */
+            mode = S_IFIFO;
+            file_type = G_FILE_TYPE_SPECIAL;
+            break;
+          default:
+            g_debug ("file_type: unknown (%c)\n", file_mode[0]);
+            mode = 0;
+            file_type = G_FILE_TYPE_UNKNOWN;
+            break;
+            }
+          g_file_info_set_file_type (info, file_type);
+
+          /* Permissions */
+          if ( 'r' == file_mode[1])
+            mode |= S_IRUSR;
+          if ( 'w' == file_mode[2])
+            mode |= S_IWUSR;
+          switch ( file_mode[3])
+            {
+          case 'x': mode |= S_IXUSR; break;
+          case 'S': mode |= S_ISUID; break;
+          case 's': mode |= S_ISUID | S_IXUSR; break;
+            }
+
+          if ( 'r' == file_mode[4])
+            mode |= S_IRGRP;
+          if ( 'w' == file_mode[5])
+            mode |= S_IWGRP;
+          switch ( file_mode[6])
+            {
+          case 'x': mode |= S_IXGRP; break;
+          case 'S': mode |= S_ISGID; break;
+          case 's': mode |= S_ISGID | S_IXGRP; break;
+            }
+
+          if ( 'r' == file_mode[7])
+            mode |= S_IROTH;
+          if ( 'w' == file_mode[8])
+            mode |= S_IWOTH;
+          switch ( file_mode[9])
+            {
+          case 'x': mode |= S_IXOTH; break;
+          case 'T': mode |= S_ISVTX; break;
+          case 't': mode |= S_ISVTX | S_IXOTH; break;
+            }
+
+          if ( mode)
+            g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, mode);
+          else
+            g_debug ("# unknown file_mode: %.10s\n", file_mode);
+
+          g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER, uid);
+          g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP, gid);
+          }
+        else
+          g_debug ("# unknown listing format\n");
+        }
+
       /* Workaround:
        * result.fetime.tm_year contains actual year instead of offset-from-1900,
        * which mktime expects.
