(root)/
glib-2.79.0/
gio/
glocalfileinfo.c
       1  /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
       2  
       3  /* GIO - GLib Input, Output and Streaming Library
       4   * 
       5   * Copyright (C) 2006-2007 Red Hat, Inc.
       6   *
       7   * SPDX-License-Identifier: LGPL-2.1-or-later
       8   *
       9   * This library is free software; you can redistribute it and/or
      10   * modify it under the terms of the GNU Lesser General Public
      11   * License as published by the Free Software Foundation; either
      12   * version 2.1 of the License, or (at your option) any later version.
      13   *
      14   * This library is distributed in the hope that it will be useful,
      15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17   * Lesser General Public License for more details.
      18   *
      19   * You should have received a copy of the GNU Lesser General
      20   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      21   *
      22   * Author: Alexander Larsson <alexl@redhat.com>
      23   */
      24  
      25  #include "config.h"
      26  
      27  #include <glib.h>
      28  
      29  #ifdef HAVE_SYS_TIME_H
      30  #include <sys/time.h>
      31  #endif
      32  #include <sys/types.h>
      33  #include <sys/stat.h>
      34  #include <string.h>
      35  #include <fcntl.h>
      36  #include <errno.h>
      37  #ifdef G_OS_UNIX
      38  #include <grp.h>
      39  #include <pwd.h>
      40  #endif
      41  #ifdef HAVE_SELINUX
      42  #include <selinux/selinux.h>
      43  #endif
      44  
      45  #ifdef HAVE_XATTR
      46  
      47  #if defined HAVE_SYS_XATTR_H
      48    #include <sys/xattr.h>
      49  #elif defined HAVE_ATTR_XATTR_H
      50    #include <attr/xattr.h>
      51  #else
      52    #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
      53  #endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
      54  
      55  #endif /* HAVE_XATTR */
      56  
      57  #include <glib/gstdio.h>
      58  #include <glib/gstdioprivate.h>
      59  #include <gfileattribute-priv.h>
      60  #include <gfileinfo-priv.h>
      61  #include <gvfs.h>
      62  
      63  #ifdef G_OS_UNIX
      64  #include <unistd.h>
      65  #include "glib-unix.h"
      66  #endif
      67  
      68  #include "glib-private.h"
      69  
      70  #include "thumbnail-verify.h"
      71  
      72  #ifdef G_OS_WIN32
      73  #include <windows.h>
      74  #include <io.h>
      75  #ifndef W_OK
      76  #define W_OK 2
      77  #endif
      78  #ifndef R_OK
      79  #define R_OK 4
      80  #endif
      81  #ifndef X_OK
      82  #define X_OK 0 /* not really */
      83  #endif
      84  #ifndef S_ISREG
      85  #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
      86  #endif
      87  #ifndef S_ISDIR
      88  #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
      89  #endif
      90  #ifndef S_IXUSR
      91  #define S_IXUSR _S_IEXEC
      92  #endif
      93  #endif
      94  
      95  #ifndef O_CLOEXEC
      96  #define O_CLOEXEC 0
      97  #endif
      98  
      99  #include "glocalfileinfo.h"
     100  #include "gioerror.h"
     101  #include "gthemedicon.h"
     102  #include "gcontenttypeprivate.h"
     103  #include "glibintl.h"
     104  
     105  
     106  struct ThumbMD5Context {
     107  	guint32 buf[4];
     108  	guint32 bits[2];
     109  	unsigned char in[64];
     110  };
     111  
     112  #ifndef G_OS_WIN32
     113  
     114  typedef struct {
     115    char *user_name;
     116    char *real_name;
     117  } UidData;
     118  
     119  G_LOCK_DEFINE_STATIC (uid_cache);
     120  static GHashTable *uid_cache = NULL;
     121  
     122  G_LOCK_DEFINE_STATIC (gid_cache);
     123  static GHashTable *gid_cache = NULL;
     124  
     125  #endif  /* !G_OS_WIN32 */
     126  
     127  char *
     128  _g_local_file_info_create_etag (GLocalFileStat *statbuf)
     129  {
     130    glong sec, usec, nsec;
     131  
     132    g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL);
     133  
     134    sec = _g_stat_mtime (statbuf);
     135    usec = _g_stat_mtim_nsec (statbuf) / 1000;
     136    nsec = _g_stat_mtim_nsec (statbuf);
     137  
     138    return g_strdup_printf ("%lu:%lu:%lu", sec, usec, nsec);
     139  }
     140  
     141  static char *
     142  _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
     143  {
     144    guint64 ino;
     145  #ifdef G_OS_WIN32
     146    ino = statbuf->file_index;
     147  #else
     148    ino = _g_stat_ino (statbuf);
     149  #endif
     150    return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
     151  			  (guint64) _g_stat_dev (statbuf),
     152  			  ino);
     153  }
     154  
     155  static char *
     156  _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
     157  {
     158    return g_strdup_printf ("l%" G_GUINT64_FORMAT,
     159  			  (guint64) _g_stat_dev (statbuf));
     160  }
     161  
     162  #if defined (S_ISLNK) || defined (G_OS_WIN32)
     163  
     164  static gchar *
     165  read_link (const gchar *full_name)
     166  {
     167  #if defined (HAVE_READLINK)
     168    gchar *buffer;
     169    gsize size;
     170    
     171    size = 256;
     172    buffer = g_malloc (size);
     173    
     174    while (1)
     175      {
     176        gssize read_size;
     177  
     178        read_size = readlink (full_name, buffer, size);
     179        if (read_size < 0)
     180  	{
     181  	  g_free (buffer);
     182  	  return NULL;
     183  	}
     184        if ((gsize) read_size < size)
     185  	{
     186  	  buffer[read_size] = 0;
     187  	  return buffer;
     188  	}
     189        size *= 2;
     190        buffer = g_realloc (buffer, size);
     191      }
     192  #elif defined (G_OS_WIN32)
     193    gchar *buffer;
     194    int read_size;
     195  
     196    read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, NULL, 0, &buffer, TRUE);
     197    if (read_size < 0)
     198      return NULL;
     199    else if (read_size == 0)
     200      return strdup ("");
     201    else
     202      return buffer;
     203  #else
     204    return NULL;
     205  #endif
     206  }
     207  
     208  #endif  /* S_ISLNK || G_OS_WIN32 */
     209  
     210  #ifdef HAVE_SELINUX
     211  /* Get the SELinux security context */
     212  static void
     213  get_selinux_context (const char            *path,
     214  		     GFileInfo             *info,
     215  		     GFileAttributeMatcher *attribute_matcher,
     216  		     gboolean               follow_symlinks)
     217  {
     218    char *context;
     219  
     220    if (!_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
     221      return;
     222    
     223    if (is_selinux_enabled ())
     224      {
     225        if (follow_symlinks)
     226  	{
     227  	  if (lgetfilecon_raw (path, &context) < 0)
     228  	    return;
     229  	}
     230        else
     231  	{
     232  	  if (getfilecon_raw (path, &context) < 0)
     233  	    return;
     234  	}
     235  
     236        if (context)
     237  	{
     238  	  _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
     239  	  freecon (context);
     240  	}
     241      }
     242  }
     243  #endif
     244  
     245  #ifdef HAVE_XATTR
     246  
     247  /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
     248   * (Mac) getxattr(..., XATTR_NOFOLLOW)
     249   */
     250  #ifdef HAVE_XATTR_NOFOLLOW
     251  #define g_fgetxattr(fd,name,value,size)  fgetxattr(fd,name,value,size,0,0)
     252  #define g_flistxattr(fd,name,size)       flistxattr(fd,name,size,0)
     253  #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
     254  #define g_removexattr(path,name) removexattr(path,name,0)
     255  #else
     256  #define g_fgetxattr     fgetxattr
     257  #define g_flistxattr    flistxattr
     258  #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
     259  #define g_removexattr(path,name) removexattr(path,name)
     260  #endif
     261  
     262  static gssize
     263  g_getxattr (const char *path, const char *name, void *value, size_t size,
     264              gboolean follow_symlinks)
     265  {
     266  #ifdef HAVE_XATTR_NOFOLLOW
     267    return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
     268  #else
     269    if (follow_symlinks)
     270      return getxattr (path, name, value, size);
     271    else
     272      return lgetxattr (path, name, value, size);
     273  #endif
     274  }
     275  
     276  static gssize
     277  g_listxattr(const char *path, char *namebuf, size_t size,
     278              gboolean follow_symlinks)
     279  {
     280  #ifdef HAVE_XATTR_NOFOLLOW
     281    return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
     282  #else
     283    if (follow_symlinks)
     284      return listxattr (path, namebuf, size);
     285    else
     286      return llistxattr (path, namebuf, size);
     287  #endif
     288  }
     289  
     290  static gboolean
     291  valid_char (char c)
     292  {
     293    return c >= 32 && c <= 126 && c != '\\';
     294  }
     295  
     296  static gboolean
     297  name_is_valid (const char *str)
     298  {
     299    while (*str)
     300      {
     301        if (!valid_char (*str++))
     302  	return FALSE;
     303      }
     304    return TRUE;
     305  }
     306  
     307  static char *
     308  hex_escape_buffer (const char *str,
     309                     size_t      len,
     310                     gboolean   *free_return)
     311  {
     312    size_t num_invalid, i;
     313    char *escaped_str, *p;
     314    unsigned char c;
     315    static char *hex_digits = "0123456789abcdef";
     316  
     317    num_invalid = 0;
     318    for (i = 0; i < len; i++)
     319      {
     320        if (!valid_char (str[i]))
     321  	num_invalid++;
     322      }
     323  
     324    if (num_invalid == 0)
     325      {
     326        *free_return = FALSE;
     327        return (char *)str;
     328      }
     329  
     330    escaped_str = g_malloc (len + num_invalid*3 + 1);
     331  
     332    p = escaped_str;
     333    for (i = 0; i < len; i++)
     334      {
     335        if (valid_char (str[i]))
     336  	*p++ = str[i];
     337        else
     338  	{
     339  	  c = str[i];
     340  	  *p++ = '\\';
     341  	  *p++ = 'x';
     342  	  *p++ = hex_digits[(c >> 4) & 0xf];
     343  	  *p++ = hex_digits[c & 0xf];
     344  	}
     345      }
     346    *p = 0;
     347  
     348    *free_return = TRUE;
     349    return escaped_str;
     350  }
     351  
     352  static char *
     353  hex_escape_string (const char *str,
     354                     gboolean   *free_return)
     355  {
     356    return hex_escape_buffer (str, strlen (str), free_return);
     357  }
     358  
     359  static char *
     360  hex_unescape_string (const char *str, 
     361                       int        *out_len, 
     362                       gboolean   *free_return)
     363  {
     364    int i;
     365    char *unescaped_str, *p;
     366    unsigned char c;
     367    int len;
     368  
     369    len = strlen (str);
     370    
     371    if (strchr (str, '\\') == NULL)
     372      {
     373        if (out_len)
     374  	*out_len = len;
     375        *free_return = FALSE;
     376        return (char *)str;
     377      }
     378    
     379    unescaped_str = g_malloc (len + 1);
     380  
     381    p = unescaped_str;
     382    for (i = 0; i < len; i++)
     383      {
     384        if (str[i] == '\\' &&
     385  	  str[i+1] == 'x' &&
     386  	  len - i >= 4)
     387  	{
     388  	  c =
     389  	    (g_ascii_xdigit_value (str[i+2]) << 4) |
     390  	    g_ascii_xdigit_value (str[i+3]);
     391  	  *p++ = c;
     392  	  i += 3;
     393  	}
     394        else
     395  	*p++ = str[i];
     396      }
     397    if (out_len)
     398      *out_len = p - unescaped_str;
     399    *p++ = 0;
     400  
     401    *free_return = TRUE;
     402    return unescaped_str;
     403  }
     404  
     405  static void
     406  escape_xattr (GFileInfo  *info,
     407  	      const char *gio_attr, /* gio attribute name */
     408  	      const char *value, /* Is zero terminated */
     409  	      size_t      len /* not including zero termination */)
     410  {
     411    char *escaped_val;
     412    gboolean free_escaped_val;
     413    
     414    escaped_val = hex_escape_buffer (value, len, &free_escaped_val);
     415    
     416    g_file_info_set_attribute_string (info, gio_attr, escaped_val);
     417    
     418    if (free_escaped_val)
     419      g_free (escaped_val);
     420  }
     421  
     422  static void
     423  get_one_xattr (const char *path,
     424  	       GFileInfo  *info,
     425  	       const char *gio_attr,
     426  	       const char *xattr,
     427  	       gboolean    follow_symlinks)
     428  {
     429    char value[64];
     430    char *value_p;
     431    gssize len;
     432    int errsv;
     433  
     434    len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
     435    errsv = errno;
     436  
     437    value_p = NULL;
     438    if (len >= 0)
     439      value_p = value;
     440    else if (len == -1 && errsv == ERANGE)
     441      {
     442        len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
     443  
     444        if (len < 0)
     445  	return;
     446  
     447        value_p = g_malloc (len+1);
     448  
     449        len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
     450  
     451        if (len < 0)
     452  	{
     453  	  g_free (value_p);
     454  	  return;
     455  	}
     456      }
     457    else
     458      return;
     459    
     460    /* Null terminate */
     461    value_p[len] = 0;
     462  
     463    escape_xattr (info, gio_attr, value_p, len);
     464    
     465    if (value_p != value)
     466      g_free (value_p);
     467  }
     468  
     469  #endif /* defined HAVE_XATTR */
     470  
     471  static void
     472  get_xattrs (const char            *path,
     473  	    gboolean               user,
     474  	    GFileInfo             *info,
     475  	    GFileAttributeMatcher *matcher,
     476  	    gboolean               follow_symlinks)
     477  {
     478  #ifdef HAVE_XATTR
     479    gboolean all;
     480    gsize list_size;
     481    gssize list_res_size;
     482    size_t len;
     483    char *list;
     484    const char *attr, *attr2;
     485  
     486    if (user)
     487      all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
     488    else
     489      all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
     490  
     491    if (all)
     492      {
     493        int errsv;
     494  
     495        list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
     496  
     497        if (list_res_size == -1 ||
     498  	  list_res_size == 0)
     499  	return;
     500  
     501        list_size = list_res_size;
     502        list = g_malloc (list_size);
     503  
     504      retry:
     505        
     506        list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
     507        errsv = errno;
     508        
     509        if (list_res_size == -1 && errsv == ERANGE)
     510  	{
     511  	  list_size = list_size * 2;
     512  	  list = g_realloc (list, list_size);
     513  	  goto retry;
     514  	}
     515  
     516        if (list_res_size == -1)
     517          {
     518            g_free (list);
     519            return;
     520          }
     521  
     522        attr = list;
     523        while (list_res_size > 0)
     524  	{
     525  	  if ((user && g_str_has_prefix (attr, "user.")) ||
     526  	      (!user && !g_str_has_prefix (attr, "user.")))
     527  	    {
     528  	      char *escaped_attr, *gio_attr;
     529  	      gboolean free_escaped_attr;
     530  	      
     531  	      if (user)
     532  		{
     533  		  escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
     534  		  gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
     535  		}
     536  	      else
     537  		{
     538  		  escaped_attr = hex_escape_string (attr, &free_escaped_attr);
     539  		  gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
     540  		}
     541  	      
     542  	      if (free_escaped_attr)
     543  		g_free (escaped_attr);
     544  	      
     545  	      get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
     546  
     547  	      g_free (gio_attr);
     548  	    }
     549  	      
     550  	  len = strlen (attr) + 1;
     551  	  attr += len;
     552  	  list_res_size -= len;
     553  	}
     554  
     555        g_free (list);
     556      }
     557    else
     558      {
     559        while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
     560  	{
     561  	  char *unescaped_attribute, *a;
     562  	  gboolean free_unescaped_attribute;
     563  
     564  	  attr2 = strchr (attr, ':');
     565  	  if (attr2)
     566  	    {
     567  	      attr2 += 2; /* Skip '::' */
     568  	      unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
     569  	      if (user)
     570  		a = g_strconcat ("user.", unescaped_attribute, NULL);
     571  	      else
     572  		a = unescaped_attribute;
     573  	      
     574  	      get_one_xattr (path, info, attr, a, follow_symlinks);
     575  
     576  	      if (user)
     577  		g_free (a);
     578  	      
     579  	      if (free_unescaped_attribute)
     580  		g_free (unescaped_attribute);
     581  	    }
     582  	}
     583      }
     584  #endif /* defined HAVE_XATTR */
     585  }
     586  
     587  #ifdef HAVE_XATTR
     588  static void
     589  get_one_xattr_from_fd (int         fd,
     590  		       GFileInfo  *info,
     591  		       const char *gio_attr,
     592  		       const char *xattr)
     593  {
     594    char value[64];
     595    char *value_p;
     596    gssize len;
     597    int errsv;
     598  
     599    len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
     600    errsv = errno;
     601  
     602    value_p = NULL;
     603    if (len >= 0)
     604      value_p = value;
     605    else if (len == -1 && errsv == ERANGE)
     606      {
     607        len = g_fgetxattr (fd, xattr, NULL, 0);
     608  
     609        if (len < 0)
     610  	return;
     611  
     612        value_p = g_malloc (len + 1);
     613  
     614        len = g_fgetxattr (fd, xattr, value_p, len);
     615  
     616        if (len < 0)
     617  	{
     618  	  g_free (value_p);
     619  	  return;
     620  	}
     621      }
     622    else
     623      return;
     624    
     625    /* Null terminate */
     626    value_p[len] = 0;
     627  
     628    escape_xattr (info, gio_attr, value_p, len);
     629    
     630    if (value_p != value)
     631      g_free (value_p);
     632  }
     633  #endif /* defined HAVE_XATTR */
     634  
     635  static void
     636  get_xattrs_from_fd (int                    fd,
     637  		    gboolean               user,
     638  		    GFileInfo             *info,
     639  		    GFileAttributeMatcher *matcher)
     640  {
     641  #ifdef HAVE_XATTR
     642    gboolean all;
     643    gsize list_size;
     644    gssize list_res_size;
     645    size_t len;
     646    char *list;
     647    const char *attr, *attr2;
     648  
     649    if (user)
     650      all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
     651    else
     652      all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
     653  
     654    if (all)
     655      {
     656        int errsv;
     657  
     658        list_res_size = g_flistxattr (fd, NULL, 0);
     659  
     660        if (list_res_size == -1 ||
     661  	  list_res_size == 0)
     662  	return;
     663  
     664        list_size = list_res_size;
     665        list = g_malloc (list_size);
     666  
     667      retry:
     668        
     669        list_res_size = g_flistxattr (fd, list, list_size);
     670        errsv = errno;
     671        
     672        if (list_res_size == -1 && errsv == ERANGE)
     673  	{
     674  	  list_size = list_size * 2;
     675  	  list = g_realloc (list, list_size);
     676  	  goto retry;
     677  	}
     678  
     679        if (list_res_size == -1)
     680          {
     681            g_free (list);
     682            return;
     683          }
     684  
     685        attr = list;
     686        while (list_res_size > 0)
     687  	{
     688  	  if ((user && g_str_has_prefix (attr, "user.")) ||
     689  	      (!user && !g_str_has_prefix (attr, "user.")))
     690  	    {
     691  	      char *escaped_attr, *gio_attr;
     692  	      gboolean free_escaped_attr;
     693  	      
     694  	      if (user)
     695  		{
     696  		  escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
     697  		  gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
     698  		}
     699  	      else
     700  		{
     701  		  escaped_attr = hex_escape_string (attr, &free_escaped_attr);
     702  		  gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
     703  		}
     704  	      
     705  	      if (free_escaped_attr)
     706  		g_free (escaped_attr);
     707  	      
     708  	      get_one_xattr_from_fd (fd, info, gio_attr, attr);
     709  	      g_free (gio_attr);
     710  	    }
     711  	  
     712  	  len = strlen (attr) + 1;
     713  	  attr += len;
     714  	  list_res_size -= len;
     715  	}
     716  
     717        g_free (list);
     718      }
     719    else
     720      {
     721        while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
     722  	{
     723  	  char *unescaped_attribute, *a;
     724  	  gboolean free_unescaped_attribute;
     725  
     726  	  attr2 = strchr (attr, ':');
     727  	  if (attr2)
     728  	    {
     729  	      attr2++; /* Skip ':' */
     730  	      unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
     731  	      if (user)
     732  		a = g_strconcat ("user.", unescaped_attribute, NULL);
     733  	      else
     734  		a = unescaped_attribute;
     735  	      
     736  	      get_one_xattr_from_fd (fd, info, attr, a);
     737  
     738  	      if (user)
     739  		g_free (a);
     740  	      
     741  	      if (free_unescaped_attribute)
     742  		g_free (unescaped_attribute);
     743  	    }
     744  	}
     745      }
     746  #endif /* defined HAVE_XATTR */
     747  }
     748  
     749  #ifdef HAVE_XATTR
     750  static gboolean
     751  set_xattr (char                       *filename,
     752  	   const char                 *escaped_attribute,
     753  	   const GFileAttributeValue  *attr_value,
     754  	   GError                    **error)
     755  {
     756    char *attribute, *value;
     757    gboolean free_attribute, free_value;
     758    int val_len, res, errsv;
     759    gboolean is_user;
     760    char *a;
     761  
     762    if (attr_value == NULL)
     763      {
     764        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
     765                             _("Attribute value must be non-NULL"));
     766        return FALSE;
     767      }
     768  
     769    if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING && attr_value->type != G_FILE_ATTRIBUTE_TYPE_INVALID)
     770      {
     771        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
     772                             _("Invalid attribute type (string or invalid expected)"));
     773        return FALSE;
     774      }
     775  
     776    if (!name_is_valid (escaped_attribute))
     777      {
     778        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
     779                             _("Invalid extended attribute name"));
     780        return FALSE;
     781      }
     782  
     783    if (g_str_has_prefix (escaped_attribute, "xattr::"))
     784      {
     785        escaped_attribute += strlen ("xattr::");
     786        is_user = TRUE;
     787      }
     788    else
     789      {
     790        g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
     791        escaped_attribute += strlen ("xattr-sys::");
     792        is_user = FALSE;
     793      }
     794  
     795    attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
     796  
     797    if (is_user)
     798      a = g_strconcat ("user.", attribute, NULL);
     799    else
     800      a = attribute;
     801  
     802    if (attr_value->type == G_FILE_ATTRIBUTE_TYPE_STRING)
     803      {
     804        value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
     805        res = g_setxattr (filename, a, value, val_len);
     806      }
     807    else
     808      {
     809        value = NULL;
     810        val_len = 0;
     811        free_value = FALSE;
     812        res = g_removexattr (filename, a);
     813      }
     814  
     815    errsv = errno;
     816    
     817    if (is_user)
     818      g_free (a);
     819    
     820    if (free_attribute)
     821      g_free (attribute);
     822    
     823    if (free_value)
     824      g_free (value);
     825  
     826    if (res == -1)
     827      {
     828        g_set_error (error, G_IO_ERROR,
     829  		   g_io_error_from_errno (errsv),
     830  		   _("Error setting extended attribute “%s”: %s"),
     831  		   escaped_attribute, g_strerror (errsv));
     832        return FALSE;
     833      }
     834    
     835    return TRUE;
     836  }
     837  
     838  #endif
     839  
     840  
     841  void
     842  _g_local_file_info_get_parent_info (const char            *dir,
     843  				    GFileAttributeMatcher *attribute_matcher,
     844  				    GLocalParentFileInfo  *parent_info)
     845  {
     846    GStatBuf statbuf;
     847    int res;
     848  
     849    parent_info->extra_data = NULL;
     850    parent_info->free_extra_data = NULL;
     851    parent_info->writable = FALSE;
     852    parent_info->is_sticky = FALSE;
     853    parent_info->has_trash_dir = FALSE;
     854    parent_info->device = 0;
     855    parent_info->inode = 0;
     856  
     857    if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) ||
     858        _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) ||
     859        _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH) ||
     860        _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
     861      {
     862        /* FIXME: Windows: The underlying _waccess() call in the C
     863         * library is mostly pointless as it only looks at the READONLY
     864         * FAT-style attribute of the file, it doesn't check the ACL at
     865         * all.
     866         */
     867        parent_info->writable = (g_access (dir, W_OK) == 0);
     868        
     869        res = g_stat (dir, &statbuf);
     870  
     871        /*
     872         * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
     873         * renamed or deleted only by the owner of the file, by the owner of the directory, and
     874         * by a privileged process.
     875         */
     876        if (res == 0)
     877  	{
     878  #ifdef S_ISVTX
     879  	  parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
     880  #else
     881  	  parent_info->is_sticky = FALSE;
     882  #endif
     883  	  parent_info->owner = statbuf.st_uid;
     884  	  parent_info->device = statbuf.st_dev;
     885  	  parent_info->inode = statbuf.st_ino;
     886            /* No need to find trash dir if it's not writable anyway */
     887            if (parent_info->writable &&
     888                _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
     889              parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev);
     890  	}
     891      }
     892  }
     893  
     894  void
     895  _g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
     896  {
     897    if (parent_info->extra_data &&
     898        parent_info->free_extra_data)
     899      parent_info->free_extra_data (parent_info->extra_data);
     900  }
     901  
     902  static void
     903  get_access_rights (GFileAttributeMatcher *attribute_matcher,
     904  		   GFileInfo             *info,
     905  		   const gchar           *path,
     906  		   GLocalFileStat        *statbuf,
     907  		   GLocalParentFileInfo  *parent_info)
     908  {
     909    /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
     910    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
     911  					    G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ))
     912      _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ,
     913  				             g_access (path, R_OK) == 0);
     914    
     915    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
     916  					    G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE))
     917      _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE,
     918  				             g_access (path, W_OK) == 0);
     919    
     920    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
     921  					    G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE))
     922      _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE,
     923  				             g_access (path, X_OK) == 0);
     924  
     925  
     926    if (parent_info)
     927      {
     928        gboolean writable;
     929  
     930        writable = FALSE;
     931        if (parent_info->writable)
     932  	{
     933  #ifdef G_OS_WIN32
     934  	  writable = TRUE;
     935  #else
     936  	  if (parent_info->is_sticky)
     937  	    {
     938  	      uid_t uid = geteuid ();
     939  
     940  	      if (uid == _g_stat_uid (statbuf) ||
     941  		  uid == (uid_t) parent_info->owner ||
     942  		  uid == 0)
     943  		writable = TRUE;
     944  	    }
     945  	  else
     946  	    writable = TRUE;
     947  #endif
     948  	}
     949  
     950        if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME))
     951  	_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME,
     952  					         writable);
     953        
     954        if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE))
     955  	_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
     956  					         writable);
     957  
     958        if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
     959          _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
     960                                                   writable && parent_info->has_trash_dir);
     961      }
     962  }
     963  
     964  static void
     965  set_info_from_stat (GFileInfo             *info, 
     966                      GLocalFileStat        *statbuf,
     967  		    GFileAttributeMatcher *attribute_matcher)
     968  {
     969    GFileType file_type;
     970  
     971    file_type = G_FILE_TYPE_UNKNOWN;
     972  
     973    if (S_ISREG (_g_stat_mode (statbuf)))
     974      file_type = G_FILE_TYPE_REGULAR;
     975    else if (S_ISDIR (_g_stat_mode (statbuf)))
     976      file_type = G_FILE_TYPE_DIRECTORY;
     977  #ifndef G_OS_WIN32
     978    else if (S_ISCHR (_g_stat_mode (statbuf)) ||
     979  	   S_ISBLK (_g_stat_mode (statbuf)) ||
     980  	   S_ISFIFO (_g_stat_mode (statbuf))
     981  #ifdef S_ISSOCK
     982  	   || S_ISSOCK (_g_stat_mode (statbuf))
     983  #endif
     984  	   )
     985      file_type = G_FILE_TYPE_SPECIAL;
     986  #endif
     987  #ifdef S_ISLNK
     988    else if (S_ISLNK (_g_stat_mode (statbuf)))
     989      file_type = G_FILE_TYPE_SYMBOLIC_LINK;
     990  #elif defined (G_OS_WIN32)
     991    else if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK ||
     992             statbuf->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
     993      file_type = G_FILE_TYPE_SYMBOLIC_LINK;
     994  #endif
     995  
     996    g_file_info_set_file_type (info, file_type);
     997    g_file_info_set_size (info, _g_stat_size (statbuf));
     998  
     999    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, _g_stat_dev (statbuf));
    1000    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, _g_stat_nlink (statbuf));
    1001  #ifndef G_OS_WIN32
    1002    /* Pointless setting these on Windows even if they exist in the struct */
    1003    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, _g_stat_ino (statbuf));
    1004    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, _g_stat_uid (statbuf));
    1005    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, _g_stat_gid (statbuf));
    1006    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, _g_stat_rdev (statbuf));
    1007  #endif
    1008    /* Mostly pointless on Windows.
    1009     * Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks.
    1010     */
    1011    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, _g_stat_mode (statbuf));
    1012  #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
    1013    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, _g_stat_blksize (statbuf));
    1014  #endif
    1015  #if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
    1016    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, _g_stat_blocks (statbuf));
    1017    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
    1018                                             _g_stat_blocks (statbuf) * G_GUINT64_CONSTANT (512));
    1019  #elif defined (G_OS_WIN32)
    1020    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
    1021                                             statbuf->allocated_size);
    1022  
    1023  #endif
    1024  
    1025    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, _g_stat_mtime (statbuf));
    1026    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, _g_stat_mtim_nsec (statbuf) / 1000);
    1027    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC, _g_stat_mtim_nsec (statbuf));
    1028  
    1029    if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_ATIME))
    1030      {
    1031        _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, _g_stat_atime (statbuf));
    1032        _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, _g_stat_atim_nsec (statbuf) / 1000);
    1033        _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_NSEC, _g_stat_atim_nsec (statbuf));
    1034      }
    1035  
    1036  #ifndef G_OS_WIN32
    1037    /* Microsoft uses st_ctime for file creation time,
    1038     * instead of file change time:
    1039     * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions#generic-text-routine-mappings
    1040     * Thank you, Microsoft!
    1041     */
    1042    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, _g_stat_ctime (statbuf));
    1043    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, _g_stat_ctim_nsec (statbuf) / 1000);
    1044    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_NSEC, _g_stat_ctim_nsec (statbuf));
    1045  #endif
    1046  
    1047  #if defined (HAVE_STATX)
    1048    if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_BTIME))
    1049      {
    1050        _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->stx_btime.tv_sec);
    1051        _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->stx_btime.tv_nsec / 1000);
    1052        _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->stx_btime.tv_nsec);
    1053      }
    1054  #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
    1055    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
    1056    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtimensec / 1000);
    1057    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->st_birthtimensec);
    1058  #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
    1059    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim.tv_sec);
    1060    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtim.tv_nsec / 1000);
    1061    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->st_birthtim.tv_nsec);
    1062  #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME)
    1063    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
    1064  #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM)
    1065    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim);
    1066  #elif defined (G_OS_WIN32)
    1067    _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_ctim.tv_sec);
    1068    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_ctim.tv_nsec / 1000);
    1069    _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->st_ctim.tv_nsec);
    1070  #endif
    1071  
    1072    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    1073  					    G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
    1074      {
    1075        char *etag = _g_local_file_info_create_etag (statbuf);
    1076        _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ETAG_VALUE, etag);
    1077        g_free (etag);
    1078      }
    1079  
    1080    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    1081  					    G_FILE_ATTRIBUTE_ID_ID_FILE))
    1082      {
    1083        char *id = _g_local_file_info_create_file_id (statbuf);
    1084        _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILE, id);
    1085        g_free (id);
    1086      }
    1087  
    1088    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    1089  					    G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
    1090      {
    1091        char *id = _g_local_file_info_create_fs_id (statbuf);
    1092        _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM, id);
    1093        g_free (id);
    1094      }
    1095  }
    1096  
    1097  #ifndef G_OS_WIN32
    1098  
    1099  static char *
    1100  make_valid_utf8 (const char *name)
    1101  {
    1102    GString *string;
    1103    const gchar *remainder, *invalid;
    1104    gsize remaining_bytes, valid_bytes;
    1105    
    1106    string = NULL;
    1107    remainder = name;
    1108    remaining_bytes = strlen (name);
    1109    
    1110    while (remaining_bytes != 0) 
    1111      {
    1112        if (g_utf8_validate_len (remainder, remaining_bytes, &invalid))
    1113  	break;
    1114        valid_bytes = invalid - remainder;
    1115      
    1116        if (string == NULL) 
    1117  	string = g_string_sized_new (remaining_bytes);
    1118  
    1119        g_string_append_len (string, remainder, valid_bytes);
    1120        /* append U+FFFD REPLACEMENT CHARACTER */
    1121        g_string_append (string, "\357\277\275");
    1122        
    1123        remaining_bytes -= valid_bytes + 1;
    1124        remainder = invalid + 1;
    1125      }
    1126    
    1127    if (string == NULL)
    1128      return g_strdup (name);
    1129    
    1130    g_string_append (string, remainder);
    1131  
    1132    g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
    1133    
    1134    return g_string_free (string, FALSE);
    1135  }
    1136  
    1137  static char *
    1138  convert_pwd_string_to_utf8 (char *pwd_str)
    1139  {
    1140    char *utf8_string;
    1141    
    1142    if (!g_utf8_validate (pwd_str, -1, NULL))
    1143      {
    1144        utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
    1145        if (utf8_string == NULL)
    1146  	utf8_string = make_valid_utf8 (pwd_str);
    1147      }
    1148    else 
    1149      utf8_string = g_strdup (pwd_str);
    1150    
    1151    return utf8_string;
    1152  }
    1153  
    1154  static void
    1155  uid_data_free (UidData *data)
    1156  {
    1157    g_free (data->user_name);
    1158    g_free (data->real_name);
    1159    g_free (data);
    1160  }
    1161  
    1162  /* called with lock held */
    1163  static UidData *
    1164  lookup_uid_data (uid_t uid)
    1165  {
    1166    UidData *data;
    1167    char buffer[4096];
    1168    struct passwd pwbuf;
    1169    struct passwd *pwbufp;
    1170  #ifndef __BIONIC__
    1171    char *gecos, *comma;
    1172  #endif
    1173  
    1174    if (uid_cache == NULL)
    1175      uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
    1176  
    1177    data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
    1178  
    1179    if (data)
    1180      return data;
    1181  
    1182    data = g_new0 (UidData, 1);
    1183  
    1184  #if defined(HAVE_GETPWUID_R)
    1185    getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
    1186  #else
    1187    pwbufp = getpwuid (uid);
    1188  #endif
    1189  
    1190    if (pwbufp != NULL)
    1191      {
    1192        if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
    1193  	data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
    1194  
    1195  #ifndef __BIONIC__
    1196        gecos = pwbufp->pw_gecos;
    1197  
    1198        if (gecos)
    1199  	{
    1200  	  comma = strchr (gecos, ',');
    1201  	  if (comma)
    1202  	    *comma = 0;
    1203  	  data->real_name = convert_pwd_string_to_utf8 (gecos);
    1204  	}
    1205  #endif
    1206      }
    1207  
    1208    /* Default fallbacks */
    1209    if (data->real_name == NULL)
    1210      {
    1211        if (data->user_name != NULL)
    1212  	data->real_name = g_strdup (data->user_name);
    1213        else
    1214  	data->real_name = g_strdup_printf ("user #%d", (int)uid);
    1215      }
    1216    
    1217    if (data->user_name == NULL)
    1218      data->user_name = g_strdup_printf ("%d", (int)uid);
    1219    
    1220    g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
    1221    
    1222    return data;
    1223  }
    1224  
    1225  static char *
    1226  get_username_from_uid (uid_t uid)
    1227  {
    1228    char *res;
    1229    UidData *data;
    1230    
    1231    G_LOCK (uid_cache);
    1232    data = lookup_uid_data (uid);
    1233    res = g_strdup (data->user_name);  
    1234    G_UNLOCK (uid_cache);
    1235  
    1236    return res;
    1237  }
    1238  
    1239  static char *
    1240  get_realname_from_uid (uid_t uid)
    1241  {
    1242    char *res;
    1243    UidData *data;
    1244    
    1245    G_LOCK (uid_cache);
    1246    data = lookup_uid_data (uid);
    1247    res = g_strdup (data->real_name);  
    1248    G_UNLOCK (uid_cache);
    1249    
    1250    return res;
    1251  }
    1252  
    1253  /* called with lock held */
    1254  static char *
    1255  lookup_gid_name (gid_t gid)
    1256  {
    1257    char *name;
    1258  #if defined (HAVE_GETGRGID_R)
    1259    char buffer[4096];
    1260    struct group gbuf;
    1261  #endif
    1262    struct group *gbufp;
    1263  
    1264    if (gid_cache == NULL)
    1265      gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
    1266  
    1267    name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
    1268  
    1269    if (name)
    1270      return name;
    1271  
    1272  #if defined (HAVE_GETGRGID_R)
    1273    getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
    1274  #else
    1275    gbufp = getgrgid (gid);
    1276  #endif
    1277  
    1278    if (gbufp != NULL &&
    1279        gbufp->gr_name != NULL &&
    1280        gbufp->gr_name[0] != 0)
    1281      name = convert_pwd_string_to_utf8 (gbufp->gr_name);
    1282    else
    1283      name = g_strdup_printf("%d", (int)gid);
    1284    
    1285    g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
    1286    
    1287    return name;
    1288  }
    1289  
    1290  static char *
    1291  get_groupname_from_gid (gid_t gid)
    1292  {
    1293    char *res;
    1294    char *name;
    1295    
    1296    G_LOCK (gid_cache);
    1297    name = lookup_gid_name (gid);
    1298    res = g_strdup (name);  
    1299    G_UNLOCK (gid_cache);
    1300    return res;
    1301  }
    1302  
    1303  #endif /* !G_OS_WIN32 */
    1304  
    1305  static char *
    1306  get_content_type (const char          *basename,
    1307  		  const char          *path,
    1308  		  GLocalFileStat      *statbuf,
    1309  		  gboolean             is_symlink,
    1310  		  gboolean             symlink_broken,
    1311  		  GFileQueryInfoFlags  flags,
    1312  		  gboolean             fast)
    1313  {
    1314    if (is_symlink &&
    1315        (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
    1316      return g_content_type_from_mime_type ("inode/symlink");
    1317    else if (statbuf != NULL && S_ISDIR(_g_stat_mode (statbuf)))
    1318      return g_content_type_from_mime_type ("inode/directory");
    1319  #ifndef G_OS_WIN32
    1320    else if (statbuf != NULL && S_ISCHR(_g_stat_mode (statbuf)))
    1321      return g_content_type_from_mime_type ("inode/chardevice");
    1322    else if (statbuf != NULL && S_ISBLK(_g_stat_mode (statbuf)))
    1323      return g_content_type_from_mime_type ("inode/blockdevice");
    1324    else if (statbuf != NULL && S_ISFIFO(_g_stat_mode (statbuf)))
    1325      return g_content_type_from_mime_type ("inode/fifo");
    1326    else if (statbuf != NULL && S_ISREG(_g_stat_mode (statbuf)) && _g_stat_size (statbuf) == 0)
    1327      {
    1328        /* Don't sniff zero-length files in order to avoid reading files
    1329         * that appear normal but are not (eg: files in /proc and /sys)
    1330         */
    1331        return g_content_type_from_mime_type ("application/x-zerosize");
    1332      }
    1333  #endif
    1334  #ifdef S_ISSOCK
    1335    else if (statbuf != NULL && S_ISSOCK(_g_stat_mode (statbuf)))
    1336      return g_content_type_from_mime_type ("inode/socket");
    1337  #endif
    1338    else
    1339      {
    1340        char *content_type;
    1341        gboolean result_uncertain;
    1342  
    1343        content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
    1344        
    1345  #if !defined(G_OS_WIN32) && !defined(__APPLE__)
    1346        if (!fast && result_uncertain && path != NULL)
    1347  	{
    1348            /* Sniff the first 16KiB of the file (sometimes less, if xdgmime
    1349             * says it doesn’t need so much). Most files need less than 4KiB of
    1350             * sniffing, but some disk images need more (see
    1351             * https://gitlab.gnome.org/GNOME/glib/-/issues/3186). */
    1352  	  guchar sniff_buffer[16384];
    1353  	  gsize sniff_length;
    1354  #ifdef O_NOATIME
    1355  	  int errsv;
    1356  #endif
    1357  	  int fd;
    1358  
    1359  	  sniff_length = _g_unix_content_type_get_sniff_len ();
    1360  	  if (sniff_length == 0 || sniff_length > sizeof (sniff_buffer))
    1361  	    sniff_length = sizeof (sniff_buffer);
    1362  
    1363  #ifdef O_NOATIME	  
    1364            fd = g_open (path, O_RDONLY | O_NOATIME | O_CLOEXEC, 0);
    1365            errsv = errno;
    1366            if (fd < 0 && errsv == EPERM)
    1367  #endif
    1368  	    fd = g_open (path, O_RDONLY | O_CLOEXEC, 0);
    1369  
    1370  	  if (fd != -1)
    1371  	    {
    1372  	      gssize res;
    1373  	      
    1374  	      res = read (fd, sniff_buffer, sniff_length);
    1375  	      (void) g_close (fd, NULL);
    1376  	      if (res >= 0)
    1377  		{
    1378  		  g_free (content_type);
    1379  		  content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
    1380  		}
    1381  	    }
    1382  	}
    1383  #endif
    1384        
    1385        return content_type;
    1386      }
    1387    
    1388  }
    1389  
    1390  typedef enum {
    1391    THUMBNAIL_SIZE_AUTO,
    1392    THUMBNAIL_SIZE_NORMAL,
    1393    THUMBNAIL_SIZE_LARGE,
    1394    THUMBNAIL_SIZE_XLARGE,
    1395    THUMBNAIL_SIZE_XXLARGE,
    1396    THUMBNAIL_SIZE_LAST,
    1397  } ThumbnailSize;
    1398  
    1399  static const char *
    1400  get_thumbnail_dirname_from_size (ThumbnailSize size)
    1401  {
    1402    switch (size)
    1403      {
    1404      case THUMBNAIL_SIZE_AUTO:
    1405        return NULL;
    1406        break;
    1407      case THUMBNAIL_SIZE_NORMAL:
    1408        return "normal";
    1409        break;
    1410      case THUMBNAIL_SIZE_LARGE:
    1411        return "large";
    1412        break;
    1413      case THUMBNAIL_SIZE_XLARGE:
    1414        return "x-large";
    1415        break;
    1416      case THUMBNAIL_SIZE_XXLARGE:
    1417        return "xx-large";
    1418        break;
    1419      default:
    1420        g_assert_not_reached ();
    1421      }
    1422  
    1423    g_return_val_if_reached (NULL);
    1424  }
    1425  
    1426  /* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
    1427  static void
    1428  get_thumbnail_attributes (const char     *path,
    1429                            GFileInfo      *info,
    1430                            const GLocalFileStat *stat_buf,
    1431                            ThumbnailSize   size)
    1432  {
    1433    GChecksum *checksum;
    1434    const char *dirname;
    1435    char *uri;
    1436    char *filename = NULL;
    1437    char *basename;
    1438    guint32 failed_attr_id;
    1439    guint32 is_valid_attr_id;
    1440    guint32 path_attr_id;
    1441  
    1442    switch (size)
    1443      {
    1444      case THUMBNAIL_SIZE_AUTO:
    1445        failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED;
    1446        is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID;
    1447        path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH;
    1448        break;
    1449      case THUMBNAIL_SIZE_NORMAL:
    1450        failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_NORMAL;
    1451        is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_NORMAL;
    1452        path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_NORMAL;
    1453        break;
    1454      case THUMBNAIL_SIZE_LARGE:
    1455        failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_LARGE;
    1456        is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_LARGE;
    1457        path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_LARGE;
    1458        break;
    1459      case THUMBNAIL_SIZE_XLARGE:
    1460        failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XLARGE;
    1461        is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XLARGE;
    1462        path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XLARGE;
    1463        break;
    1464      case THUMBNAIL_SIZE_XXLARGE:
    1465        failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XXLARGE;
    1466        is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XXLARGE;
    1467        path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XXLARGE;
    1468        break;
    1469      default:
    1470        g_assert_not_reached ();
    1471      }
    1472  
    1473    dirname = get_thumbnail_dirname_from_size (size);
    1474    uri = g_filename_to_uri (path, NULL, NULL);
    1475  
    1476    checksum = g_checksum_new (G_CHECKSUM_MD5);
    1477    g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
    1478  
    1479    basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
    1480    g_checksum_free (checksum);
    1481  
    1482    if (dirname)
    1483      {
    1484        filename = g_build_filename (g_get_user_cache_dir (),
    1485                                     "thumbnails", dirname, basename,
    1486                                     NULL);
    1487  
    1488        if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
    1489          g_clear_pointer (&filename, g_free);
    1490      }
    1491    else
    1492      {
    1493        gssize i;
    1494  
    1495        for (i = THUMBNAIL_SIZE_LAST - 1; i >= 0 ; i--)
    1496          {
    1497            filename = g_build_filename (g_get_user_cache_dir (),
    1498                                         "thumbnails",
    1499                                         get_thumbnail_dirname_from_size (i),
    1500                                         basename,
    1501                                        NULL);
    1502            if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
    1503              break;
    1504  
    1505            g_clear_pointer (&filename, g_free);
    1506          }
    1507      }
    1508  
    1509    if (filename)
    1510      {
    1511        _g_file_info_set_attribute_byte_string_by_id (info, path_attr_id, filename);
    1512        _g_file_info_set_attribute_boolean_by_id (info, is_valid_attr_id,
    1513                                                  thumbnail_verify (filename, uri, stat_buf));
    1514      }
    1515    else
    1516      {
    1517        filename = g_build_filename (g_get_user_cache_dir (),
    1518                                     "thumbnails", "fail",
    1519                                     "gnome-thumbnail-factory",
    1520                                     basename,
    1521                                     NULL);
    1522  
    1523        if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
    1524          {
    1525            _g_file_info_set_attribute_boolean_by_id (info, failed_attr_id, TRUE);
    1526            _g_file_info_set_attribute_boolean_by_id (info, is_valid_attr_id,
    1527                                                      thumbnail_verify (filename, uri, stat_buf));
    1528          }
    1529      }
    1530  
    1531    g_free (basename);
    1532    g_free (filename);
    1533    g_free (uri);
    1534  }
    1535  
    1536  #ifdef G_OS_WIN32
    1537  static void
    1538  win32_get_file_user_info (const gchar  *filename,
    1539  			  gchar       **group_name, 
    1540  			  gchar       **user_name, 
    1541  			  gchar       **real_name)
    1542  {
    1543    PSECURITY_DESCRIPTOR psd = NULL;
    1544    DWORD sd_size = 0; /* first call calculates the size required */
    1545    
    1546    wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
    1547    if ((GetFileSecurityW (wfilename, 
    1548                          GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
    1549  			NULL,
    1550  			sd_size,
    1551  			&sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
    1552       (psd = g_try_malloc (sd_size)) != NULL &&
    1553       GetFileSecurityW (wfilename, 
    1554                         GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
    1555  		       psd,
    1556  		       sd_size,
    1557  		       &sd_size))
    1558      {
    1559        PSID psid = 0;
    1560        BOOL defaulted;
    1561        SID_NAME_USE name_use = 0; /* don't care? */
    1562        wchar_t *name = NULL;
    1563        wchar_t *domain = NULL;
    1564        DWORD name_len = 0;
    1565        DWORD domain_len = 0;
    1566        /* get the user name */
    1567        do {
    1568          if (!user_name)
    1569  	  break;
    1570  	if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
    1571  	  break;
    1572  	if (!LookupAccountSidW (NULL, /* local machine */
    1573                                  psid, 
    1574  			        name, &name_len,
    1575  			        domain, &domain_len, /* no domain info yet */
    1576  			        &name_use)  && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
    1577  	  break;
    1578  	name = g_try_malloc (name_len * sizeof (wchar_t));
    1579  	domain = g_try_malloc (domain_len * sizeof (wchar_t));
    1580  	if (name && domain &&
    1581              LookupAccountSidW (NULL, /* local machine */
    1582                                 psid, 
    1583  			       name, &name_len,
    1584  			       domain, &domain_len, /* no domain info yet */
    1585  			       &name_use))
    1586  	  {
    1587  	    *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
    1588  	  }
    1589  	g_free (name);
    1590  	g_free (domain);
    1591        } while (FALSE);
    1592  
    1593        /* get the group name */
    1594        do {
    1595          if (!group_name)
    1596  	  break;
    1597  	if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
    1598  	  break;
    1599  	if (!LookupAccountSidW (NULL, /* local machine */
    1600                                  psid, 
    1601  			        name, &name_len,
    1602  			        domain, &domain_len, /* no domain info yet */
    1603  			        &name_use)  && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
    1604  	  break;
    1605  	name = g_try_malloc (name_len * sizeof (wchar_t));
    1606  	domain = g_try_malloc (domain_len * sizeof (wchar_t));
    1607  	if (name && domain &&
    1608              LookupAccountSidW (NULL, /* local machine */
    1609                                 psid, 
    1610  			       name, &name_len,
    1611  			       domain, &domain_len, /* no domain info yet */
    1612  			       &name_use))
    1613  	  {
    1614  	    *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
    1615  	  }
    1616  	g_free (name);
    1617  	g_free (domain);
    1618        } while (FALSE);
    1619  
    1620        /* TODO: get real name */
    1621  
    1622        g_free (psd);
    1623      }
    1624    g_free (wfilename);
    1625  }
    1626  #endif /* G_OS_WIN32 */
    1627  
    1628  #ifndef G_OS_WIN32
    1629  /* support for '.hidden' files */
    1630  G_LOCK_DEFINE_STATIC (hidden_cache);
    1631  static GHashTable *hidden_cache;
    1632  static GSource *hidden_cache_source = NULL; /* Under the hidden_cache lock */
    1633  static guint hidden_cache_ttl_secs = 5;
    1634  static guint hidden_cache_ttl_jitter_secs = 2;
    1635  
    1636  typedef struct
    1637  {
    1638    GHashTable *hidden_files;
    1639    gint64 timestamp_secs;
    1640  } HiddenCacheData;
    1641  
    1642  static gboolean
    1643  remove_from_hidden_cache (gpointer user_data)
    1644  {
    1645    HiddenCacheData *data;
    1646    GHashTableIter iter;
    1647    gboolean retval;
    1648    gint64 timestamp_secs;
    1649  
    1650    G_LOCK (hidden_cache);
    1651    timestamp_secs = g_source_get_time (hidden_cache_source) / G_USEC_PER_SEC;
    1652  
    1653    g_hash_table_iter_init (&iter, hidden_cache);
    1654    while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
    1655      {
    1656        if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
    1657          g_hash_table_iter_remove (&iter);
    1658      }
    1659  
    1660    if (g_hash_table_size (hidden_cache) == 0)
    1661      {
    1662        g_clear_pointer (&hidden_cache_source, g_source_unref);
    1663        retval = G_SOURCE_REMOVE;
    1664      }
    1665    else
    1666      retval = G_SOURCE_CONTINUE;
    1667  
    1668    G_UNLOCK (hidden_cache);
    1669  
    1670    return retval;
    1671  }
    1672  
    1673  static GHashTable *
    1674  read_hidden_file (const gchar *dirname)
    1675  {
    1676    gchar *contents = NULL;
    1677    gchar *filename;
    1678  
    1679    filename = g_build_path ("/", dirname, ".hidden", NULL);
    1680    (void) g_file_get_contents (filename, &contents, NULL, NULL);
    1681    g_free (filename);
    1682  
    1683    if (contents != NULL)
    1684      {
    1685        GHashTable *table;
    1686        gchar **lines;
    1687        gint i;
    1688  
    1689        table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
    1690  
    1691        lines = g_strsplit (contents, "\n", 0);
    1692        g_free (contents);
    1693  
    1694        for (i = 0; lines[i]; i++)
    1695          /* hash table takes the individual strings... */
    1696          g_hash_table_add (table, lines[i]);
    1697  
    1698        /* ... so we only free the container. */
    1699        g_free (lines);
    1700  
    1701        return table;
    1702      }
    1703    else
    1704      return NULL;
    1705  }
    1706  
    1707  static void
    1708  free_hidden_file_data (gpointer user_data)
    1709  {
    1710    HiddenCacheData *data = user_data;
    1711  
    1712    g_clear_pointer (&data->hidden_files, g_hash_table_unref);
    1713    g_free (data);
    1714  }
    1715  
    1716  static gboolean
    1717  file_is_hidden (const gchar *path,
    1718                  const gchar *basename)
    1719  {
    1720    HiddenCacheData *data;
    1721    gboolean result;
    1722    gchar *dirname;
    1723    gpointer table;
    1724  
    1725    dirname = g_path_get_dirname (path);
    1726  
    1727    G_LOCK (hidden_cache);
    1728  
    1729    if G_UNLIKELY (hidden_cache == NULL)
    1730      hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
    1731                                            g_free, free_hidden_file_data);
    1732  
    1733    if (!g_hash_table_lookup_extended (hidden_cache, dirname,
    1734                                       NULL, (gpointer *) &data))
    1735      {
    1736        data = g_new0 (HiddenCacheData, 1);
    1737        data->hidden_files = table = read_hidden_file (dirname);
    1738        data->timestamp_secs = g_get_monotonic_time () / G_USEC_PER_SEC;
    1739  
    1740        g_hash_table_insert (hidden_cache,
    1741                             g_strdup (dirname),
    1742                             data);
    1743  
    1744        if (!hidden_cache_source)
    1745          {
    1746            hidden_cache_source =
    1747              g_timeout_source_new_seconds (hidden_cache_ttl_secs +
    1748                                            hidden_cache_ttl_jitter_secs);
    1749            g_source_set_priority (hidden_cache_source, G_PRIORITY_DEFAULT);
    1750            g_source_set_static_name (hidden_cache_source,
    1751                                      "[gio] remove_from_hidden_cache");
    1752            g_source_set_callback (hidden_cache_source,
    1753                                   remove_from_hidden_cache,
    1754                                   NULL, NULL);
    1755            g_source_attach (hidden_cache_source,
    1756                             GLIB_PRIVATE_CALL (g_get_worker_context) ());
    1757          }
    1758      }
    1759    else
    1760      table = data->hidden_files;
    1761  
    1762    result = table != NULL && g_hash_table_contains (table, basename);
    1763  
    1764    G_UNLOCK (hidden_cache);
    1765  
    1766    g_free (dirname);
    1767  
    1768    return result;
    1769  }
    1770  #endif /* !G_OS_WIN32 */
    1771  
    1772  void
    1773  _g_local_file_info_get_nostat (GFileInfo              *info,
    1774                                 const char             *basename,
    1775  			       const char             *path,
    1776                                 GFileAttributeMatcher  *attribute_matcher)
    1777  {
    1778    g_file_info_set_name (info, basename);
    1779  
    1780    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    1781  					    G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
    1782      {
    1783        char *display_name = g_filename_display_basename (path);
    1784       
    1785        /* look for U+FFFD REPLACEMENT CHARACTER */ 
    1786        if (strstr (display_name, "\357\277\275") != NULL)
    1787  	{
    1788  	  char *p = display_name;
    1789  	  display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
    1790  	  g_free (p);
    1791  	}
    1792        g_file_info_set_display_name (info, display_name);
    1793        g_free (display_name);
    1794      }
    1795    
    1796    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    1797  					    G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
    1798      {
    1799        char *edit_name = g_filename_display_basename (path);
    1800        g_file_info_set_edit_name (info, edit_name);
    1801        g_free (edit_name);
    1802      }
    1803  
    1804    
    1805    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    1806  					    G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
    1807      {
    1808        char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
    1809        if (copy_name)
    1810  	_g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
    1811        g_free (copy_name);
    1812      }
    1813  }
    1814  
    1815  static const char *
    1816  get_icon_name (const char *path,
    1817                 gboolean    use_symbolic,
    1818                 gboolean   *with_fallbacks_out)
    1819  {
    1820    const char *name = NULL;
    1821    gboolean with_fallbacks = TRUE;
    1822  
    1823    if (g_strcmp0 (path, g_get_home_dir ()) == 0)
    1824      {
    1825        name = use_symbolic ? "user-home-symbolic" : "user-home";
    1826        with_fallbacks = FALSE;
    1827      }
    1828    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
    1829      {
    1830        name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
    1831        with_fallbacks = FALSE;
    1832      }
    1833    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
    1834      {
    1835        name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
    1836      }
    1837    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
    1838      {
    1839        name = use_symbolic ? "folder-download-symbolic" : "folder-download";
    1840      }
    1841    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
    1842      {
    1843        name = use_symbolic ? "folder-music-symbolic" : "folder-music";
    1844      }
    1845    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
    1846      {
    1847        name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
    1848      }
    1849    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
    1850      {
    1851        name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
    1852      }
    1853    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
    1854      {
    1855        name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
    1856      }
    1857    else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
    1858      {
    1859        name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
    1860      }
    1861    else
    1862      {
    1863        name = NULL;
    1864      }
    1865  
    1866    if (with_fallbacks_out != NULL)
    1867      *with_fallbacks_out = with_fallbacks;
    1868  
    1869    return name;
    1870  }
    1871  
    1872  static GIcon *
    1873  get_icon (const char *path,
    1874            const char *content_type,
    1875            gboolean    use_symbolic)
    1876  {
    1877    GIcon *icon = NULL;
    1878    const char *icon_name;
    1879    gboolean with_fallbacks;
    1880  
    1881    icon_name = get_icon_name (path, use_symbolic, &with_fallbacks);
    1882    if (icon_name != NULL)
    1883      {
    1884        if (with_fallbacks)
    1885          icon = g_themed_icon_new_with_default_fallbacks (icon_name);
    1886        else
    1887          icon = g_themed_icon_new (icon_name);
    1888      }
    1889    else
    1890      {
    1891        if (use_symbolic)
    1892          icon = g_content_type_get_symbolic_icon (content_type);
    1893        else
    1894          icon = g_content_type_get_icon (content_type);
    1895      }
    1896  
    1897    return icon;
    1898  }
    1899  
    1900  GFileInfo *
    1901  _g_local_file_info_get (const char             *basename,
    1902  			const char             *path,
    1903  			GFileAttributeMatcher  *attribute_matcher,
    1904  			GFileQueryInfoFlags     flags,
    1905  			GLocalParentFileInfo   *parent_info,
    1906  			GError                **error)
    1907  {
    1908    GFileInfo *info;
    1909    GLocalFileStat statbuf;
    1910    GLocalFileStat statbuf2;
    1911    int res;
    1912    gboolean stat_ok;
    1913    gboolean is_symlink, symlink_broken;
    1914    char *symlink_target;
    1915    GVfs *vfs;
    1916    GVfsClass *class;
    1917    guint64 device;
    1918  
    1919    info = g_file_info_new ();
    1920  
    1921    /* Make sure we don't set any unwanted attributes */
    1922    g_file_info_set_attribute_mask (info, attribute_matcher);
    1923    
    1924    _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
    1925  
    1926    if (attribute_matcher == NULL)
    1927      {
    1928        g_file_info_unset_attribute_mask (info);
    1929        return info;
    1930      }
    1931  
    1932    res = g_local_file_lstat (path,
    1933                              G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
    1934                              G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
    1935                              &statbuf);
    1936  
    1937    if (res == -1)
    1938      {
    1939        int errsv = errno;
    1940  
    1941        /* Don't bail out if we get Permission denied (SELinux?) */
    1942        if (errsv != EACCES)
    1943          {
    1944            char *display_name = g_filename_display_name (path);
    1945            g_object_unref (info);
    1946            g_set_error (error, G_IO_ERROR,
    1947  		       g_io_error_from_errno (errsv),
    1948  		       _("Error when getting information for file “%s”: %s"),
    1949  		       display_name, g_strerror (errsv));
    1950            g_free (display_name);
    1951            return NULL;
    1952          }
    1953      }
    1954  
    1955    /* Even if stat() fails, try to get as much as other attributes possible */
    1956    stat_ok = res != -1;
    1957  
    1958    if (stat_ok)
    1959      device = _g_stat_dev (&statbuf);
    1960    else
    1961      device = 0;
    1962  
    1963  #ifdef S_ISLNK
    1964    is_symlink = stat_ok && S_ISLNK (_g_stat_mode (&statbuf));
    1965  #elif defined (G_OS_WIN32)
    1966    /* glib already checked the FILE_ATTRIBUTE_REPARSE_POINT for us */
    1967    is_symlink = stat_ok &&
    1968        (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
    1969         statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT);
    1970  #else
    1971    is_symlink = FALSE;
    1972  #endif
    1973    symlink_broken = FALSE;
    1974  
    1975    if (is_symlink)
    1976      {
    1977        g_file_info_set_is_symlink (info, TRUE);
    1978  
    1979        /* Unless NOFOLLOW was set we default to following symlinks */
    1980        if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
    1981  	{
    1982            res = g_local_file_stat (path,
    1983                                     G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
    1984                                     G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
    1985                                     &statbuf2);
    1986  
    1987  	  /* Report broken links as symlinks */
    1988  	  if (res != -1)
    1989  	    {
    1990  	      statbuf = statbuf2;
    1991  	      stat_ok = TRUE;
    1992  	    }
    1993  	  else
    1994  	    symlink_broken = TRUE;
    1995  	}
    1996      }
    1997    else
    1998      g_file_info_set_is_symlink (info, FALSE);
    1999  
    2000    if (stat_ok)
    2001      set_info_from_stat (info, &statbuf, attribute_matcher);
    2002  
    2003  #ifndef G_OS_WIN32
    2004    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2005  					    G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
    2006      {
    2007        g_file_info_set_is_hidden (info,
    2008                                   (basename != NULL &&
    2009                                    (basename[0] == '.' ||
    2010                                     file_is_hidden (path, basename) ||
    2011                                     (stat_ok &&
    2012                                      _g_local_file_is_lost_found_dir (path, _g_stat_dev (&statbuf))))));
    2013      }
    2014  
    2015    _g_file_info_set_attribute_boolean_by_id (info,
    2016                                              G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP,
    2017                                              basename != NULL && basename[strlen (basename) - 1] == '~' &&
    2018                                                  (stat_ok && S_ISREG (_g_stat_mode (&statbuf))));
    2019  #else
    2020    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, FALSE);
    2021  
    2022    g_file_info_set_is_hidden (info, (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN));
    2023  
    2024    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE,
    2025                                              (statbuf.attributes & FILE_ATTRIBUTE_ARCHIVE));
    2026  
    2027    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM,
    2028                                              (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM));
    2029  
    2030    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_MOUNTPOINT,
    2031                                              (statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT));
    2032  
    2033    if (statbuf.reparse_tag != 0)
    2034      _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_REPARSE_POINT_TAG, statbuf.reparse_tag);
    2035  
    2036    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, FALSE);
    2037  #endif
    2038  
    2039    symlink_target = NULL;
    2040    if (is_symlink)
    2041      {
    2042  #if defined (S_ISLNK) || defined (G_OS_WIN32)
    2043        symlink_target = read_link (path);
    2044  #endif
    2045        if (symlink_target &&
    2046            _g_file_attribute_matcher_matches_id (attribute_matcher,
    2047                                                  G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
    2048          g_file_info_set_symlink_target (info, symlink_target);
    2049      }
    2050  
    2051    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2052  					    G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
    2053        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2054  					    G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
    2055        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2056  					    G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
    2057      {
    2058        char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
    2059  
    2060        if (content_type)
    2061  	{
    2062  	  g_file_info_set_content_type (info, content_type);
    2063  
    2064  	  if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2065                                                       G_FILE_ATTRIBUTE_ID_STANDARD_ICON)
    2066                 || _g_file_attribute_matcher_matches_id (attribute_matcher,
    2067                                                          G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
    2068  	    {
    2069  	      GIcon *icon;
    2070  
    2071                /* non symbolic icon */
    2072                icon = get_icon (path, content_type, FALSE);
    2073                if (icon != NULL)
    2074                  {
    2075                    g_file_info_set_icon (info, icon);
    2076                    g_object_unref (icon);
    2077                  }
    2078  
    2079                /* symbolic icon */
    2080                icon = get_icon (path, content_type, TRUE);
    2081                if (icon != NULL)
    2082                  {
    2083                    g_file_info_set_symbolic_icon (info, icon);
    2084                    g_object_unref (icon);
    2085                  }
    2086  
    2087  	    }
    2088  	  
    2089  	  g_free (content_type);
    2090  	}
    2091      }
    2092  
    2093    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2094  					    G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
    2095      {
    2096        char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
    2097        
    2098        if (content_type)
    2099  	{
    2100  	  _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
    2101  	  g_free (content_type);
    2102  	}
    2103      }
    2104  
    2105    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2106  					    G_FILE_ATTRIBUTE_ID_OWNER_USER))
    2107      {
    2108        char *name = NULL;
    2109        
    2110  #ifdef G_OS_WIN32
    2111        win32_get_file_user_info (path, NULL, &name, NULL);
    2112  #else
    2113        if (stat_ok)
    2114          name = get_username_from_uid (_g_stat_uid (&statbuf));
    2115  #endif
    2116        if (name)
    2117  	_g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
    2118        g_free (name);
    2119      }
    2120  
    2121    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2122  					    G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
    2123      {
    2124        char *name = NULL;
    2125  #ifdef G_OS_WIN32
    2126        win32_get_file_user_info (path, NULL, NULL, &name);
    2127  #else
    2128        if (stat_ok)
    2129          name = get_realname_from_uid (_g_stat_uid (&statbuf));
    2130  #endif
    2131        if (name)
    2132  	_g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
    2133        g_free (name);
    2134      }
    2135    
    2136    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2137  					    G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
    2138      {
    2139        char *name = NULL;
    2140  #ifdef G_OS_WIN32
    2141        win32_get_file_user_info (path, &name, NULL, NULL);
    2142  #else
    2143        if (stat_ok)
    2144          name = get_groupname_from_gid (_g_stat_gid (&statbuf));
    2145  #endif
    2146        if (name)
    2147  	_g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
    2148        g_free (name);
    2149      }
    2150  
    2151    if (stat_ok && parent_info && parent_info->device != 0 &&
    2152        _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
    2153      _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT,
    2154                                                (_g_stat_dev (&statbuf) != parent_info->device || _g_stat_ino (&statbuf) == parent_info->inode));
    2155    
    2156    if (stat_ok)
    2157      get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
    2158    
    2159  #ifdef HAVE_SELINUX
    2160    get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
    2161  #endif
    2162    get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
    2163    get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
    2164  
    2165    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2166                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH) ||
    2167        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2168                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID) ||
    2169        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2170                                              G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED))
    2171      {
    2172        get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_AUTO);
    2173      }
    2174  
    2175    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2176                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_NORMAL) ||
    2177        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2178                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_NORMAL) ||
    2179        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2180                                              G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_NORMAL))
    2181      {
    2182        get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_NORMAL);
    2183      }
    2184  
    2185    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2186                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_LARGE) ||
    2187        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2188                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_LARGE) ||
    2189        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2190                                              G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_LARGE))
    2191      {
    2192        get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_LARGE);
    2193      }
    2194  
    2195    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2196                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XLARGE) ||
    2197        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2198                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XLARGE) ||
    2199        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2200                                              G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XLARGE))
    2201      {
    2202        get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_XLARGE);
    2203      }
    2204  
    2205    if (_g_file_attribute_matcher_matches_id (attribute_matcher,
    2206                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XXLARGE) ||
    2207        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2208                                              G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XXLARGE) ||
    2209        _g_file_attribute_matcher_matches_id (attribute_matcher,
    2210                                              G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XXLARGE))
    2211      {
    2212        get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_XXLARGE);
    2213      }
    2214  
    2215    vfs = g_vfs_get_default ();
    2216    class = G_VFS_GET_CLASS (vfs);
    2217    if (class->local_file_add_info)
    2218      {
    2219        class->local_file_add_info (vfs,
    2220                                    path,
    2221                                    device,
    2222                                    attribute_matcher,
    2223                                    info,
    2224                                    NULL,
    2225                                    &parent_info->extra_data,
    2226                                    &parent_info->free_extra_data);
    2227      }
    2228  
    2229    g_file_info_unset_attribute_mask (info);
    2230  
    2231    g_free (symlink_target);
    2232  
    2233    return info;
    2234  }
    2235  
    2236  GFileInfo *
    2237  _g_local_file_info_get_from_fd (int         fd,
    2238  				const char *attributes,
    2239  				GError    **error)
    2240  {
    2241    GLocalFileStat stat_buf;
    2242    GFileAttributeMatcher *matcher;
    2243    GFileInfo *info;
    2244  
    2245    if (g_local_file_fstat (fd,
    2246                            G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
    2247                            G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
    2248                            &stat_buf) == -1)
    2249      {
    2250        int errsv = errno;
    2251  
    2252        g_set_error (error, G_IO_ERROR,
    2253  		   g_io_error_from_errno (errsv),
    2254  		   _("Error when getting information for file descriptor: %s"),
    2255  		   g_strerror (errsv));
    2256        return NULL;
    2257      }
    2258  
    2259    info = g_file_info_new ();
    2260  
    2261    matcher = g_file_attribute_matcher_new (attributes);
    2262  
    2263    /* Make sure we don't set any unwanted attributes */
    2264    g_file_info_set_attribute_mask (info, matcher);
    2265    
    2266    set_info_from_stat (info, &stat_buf, matcher);
    2267    
    2268  #ifdef HAVE_SELINUX
    2269    if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
    2270        is_selinux_enabled ())
    2271      {
    2272        char *context;
    2273        if (fgetfilecon_raw (fd, &context) >= 0)
    2274  	{
    2275  	  _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
    2276  	  freecon (context);
    2277  	}
    2278      }
    2279  #endif
    2280  
    2281    get_xattrs_from_fd (fd, TRUE, info, matcher);
    2282    get_xattrs_from_fd (fd, FALSE, info, matcher);
    2283    
    2284    g_file_attribute_matcher_unref (matcher);
    2285  
    2286    g_file_info_unset_attribute_mask (info);
    2287    
    2288    return info;
    2289  }
    2290  
    2291  static gboolean
    2292  get_uint32 (const GFileAttributeValue  *value,
    2293  	    guint32                    *val_out,
    2294  	    GError                    **error)
    2295  {
    2296    if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
    2297      {
    2298        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    2299                             _("Invalid attribute type (uint32 expected)"));
    2300        return FALSE;
    2301      }
    2302  
    2303    *val_out = value->u.uint32;
    2304    
    2305    return TRUE;
    2306  }
    2307  
    2308  #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
    2309  static gboolean
    2310  get_uint64 (const GFileAttributeValue  *value,
    2311  	    guint64                    *val_out,
    2312  	    GError                    **error)
    2313  {
    2314    if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
    2315      {
    2316        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    2317                             _("Invalid attribute type (uint64 expected)"));
    2318        return FALSE;
    2319      }
    2320  
    2321    *val_out = value->u.uint64;
    2322    
    2323    return TRUE;
    2324  }
    2325  #endif
    2326  
    2327  #if defined(HAVE_SYMLINK)
    2328  static gboolean
    2329  get_byte_string (const GFileAttributeValue  *value,
    2330  		 const char                **val_out,
    2331  		 GError                    **error)
    2332  {
    2333    if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
    2334      {
    2335        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    2336                             _("Invalid attribute type (byte string expected)"));
    2337        return FALSE;
    2338      }
    2339  
    2340    *val_out = value->u.string;
    2341    
    2342    return TRUE;
    2343  }
    2344  #endif
    2345  
    2346  #ifdef HAVE_SELINUX
    2347  static gboolean
    2348  get_string (const GFileAttributeValue  *value,
    2349  	    const char                **val_out,
    2350  	    GError                    **error)
    2351  {
    2352    if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
    2353      {
    2354        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    2355                             _("Invalid attribute type (byte string expected)"));
    2356        return FALSE;
    2357      }
    2358  
    2359    *val_out = value->u.string;
    2360    
    2361    return TRUE;
    2362  }
    2363  #endif
    2364  
    2365  static gboolean
    2366  set_unix_mode (char                       *filename,
    2367                 GFileQueryInfoFlags         flags,
    2368  	       const GFileAttributeValue  *value,
    2369  	       GError                    **error)
    2370  {
    2371    guint32 val = 0;
    2372    int res = 0;
    2373    
    2374    if (!get_uint32 (value, &val, error))
    2375      return FALSE;
    2376  
    2377  #if defined (HAVE_SYMLINK) || defined (G_OS_WIN32)
    2378    if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
    2379  #ifdef HAVE_LCHMOD
    2380      res = lchmod (filename, val);
    2381  #else
    2382      gboolean is_symlink;
    2383  #ifndef G_OS_WIN32
    2384      struct stat statbuf;
    2385      /* Calling chmod on a symlink changes permissions on the symlink.
    2386       * We don't want to do this, so we need to check for a symlink */
    2387      res = g_lstat (filename, &statbuf);
    2388      is_symlink = (res == 0 && S_ISLNK (statbuf.st_mode));
    2389  #else
    2390      /* FIXME: implement lchmod for W32, should be doable */
    2391      GWin32PrivateStat statbuf;
    2392  
    2393      res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (filename, &statbuf);
    2394      is_symlink = (res == 0 &&
    2395                    (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
    2396                     statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT));
    2397  #endif
    2398      if (is_symlink)
    2399        {
    2400          g_set_error_literal (error, G_IO_ERROR,
    2401                               G_IO_ERROR_NOT_SUPPORTED,
    2402                               _("Cannot set permissions on symlinks"));
    2403          return FALSE;
    2404        }
    2405      else if (res == 0)
    2406        res = g_chmod (filename, val);
    2407  #endif
    2408    } else
    2409  #endif
    2410      res = g_chmod (filename, val);
    2411  
    2412    if (res == -1)
    2413      {
    2414        int errsv = errno;
    2415  
    2416        g_set_error (error, G_IO_ERROR,
    2417  		   g_io_error_from_errno (errsv),
    2418  		   _("Error setting permissions: %s"),
    2419  		   g_strerror (errsv));
    2420        return FALSE;
    2421      }
    2422    return TRUE;
    2423  }
    2424  
    2425  #ifdef G_OS_UNIX
    2426  static gboolean
    2427  set_unix_uid_gid (char                       *filename,
    2428  		  const GFileAttributeValue  *uid_value,
    2429  		  const GFileAttributeValue  *gid_value,
    2430  		  GFileQueryInfoFlags         flags,
    2431  		  GError                    **error)
    2432  {
    2433    int res;
    2434    guint32 val = 0;
    2435    uid_t uid;
    2436    gid_t gid;
    2437    
    2438    if (uid_value)
    2439      {
    2440        if (!get_uint32 (uid_value, &val, error))
    2441  	return FALSE;
    2442        uid = val;
    2443      }
    2444    else
    2445      uid = -1;
    2446    
    2447    if (gid_value)
    2448      {
    2449        if (!get_uint32 (gid_value, &val, error))
    2450  	return FALSE;
    2451        gid = val;
    2452      }
    2453    else
    2454      gid = -1;
    2455    
    2456  #ifdef HAVE_LCHOWN
    2457    if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
    2458      res = lchown (filename, uid, gid);
    2459    else
    2460  #endif
    2461      res = chown (filename, uid, gid);
    2462    
    2463    if (res == -1)
    2464      {
    2465        int errsv = errno;
    2466  
    2467        g_set_error (error, G_IO_ERROR,
    2468  		   g_io_error_from_errno (errsv),
    2469  		   _("Error setting owner: %s"),
    2470  		   g_strerror (errsv));
    2471  	  return FALSE;
    2472      }
    2473    return TRUE;
    2474  }
    2475  #endif
    2476  
    2477  #ifdef HAVE_SYMLINK
    2478  static gboolean
    2479  set_symlink (char                       *filename,
    2480  	     const GFileAttributeValue  *value,
    2481  	     GError                    **error)
    2482  {
    2483    const char *val;
    2484    struct stat statbuf;
    2485    
    2486    if (!get_byte_string (value, &val, error))
    2487      return FALSE;
    2488    
    2489    if (val == NULL)
    2490      {
    2491        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    2492                             _("symlink must be non-NULL"));
    2493        return FALSE;
    2494      }
    2495    
    2496    if (g_lstat (filename, &statbuf))
    2497      {
    2498        int errsv = errno;
    2499  
    2500        g_set_error (error, G_IO_ERROR,
    2501  		   g_io_error_from_errno (errsv),
    2502  		   _("Error setting symlink: %s"),
    2503  		   g_strerror (errsv));
    2504        return FALSE;
    2505      }
    2506    
    2507    if (!S_ISLNK (statbuf.st_mode))
    2508      {
    2509        g_set_error_literal (error, G_IO_ERROR,
    2510                             G_IO_ERROR_NOT_SYMBOLIC_LINK,
    2511                             _("Error setting symlink: file is not a symlink"));
    2512        return FALSE;
    2513      }
    2514    
    2515    if (g_unlink (filename))
    2516      {
    2517        int errsv = errno;
    2518  
    2519        g_set_error (error, G_IO_ERROR,
    2520  		   g_io_error_from_errno (errsv),
    2521  		   _("Error setting symlink: %s"),
    2522  		   g_strerror (errsv));
    2523        return FALSE;
    2524      }
    2525    
    2526    if (symlink (filename, val) != 0)
    2527      {
    2528        int errsv = errno;
    2529  
    2530        g_set_error (error, G_IO_ERROR,
    2531  		   g_io_error_from_errno (errsv),
    2532  		   _("Error setting symlink: %s"),
    2533  		   g_strerror (errsv));
    2534        return FALSE;
    2535      }
    2536    
    2537    return TRUE;
    2538  }
    2539  #endif
    2540  
    2541  #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined(G_OS_WIN32)
    2542  static int
    2543  lazy_stat (const char  *filename,
    2544             GStatBuf    *statbuf,
    2545             gboolean    *called_stat)
    2546  {
    2547    int res;
    2548  
    2549    if (*called_stat)
    2550      return 0;
    2551  
    2552    res = g_stat (filename, statbuf);
    2553  
    2554    if (res == 0)
    2555      *called_stat = TRUE;
    2556  
    2557    return res;
    2558  }
    2559  #endif
    2560  
    2561  #if defined (G_OS_WIN32)
    2562  /* From
    2563   * https://support.microsoft.com/en-ca/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime
    2564   * FT = UT * 10000000 + 116444736000000000.
    2565   * Converts unix epoch time (a signed 64-bit integer) to FILETIME.
    2566   * Can optionally use a more precise timestamp that has
    2567   * a fraction of a second expressed in nanoseconds.
    2568   * UT must be between January 1st of year 1601 and December 31st of year 30827.
    2569   * nsec must be non-negative and < 1000000000.
    2570   * Returns TRUE if conversion succeeded, FALSE otherwise.
    2571   *
    2572   * The function that does the reverse can be found in
    2573   * glib/gstdio.c.
    2574   */
    2575  static gboolean
    2576  _g_win32_unix_time_to_filetime (gint64     ut,
    2577                                  gint32     nsec,
    2578                                  FILETIME  *ft,
    2579                                  GError   **error)
    2580  {
    2581    gint64 result;
    2582    /* 1 unit of FILETIME is 100ns */
    2583    const gint64 hundreds_of_nsec_per_sec = 10000000;
    2584    /* The difference between January 1, 1601 UTC (FILETIME epoch) and UNIX epoch
    2585     * in hundreds of nanoseconds.
    2586     */
    2587    const gint64 filetime_unix_epoch_offset = 116444736000000000;
    2588    /* This is the maximum timestamp that SYSTEMTIME can
    2589     * represent (last millisecond of the year 30827).
    2590     * Since FILETIME and SYSTEMTIME are both used on Windows,
    2591     * we use this as a limit (FILETIME can support slightly
    2592     * larger interval, up to year 30828).
    2593     */
    2594    const gint64 max_systemtime = 0x7fff35f4f06c58f0;
    2595  
    2596    g_return_val_if_fail (ft != NULL, FALSE);
    2597    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    2598  
    2599    if (nsec < 0)
    2600      {
    2601        g_set_error (error, G_IO_ERROR,
    2602                     G_IO_ERROR_INVALID_DATA,
    2603                     _("Extra nanoseconds %d for UNIX timestamp %lld are negative"),
    2604                     nsec, ut);
    2605        return FALSE;
    2606      }
    2607  
    2608    if (nsec >= hundreds_of_nsec_per_sec * 100)
    2609      {
    2610        g_set_error (error, G_IO_ERROR,
    2611                     G_IO_ERROR_INVALID_DATA,
    2612                     _("Extra nanoseconds %d for UNIX timestamp %lld reach 1 second"),
    2613                     nsec, ut);
    2614        return FALSE;
    2615      }
    2616  
    2617    if (ut >= (G_MAXINT64 / hundreds_of_nsec_per_sec) ||
    2618        (ut * hundreds_of_nsec_per_sec) >= (G_MAXINT64 - filetime_unix_epoch_offset))
    2619      {
    2620        g_set_error (error, G_IO_ERROR,
    2621                     G_IO_ERROR_INVALID_DATA,
    2622                     _("UNIX timestamp %lld does not fit into 64 bits"),
    2623                     ut);
    2624        return FALSE;
    2625      }
    2626  
    2627    result = ut * hundreds_of_nsec_per_sec + filetime_unix_epoch_offset + nsec / 100;
    2628  
    2629    if (result >= max_systemtime || result < 0)
    2630      {
    2631        g_set_error (error, G_IO_ERROR,
    2632                     G_IO_ERROR_INVALID_DATA,
    2633                     _("UNIX timestamp %lld is outside of the range supported by Windows"),
    2634                     ut);
    2635        return FALSE;
    2636      }
    2637  
    2638    ft->dwLowDateTime = (DWORD) (result);
    2639    ft->dwHighDateTime = (DWORD) (result >> 32);
    2640  
    2641    return TRUE;
    2642  }
    2643  
    2644  static gboolean
    2645  set_mtime_atime (const char                 *filename,
    2646  		 const GFileAttributeValue  *mtime_value,
    2647  		 const GFileAttributeValue  *mtime_usec_value,
    2648  		 const GFileAttributeValue  *mtime_nsec_value,
    2649  		 const GFileAttributeValue  *atime_value,
    2650  		 const GFileAttributeValue  *atime_usec_value,
    2651  		 const GFileAttributeValue  *atime_nsec_value,
    2652  		 GError                    **error)
    2653  {
    2654    BOOL res;
    2655    guint64 val = 0;
    2656    guint32 val_usec = 0;
    2657    guint32 val_nsec = 0;
    2658    gunichar2 *filename_utf16;
    2659    SECURITY_ATTRIBUTES sec = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
    2660    HANDLE file_handle;
    2661    FILETIME mtime;
    2662    FILETIME atime;
    2663    FILETIME *p_mtime = NULL;
    2664    FILETIME *p_atime = NULL;
    2665    DWORD gle;
    2666    GStatBuf statbuf;
    2667    gboolean got_stat = FALSE;
    2668  
    2669    /* ATIME */
    2670    if (atime_value)
    2671      {
    2672        if (!get_uint64 (atime_value, &val, error))
    2673          return FALSE;
    2674        val_usec = 0;
    2675        val_nsec = 0;
    2676      }
    2677    else
    2678      {
    2679        if (lazy_stat (filename, &statbuf, &got_stat) == 0)
    2680  	{
    2681            val = statbuf.st_atime;
    2682  #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
    2683            val_nsec = statbuf.st_atimensec;
    2684  #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
    2685            val_nsec = statbuf.st_atim.tv_nsec;
    2686  #endif
    2687  	}
    2688      }
    2689  
    2690    if (atime_usec_value &&
    2691        !get_uint32 (atime_usec_value, &val_usec, error))
    2692      return FALSE;
    2693  
    2694    /* Convert to nanoseconds. Clamp the usec value if it’s going to overflow,
    2695     * as %G_MAXINT32 will trigger a ‘too big’ error in
    2696     * _g_win32_unix_time_to_filetime() anyway. */
    2697    val_nsec = (val_usec > G_MAXINT32 / 1000) ? G_MAXINT32 : (val_usec * 1000);
    2698  
    2699    if (atime_nsec_value &&
    2700        !get_uint32 (atime_nsec_value, &val_nsec, error))
    2701      return FALSE;
    2702    if (val_nsec > 0)
    2703      {
    2704        if (!_g_win32_unix_time_to_filetime (val, val_nsec, &atime, error))
    2705          return FALSE;
    2706      }
    2707    else
    2708      {
    2709        if (!_g_win32_unix_time_to_filetime (val, val_usec, &atime, error))
    2710          return FALSE;
    2711      }
    2712  
    2713    p_atime = &atime;
    2714  
    2715    /* MTIME */
    2716    if (mtime_value)
    2717      {
    2718        if (!get_uint64 (mtime_value, &val, error))
    2719  	return FALSE;
    2720        val_usec = 0;
    2721        val_nsec = 0;
    2722      }
    2723    else
    2724      {
    2725        if (lazy_stat (filename, &statbuf, &got_stat) == 0)
    2726  	{
    2727            val = statbuf.st_mtime;
    2728  #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
    2729            val_nsec = statbuf.st_mtimensec;
    2730  #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
    2731            val_nsec = statbuf.st_mtim.tv_nsec;
    2732  #endif
    2733  	}
    2734      }
    2735  
    2736    if (mtime_usec_value &&
    2737        !get_uint32 (mtime_usec_value, &val_usec, error))
    2738      return FALSE;
    2739  
    2740    /* Convert to nanoseconds. Clamp the usec value if it’s going to overflow,
    2741     * as %G_MAXINT32 will trigger a ‘too big’ error in
    2742     * _g_win32_unix_time_to_filetime() anyway. */
    2743    val_nsec = (val_usec > G_MAXINT32 / 1000) ? G_MAXINT32 : (val_usec * 1000);
    2744  
    2745    if (mtime_nsec_value &&
    2746        !get_uint32 (mtime_nsec_value, &val_nsec, error))
    2747      return FALSE;
    2748    if (val_nsec > 0)
    2749      {
    2750        if (!_g_win32_unix_time_to_filetime (val, val_nsec, &mtime, error))
    2751          return FALSE;
    2752      }
    2753    else
    2754      {
    2755        if (!_g_win32_unix_time_to_filetime (val, val_usec, &mtime, error))
    2756          return FALSE;
    2757      }
    2758    p_mtime = &mtime;
    2759  
    2760    filename_utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
    2761  
    2762    if (filename_utf16 == NULL)
    2763      {
    2764        g_prefix_error (error,
    2765                        _("File name “%s” cannot be converted to UTF-16"),
    2766                        filename);
    2767        return FALSE;
    2768      }
    2769  
    2770    file_handle = CreateFileW (filename_utf16,
    2771                               FILE_WRITE_ATTRIBUTES,
    2772                               FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
    2773                               &sec,
    2774                               OPEN_EXISTING,
    2775                               FILE_FLAG_BACKUP_SEMANTICS,
    2776                               NULL);
    2777    gle = GetLastError ();
    2778    g_clear_pointer (&filename_utf16, g_free);
    2779  
    2780    if (file_handle == INVALID_HANDLE_VALUE)
    2781      {
    2782        g_set_error (error, G_IO_ERROR,
    2783                     g_io_error_from_errno (gle),
    2784                     _("File “%s” cannot be opened: Windows Error %lu"),
    2785                     filename, gle);
    2786  
    2787        return FALSE;
    2788      }
    2789  
    2790    res = SetFileTime (file_handle, NULL, p_atime, p_mtime);
    2791    gle = GetLastError ();
    2792    CloseHandle (file_handle);
    2793  
    2794    if (!res)
    2795      g_set_error (error, G_IO_ERROR,
    2796                   g_io_error_from_errno (gle),
    2797                   _("Error setting modification or access time for file “%s”: %lu"),
    2798                   filename, gle);
    2799  
    2800    return res;
    2801  }
    2802  #elif defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT)
    2803  static gboolean
    2804  set_mtime_atime (char                       *filename,
    2805  		 const GFileAttributeValue  *mtime_value,
    2806  		 const GFileAttributeValue  *mtime_usec_value,
    2807  		 const GFileAttributeValue  *mtime_nsec_value,
    2808  		 const GFileAttributeValue  *atime_value,
    2809  		 const GFileAttributeValue  *atime_usec_value,
    2810  		 const GFileAttributeValue  *atime_nsec_value,
    2811  		 GError                    **error)
    2812  {
    2813    int res;
    2814    guint64 val = 0;
    2815    GStatBuf statbuf;
    2816    gboolean got_stat = FALSE;
    2817  #ifdef HAVE_UTIMENSAT
    2818    struct timespec times_n[2] = { {0, 0}, {0, 0} };
    2819    /* ATIME */
    2820    if (atime_value)
    2821      {
    2822        if (!get_uint64 (atime_value, &val, error))
    2823  	return FALSE;
    2824        times_n[0].tv_sec = val;
    2825      }
    2826    else
    2827      {
    2828        if (lazy_stat (filename, &statbuf, &got_stat) == 0)
    2829  	{
    2830            times_n[0].tv_sec = statbuf.st_atime;
    2831  #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
    2832            times_n[0].tv_nsec = statbuf.st_atimensec;
    2833  #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
    2834            times_n[0].tv_nsec = statbuf.st_atim.tv_nsec;
    2835  #endif
    2836  	}
    2837      }
    2838  
    2839    if (atime_usec_value)
    2840      {
    2841        guint32 val_usec = 0;
    2842  
    2843        if (!get_uint32 (atime_usec_value, &val_usec, error))
    2844          return FALSE;
    2845  
    2846        times_n[0].tv_nsec = val_usec * 1000;
    2847      }
    2848  
    2849    if (atime_nsec_value)
    2850      {
    2851        guint32 val_nsec = 0;
    2852  
    2853        if (!get_uint32 (atime_nsec_value, &val_nsec, error))
    2854          return FALSE;
    2855        times_n[0].tv_nsec = val_nsec;
    2856      }
    2857  
    2858    /* MTIME */
    2859    if (mtime_value)
    2860      {
    2861        if (!get_uint64 (mtime_value, &val, error))
    2862  	return FALSE;
    2863        times_n[1].tv_sec = val;
    2864      }
    2865    else
    2866      {
    2867        if (lazy_stat (filename, &statbuf, &got_stat) == 0)
    2868  	{
    2869            times_n[1].tv_sec = statbuf.st_mtime;
    2870  #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
    2871            times_n[1].tv_nsec = statbuf.st_mtimensec;
    2872  #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
    2873            times_n[1].tv_nsec = statbuf.st_mtim.tv_nsec;
    2874  #endif
    2875  	}
    2876      }
    2877  
    2878    if (mtime_usec_value)
    2879      {
    2880        guint32 val_usec = 0;
    2881  
    2882        if (!get_uint32 (mtime_usec_value, &val_usec, error))
    2883          return FALSE;
    2884  
    2885        times_n[1].tv_nsec = val_usec * 1000;
    2886      }
    2887  
    2888    if (mtime_nsec_value)
    2889      {
    2890        guint32 val_nsec = 0;
    2891  
    2892        if (!get_uint32 (mtime_nsec_value, &val_nsec, error))
    2893          return FALSE;
    2894        times_n[1].tv_nsec = val_nsec;
    2895      }
    2896  
    2897    res = utimensat (AT_FDCWD, filename, times_n, 0);
    2898  
    2899  #else /* HAVE_UTIMES */
    2900  
    2901    struct timeval times[2] = { {0, 0}, {0, 0} };
    2902  
    2903    /* ATIME */
    2904    if (atime_value)
    2905      {
    2906        if (!get_uint64 (atime_value, &val, error))
    2907          return FALSE;
    2908  
    2909        times[0].tv_sec = val;
    2910      }
    2911    else
    2912      {
    2913        if (lazy_stat (filename, &statbuf, &got_stat) == 0)
    2914          {
    2915            times[0].tv_sec = statbuf.st_atime;
    2916  #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
    2917            times[0].tv_usec = statbuf.st_atimensec / 1000;
    2918  #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
    2919            times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
    2920  #endif
    2921          }
    2922      }
    2923  
    2924    if (atime_usec_value)
    2925      {
    2926        guint32 val_usec = 0;
    2927  
    2928        if (!get_uint32 (atime_usec_value, &val_usec, error))
    2929          return FALSE;
    2930  
    2931        times[0].tv_usec = val_usec;
    2932      }
    2933  
    2934    /* MTIME */
    2935    if (mtime_value)
    2936      {
    2937        if (!get_uint64 (mtime_value, &val, error))
    2938          return FALSE;
    2939  
    2940        times[1].tv_sec = val;
    2941      }
    2942    else
    2943      {
    2944        if (lazy_stat (filename, &statbuf, &got_stat) == 0)
    2945          {
    2946            times[1].tv_sec = statbuf.st_mtime;
    2947  #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
    2948            times[1].tv_usec = statbuf.st_mtimensec / 1000;
    2949  #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
    2950            times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
    2951  #endif
    2952          }
    2953      }
    2954  
    2955    if (mtime_usec_value)
    2956      {
    2957        guint32 val_usec = 0;
    2958  
    2959        if (!get_uint32 (mtime_usec_value, &val_usec, error))
    2960          return FALSE;
    2961  
    2962        times[1].tv_usec = val_usec;
    2963      }
    2964  
    2965    res = utimes (filename, times);
    2966  #endif
    2967  
    2968    if (res == -1)
    2969      {
    2970        int errsv = errno;
    2971  
    2972        g_set_error (error, G_IO_ERROR,
    2973                     g_io_error_from_errno (errsv),
    2974                     _("Error setting modification or access time: %s"),
    2975                     g_strerror (errsv));
    2976        return FALSE;
    2977      }
    2978    return TRUE;
    2979  }
    2980  #endif
    2981  
    2982  
    2983  #ifdef HAVE_SELINUX
    2984  static gboolean
    2985  set_selinux_context (char                       *filename,
    2986                       const GFileAttributeValue  *value,
    2987                       GError                    **error)
    2988  {
    2989    const char *val;
    2990  
    2991    if (!get_string (value, &val, error))
    2992      return FALSE;
    2993  
    2994    if (val == NULL)
    2995      {
    2996        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    2997                             _("SELinux context must be non-NULL"));
    2998        return FALSE;
    2999      }
    3000  
    3001    if (!is_selinux_enabled ())
    3002      {
    3003        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    3004                             _("SELinux is not enabled on this system"));
    3005        return FALSE;
    3006      }
    3007  
    3008    if (setfilecon_raw (filename, val) < 0)
    3009      {
    3010        int errsv = errno;
    3011              
    3012        g_set_error (error, G_IO_ERROR,
    3013                     g_io_error_from_errno (errsv),
    3014                     _("Error setting SELinux context: %s"),
    3015                     g_strerror (errsv));
    3016        return FALSE;
    3017      }
    3018  
    3019    return TRUE;
    3020  }
    3021  #endif 
    3022  
    3023  
    3024  gboolean
    3025  _g_local_file_info_set_attribute (char                 *filename,
    3026  				  const char           *attribute,
    3027  				  GFileAttributeType    type,
    3028  				  gpointer              value_p,
    3029  				  GFileQueryInfoFlags   flags,
    3030  				  GCancellable         *cancellable,
    3031  				  GError              **error)
    3032  {
    3033    GFileAttributeValue value = { 0 };
    3034    GVfsClass *class;
    3035    GVfs *vfs;
    3036  
    3037    _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
    3038    
    3039    if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
    3040      return set_unix_mode (filename, flags, &value, error);
    3041    
    3042  #ifdef G_OS_UNIX
    3043    else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
    3044      return set_unix_uid_gid (filename, &value, NULL, flags, error);
    3045    else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
    3046      return set_unix_uid_gid (filename, NULL, &value, flags, error);
    3047  #endif
    3048    
    3049  #ifdef HAVE_SYMLINK
    3050    else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
    3051      return set_symlink (filename, &value, error);
    3052  #endif
    3053  
    3054  #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
    3055    else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
    3056      return set_mtime_atime (filename, &value, NULL, NULL, NULL, NULL, NULL, error);
    3057    else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
    3058      return set_mtime_atime (filename, NULL, &value, NULL, NULL, NULL, NULL, error);
    3059    else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC) == 0)
    3060      return set_mtime_atime (filename, NULL, NULL, &value, NULL, NULL, NULL, error);
    3061    else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
    3062      return set_mtime_atime (filename, NULL, NULL, NULL, &value, NULL, NULL, error);
    3063    else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
    3064      return set_mtime_atime (filename, NULL, NULL, NULL, NULL, &value, NULL, error);
    3065    else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC) == 0)
    3066      return set_mtime_atime (filename, NULL, NULL, NULL, NULL, NULL, &value, error);
    3067  #endif
    3068  
    3069  #ifdef HAVE_XATTR
    3070    else if (g_str_has_prefix (attribute, "xattr::"))
    3071      return set_xattr (filename, attribute, &value, error);
    3072    else if (g_str_has_prefix (attribute, "xattr-sys::"))
    3073      return set_xattr (filename, attribute, &value, error);
    3074  #endif
    3075  
    3076  #ifdef HAVE_SELINUX 
    3077    else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
    3078      return set_selinux_context (filename, &value, error);
    3079  #endif
    3080  
    3081    vfs = g_vfs_get_default ();
    3082    class = G_VFS_GET_CLASS (vfs);
    3083    if (class->local_file_set_attributes)
    3084      {
    3085        GFileInfo *info;
    3086  
    3087        info = g_file_info_new ();
    3088        g_file_info_set_attribute (info,
    3089                                   attribute,
    3090                                   type,
    3091                                   value_p);
    3092        if (!class->local_file_set_attributes (vfs, filename,
    3093                                               info,
    3094                                               flags, cancellable,
    3095                                               error))
    3096          {
    3097            g_object_unref (info);
    3098  	  return FALSE;
    3099          }
    3100  
    3101        if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
    3102          {
    3103            g_object_unref (info);
    3104            return TRUE;
    3105          }
    3106  
    3107        g_object_unref (info);
    3108      }
    3109  
    3110    g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    3111  	       _("Setting attribute %s not supported"), attribute);
    3112    return FALSE;
    3113  }
    3114  
    3115  gboolean
    3116  _g_local_file_info_set_attributes  (char                 *filename,
    3117  				    GFileInfo            *info,
    3118  				    GFileQueryInfoFlags   flags,
    3119  				    GCancellable         *cancellable,
    3120  				    GError              **error)
    3121  {
    3122    GFileAttributeValue *value;
    3123  #ifdef G_OS_UNIX
    3124    GFileAttributeValue *uid, *gid;
    3125  #endif
    3126  #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
    3127    GFileAttributeValue *mtime, *mtime_usec, *mtime_nsec, *atime, *atime_usec, *atime_nsec;
    3128  #endif
    3129  #if defined (G_OS_UNIX) || defined (G_OS_WIN32)
    3130    GFileAttributeStatus status;
    3131  #endif
    3132    gboolean res;
    3133    GVfsClass *class;
    3134    GVfs *vfs;
    3135    
    3136    /* Handles setting multiple specified data in a single set, and takes care
    3137       of ordering restrictions when setting attributes */
    3138  
    3139    res = TRUE;
    3140  
    3141    /* Set symlink first, since this recreates the file */
    3142  #ifdef HAVE_SYMLINK
    3143    value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
    3144    if (value)
    3145      {
    3146        if (!set_symlink (filename, value, error))
    3147  	{
    3148  	  value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
    3149  	  res = FALSE;
    3150  	  /* Don't set error multiple times */
    3151  	  error = NULL;
    3152  	}
    3153        else
    3154  	value->status = G_FILE_ATTRIBUTE_STATUS_SET;
    3155  	
    3156      }
    3157  #endif
    3158  
    3159  #ifdef G_OS_UNIX
    3160    /* Group uid and gid setting into one call
    3161     * Change ownership before permissions, since ownership changes can
    3162       change permissions (e.g. setuid)
    3163     */
    3164    uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
    3165    gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
    3166    
    3167    if (uid || gid)
    3168      {
    3169        if (!set_unix_uid_gid (filename, uid, gid, flags, error))
    3170  	{
    3171  	  status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
    3172  	  res = FALSE;
    3173  	  /* Don't set error multiple times */
    3174  	  error = NULL;
    3175  	}
    3176        else
    3177  	status = G_FILE_ATTRIBUTE_STATUS_SET;
    3178        if (uid)
    3179  	uid->status = status;
    3180        if (gid)
    3181  	gid->status = status;
    3182      }
    3183  #endif
    3184    
    3185    value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
    3186    if (value)
    3187      {
    3188        if (!set_unix_mode (filename, flags, value, error))
    3189  	{
    3190  	  value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
    3191  	  res = FALSE;
    3192  	  /* Don't set error multiple times */
    3193  	  error = NULL;
    3194  	}
    3195        else
    3196  	value->status = G_FILE_ATTRIBUTE_STATUS_SET;
    3197  	
    3198      }
    3199  
    3200  #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
    3201    /* Group all time settings into one call
    3202     * Change times as the last thing to avoid it changing due to metadata changes
    3203     */
    3204    
    3205    mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
    3206    mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
    3207    mtime_nsec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
    3208    atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
    3209    atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
    3210    atime_nsec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
    3211  
    3212    if (mtime || mtime_usec || mtime_nsec || atime || atime_usec || atime_nsec)
    3213      {
    3214        if (!set_mtime_atime (filename, mtime, mtime_usec, mtime_nsec, atime, atime_usec, atime_nsec, error))
    3215  	{
    3216  	  status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
    3217  	  res = FALSE;
    3218  	  /* Don't set error multiple times */
    3219  	  error = NULL;
    3220  	}
    3221        else
    3222  	status = G_FILE_ATTRIBUTE_STATUS_SET;
    3223        
    3224        if (mtime)
    3225  	mtime->status = status;
    3226        if (mtime_usec)
    3227  	mtime_usec->status = status;
    3228        if (mtime_nsec)
    3229  	mtime_nsec->status = status;
    3230        if (atime)
    3231  	atime->status = status;
    3232        if (atime_usec)
    3233  	atime_usec->status = status;
    3234        if (atime_nsec)
    3235  	atime_nsec->status = status;
    3236      }
    3237  #endif
    3238  
    3239    /* xattrs are handled by default callback */
    3240  
    3241  
    3242    /*  SELinux context */
    3243  #ifdef HAVE_SELINUX 
    3244    if (is_selinux_enabled ()) {
    3245      value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
    3246      if (value)
    3247      {
    3248        if (!set_selinux_context (filename, value, error))
    3249          {
    3250            value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
    3251            res = FALSE;
    3252            /* Don't set error multiple times */
    3253            error = NULL;
    3254          }
    3255        else
    3256          value->status = G_FILE_ATTRIBUTE_STATUS_SET;
    3257      }
    3258    }
    3259  #endif
    3260  
    3261    vfs = g_vfs_get_default ();
    3262    class = G_VFS_GET_CLASS (vfs);
    3263    if (class->local_file_set_attributes)
    3264      {
    3265        if (!class->local_file_set_attributes (vfs, filename,
    3266                                               info,
    3267                                               flags, cancellable,
    3268                                               error))
    3269          {
    3270  	  res = FALSE;
    3271  	  /* Don't set error multiple times */
    3272  	  error = NULL;
    3273          }
    3274      }
    3275  
    3276    return res;
    3277  }