[kdbus] Rework kdbus core files
[platform/upstream/glib.git] / gio / glocalfileinfo.c
index 7933ed9..7c46837 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: Alexander Larsson <alexl@redhat.com>
  */
 
 #include "config.h"
 
+#include <glib.h>
+
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#define _GNU_SOURCE
 #include <fcntl.h>
 #include <errno.h>
-#ifdef HAVE_GRP_H
+#ifdef G_OS_UNIX
 #include <grp.h>
-#endif
-#ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
 #ifdef HAVE_SELINUX
 #include <gfileinfo-priv.h>
 #include <gvfs.h>
 
-#include "glibintl.h"
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#include "glib-unix.h"
+#include "glib-private.h"
+#endif
+
+#include "thumbnail-verify.h"
 
 #ifdef G_OS_WIN32
 #include <windows.h>
 #include "glocalfileinfo.h"
 #include "gioerror.h"
 #include "gthemedicon.h"
-#include "gcontenttype.h"
 #include "gcontenttypeprivate.h"
+#include "glibintl.h"
 
-#include "gioalias.h"
 
 struct ThumbMD5Context {
        guint32 buf[4];
@@ -120,18 +119,18 @@ static GHashTable *gid_cache = NULL;
 char *
 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
 {
-  GTimeVal tv;
-  
-  tv.tv_sec = statbuf->st_mtime;
+  glong sec, usec;
+
+  sec = statbuf->st_mtime;
 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
-  tv.tv_usec = statbuf->st_mtimensec / 1000;
+  usec = statbuf->st_mtimensec / 1000;
 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
-  tv.tv_usec = statbuf->st_mtim.tv_nsec / 1000;
+  usec = statbuf->st_mtim.tv_nsec / 1000;
 #else
-  tv.tv_usec = 0;
+  usec = 0;
 #endif
 
-  return g_strdup_printf ("%lu:%lu", tv.tv_sec, tv.tv_usec);
+  return g_strdup_printf ("%lu:%lu", sec, usec);
 }
 
 static char *
@@ -323,7 +322,7 @@ hex_escape_string (const char *str,
          *p++ = hex_digits[c & 0xf];
        }
     }
-  *p++ = 0;
+  *p = 0;
 
   *free_return = TRUE;
   return escaped_str;
@@ -663,6 +662,7 @@ get_xattrs_from_fd (int                    fd,
                g_free (escaped_attr);
              
              get_one_xattr_from_fd (fd, info, gio_attr, attr);
+             g_free (gio_attr);
            }
          
          len = strlen (attr) + 1;
@@ -788,10 +788,7 @@ _g_local_file_info_get_parent_info (const char            *dir,
                                    GFileAttributeMatcher *attribute_matcher,
                                    GLocalParentFileInfo  *parent_info)
 {
-  /* Use plain struct stat for now as long as we only look at the
-   * S_ISVTX bit which doesn't exist on Win32 anyway.
-   */
-  struct stat statbuf;
+  GStatBuf statbuf;
   int res;
 
   parent_info->extra_data = NULL;
@@ -1100,6 +1097,7 @@ lookup_uid_data (uid_t uid)
       if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
        data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
 
+#ifndef __BIONIC__
       gecos = pwbufp->pw_gecos;
 
       if (gecos)
@@ -1109,6 +1107,7 @@ lookup_uid_data (uid_t uid)
            *comma = 0;
          data->real_name = convert_pwd_string_to_utf8 (gecos);
        }
+#endif
     }
 
   /* Default fallbacks */
@@ -1219,19 +1218,19 @@ get_content_type (const char          *basename,
 {
   if (is_symlink &&
       (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
-    return g_strdup  ("inode/symlink");
-  else if (S_ISDIR(statbuf->st_mode))
+    return g_strdup ("inode/symlink");
+  else if (statbuf != NULL && S_ISDIR(statbuf->st_mode))
     return g_strdup ("inode/directory");
 #ifndef G_OS_WIN32
-  else if (S_ISCHR(statbuf->st_mode))
+  else if (statbuf != NULL && S_ISCHR(statbuf->st_mode))
     return g_strdup ("inode/chardevice");
-  else if (S_ISBLK(statbuf->st_mode))
+  else if (statbuf != NULL && S_ISBLK(statbuf->st_mode))
     return g_strdup ("inode/blockdevice");
-  else if (S_ISFIFO(statbuf->st_mode))
+  else if (statbuf != NULL && S_ISFIFO(statbuf->st_mode))
     return g_strdup ("inode/fifo");
 #endif
 #ifdef S_ISSOCK
-  else if (S_ISSOCK(statbuf->st_mode))
+  else if (statbuf != NULL && S_ISSOCK(statbuf->st_mode))
     return g_strdup ("inode/socket");
 #endif
   else
@@ -1253,17 +1252,17 @@ get_content_type (const char          *basename,
            sniff_length = 4096;
 
 #ifdef O_NOATIME         
-          fd = open (path, O_RDONLY | O_NOATIME);
+          fd = g_open (path, O_RDONLY | O_NOATIME, 0);
           if (fd < 0 && errno == EPERM)
 #endif
-           fd = open (path, O_RDONLY);
+           fd = g_open (path, O_RDONLY, 0);
 
          if (fd != -1)
            {
              ssize_t res;
              
              res = read (fd, sniff_buffer, sniff_length);
-             close (fd);
+             (void) g_close (fd, NULL);
              if (res >= 0)
                {
                  g_free (content_type);
@@ -1278,9 +1277,11 @@ get_content_type (const char          *basename,
   
 }
 
+/* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
 static void
-get_thumbnail_attributes (const char *path,
-                          GFileInfo  *info)
+get_thumbnail_attributes (const char     *path,
+                          GFileInfo      *info,
+                          const GLocalFileStat *stat_buf)
 {
   GChecksum *checksum;
   char *uri;
@@ -1291,32 +1292,53 @@ get_thumbnail_attributes (const char *path,
 
   checksum = g_checksum_new (G_CHECKSUM_MD5);
   g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
-  
-  g_free (uri);
 
   basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
   g_checksum_free (checksum);
 
-  filename = g_build_filename (g_get_home_dir (),
-                               ".thumbnails", "normal", basename,
+  filename = g_build_filename (g_get_user_cache_dir (),
+                               "thumbnails", "large", basename,
                                NULL);
 
   if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
-    _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
+    {
+      _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
+      _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
+                                                thumbnail_verify (filename, uri, stat_buf));
+    }
   else
     {
       g_free (filename);
-      filename = g_build_filename (g_get_home_dir (),
-                                   ".thumbnails", "fail",
-                                   "gnome-thumbnail-factory",
-                                   basename,
+      filename = g_build_filename (g_get_user_cache_dir (),
+                                   "thumbnails", "normal", basename,
                                    NULL);
 
       if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
-       _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
+        {
+          _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
+          _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
+                                                    thumbnail_verify (filename, uri, stat_buf));
+        }
+      else
+        {
+          g_free (filename);
+          filename = g_build_filename (g_get_user_cache_dir (),
+                                       "thumbnails", "fail",
+                                       "gnome-thumbnail-factory",
+                                       basename,
+                                       NULL);
+
+          if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+            {
+              _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
+              _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
+                                                        thumbnail_verify (filename, uri, stat_buf));
+            }
+        }
     }
   g_free (basename);
   g_free (filename);
+  g_free (uri);
 }
 
 #ifdef G_OS_WIN32
@@ -1411,6 +1433,242 @@ win32_get_file_user_info (const gchar  *filename,
 }
 #endif /* G_OS_WIN32 */
 
+#ifndef G_OS_WIN32
+/* support for '.hidden' files */
+G_LOCK_DEFINE_STATIC (hidden_cache);
+static GHashTable *hidden_cache;
+
+static gboolean
+remove_from_hidden_cache (gpointer user_data)
+{
+  G_LOCK (hidden_cache);
+  g_hash_table_remove (hidden_cache, user_data);
+  G_UNLOCK (hidden_cache);
+
+  return FALSE;
+}
+
+static GHashTable *
+read_hidden_file (const gchar *dirname)
+{
+  gchar *contents = NULL;
+  gchar *filename;
+
+  filename = g_build_path ("/", dirname, ".hidden", NULL);
+  (void) g_file_get_contents (filename, &contents, NULL, NULL);
+  g_free (filename);
+
+  if (contents != NULL)
+    {
+      GHashTable *table;
+      gchar **lines;
+      gint i;
+
+      table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+      lines = g_strsplit (contents, "\n", 0);
+      g_free (contents);
+
+      for (i = 0; lines[i]; i++)
+        /* hash table takes the individual strings... */
+        g_hash_table_add (table, lines[i]);
+
+      /* ... so we only free the container. */
+      g_free (lines);
+
+      return table;
+    }
+  else
+    return NULL;
+}
+
+static void
+maybe_unref_hash_table (gpointer data)
+{
+  if (data != NULL)
+    g_hash_table_unref (data);
+}
+
+static gboolean
+file_is_hidden (const gchar *path,
+                const gchar *basename)
+{
+  gboolean result;
+  gchar *dirname;
+  gpointer table;
+
+  dirname = g_path_get_dirname (path);
+
+  G_LOCK (hidden_cache);
+
+  if G_UNLIKELY (hidden_cache == NULL)
+    hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                          g_free, maybe_unref_hash_table);
+
+  if (!g_hash_table_lookup_extended (hidden_cache, dirname,
+                                     NULL, &table))
+    {
+      gchar *mydirname;
+      GSource *remove_from_cache_source;
+
+      g_hash_table_insert (hidden_cache,
+                           mydirname = g_strdup (dirname),
+                           table = read_hidden_file (dirname));
+
+      remove_from_cache_source = g_timeout_source_new_seconds (5);
+      g_source_set_priority (remove_from_cache_source, G_PRIORITY_DEFAULT);
+      g_source_set_callback (remove_from_cache_source, 
+                             remove_from_hidden_cache, 
+                             mydirname, 
+                             NULL);
+      g_source_attach (remove_from_cache_source, 
+                       GLIB_PRIVATE_CALL (g_get_worker_context) ());
+      g_source_unref (remove_from_cache_source);
+    }
+
+  result = table != NULL && g_hash_table_contains (table, basename);
+
+  G_UNLOCK (hidden_cache);
+
+  g_free (dirname);
+
+  return result;
+}
+#endif /* !G_OS_WIN32 */
+
+void
+_g_local_file_info_get_nostat (GFileInfo              *info,
+                               const char             *basename,
+                              const char             *path,
+                               GFileAttributeMatcher  *attribute_matcher)
+{
+  g_file_info_set_name (info, basename);
+
+  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
+    {
+      char *display_name = g_filename_display_basename (path);
+     
+      /* look for U+FFFD REPLACEMENT CHARACTER */ 
+      if (strstr (display_name, "\357\277\275") != NULL)
+       {
+         char *p = display_name;
+         display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
+         g_free (p);
+       }
+      g_file_info_set_display_name (info, display_name);
+      g_free (display_name);
+    }
+  
+  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
+    {
+      char *edit_name = g_filename_display_basename (path);
+      g_file_info_set_edit_name (info, edit_name);
+      g_free (edit_name);
+    }
+
+  
+  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
+    {
+      char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
+      if (copy_name)
+       _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
+      g_free (copy_name);
+    }
+}
+
+static const char *
+get_icon_name (const char *path,
+               const char *content_type,
+               gboolean    use_symbolic,
+               gboolean   *with_fallbacks_out)
+{
+  const char *name = NULL;
+  gboolean with_fallbacks = TRUE;
+
+  if (strcmp (path, g_get_home_dir ()) == 0)
+    {
+      name = use_symbolic ? "user-home-symbolic" : "user-home";
+      with_fallbacks = FALSE;
+    }
+  else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
+    {
+      name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
+      with_fallbacks = FALSE;
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
+    {
+      name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
+    {
+      name = use_symbolic ? "folder-download-symbolic" : "folder-download";
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
+    {
+      name = use_symbolic ? "folder-music-symbolic" : "folder-music";
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
+    {
+      name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
+    {
+      name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
+    {
+      name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
+    }
+  else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
+    {
+      name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
+    }
+  else if (g_strcmp0 (content_type, "inode/directory") == 0)
+    {
+      name = use_symbolic ? "folder-symbolic" : "folder";
+    }
+  else
+    {
+      name = NULL;
+    }
+
+  if (with_fallbacks_out != NULL)
+    *with_fallbacks_out = with_fallbacks;
+
+  return name;
+}
+
+static GIcon *
+get_icon (const char *path,
+          const char *content_type,
+          gboolean    use_symbolic)
+{
+  GIcon *icon = NULL;
+  const char *icon_name;
+  gboolean with_fallbacks;
+
+  icon_name = get_icon_name (path, content_type, use_symbolic, &with_fallbacks);
+  if (icon_name != NULL)
+    {
+      if (with_fallbacks)
+        icon = g_themed_icon_new_with_default_fallbacks (icon_name);
+      else
+        icon = g_themed_icon_new (icon_name);
+    }
+  else
+    {
+      if (use_symbolic)
+        icon = g_content_type_get_symbolic_icon (content_type);
+      else
+        icon = g_content_type_get_icon (content_type);
+    }
+
+  return icon;
+}
+
 GFileInfo *
 _g_local_file_info_get (const char             *basename,
                        const char             *path,
@@ -1425,6 +1683,7 @@ _g_local_file_info_get (const char             *basename,
   struct stat statbuf2;
 #endif
   int res;
+  gboolean stat_ok;
   gboolean is_symlink, symlink_broken;
 #ifdef G_OS_WIN32
   DWORD dos_attributes;
@@ -1432,17 +1691,20 @@ _g_local_file_info_get (const char             *basename,
   char *symlink_target;
   GVfs *vfs;
   GVfsClass *class;
+  guint64 device;
 
   info = g_file_info_new ();
 
   /* Make sure we don't set any unwanted attributes */
   g_file_info_set_attribute_mask (info, attribute_matcher);
   
-  g_file_info_set_name (info, basename);
+  _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
 
-  /* Avoid stat in trivial case */
   if (attribute_matcher == NULL)
-    return info;
+    {
+      g_file_info_unset_attribute_mask (info);
+      return info;
+    }
 
 #ifndef G_OS_WIN32
   res = g_lstat (path, &statbuf);
@@ -1474,18 +1736,31 @@ _g_local_file_info_get (const char             *basename,
   if (res == -1)
     {
       int errsv = errno;
-      char *display_name = g_filename_display_name (path);
-      g_object_unref (info);
-      g_set_error (error, G_IO_ERROR,
-                  g_io_error_from_errno (errsv),
-                  _("Error stating file '%s': %s"),
-                  display_name, g_strerror (errsv));
-      g_free (display_name);
-      return NULL;
+
+      /* Don't bail out if we get Permission denied (SELinux?) */
+      if (errsv != EACCES)
+        {
+          char *display_name = g_filename_display_name (path);
+          g_object_unref (info);
+          g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errsv),
+                      _("Error when getting information for file '%s': %s"),
+                      display_name, g_strerror (errsv));
+          g_free (display_name);
+          return NULL;
+        }
     }
-  
+
+  /* Even if stat() fails, try to get as much as other attributes possible */
+  stat_ok = res != -1;
+
+  if (stat_ok)
+    device = statbuf.st_dev;
+  else
+    device = 0;
+
 #ifdef S_ISLNK
-  is_symlink = S_ISLNK (statbuf.st_mode);
+  is_symlink = stat_ok && S_ISLNK (statbuf.st_mode);
 #else
   is_symlink = FALSE;
 #endif
@@ -1500,23 +1775,38 @@ _g_local_file_info_get (const char             *basename,
        {
          res = stat (path, &statbuf2);
 
-           /* Report broken links as symlinks */
+         /* Report broken links as symlinks */
          if (res != -1)
-           statbuf = statbuf2;
+           {
+             statbuf = statbuf2;
+             stat_ok = TRUE;
+           }
          else
            symlink_broken = TRUE;
        }
     }
 #endif
 
-  set_info_from_stat (info, &statbuf, attribute_matcher);
-  
-#ifndef G_OS_WIN32
-  if (basename != NULL && basename[0] == '.')
+  if (stat_ok)
+    set_info_from_stat (info, &statbuf, attribute_matcher);
+
+#ifdef G_OS_UNIX
+  if (stat_ok && _g_local_file_is_lost_found_dir (path, statbuf.st_dev))
     g_file_info_set_is_hidden (info, TRUE);
+#endif
+
+#ifndef G_OS_WIN32
+  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
+    {
+      if (basename != NULL &&
+          (basename[0] == '.' ||
+           file_is_hidden (path, basename)))
+        g_file_info_set_is_hidden (info, TRUE);
+    }
 
   if (basename != NULL && basename[strlen (basename) -1] == '~' &&
-      S_ISREG (statbuf.st_mode))
+      (stat_ok && S_ISREG (statbuf.st_mode)))
     _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE);
 #else
   if (dos_attributes & FILE_ATTRIBUTE_HIDDEN)
@@ -1541,92 +1831,41 @@ _g_local_file_info_get (const char             *basename,
     }
 #endif
   if (_g_file_attribute_matcher_matches_id (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
-    {
-      char *display_name = g_filename_display_basename (path);
-     
-      /* look for U+FFFD REPLACEMENT CHARACTER */ 
-      if (strstr (display_name, "\357\277\275") != NULL)
-       {
-         char *p = display_name;
-         display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
-         g_free (p);
-       }
-      g_file_info_set_display_name (info, display_name);
-      g_free (display_name);
-    }
-  
-  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
-    {
-      char *edit_name = g_filename_display_basename (path);
-      g_file_info_set_edit_name (info, edit_name);
-      g_free (edit_name);
-    }
-
-  
-  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
-    {
-      char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
-      if (copy_name)
-       _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
-      g_free (copy_name);
-    }
-
-  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
                                            G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
       _g_file_attribute_matcher_matches_id (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_ID_STANDARD_ICON))
+                                           G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
+      _g_file_attribute_matcher_matches_id (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
     {
-      char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, FALSE);
+      char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
 
       if (content_type)
        {
          g_file_info_set_content_type (info, content_type);
 
          if (_g_file_attribute_matcher_matches_id (attribute_matcher,
-                                                   G_FILE_ATTRIBUTE_ID_STANDARD_ICON))
+                                                     G_FILE_ATTRIBUTE_ID_STANDARD_ICON)
+               || _g_file_attribute_matcher_matches_id (attribute_matcher,
+                                                        G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
            {
              GIcon *icon;
 
-              if (strcmp (path, g_get_home_dir ()) == 0)
-                icon = g_themed_icon_new ("user-home");
-              else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
-                icon = g_themed_icon_new ("user-desktop");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-documents");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-download");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-music");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-pictures");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-templates");
-              else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
-                icon = g_themed_icon_new_with_default_fallbacks ("folder-videos");
-              else
+              /* non symbolic icon */
+              icon = get_icon (path, content_type, FALSE);
+              if (icon != NULL)
                 {
-                  icon = g_content_type_get_icon (content_type);
-                  if (G_IS_THEMED_ICON (icon))
-                    {
-                      const char *type_icon = NULL;
-
-                      if (S_ISDIR (statbuf.st_mode)) 
-                        type_icon = "folder";
-                      if (type_icon)
-                        g_themed_icon_append_name (G_THEMED_ICON (icon), type_icon);
-                    }
+                  g_file_info_set_icon (info, icon);
+                  g_object_unref (icon);
                 }
 
+              /* symbolic icon */
+              icon = get_icon (path, content_type, TRUE);
               if (icon != NULL)
                 {
-                  g_file_info_set_icon (info, icon);
+                  g_file_info_set_symbolic_icon (info, icon);
                   g_object_unref (icon);
                 }
+
            }
          
          g_free (content_type);
@@ -1636,7 +1875,7 @@ _g_local_file_info_get (const char             *basename,
   if (_g_file_attribute_matcher_matches_id (attribute_matcher,
                                            G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
     {
-      char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, TRUE);
+      char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
       
       if (content_type)
        {
@@ -1653,7 +1892,8 @@ _g_local_file_info_get (const char             *basename,
 #ifdef G_OS_WIN32
       win32_get_file_user_info (path, NULL, &name, NULL);
 #else
-      name = get_username_from_uid (statbuf.st_uid);
+      if (stat_ok)
+        name = get_username_from_uid (statbuf.st_uid);
 #endif
       if (name)
        _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
@@ -1667,7 +1907,8 @@ _g_local_file_info_get (const char             *basename,
 #ifdef G_OS_WIN32
       win32_get_file_user_info (path, NULL, NULL, &name);
 #else
-      name = get_realname_from_uid (statbuf.st_uid);
+      if (stat_ok)
+        name = get_realname_from_uid (statbuf.st_uid);
 #endif
       if (name)
        _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
@@ -1681,19 +1922,21 @@ _g_local_file_info_get (const char             *basename,
 #ifdef G_OS_WIN32
       win32_get_file_user_info (path, &name, NULL, NULL);
 #else
-      name = get_groupname_from_gid (statbuf.st_gid);
+      if (stat_ok)
+        name = get_groupname_from_gid (statbuf.st_gid);
 #endif
       if (name)
        _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
       g_free (name);
     }
 
-  if (parent_info && parent_info->device != 0 &&
+  if (stat_ok && parent_info && parent_info->device != 0 &&
       _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) &&
       statbuf.st_dev != parent_info->device) 
     _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE);
   
-  get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
+  if (stat_ok)
+    get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
   
 #ifdef HAVE_SELINUX
   get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
@@ -1703,24 +1946,20 @@ _g_local_file_info_get (const char             *basename,
 
   if (_g_file_attribute_matcher_matches_id (attribute_matcher,
                                            G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH))
-    get_thumbnail_attributes (path, info);
+    {
+      if (stat_ok)
+          get_thumbnail_attributes (path, info, &statbuf);
+      else
+          get_thumbnail_attributes (path, info, NULL);
+    }
 
   vfs = g_vfs_get_default ();
   class = G_VFS_GET_CLASS (vfs);
   if (class->local_file_add_info)
     {
-      const char *extra_target;
-
-      extra_target = path;
-      if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) &&
-          is_symlink &&
-          !symlink_broken &&
-          symlink_target != NULL)
-        extra_target = symlink_target;
-
       class->local_file_add_info (vfs,
-                                  extra_target,
-                                  statbuf.st_dev,
+                                  path,
+                                  device,
                                   attribute_matcher,
                                   info,
                                   NULL,
@@ -1756,7 +1995,7 @@ _g_local_file_info_get_from_fd (int         fd,
 
       g_set_error (error, G_IO_ERROR,
                   g_io_error_from_errno (errsv),
-                  _("Error stating file descriptor: %s"),
+                  _("Error when getting information for file descriptor: %s"),
                   g_strerror (errsv));
       return NULL;
     }
@@ -1873,19 +2112,36 @@ set_unix_mode (char                       *filename,
               const GFileAttributeValue  *value,
               GError                    **error)
 {
-  guint32 val;
+  guint32 val = 0;
+  int res = 0;
   
   if (!get_uint32 (value, &val, error))
     return FALSE;
-  
+
+#ifdef HAVE_SYMLINK
   if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
-    g_set_error_literal (error, G_IO_ERROR,
-                         G_IO_ERROR_NOT_SUPPORTED,
-                         _("Cannot set permissions on symlinks"));
-    return FALSE;
-  }
+#ifdef HAVE_LCHMOD
+    res = lchmod (filename, val);
+#else
+    struct stat statbuf;
+    /* Calling chmod on a symlink changes permissions on the symlink.
+     * We don't want to do this, so we need to check for a symlink */
+    res = g_lstat (filename, &statbuf);
+    if (res == 0 && S_ISLNK (statbuf.st_mode))
+      {
+        g_set_error_literal (error, G_IO_ERROR,
+                             G_IO_ERROR_NOT_SUPPORTED,
+                             _("Cannot set permissions on symlinks"));
+        return FALSE;
+      }
+    else if (res == 0)
+      res = g_chmod (filename, val);
+#endif
+  } else
+#endif
+    res = g_chmod (filename, val);
 
-  if (g_chmod (filename, val) == -1)
+  if (res == -1)
     {
       int errsv = errno;
 
@@ -1898,7 +2154,7 @@ set_unix_mode (char                       *filename,
   return TRUE;
 }
 
-#ifdef HAVE_CHOWN
+#ifdef G_OS_UNIX
 static gboolean
 set_unix_uid_gid (char                       *filename,
                  const GFileAttributeValue  *uid_value,
@@ -1907,7 +2163,7 @@ set_unix_uid_gid (char                       *filename,
                  GError                    **error)
 {
   int res;
-  guint32 val;
+  guint32 val = 0;
   uid_t uid;
   gid_t gid;
   
@@ -2043,8 +2299,8 @@ set_mtime_atime (char                       *filename,
                 GError                    **error)
 {
   int res;
-  guint64 val;
-  guint32 val_usec;
+  guint64 val = 0;
+  guint32 val_usec = 0;
   struct stat statbuf;
   gboolean got_stat = FALSE;
   struct timeval times[2] = { {0, 0}, {0, 0} };
@@ -2182,7 +2438,7 @@ _g_local_file_info_set_attribute (char                 *filename,
   if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
     return set_unix_mode (filename, flags, &value, error);
   
-#ifdef HAVE_CHOWN
+#ifdef G_OS_UNIX
   else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
     return set_unix_uid_gid (filename, &value, NULL, flags, error);
   else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
@@ -2259,13 +2515,11 @@ _g_local_file_info_set_attributes  (char                 *filename,
                                    GError              **error)
 {
   GFileAttributeValue *value;
-#ifdef HAVE_CHOWN
+#ifdef G_OS_UNIX
   GFileAttributeValue *uid, *gid;
-#endif
 #ifdef HAVE_UTIMES
   GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
 #endif
-#if defined (HAVE_CHOWN) || defined (HAVE_UTIMES)
   GFileAttributeStatus status;
 #endif
   gboolean res;
@@ -2295,7 +2549,7 @@ _g_local_file_info_set_attributes  (char                 *filename,
     }
 #endif
 
-#ifdef HAVE_CHOWN
+#ifdef G_OS_UNIX
   /* Group uid and gid setting into one call
    * Change ownership before permissions, since ownership changes can
      change permissions (e.g. setuid)