Home | History | Annotate | Download | only in gio
      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  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General
     18  * Public License along with this library; if not, write to the
     19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     20  * Boston, MA 02111-1307, USA.
     21  *
     22  * Author: Alexander Larsson <alexl (at) redhat.com>
     23  */
     24 
     25 #include "config.h"
     26 
     27 #ifdef HAVE_SYS_TIME_H
     28 #include <sys/time.h>
     29 #endif
     30 #include <sys/types.h>
     31 #include <sys/stat.h>
     32 #include <string.h>
     33 #ifdef HAVE_UNISTD_H
     34 #include <unistd.h>
     35 #endif
     36 #define _GNU_SOURCE
     37 #include <fcntl.h>
     38 #include <errno.h>
     39 #ifdef HAVE_GRP_H
     40 #include <grp.h>
     41 #endif
     42 #ifdef HAVE_PWD_H
     43 #include <pwd.h>
     44 #endif
     45 #ifdef HAVE_SELINUX
     46 #include <selinux/selinux.h>
     47 #endif
     48 
     49 #ifdef HAVE_XATTR
     50 
     51 #if defined HAVE_SYS_XATTR_H
     52   #include <sys/xattr.h>
     53 #elif defined HAVE_ATTR_XATTR_H
     54   #include <attr/xattr.h>
     55 #else
     56   #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
     57 #endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
     58 
     59 #endif /* HAVE_XATTR */
     60 
     61 #include <glib/gstdio.h>
     62 #include <gfileattribute-priv.h>
     63 
     64 #include "glibintl.h"
     65 
     66 #ifdef G_OS_WIN32
     67 #include <windows.h>
     68 #include <io.h>
     69 #ifndef W_OK
     70 #define W_OK 2
     71 #endif
     72 #ifndef R_OK
     73 #define R_OK 4
     74 #endif
     75 #ifndef X_OK
     76 #define X_OK 0 /* not really */
     77 #endif
     78 #ifndef S_ISREG
     79 #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
     80 #endif
     81 #ifndef S_ISDIR
     82 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
     83 #endif
     84 #ifndef S_IXUSR
     85 #define S_IXUSR _S_IEXEC
     86 #endif
     87 #endif
     88 
     89 #include "glocalfileinfo.h"
     90 #include "gioerror.h"
     91 #include "gthemedicon.h"
     92 #include "gcontenttype.h"
     93 #include "gcontenttypeprivate.h"
     94 
     95 #include "gioalias.h"
     96 
     97 struct ThumbMD5Context {
     98 	guint32 buf[4];
     99 	guint32 bits[2];
    100 	unsigned char in[64];
    101 };
    102 
    103 #ifndef G_OS_WIN32
    104 
    105 typedef struct {
    106   char *user_name;
    107   char *real_name;
    108 } UidData;
    109 
    110 G_LOCK_DEFINE_STATIC (uid_cache);
    111 static GHashTable *uid_cache = NULL;
    112 
    113 G_LOCK_DEFINE_STATIC (gid_cache);
    114 static GHashTable *gid_cache = NULL;
    115 
    116 #endif  /* !G_OS_WIN32 */
    117 
    118 char *
    119 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
    120 {
    121   GTimeVal tv;
    122 
    123   tv.tv_sec = statbuf->st_mtime;
    124 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
    125   tv.tv_usec = statbuf->st_mtimensec / 1000;
    126 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
    127   tv.tv_usec = statbuf->st_mtim.tv_nsec / 1000;
    128 #else
    129   tv.tv_usec = 0;
    130 #endif
    131 
    132   return g_strdup_printf ("%lu:%lu", tv.tv_sec, tv.tv_usec);
    133 }
    134 
    135 static char *
    136 _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
    137 {
    138   return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
    139 			  (guint64) statbuf->st_dev,
    140 			  (guint64) statbuf->st_ino);
    141 }
    142 
    143 static char *
    144 _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
    145 {
    146   return g_strdup_printf ("l%" G_GUINT64_FORMAT,
    147 			  (guint64) statbuf->st_dev);
    148 }
    149 
    150 
    151 #ifdef S_ISLNK
    152 
    153 static gchar *
    154 read_link (const gchar *full_name)
    155 {
    156 #ifdef HAVE_READLINK
    157   gchar *buffer;
    158   guint size;
    159 
    160   size = 256;
    161   buffer = g_malloc (size);
    162 
    163   while (1)
    164     {
    165       int read_size;
    166 
    167       read_size = readlink (full_name, buffer, size);
    168       if (read_size < 0)
    169 	{
    170 	  g_free (buffer);
    171 	  return NULL;
    172 	}
    173       if (read_size < size)
    174 	{
    175 	  buffer[read_size] = 0;
    176 	  return buffer;
    177 	}
    178       size *= 2;
    179       buffer = g_realloc (buffer, size);
    180     }
    181 #else
    182   return NULL;
    183 #endif
    184 }
    185 
    186 #endif  /* S_ISLNK */
    187 
    188 #ifdef HAVE_SELINUX
    189 /* Get the SELinux security context */
    190 static void
    191 get_selinux_context (const char            *path,
    192 		     GFileInfo             *info,
    193 		     GFileAttributeMatcher *attribute_matcher,
    194 		     gboolean               follow_symlinks)
    195 {
    196   char *context;
    197 
    198   if (!g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_SELINUX_CONTEXT))
    199     return;
    200 
    201   if (is_selinux_enabled ())
    202     {
    203       if (follow_symlinks)
    204 	{
    205 	  if (lgetfilecon_raw (path, &context) < 0)
    206 	    return;
    207 	}
    208       else
    209 	{
    210 	  if (getfilecon_raw (path, &context) < 0)
    211 	    return;
    212 	}
    213 
    214       if (context)
    215 	{
    216 	  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT, context);
    217 	  freecon (context);
    218 	}
    219     }
    220 }
    221 #endif
    222 
    223 #ifdef HAVE_XATTR
    224 
    225 /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
    226  * (Mac) getxattr(..., XATTR_NOFOLLOW)
    227  */
    228 #ifdef HAVE_XATTR_NOFOLLOW
    229 #define g_fgetxattr(fd,name,value,size)  fgetxattr(fd,name,value,size,0,0)
    230 #define g_flistxattr(fd,name,size)       flistxattr(fd,name,size,0)
    231 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
    232 #else
    233 #define g_fgetxattr     fgetxattr
    234 #define g_flistxattr    flistxattr
    235 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
    236 #endif
    237 
    238 static ssize_t
    239 g_getxattr (const char *path, const char *name, void *value, size_t size,
    240             gboolean follow_symlinks)
    241 {
    242 #ifdef HAVE_XATTR_NOFOLLOW
    243   return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
    244 #else
    245   if (follow_symlinks)
    246     return getxattr (path, name, value, size);
    247   else
    248     return lgetxattr (path, name, value, size);
    249 #endif
    250 }
    251 
    252 static ssize_t
    253 g_listxattr(const char *path, char *namebuf, size_t size,
    254             gboolean follow_symlinks)
    255 {
    256 #ifdef HAVE_XATTR_NOFOLLOW
    257   return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
    258 #else
    259   if (follow_symlinks)
    260     return listxattr (path, namebuf, size);
    261   else
    262     return llistxattr (path, namebuf, size);
    263 #endif
    264 }
    265 
    266 static gboolean
    267 valid_char (char c)
    268 {
    269   return c >= 32 && c <= 126 && c != '\\';
    270 }
    271 
    272 static gboolean
    273 name_is_valid (const char *str)
    274 {
    275   while (*str)
    276     {
    277       if (!valid_char (*str++))
    278 	return FALSE;
    279     }
    280   return TRUE;
    281 }
    282 
    283 static char *
    284 hex_escape_string (const char *str,
    285                    gboolean   *free_return)
    286 {
    287   int num_invalid, i;
    288   char *escaped_str, *p;
    289   unsigned char c;
    290   static char *hex_digits = "0123456789abcdef";
    291   int len;
    292 
    293   len = strlen (str);
    294 
    295   num_invalid = 0;
    296   for (i = 0; i < len; i++)
    297     {
    298       if (!valid_char (str[i]))
    299 	num_invalid++;
    300     }
    301 
    302   if (num_invalid == 0)
    303     {
    304       *free_return = FALSE;
    305       return (char *)str;
    306     }
    307 
    308   escaped_str = g_malloc (len + num_invalid*3 + 1);
    309 
    310   p = escaped_str;
    311   for (i = 0; i < len; i++)
    312     {
    313       if (valid_char (str[i]))
    314 	*p++ = str[i];
    315       else
    316 	{
    317 	  c = str[i];
    318 	  *p++ = '\\';
    319 	  *p++ = 'x';
    320 	  *p++ = hex_digits[(c >> 4) & 0xf];
    321 	  *p++ = hex_digits[c & 0xf];
    322 	}
    323     }
    324   *p++ = 0;
    325 
    326   *free_return = TRUE;
    327   return escaped_str;
    328 }
    329 
    330 static char *
    331 hex_unescape_string (const char *str,
    332                      int        *out_len,
    333                      gboolean   *free_return)
    334 {
    335   int i;
    336   char *unescaped_str, *p;
    337   unsigned char c;
    338   int len;
    339 
    340   len = strlen (str);
    341 
    342   if (strchr (str, '\\') == NULL)
    343     {
    344       if (out_len)
    345 	*out_len = len;
    346       *free_return = FALSE;
    347       return (char *)str;
    348     }
    349 
    350   unescaped_str = g_malloc (len + 1);
    351 
    352   p = unescaped_str;
    353   for (i = 0; i < len; i++)
    354     {
    355       if (str[i] == '\\' &&
    356 	  str[i+1] == 'x' &&
    357 	  len - i >= 4)
    358 	{
    359 	  c =
    360 	    (g_ascii_xdigit_value (str[i+2]) << 4) |
    361 	    g_ascii_xdigit_value (str[i+3]);
    362 	  *p++ = c;
    363 	  i += 3;
    364 	}
    365       else
    366 	*p++ = str[i];
    367     }
    368   *p++ = 0;
    369 
    370   if (out_len)
    371     *out_len = p - unescaped_str;
    372   *free_return = TRUE;
    373   return unescaped_str;
    374 }
    375 
    376 static void
    377 escape_xattr (GFileInfo  *info,
    378 	      const char *gio_attr, /* gio attribute name */
    379 	      const char *value, /* Is zero terminated */
    380 	      size_t      len /* not including zero termination */)
    381 {
    382   char *escaped_val;
    383   gboolean free_escaped_val;
    384 
    385   escaped_val = hex_escape_string (value, &free_escaped_val);
    386 
    387   g_file_info_set_attribute_string (info, gio_attr, escaped_val);
    388 
    389   if (free_escaped_val)
    390     g_free (escaped_val);
    391 }
    392 
    393 static void
    394 get_one_xattr (const char *path,
    395 	       GFileInfo  *info,
    396 	       const char *gio_attr,
    397 	       const char *xattr,
    398 	       gboolean    follow_symlinks)
    399 {
    400   char value[64];
    401   char *value_p;
    402   ssize_t len;
    403 
    404   len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
    405 
    406   value_p = NULL;
    407   if (len >= 0)
    408     value_p = value;
    409   else if (len == -1 && errno == ERANGE)
    410     {
    411       len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
    412 
    413       if (len < 0)
    414 	return;
    415 
    416       value_p = g_malloc (len+1);
    417 
    418       len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
    419 
    420       if (len < 0)
    421 	{
    422 	  g_free (value_p);
    423 	  return;
    424 	}
    425     }
    426   else
    427     return;
    428 
    429   /* Null terminate */
    430   value_p[len] = 0;
    431 
    432   escape_xattr (info, gio_attr, value_p, len);
    433 
    434   if (value_p != value)
    435     g_free (value_p);
    436 }
    437 
    438 #endif /* defined HAVE_XATTR */
    439 
    440 static void
    441 get_xattrs (const char            *path,
    442 	    gboolean               user,
    443 	    GFileInfo             *info,
    444 	    GFileAttributeMatcher *matcher,
    445 	    gboolean               follow_symlinks)
    446 {
    447 #ifdef HAVE_XATTR
    448   gboolean all;
    449   gsize list_size;
    450   ssize_t list_res_size;
    451   size_t len;
    452   char *list;
    453   const char *attr, *attr2;
    454 
    455   if (user)
    456     all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
    457   else
    458     all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
    459 
    460   if (all)
    461     {
    462       list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
    463 
    464       if (list_res_size == -1 ||
    465 	  list_res_size == 0)
    466 	return;
    467 
    468       list_size = list_res_size;
    469       list = g_malloc (list_size);
    470 
    471     retry:
    472 
    473       list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
    474 
    475       if (list_res_size == -1 && errno == ERANGE)
    476 	{
    477 	  list_size = list_size * 2;
    478 	  list = g_realloc (list, list_size);
    479 	  goto retry;
    480 	}
    481 
    482       if (list_res_size == -1)
    483 	return;
    484 
    485       attr = list;
    486       while (list_res_size > 0)
    487 	{
    488 	  if ((user && g_str_has_prefix (attr, "user.")) ||
    489 	      (!user && !g_str_has_prefix (attr, "user.")))
    490 	    {
    491 	      char *escaped_attr, *gio_attr;
    492 	      gboolean free_escaped_attr;
    493 
    494 	      if (user)
    495 		{
    496 		  escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
    497 		  gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
    498 		}
    499 	      else
    500 		{
    501 		  escaped_attr = hex_escape_string (attr, &free_escaped_attr);
    502 		  gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
    503 		}
    504 
    505 	      if (free_escaped_attr)
    506 		g_free (escaped_attr);
    507 
    508 	      get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
    509 
    510 	      g_free (gio_attr);
    511 	    }
    512 
    513 	  len = strlen (attr) + 1;
    514 	  attr += len;
    515 	  list_res_size -= len;
    516 	}
    517 
    518       g_free (list);
    519     }
    520   else
    521     {
    522       while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
    523 	{
    524 	  char *unescaped_attribute, *a;
    525 	  gboolean free_unescaped_attribute;
    526 
    527 	  attr2 = strchr (attr, ':');
    528 	  if (attr2)
    529 	    {
    530 	      attr2 += 2; /* Skip '::' */
    531 	      unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
    532 	      if (user)
    533 		a = g_strconcat ("user.", unescaped_attribute, NULL);
    534 	      else
    535 		a = unescaped_attribute;
    536 
    537 	      get_one_xattr (path, info, attr, a, follow_symlinks);
    538 
    539 	      if (user)
    540 		g_free (a);
    541 
    542 	      if (free_unescaped_attribute)
    543 		g_free (unescaped_attribute);
    544 	    }
    545 	}
    546     }
    547 #endif /* defined HAVE_XATTR */
    548 }
    549 
    550 #ifdef HAVE_XATTR
    551 static void
    552 get_one_xattr_from_fd (int         fd,
    553 		       GFileInfo  *info,
    554 		       const char *gio_attr,
    555 		       const char *xattr)
    556 {
    557   char value[64];
    558   char *value_p;
    559   ssize_t len;
    560 
    561   len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
    562 
    563   value_p = NULL;
    564   if (len >= 0)
    565     value_p = value;
    566   else if (len == -1 && errno == ERANGE)
    567     {
    568       len = g_fgetxattr (fd, xattr, NULL, 0);
    569 
    570       if (len < 0)
    571 	return;
    572 
    573       value_p = g_malloc (len + 1);
    574 
    575       len = g_fgetxattr (fd, xattr, value_p, len);
    576 
    577       if (len < 0)
    578 	{
    579 	  g_free (value_p);
    580 	  return;
    581 	}
    582     }
    583   else
    584     return;
    585 
    586   /* Null terminate */
    587   value_p[len] = 0;
    588 
    589   escape_xattr (info, gio_attr, value_p, len);
    590 
    591   if (value_p != value)
    592     g_free (value_p);
    593 }
    594 #endif /* defined HAVE_XATTR */
    595 
    596 static void
    597 get_xattrs_from_fd (int                    fd,
    598 		    gboolean               user,
    599 		    GFileInfo             *info,
    600 		    GFileAttributeMatcher *matcher)
    601 {
    602 #ifdef HAVE_XATTR
    603   gboolean all;
    604   gsize list_size;
    605   ssize_t list_res_size;
    606   size_t len;
    607   char *list;
    608   const char *attr, *attr2;
    609 
    610   if (user)
    611     all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
    612   else
    613     all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
    614 
    615   if (all)
    616     {
    617       list_res_size = g_flistxattr (fd, NULL, 0);
    618 
    619       if (list_res_size == -1 ||
    620 	  list_res_size == 0)
    621 	return;
    622 
    623       list_size = list_res_size;
    624       list = g_malloc (list_size);
    625 
    626     retry:
    627 
    628       list_res_size = g_flistxattr (fd, list, list_size);
    629 
    630       if (list_res_size == -1 && errno == ERANGE)
    631 	{
    632 	  list_size = list_size * 2;
    633 	  list = g_realloc (list, list_size);
    634 	  goto retry;
    635 	}
    636 
    637       if (list_res_size == -1)
    638 	return;
    639 
    640       attr = list;
    641       while (list_res_size > 0)
    642 	{
    643 	  if ((user && g_str_has_prefix (attr, "user.")) ||
    644 	      (!user && !g_str_has_prefix (attr, "user.")))
    645 	    {
    646 	      char *escaped_attr, *gio_attr;
    647 	      gboolean free_escaped_attr;
    648 
    649 	      if (user)
    650 		{
    651 		  escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
    652 		  gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
    653 		}
    654 	      else
    655 		{
    656 		  escaped_attr = hex_escape_string (attr, &free_escaped_attr);
    657 		  gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
    658 		}
    659 
    660 	      if (free_escaped_attr)
    661 		g_free (escaped_attr);
    662 
    663 	      get_one_xattr_from_fd (fd, info, gio_attr, attr);
    664 	    }
    665 
    666 	  len = strlen (attr) + 1;
    667 	  attr += len;
    668 	  list_res_size -= len;
    669 	}
    670 
    671       g_free (list);
    672     }
    673   else
    674     {
    675       while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
    676 	{
    677 	  char *unescaped_attribute, *a;
    678 	  gboolean free_unescaped_attribute;
    679 
    680 	  attr2 = strchr (attr, ':');
    681 	  if (attr2)
    682 	    {
    683 	      attr2++; /* Skip ':' */
    684 	      unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
    685 	      if (user)
    686 		a = g_strconcat ("user.", unescaped_attribute, NULL);
    687 	      else
    688 		a = unescaped_attribute;
    689 
    690 	      get_one_xattr_from_fd (fd, info, attr, a);
    691 
    692 	      if (user)
    693 		g_free (a);
    694 
    695 	      if (free_unescaped_attribute)
    696 		g_free (unescaped_attribute);
    697 	    }
    698 	}
    699     }
    700 #endif /* defined HAVE_XATTR */
    701 }
    702 
    703 #ifdef HAVE_XATTR
    704 static gboolean
    705 set_xattr (char                       *filename,
    706 	   const char                 *escaped_attribute,
    707 	   const GFileAttributeValue  *attr_value,
    708 	   GError                    **error)
    709 {
    710   char *attribute, *value;
    711   gboolean free_attribute, free_value;
    712   int val_len, res, errsv;
    713   gboolean is_user;
    714   char *a;
    715 
    716   if (attr_value == NULL)
    717     {
    718       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    719                            _("Attribute value must be non-NULL"));
    720       return FALSE;
    721     }
    722 
    723   if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
    724     {
    725       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    726                            _("Invalid attribute type (string expected)"));
    727       return FALSE;
    728     }
    729 
    730   if (!name_is_valid (escaped_attribute))
    731     {
    732       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
    733                            _("Invalid extended attribute name"));
    734       return FALSE;
    735     }
    736 
    737   if (g_str_has_prefix (escaped_attribute, "xattr::"))
    738     {
    739       escaped_attribute += strlen ("xattr::");
    740       is_user = TRUE;
    741     }
    742   else
    743     {
    744       g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
    745       escaped_attribute += strlen ("xattr-sys::");
    746       is_user = FALSE;
    747     }
    748 
    749   attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
    750   value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
    751 
    752   if (is_user)
    753     a = g_strconcat ("user.", attribute, NULL);
    754   else
    755     a = attribute;
    756 
    757   res = g_setxattr (filename, a, value, val_len);
    758   errsv = errno;
    759 
    760   if (is_user)
    761     g_free (a);
    762 
    763   if (free_attribute)
    764     g_free (attribute);
    765 
    766   if (free_value)
    767     g_free (value);
    768 
    769   if (res == -1)
    770     {
    771       g_set_error (error, G_IO_ERROR,
    772 		   g_io_error_from_errno (errsv),
    773 		   _("Error setting extended attribute '%s': %s"),
    774 		   escaped_attribute, g_strerror (errsv));
    775       return FALSE;
    776     }
    777 
    778   return TRUE;
    779 }
    780 
    781 #endif
    782 
    783 
    784 void
    785 _g_local_file_info_get_parent_info (const char            *dir,
    786 				    GFileAttributeMatcher *attribute_matcher,
    787 				    GLocalParentFileInfo  *parent_info)
    788 {
    789   /* Use plain struct stat for now as long as we only look at the
    790    * S_ISVTX bit which doesn't exist on Win32 anyway.
    791    */
    792   struct stat statbuf;
    793   int res;
    794 
    795   parent_info->writable = FALSE;
    796   parent_info->is_sticky = FALSE;
    797   parent_info->has_trash_dir = FALSE;
    798   parent_info->device = 0;
    799 
    800   if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME) ||
    801       g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE) ||
    802       g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH) ||
    803       g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT))
    804     {
    805       /* FIXME: Windows: The underlying _waccess() call in the C
    806        * library is mostly pointless as it only looks at the READONLY
    807        * FAT-style attribute of the file, it doesn't check the ACL at
    808        * all.
    809        */
    810       parent_info->writable = (g_access (dir, W_OK) == 0);
    811 
    812       res = g_stat (dir, &statbuf);
    813 
    814       /*
    815        * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
    816        * renamed or deleted only by the owner of the file, by the owner of the directory, and
    817        * by a privileged process.
    818        */
    819       if (res == 0)
    820 	{
    821 #ifdef S_ISVTX
    822 	  parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
    823 #else
    824 	  parent_info->is_sticky = FALSE;
    825 #endif
    826 	  parent_info->owner = statbuf.st_uid;
    827 	  parent_info->device = statbuf.st_dev;
    828           /* No need to find trash dir if it's not writable anyway */
    829           if (parent_info->writable &&
    830               g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH))
    831             parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev);
    832 	}
    833     }
    834 }
    835 
    836 static void
    837 get_access_rights (GFileAttributeMatcher *attribute_matcher,
    838 		   GFileInfo             *info,
    839 		   const gchar           *path,
    840 		   GLocalFileStat        *statbuf,
    841 		   GLocalParentFileInfo  *parent_info)
    842 {
    843   /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
    844   if (g_file_attribute_matcher_matches (attribute_matcher,
    845 					G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
    846     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
    847 				       g_access (path, R_OK) == 0);
    848 
    849   if (g_file_attribute_matcher_matches (attribute_matcher,
    850 					G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
    851     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
    852 				       g_access (path, W_OK) == 0);
    853 
    854   if (g_file_attribute_matcher_matches (attribute_matcher,
    855 					G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
    856     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
    857 				       g_access (path, X_OK) == 0);
    858 
    859 
    860   if (parent_info)
    861     {
    862       gboolean writable;
    863 
    864       writable = FALSE;
    865       if (parent_info->writable)
    866 	{
    867 	  if (parent_info->is_sticky)
    868 	    {
    869 #ifndef G_OS_WIN32
    870 	      uid_t uid = geteuid ();
    871 
    872 	      if (uid == statbuf->st_uid ||
    873 		  uid == parent_info->owner ||
    874 		  uid == 0)
    875 #endif
    876 		writable = TRUE;
    877 	    }
    878 	  else
    879 	    writable = TRUE;
    880 	}
    881 
    882       if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME))
    883 	g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
    884 					   writable);
    885 
    886       if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE))
    887 	g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
    888 					   writable);
    889 
    890       if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH))
    891         g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
    892                                            writable && parent_info->has_trash_dir);
    893     }
    894 }
    895 
    896 static void
    897 set_info_from_stat (GFileInfo             *info,
    898                     GLocalFileStat        *statbuf,
    899 		    GFileAttributeMatcher *attribute_matcher)
    900 {
    901   GFileType file_type;
    902 
    903   file_type = G_FILE_TYPE_UNKNOWN;
    904 
    905   if (S_ISREG (statbuf->st_mode))
    906     file_type = G_FILE_TYPE_REGULAR;
    907   else if (S_ISDIR (statbuf->st_mode))
    908     file_type = G_FILE_TYPE_DIRECTORY;
    909 #ifndef G_OS_WIN32
    910   else if (S_ISCHR (statbuf->st_mode) ||
    911 	   S_ISBLK (statbuf->st_mode) ||
    912 	   S_ISFIFO (statbuf->st_mode)
    913 #ifdef S_ISSOCK
    914 	   || S_ISSOCK (statbuf->st_mode)
    915 #endif
    916 	   )
    917     file_type = G_FILE_TYPE_SPECIAL;
    918 #endif
    919 #ifdef S_ISLNK
    920   else if (S_ISLNK (statbuf->st_mode))
    921     file_type = G_FILE_TYPE_SYMBOLIC_LINK;
    922 #endif
    923 
    924   g_file_info_set_file_type (info, file_type);
    925   g_file_info_set_size (info, statbuf->st_size);
    926 
    927   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE, statbuf->st_dev);
    928 #ifndef G_OS_WIN32
    929   /* Pointless setting these on Windows even if they exist in the struct */
    930   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE, statbuf->st_ino);
    931   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK, statbuf->st_nlink);
    932   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, statbuf->st_uid);
    933   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, statbuf->st_gid);
    934   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV, statbuf->st_rdev);
    935 #endif
    936   /* FIXME: st_mode is mostly pointless on Windows, too. Set the attribute or not? */
    937   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, statbuf->st_mode);
    938 #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
    939   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE, statbuf->st_blksize);
    940 #endif
    941 #if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
    942   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_BLOCKS, statbuf->st_blocks);
    943   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE,
    944                                     statbuf->st_blocks * G_GUINT64_CONSTANT (512));
    945 #endif
    946 
    947   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, statbuf->st_mtime);
    948 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
    949   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000);
    950 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
    951   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000);
    952 #endif
    953 
    954   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS, statbuf->st_atime);
    955 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
    956   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
    957 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
    958   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
    959 #endif
    960 
    961   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED, statbuf->st_ctime);
    962 #if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
    963   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
    964 #elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
    965   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000);
    966 #endif
    967 
    968   if (g_file_attribute_matcher_matches (attribute_matcher,
    969 					G_FILE_ATTRIBUTE_ETAG_VALUE))
    970     {
    971       char *etag = _g_local_file_info_create_etag (statbuf);
    972       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
    973       g_free (etag);
    974     }
    975 
    976   if (g_file_attribute_matcher_matches (attribute_matcher,
    977 					G_FILE_ATTRIBUTE_ID_FILE))
    978     {
    979       char *id = _g_local_file_info_create_file_id (statbuf);
    980       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE, id);
    981       g_free (id);
    982     }
    983 
    984   if (g_file_attribute_matcher_matches (attribute_matcher,
    985 					G_FILE_ATTRIBUTE_ID_FILESYSTEM))
    986     {
    987       char *id = _g_local_file_info_create_fs_id (statbuf);
    988       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM, id);
    989       g_free (id);
    990     }
    991 }
    992 
    993 #ifndef G_OS_WIN32
    994 
    995 static char *
    996 make_valid_utf8 (const char *name)
    997 {
    998   GString *string;
    999   const gchar *remainder, *invalid;
   1000   gint remaining_bytes, valid_bytes;
   1001 
   1002   string = NULL;
   1003   remainder = name;
   1004   remaining_bytes = strlen (name);
   1005 
   1006   while (remaining_bytes != 0)
   1007     {
   1008       if (g_utf8_validate (remainder, remaining_bytes, &invalid))
   1009 	break;
   1010       valid_bytes = invalid - remainder;
   1011 
   1012       if (string == NULL)
   1013 	string = g_string_sized_new (remaining_bytes);
   1014 
   1015       g_string_append_len (string, remainder, valid_bytes);
   1016       /* append U+FFFD REPLACEMENT CHARACTER */
   1017       g_string_append (string, "\357\277\275");
   1018 
   1019       remaining_bytes -= valid_bytes + 1;
   1020       remainder = invalid + 1;
   1021     }
   1022 
   1023   if (string == NULL)
   1024     return g_strdup (name);
   1025 
   1026   g_string_append (string, remainder);
   1027 
   1028   g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
   1029 
   1030   return g_string_free (string, FALSE);
   1031 }
   1032 
   1033 static char *
   1034 convert_pwd_string_to_utf8 (char *pwd_str)
   1035 {
   1036   char *utf8_string;
   1037 
   1038   if (!g_utf8_validate (pwd_str, -1, NULL))
   1039     {
   1040       utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
   1041       if (utf8_string == NULL)
   1042 	utf8_string = make_valid_utf8 (pwd_str);
   1043     }
   1044   else
   1045     utf8_string = g_strdup (pwd_str);
   1046 
   1047   return utf8_string;
   1048 }
   1049 
   1050 static void
   1051 uid_data_free (UidData *data)
   1052 {
   1053   g_free (data->user_name);
   1054   g_free (data->real_name);
   1055   g_free (data);
   1056 }
   1057 
   1058 /* called with lock held */
   1059 static UidData *
   1060 lookup_uid_data (uid_t uid)
   1061 {
   1062   UidData *data;
   1063   char buffer[4096];
   1064   struct passwd pwbuf;
   1065   struct passwd *pwbufp;
   1066   char *gecos, *comma;
   1067 
   1068   if (uid_cache == NULL)
   1069     uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
   1070 
   1071   data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
   1072 
   1073   if (data)
   1074     return data;
   1075 
   1076   data = g_new0 (UidData, 1);
   1077 
   1078 #if defined(HAVE_POSIX_GETPWUID_R)
   1079   getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
   1080 #elif defined(HAVE_NONPOSIX_GETPWUID_R)
   1081   pwbufp = getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer));
   1082 #else
   1083   pwbufp = getpwuid (uid);
   1084 #endif
   1085 
   1086   if (pwbufp != NULL)
   1087     {
   1088       if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
   1089 	data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
   1090 
   1091       gecos = pwbufp->pw_gecos;
   1092 
   1093       if (gecos)
   1094 	{
   1095 	  comma = strchr (gecos, ',');
   1096 	  if (comma)
   1097 	    *comma = 0;
   1098 	  data->real_name = convert_pwd_string_to_utf8 (gecos);
   1099 	}
   1100     }
   1101 
   1102   /* Default fallbacks */
   1103   if (data->real_name == NULL)
   1104     {
   1105       if (data->user_name != NULL)
   1106 	data->real_name = g_strdup (data->user_name);
   1107       else
   1108 	data->real_name = g_strdup_printf ("user #%d", (int)uid);
   1109     }
   1110 
   1111   if (data->user_name == NULL)
   1112     data->user_name = g_strdup_printf ("%d", (int)uid);
   1113 
   1114   g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
   1115 
   1116   return data;
   1117 }
   1118 
   1119 static char *
   1120 get_username_from_uid (uid_t uid)
   1121 {
   1122   char *res;
   1123   UidData *data;
   1124 
   1125   G_LOCK (uid_cache);
   1126   data = lookup_uid_data (uid);
   1127   res = g_strdup (data->user_name);
   1128   G_UNLOCK (uid_cache);
   1129 
   1130   return res;
   1131 }
   1132 
   1133 static char *
   1134 get_realname_from_uid (uid_t uid)
   1135 {
   1136   char *res;
   1137   UidData *data;
   1138 
   1139   G_LOCK (uid_cache);
   1140   data = lookup_uid_data (uid);
   1141   res = g_strdup (data->real_name);
   1142   G_UNLOCK (uid_cache);
   1143 
   1144   return res;
   1145 }
   1146 
   1147 /* called with lock held */
   1148 static char *
   1149 lookup_gid_name (gid_t gid)
   1150 {
   1151   char *name;
   1152   char buffer[4096];
   1153   struct group gbuf;
   1154   struct group *gbufp;
   1155 
   1156   if (gid_cache == NULL)
   1157     gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
   1158 
   1159   name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
   1160 
   1161   if (name)
   1162     return name;
   1163 
   1164 #if defined (HAVE_POSIX_GETGRGID_R)
   1165   getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
   1166 #elif defined (HAVE_NONPOSIX_GETGRGID_R)
   1167   gbufp = getgrgid_r (gid, &gbuf, buffer, sizeof(buffer));
   1168 #else
   1169   gbufp = getgrgid (gid);
   1170 #endif
   1171 
   1172   if (gbufp != NULL &&
   1173       gbufp->gr_name != NULL &&
   1174       gbufp->gr_name[0] != 0)
   1175     name = convert_pwd_string_to_utf8 (gbufp->gr_name);
   1176   else
   1177     name = g_strdup_printf("%d", (int)gid);
   1178 
   1179   g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
   1180 
   1181   return name;
   1182 }
   1183 
   1184 static char *
   1185 get_groupname_from_gid (gid_t gid)
   1186 {
   1187   char *res;
   1188   char *name;
   1189 
   1190   G_LOCK (gid_cache);
   1191   name = lookup_gid_name (gid);
   1192   res = g_strdup (name);
   1193   G_UNLOCK (gid_cache);
   1194   return res;
   1195 }
   1196 
   1197 #endif /* !G_OS_WIN32 */
   1198 
   1199 static char *
   1200 get_content_type (const char          *basename,
   1201 		  const char          *path,
   1202 		  GLocalFileStat      *statbuf,
   1203 		  gboolean             is_symlink,
   1204 		  gboolean             symlink_broken,
   1205 		  GFileQueryInfoFlags  flags,
   1206 		  gboolean             fast)
   1207 {
   1208   if (is_symlink &&
   1209       (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
   1210     return g_strdup  ("inode/symlink");
   1211   else if (S_ISDIR(statbuf->st_mode))
   1212     return g_strdup ("inode/directory");
   1213 #ifndef G_OS_WIN32
   1214   else if (S_ISCHR(statbuf->st_mode))
   1215     return g_strdup ("inode/chardevice");
   1216   else if (S_ISBLK(statbuf->st_mode))
   1217     return g_strdup ("inode/blockdevice");
   1218   else if (S_ISFIFO(statbuf->st_mode))
   1219     return g_strdup ("inode/fifo");
   1220 #endif
   1221 #ifdef S_ISSOCK
   1222   else if (S_ISSOCK(statbuf->st_mode))
   1223     return g_strdup ("inode/socket");
   1224 #endif
   1225   else
   1226     {
   1227       char *content_type;
   1228       gboolean result_uncertain;
   1229 
   1230       content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
   1231 
   1232 #ifndef G_OS_WIN32
   1233       if (!fast && result_uncertain && path != NULL)
   1234 	{
   1235 	  guchar sniff_buffer[4096];
   1236 	  gsize sniff_length;
   1237 	  int fd;
   1238 
   1239 	  sniff_length = _g_unix_content_type_get_sniff_len ();
   1240 	  if (sniff_length > 4096)
   1241 	    sniff_length = 4096;
   1242 
   1243 #ifdef O_NOATIME
   1244           fd = open (path, O_RDONLY | O_NOATIME);
   1245           if (fd < 0 && errno == EPERM)
   1246 #endif
   1247 	    fd = open (path, O_RDONLY);
   1248 
   1249 	  if (fd != -1)
   1250 	    {
   1251 	      ssize_t res;
   1252 
   1253 	      res = read (fd, sniff_buffer, sniff_length);
   1254 	      close (fd);
   1255 	      if (res >= 0)
   1256 		{
   1257 		  g_free (content_type);
   1258 		  content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
   1259 		}
   1260 	    }
   1261 	}
   1262 #endif
   1263 
   1264       return content_type;
   1265     }
   1266 
   1267 }
   1268 
   1269 static void
   1270 get_thumbnail_attributes (const char *path,
   1271                           GFileInfo  *info)
   1272 {
   1273   GChecksum *checksum;
   1274   char *uri;
   1275   char *filename;
   1276   char *basename;
   1277 
   1278   uri = g_filename_to_uri (path, NULL, NULL);
   1279 
   1280   checksum = g_checksum_new (G_CHECKSUM_MD5);
   1281   g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
   1282 
   1283   g_free (uri);
   1284 
   1285   basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
   1286   g_checksum_free (checksum);
   1287 
   1288   filename = g_build_filename (g_get_home_dir (),
   1289                                ".thumbnails", "normal", basename,
   1290                                NULL);
   1291 
   1292   if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
   1293     g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
   1294   else
   1295     {
   1296       g_free (filename);
   1297       filename = g_build_filename (g_get_home_dir (),
   1298                                    ".thumbnails", "fail",
   1299                                    "gnome-thumbnail-factory",
   1300                                    basename,
   1301                                    NULL);
   1302 
   1303       if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
   1304 	g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
   1305     }
   1306   g_free (basename);
   1307   g_free (filename);
   1308 }
   1309 
   1310 #ifdef G_OS_WIN32
   1311 static void
   1312 win32_get_file_user_info (const gchar  *filename,
   1313 			  gchar       **group_name,
   1314 			  gchar       **user_name,
   1315 			  gchar       **real_name)
   1316 {
   1317   PSECURITY_DESCRIPTOR psd = NULL;
   1318   DWORD sd_size = 0; /* first call calculates the size required */
   1319 
   1320   wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
   1321   if ((GetFileSecurityW (wfilename,
   1322                         GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
   1323 			NULL,
   1324 			sd_size,
   1325 			&sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
   1326      (psd = g_try_malloc (sd_size)) != NULL &&
   1327      GetFileSecurityW (wfilename,
   1328                        GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
   1329 		       psd,
   1330 		       sd_size,
   1331 		       &sd_size))
   1332     {
   1333       PSID psid = 0;
   1334       BOOL defaulted;
   1335       SID_NAME_USE name_use = 0; /* don't care? */
   1336       wchar_t *name = NULL;
   1337       wchar_t *domain = NULL;
   1338       DWORD name_len = 0;
   1339       DWORD domain_len = 0;
   1340       /* get the user name */
   1341       do {
   1342         if (!user_name)
   1343 	  break;
   1344 	if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
   1345 	  break;
   1346 	if (!LookupAccountSidW (NULL, /* local machine */
   1347                                 psid,
   1348 			        name, &name_len,
   1349 			        domain, &domain_len, /* no domain info yet */
   1350 			        &name_use)  && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
   1351 	  break;
   1352 	name = g_try_malloc (name_len * sizeof (wchar_t));
   1353 	domain = g_try_malloc (domain_len * sizeof (wchar_t));
   1354 	if (name && domain &&
   1355             LookupAccountSidW (NULL, /* local machine */
   1356                                psid,
   1357 			       name, &name_len,
   1358 			       domain, &domain_len, /* no domain info yet */
   1359 			       &name_use))
   1360 	  {
   1361 	    *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
   1362 	  }
   1363 	g_free (name);
   1364 	g_free (domain);
   1365       } while (FALSE);
   1366 
   1367       /* get the group name */
   1368       do {
   1369         if (!group_name)
   1370 	  break;
   1371 	if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
   1372 	  break;
   1373 	if (!LookupAccountSidW (NULL, /* local machine */
   1374                                 psid,
   1375 			        name, &name_len,
   1376 			        domain, &domain_len, /* no domain info yet */
   1377 			        &name_use)  && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
   1378 	  break;
   1379 	name = g_try_malloc (name_len * sizeof (wchar_t));
   1380 	domain = g_try_malloc (domain_len * sizeof (wchar_t));
   1381 	if (name && domain &&
   1382             LookupAccountSidW (NULL, /* local machine */
   1383                                psid,
   1384 			       name, &name_len,
   1385 			       domain, &domain_len, /* no domain info yet */
   1386 			       &name_use))
   1387 	  {
   1388 	    *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
   1389 	  }
   1390 	g_free (name);
   1391 	g_free (domain);
   1392       } while (FALSE);
   1393 
   1394       /* TODO: get real name */
   1395 
   1396       g_free (psd);
   1397     }
   1398   g_free (wfilename);
   1399 }
   1400 #endif /* G_OS_WIN32 */
   1401 
   1402 GFileInfo *
   1403 _g_local_file_info_get (const char             *basename,
   1404 			const char             *path,
   1405 			GFileAttributeMatcher  *attribute_matcher,
   1406 			GFileQueryInfoFlags     flags,
   1407 			GLocalParentFileInfo   *parent_info,
   1408 			GError                **error)
   1409 {
   1410   GFileInfo *info;
   1411   GLocalFileStat statbuf;
   1412 #ifdef S_ISLNK
   1413   struct stat statbuf2;
   1414 #endif
   1415   int res;
   1416   gboolean is_symlink, symlink_broken;
   1417 #ifdef G_OS_WIN32
   1418   DWORD dos_attributes;
   1419 #endif
   1420 
   1421   info = g_file_info_new ();
   1422 
   1423   /* Make sure we don't set any unwanted attributes */
   1424   g_file_info_set_attribute_mask (info, attribute_matcher);
   1425 
   1426   g_file_info_set_name (info, basename);
   1427 
   1428   /* Avoid stat in trivial case */
   1429   if (attribute_matcher == NULL)
   1430     return info;
   1431 
   1432 #ifndef G_OS_WIN32
   1433   res = g_lstat (path, &statbuf);
   1434 #else
   1435   {
   1436     wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, error);
   1437     int len;
   1438 
   1439     if (wpath == NULL)
   1440       {
   1441         g_object_unref (info);
   1442         return NULL;
   1443       }
   1444 
   1445     len = wcslen (wpath);
   1446     while (len > 0 && G_IS_DIR_SEPARATOR (wpath[len-1]))
   1447       len--;
   1448     if (len > 0 &&
   1449         (!g_path_is_absolute (path) || len > g_path_skip_root (path) - path))
   1450       wpath[len] = '\0';
   1451 
   1452     res = _wstati64 (wpath, &statbuf);
   1453     dos_attributes = GetFileAttributesW (wpath);
   1454 
   1455     g_free (wpath);
   1456   }
   1457 #endif
   1458 
   1459   if (res == -1)
   1460     {
   1461       int errsv = errno;
   1462       char *display_name = g_filename_display_name (path);
   1463       g_object_unref (info);
   1464       g_set_error (error, G_IO_ERROR,
   1465 		   g_io_error_from_errno (errsv),
   1466 		   _("Error stating file '%s': %s"),
   1467 		   display_name, g_strerror (errsv));
   1468       g_free (display_name);
   1469       return NULL;
   1470     }
   1471 
   1472 #ifdef S_ISLNK
   1473   is_symlink = S_ISLNK (statbuf.st_mode);
   1474 #else
   1475   is_symlink = FALSE;
   1476 #endif
   1477   symlink_broken = FALSE;
   1478 #ifdef S_ISLNK
   1479   if (is_symlink)
   1480     {
   1481       g_file_info_set_is_symlink (info, TRUE);
   1482 
   1483       /* Unless NOFOLLOW was set we default to following symlinks */
   1484       if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
   1485 	{
   1486 	  res = stat (path, &statbuf2);
   1487 
   1488 	    /* Report broken links as symlinks */
   1489 	  if (res != -1)
   1490 	    statbuf = statbuf2;
   1491 	  else
   1492 	    symlink_broken = TRUE;
   1493 	}
   1494     }
   1495 #endif
   1496 
   1497   set_info_from_stat (info, &statbuf, attribute_matcher);
   1498 
   1499 #ifndef G_OS_WIN32
   1500   if (basename != NULL && basename[0] == '.')
   1501     g_file_info_set_is_hidden (info, TRUE);
   1502 
   1503   if (basename != NULL && basename[strlen (basename) -1] == '~' &&
   1504       S_ISREG (statbuf.st_mode))
   1505     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP, TRUE);
   1506 #else
   1507   if (dos_attributes & FILE_ATTRIBUTE_HIDDEN)
   1508     g_file_info_set_is_hidden (info, TRUE);
   1509 
   1510   if (dos_attributes & FILE_ATTRIBUTE_ARCHIVE)
   1511     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE, TRUE);
   1512 
   1513   if (dos_attributes & FILE_ATTRIBUTE_SYSTEM)
   1514     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_DOS_IS_SYSTEM, TRUE);
   1515 #endif
   1516 
   1517 #ifdef S_ISLNK
   1518   if (is_symlink &&
   1519       g_file_attribute_matcher_matches (attribute_matcher,
   1520 					G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET))
   1521     {
   1522       char *link = read_link (path);
   1523       g_file_info_set_symlink_target (info, link);
   1524       g_free (link);
   1525     }
   1526 #endif
   1527 
   1528   if (g_file_attribute_matcher_matches (attribute_matcher,
   1529 					G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
   1530     {
   1531       char *display_name = g_filename_display_basename (path);
   1532 
   1533       /* look for U+FFFD REPLACEMENT CHARACTER */
   1534       if (strstr (display_name, "\357\277\275") != NULL)
   1535 	{
   1536 	  char *p = display_name;
   1537 	  display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
   1538 	  g_free (p);
   1539 	}
   1540       g_file_info_set_display_name (info, display_name);
   1541       g_free (display_name);
   1542     }
   1543 
   1544   if (g_file_attribute_matcher_matches (attribute_matcher,
   1545 					G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME))
   1546     {
   1547       char *edit_name = g_filename_display_basename (path);
   1548       g_file_info_set_edit_name (info, edit_name);
   1549       g_free (edit_name);
   1550     }
   1551 
   1552 
   1553   if (g_file_attribute_matcher_matches (attribute_matcher,
   1554 					G_FILE_ATTRIBUTE_STANDARD_COPY_NAME))
   1555     {
   1556       char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
   1557       if (copy_name)
   1558 	g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, copy_name);
   1559       g_free (copy_name);
   1560     }
   1561 
   1562   if (g_file_attribute_matcher_matches (attribute_matcher,
   1563 					G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) ||
   1564       g_file_attribute_matcher_matches (attribute_matcher,
   1565 					G_FILE_ATTRIBUTE_STANDARD_ICON))
   1566     {
   1567       char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, FALSE);
   1568 
   1569       if (content_type)
   1570 	{
   1571 	  g_file_info_set_content_type (info, content_type);
   1572 
   1573 	  if (g_file_attribute_matcher_matches (attribute_matcher,
   1574 						G_FILE_ATTRIBUTE_STANDARD_ICON))
   1575 	    {
   1576 	      GIcon *icon;
   1577 
   1578               if (strcmp (path, g_get_home_dir ()) == 0)
   1579                 icon = g_themed_icon_new ("user-home");
   1580               else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
   1581                 icon = g_themed_icon_new ("user-desktop");
   1582               else
   1583                 {
   1584                   icon = g_content_type_get_icon (content_type);
   1585                   if (G_IS_THEMED_ICON (icon))
   1586                     {
   1587                       const char *type_icon = NULL;
   1588 
   1589                       if (S_ISDIR (statbuf.st_mode))
   1590                         type_icon = "folder";
   1591                       if (type_icon)
   1592                         g_themed_icon_append_name (G_THEMED_ICON (icon), type_icon);
   1593                     }
   1594                 }
   1595 
   1596               if (icon != NULL)
   1597                 {
   1598                   g_file_info_set_icon (info, icon);
   1599                   g_object_unref (icon);
   1600                 }
   1601 	    }
   1602 
   1603 	  g_free (content_type);
   1604 	}
   1605     }
   1606 
   1607   if (g_file_attribute_matcher_matches (attribute_matcher,
   1608 					G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE))
   1609     {
   1610       char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, TRUE);
   1611 
   1612       if (content_type)
   1613 	{
   1614 	  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type);
   1615 	  g_free (content_type);
   1616 	}
   1617     }
   1618 
   1619   if (g_file_attribute_matcher_matches (attribute_matcher,
   1620 					G_FILE_ATTRIBUTE_OWNER_USER))
   1621     {
   1622       char *name = NULL;
   1623 
   1624 #ifdef G_OS_WIN32
   1625       win32_get_file_user_info (path, NULL, &name, NULL);
   1626 #else
   1627       name = get_username_from_uid (statbuf.st_uid);
   1628 #endif
   1629       if (name)
   1630 	g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER, name);
   1631       g_free (name);
   1632     }
   1633 
   1634   if (g_file_attribute_matcher_matches (attribute_matcher,
   1635 					G_FILE_ATTRIBUTE_OWNER_USER_REAL))
   1636     {
   1637       char *name = NULL;
   1638 #ifdef G_OS_WIN32
   1639       win32_get_file_user_info (path, NULL, NULL, &name);
   1640 #else
   1641       name = get_realname_from_uid (statbuf.st_uid);
   1642 #endif
   1643       if (name)
   1644 	g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER_REAL, name);
   1645       g_free (name);
   1646     }
   1647 
   1648   if (g_file_attribute_matcher_matches (attribute_matcher,
   1649 					G_FILE_ATTRIBUTE_OWNER_GROUP))
   1650     {
   1651       char *name = NULL;
   1652 #ifdef G_OS_WIN32
   1653       win32_get_file_user_info (path, &name, NULL, NULL);
   1654 #else
   1655       name = get_groupname_from_gid (statbuf.st_gid);
   1656 #endif
   1657       if (name)
   1658 	g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP, name);
   1659       g_free (name);
   1660     }
   1661 
   1662   if (parent_info && parent_info->device != 0 &&
   1663       g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT) &&
   1664       statbuf.st_dev != parent_info->device)
   1665     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT, TRUE);
   1666 
   1667   get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
   1668 
   1669 #ifdef HAVE_SELINUX
   1670   get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
   1671 #endif
   1672   get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
   1673   get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
   1674 
   1675   if (g_file_attribute_matcher_matches (attribute_matcher,
   1676 					G_FILE_ATTRIBUTE_THUMBNAIL_PATH))
   1677     get_thumbnail_attributes (path, info);
   1678 
   1679   g_file_info_unset_attribute_mask (info);
   1680 
   1681   return info;
   1682 }
   1683 
   1684 GFileInfo *
   1685 _g_local_file_info_get_from_fd (int         fd,
   1686 				const char *attributes,
   1687 				GError    **error)
   1688 {
   1689   GLocalFileStat stat_buf;
   1690   GFileAttributeMatcher *matcher;
   1691   GFileInfo *info;
   1692 
   1693 #ifdef G_OS_WIN32
   1694 #define FSTAT _fstati64
   1695 #else
   1696 #define FSTAT fstat
   1697 #endif
   1698 
   1699   if (FSTAT (fd, &stat_buf) == -1)
   1700     {
   1701       int errsv = errno;
   1702 
   1703       g_set_error (error, G_IO_ERROR,
   1704 		   g_io_error_from_errno (errsv),
   1705 		   _("Error stating file descriptor: %s"),
   1706 		   g_strerror (errsv));
   1707       return NULL;
   1708     }
   1709 
   1710   info = g_file_info_new ();
   1711 
   1712   matcher = g_file_attribute_matcher_new (attributes);
   1713 
   1714   /* Make sure we don't set any unwanted attributes */
   1715   g_file_info_set_attribute_mask (info, matcher);
   1716 
   1717   set_info_from_stat (info, &stat_buf, matcher);
   1718 
   1719 #ifdef HAVE_SELINUX
   1720   if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) &&
   1721       is_selinux_enabled ())
   1722     {
   1723       char *context;
   1724       if (fgetfilecon_raw (fd, &context) >= 0)
   1725 	{
   1726 	  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT, context);
   1727 	  freecon (context);
   1728 	}
   1729     }
   1730 #endif
   1731 
   1732   get_xattrs_from_fd (fd, TRUE, info, matcher);
   1733   get_xattrs_from_fd (fd, FALSE, info, matcher);
   1734 
   1735   g_file_attribute_matcher_unref (matcher);
   1736 
   1737   g_file_info_unset_attribute_mask (info);
   1738 
   1739   return info;
   1740 }
   1741 
   1742 static gboolean
   1743 get_uint32 (const GFileAttributeValue  *value,
   1744 	    guint32                    *val_out,
   1745 	    GError                    **error)
   1746 {
   1747   if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
   1748     {
   1749       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   1750                            _("Invalid attribute type (uint32 expected)"));
   1751       return FALSE;
   1752     }
   1753 
   1754   *val_out = value->u.uint32;
   1755 
   1756   return TRUE;
   1757 }
   1758 
   1759 #ifdef HAVE_UTIMES
   1760 static gboolean
   1761 get_uint64 (const GFileAttributeValue  *value,
   1762 	    guint64                    *val_out,
   1763 	    GError                    **error)
   1764 {
   1765   if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
   1766     {
   1767       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   1768                            _("Invalid attribute type (uint64 expected)"));
   1769       return FALSE;
   1770     }
   1771 
   1772   *val_out = value->u.uint64;
   1773 
   1774   return TRUE;
   1775 }
   1776 #endif
   1777 
   1778 #if defined(HAVE_SYMLINK)
   1779 static gboolean
   1780 get_byte_string (const GFileAttributeValue  *value,
   1781 		 const char                **val_out,
   1782 		 GError                    **error)
   1783 {
   1784   if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
   1785     {
   1786       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   1787                            _("Invalid attribute type (byte string expected)"));
   1788       return FALSE;
   1789     }
   1790 
   1791   *val_out = value->u.string;
   1792 
   1793   return TRUE;
   1794 }
   1795 #endif
   1796 
   1797 #ifdef HAVE_SELINUX
   1798 static gboolean
   1799 get_string (const GFileAttributeValue  *value,
   1800 	    const char                **val_out,
   1801 	    GError                    **error)
   1802 {
   1803   if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
   1804     {
   1805       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   1806                            _("Invalid attribute type (byte string expected)"));
   1807       return FALSE;
   1808     }
   1809 
   1810   *val_out = value->u.string;
   1811 
   1812   return TRUE;
   1813 }
   1814 #endif
   1815 
   1816 static gboolean
   1817 set_unix_mode (char                       *filename,
   1818 	       const GFileAttributeValue  *value,
   1819 	       GError                    **error)
   1820 {
   1821   guint32 val;
   1822 
   1823   if (!get_uint32 (value, &val, error))
   1824     return FALSE;
   1825 
   1826   if (g_chmod (filename, val) == -1)
   1827     {
   1828       int errsv = errno;
   1829 
   1830       g_set_error (error, G_IO_ERROR,
   1831 		   g_io_error_from_errno (errsv),
   1832 		   _("Error setting permissions: %s"),
   1833 		   g_strerror (errsv));
   1834       return FALSE;
   1835     }
   1836   return TRUE;
   1837 }
   1838 
   1839 #ifdef HAVE_CHOWN
   1840 static gboolean
   1841 set_unix_uid_gid (char                       *filename,
   1842 		  const GFileAttributeValue  *uid_value,
   1843 		  const GFileAttributeValue  *gid_value,
   1844 		  GFileQueryInfoFlags         flags,
   1845 		  GError                    **error)
   1846 {
   1847   int res;
   1848   guint32 val;
   1849   uid_t uid;
   1850   gid_t gid;
   1851 
   1852   if (uid_value)
   1853     {
   1854       if (!get_uint32 (uid_value, &val, error))
   1855 	return FALSE;
   1856       uid = val;
   1857     }
   1858   else
   1859     uid = -1;
   1860 
   1861   if (gid_value)
   1862     {
   1863       if (!get_uint32 (gid_value, &val, error))
   1864 	return FALSE;
   1865       gid = val;
   1866     }
   1867   else
   1868     gid = -1;
   1869 
   1870 #ifdef HAVE_LCHOWN
   1871   if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
   1872     res = lchown (filename, uid, gid);
   1873   else
   1874 #endif
   1875     res = chown (filename, uid, gid);
   1876 
   1877   if (res == -1)
   1878     {
   1879       int errsv = errno;
   1880 
   1881       g_set_error (error, G_IO_ERROR,
   1882 		   g_io_error_from_errno (errsv),
   1883 		   _("Error setting owner: %s"),
   1884 		   g_strerror (errsv));
   1885 	  return FALSE;
   1886     }
   1887   return TRUE;
   1888 }
   1889 #endif
   1890 
   1891 #ifdef HAVE_SYMLINK
   1892 static gboolean
   1893 set_symlink (char                       *filename,
   1894 	     const GFileAttributeValue  *value,
   1895 	     GError                    **error)
   1896 {
   1897   const char *val;
   1898   struct stat statbuf;
   1899 
   1900   if (!get_byte_string (value, &val, error))
   1901     return FALSE;
   1902 
   1903   if (val == NULL)
   1904     {
   1905       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   1906                            _("symlink must be non-NULL"));
   1907       return FALSE;
   1908     }
   1909 
   1910   if (g_lstat (filename, &statbuf))
   1911     {
   1912       int errsv = errno;
   1913 
   1914       g_set_error (error, G_IO_ERROR,
   1915 		   g_io_error_from_errno (errsv),
   1916 		   _("Error setting symlink: %s"),
   1917 		   g_strerror (errsv));
   1918       return FALSE;
   1919     }
   1920 
   1921   if (!S_ISLNK (statbuf.st_mode))
   1922     {
   1923       g_set_error_literal (error, G_IO_ERROR,
   1924                            G_IO_ERROR_NOT_SYMBOLIC_LINK,
   1925                            _("Error setting symlink: file is not a symlink"));
   1926       return FALSE;
   1927     }
   1928 
   1929   if (g_unlink (filename))
   1930     {
   1931       int errsv = errno;
   1932 
   1933       g_set_error (error, G_IO_ERROR,
   1934 		   g_io_error_from_errno (errsv),
   1935 		   _("Error setting symlink: %s"),
   1936 		   g_strerror (errsv));
   1937       return FALSE;
   1938     }
   1939 
   1940   if (symlink (filename, val) != 0)
   1941     {
   1942       int errsv = errno;
   1943 
   1944       g_set_error (error, G_IO_ERROR,
   1945 		   g_io_error_from_errno (errsv),
   1946 		   _("Error setting symlink: %s"),
   1947 		   g_strerror (errsv));
   1948       return FALSE;
   1949     }
   1950 
   1951   return TRUE;
   1952 }
   1953 #endif
   1954 
   1955 #ifdef HAVE_UTIMES
   1956 static int
   1957 lazy_stat (char        *filename,
   1958            struct stat *statbuf,
   1959            gboolean    *called_stat)
   1960 {
   1961   int res;
   1962 
   1963   if (*called_stat)
   1964     return 0;
   1965 
   1966   res = g_stat (filename, statbuf);
   1967 
   1968   if (res == 0)
   1969     *called_stat = TRUE;
   1970 
   1971   return res;
   1972 }
   1973 
   1974 
   1975 static gboolean
   1976 set_mtime_atime (char                       *filename,
   1977 		 const GFileAttributeValue  *mtime_value,
   1978 		 const GFileAttributeValue  *mtime_usec_value,
   1979 		 const GFileAttributeValue  *atime_value,
   1980 		 const GFileAttributeValue  *atime_usec_value,
   1981 		 GError                    **error)
   1982 {
   1983   int res;
   1984   guint64 val;
   1985   guint32 val_usec;
   1986   struct stat statbuf;
   1987   gboolean got_stat = FALSE;
   1988   struct timeval times[2] = { {0, 0}, {0, 0} };
   1989 
   1990   /* ATIME */
   1991   if (atime_value)
   1992     {
   1993       if (!get_uint64 (atime_value, &val, error))
   1994 	return FALSE;
   1995       times[0].tv_sec = val;
   1996     }
   1997   else
   1998     {
   1999       if (lazy_stat (filename, &statbuf, &got_stat) == 0)
   2000 	{
   2001 	  times[0].tv_sec = statbuf.st_mtime;
   2002 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
   2003 	  times[0].tv_usec = statbuf.st_atimensec / 1000;
   2004 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
   2005 	  times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
   2006 #endif
   2007 	}
   2008     }
   2009 
   2010   if (atime_usec_value)
   2011     {
   2012       if (!get_uint32 (atime_usec_value, &val_usec, error))
   2013 	return FALSE;
   2014       times[0].tv_usec = val_usec;
   2015     }
   2016 
   2017   /* MTIME */
   2018   if (mtime_value)
   2019     {
   2020       if (!get_uint64 (mtime_value, &val, error))
   2021 	return FALSE;
   2022       times[1].tv_sec = val;
   2023     }
   2024   else
   2025     {
   2026       if (lazy_stat (filename, &statbuf, &got_stat) == 0)
   2027 	{
   2028 	  times[1].tv_sec = statbuf.st_mtime;
   2029 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
   2030 	  times[1].tv_usec = statbuf.st_mtimensec / 1000;
   2031 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
   2032 	  times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
   2033 #endif
   2034 	}
   2035     }
   2036 
   2037   if (mtime_usec_value)
   2038     {
   2039       if (!get_uint32 (mtime_usec_value, &val_usec, error))
   2040 	return FALSE;
   2041       times[1].tv_usec = val_usec;
   2042     }
   2043 
   2044   res = utimes (filename, times);
   2045   if (res == -1)
   2046     {
   2047       int errsv = errno;
   2048 
   2049       g_set_error (error, G_IO_ERROR,
   2050 		   g_io_error_from_errno (errsv),
   2051 		   _("Error setting owner: %s"),
   2052 		   g_strerror (errsv));
   2053 	  return FALSE;
   2054     }
   2055   return TRUE;
   2056 }
   2057 #endif
   2058 
   2059 
   2060 #ifdef HAVE_SELINUX
   2061 static gboolean
   2062 set_selinux_context (char                       *filename,
   2063 		 const GFileAttributeValue  *value,
   2064 		 GError                    **error)
   2065 {
   2066   const char *val;
   2067 
   2068   if (!get_string (value, &val, error))
   2069     return FALSE;
   2070 
   2071   if (val == NULL)
   2072   {
   2073     g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   2074                          _("SELinux context must be non-NULL"));
   2075     return FALSE;
   2076   }
   2077 
   2078   if (is_selinux_enabled ()) {
   2079 	security_context_t val_s;
   2080 
   2081 	val_s = g_strdup (val);
   2082 
   2083 	if (setfilecon_raw (filename, val_s) < 0)
   2084 	{
   2085             int errsv = errno;
   2086 
   2087             g_set_error (error, G_IO_ERROR,
   2088                          g_io_error_from_errno (errsv),
   2089                 	_("Error setting SELinux context: %s"),
   2090                          g_strerror (errsv));
   2091             return FALSE;
   2092         }
   2093         g_free (val_s);
   2094   } else {
   2095     g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
   2096                          _("SELinux is not enabled on this system"));
   2097     return FALSE;
   2098   }
   2099 
   2100   return TRUE;
   2101 }
   2102 #endif
   2103 
   2104 
   2105 gboolean
   2106 _g_local_file_info_set_attribute (char                 *filename,
   2107 				  const char           *attribute,
   2108 				  GFileAttributeType    type,
   2109 				  gpointer              value_p,
   2110 				  GFileQueryInfoFlags   flags,
   2111 				  GCancellable         *cancellable,
   2112 				  GError              **error)
   2113 {
   2114   GFileAttributeValue value = { 0 };
   2115 
   2116   _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
   2117 
   2118   if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
   2119     return set_unix_mode (filename, &value, error);
   2120 
   2121 #ifdef HAVE_CHOWN
   2122   else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
   2123     return set_unix_uid_gid (filename, &value, NULL, flags, error);
   2124   else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
   2125     return set_unix_uid_gid (filename, NULL, &value, flags, error);
   2126 #endif
   2127 
   2128 #ifdef HAVE_SYMLINK
   2129   else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
   2130     return set_symlink (filename, &value, error);
   2131 #endif
   2132 
   2133 #ifdef HAVE_UTIMES
   2134   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
   2135     return set_mtime_atime (filename, &value, NULL, NULL, NULL, error);
   2136   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
   2137     return set_mtime_atime (filename, NULL, &value, NULL, NULL, error);
   2138   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
   2139     return set_mtime_atime (filename, NULL, NULL, &value, NULL, error);
   2140   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
   2141     return set_mtime_atime (filename, NULL, NULL, NULL, &value, error);
   2142 #endif
   2143 
   2144 #ifdef HAVE_XATTR
   2145   else if (g_str_has_prefix (attribute, "xattr::"))
   2146     return set_xattr (filename, attribute, &value, error);
   2147   else if (g_str_has_prefix (attribute, "xattr-sys::"))
   2148     return set_xattr (filename, attribute, &value, error);
   2149 #endif
   2150 
   2151 #ifdef HAVE_SELINUX
   2152   else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
   2153     return set_selinux_context (filename, &value, error);
   2154 #endif
   2155 
   2156   g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
   2157 	       _("Setting attribute %s not supported"), attribute);
   2158   return FALSE;
   2159 }
   2160 
   2161 gboolean
   2162 _g_local_file_info_set_attributes  (char                 *filename,
   2163 				    GFileInfo            *info,
   2164 				    GFileQueryInfoFlags   flags,
   2165 				    GCancellable         *cancellable,
   2166 				    GError              **error)
   2167 {
   2168   GFileAttributeValue *value;
   2169 #ifdef HAVE_CHOWN
   2170   GFileAttributeValue *uid, *gid;
   2171 #endif
   2172 #ifdef HAVE_UTIMES
   2173   GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
   2174 #endif
   2175 #if defined (HAVE_CHOWN) || defined (HAVE_UTIMES)
   2176   GFileAttributeStatus status;
   2177 #endif
   2178   gboolean res;
   2179 
   2180   /* Handles setting multiple specified data in a single set, and takes care
   2181      of ordering restrictions when setting attributes */
   2182 
   2183   res = TRUE;
   2184 
   2185   /* Set symlink first, since this recreates the file */
   2186 #ifdef HAVE_SYMLINK
   2187   value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
   2188   if (value)
   2189     {
   2190       if (!set_symlink (filename, value, error))
   2191 	{
   2192 	  value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
   2193 	  res = FALSE;
   2194 	  /* Don't set error multiple times */
   2195 	  error = NULL;
   2196 	}
   2197       else
   2198 	value->status = G_FILE_ATTRIBUTE_STATUS_SET;
   2199 
   2200     }
   2201 #endif
   2202 
   2203 #ifdef HAVE_CHOWN
   2204   /* Group uid and gid setting into one call
   2205    * Change ownership before permissions, since ownership changes can
   2206      change permissions (e.g. setuid)
   2207    */
   2208   uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
   2209   gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
   2210 
   2211   if (uid || gid)
   2212     {
   2213       if (!set_unix_uid_gid (filename, uid, gid, flags, error))
   2214 	{
   2215 	  status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
   2216 	  res = FALSE;
   2217 	  /* Don't set error multiple times */
   2218 	  error = NULL;
   2219 	}
   2220       else
   2221 	status = G_FILE_ATTRIBUTE_STATUS_SET;
   2222       if (uid)
   2223 	uid->status = status;
   2224       if (gid)
   2225 	gid->status = status;
   2226     }
   2227 #endif
   2228 
   2229   value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
   2230   if (value)
   2231     {
   2232       if (!set_unix_mode (filename, value, error))
   2233 	{
   2234 	  value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
   2235 	  res = FALSE;
   2236 	  /* Don't set error multiple times */
   2237 	  error = NULL;
   2238 	}
   2239       else
   2240 	value->status = G_FILE_ATTRIBUTE_STATUS_SET;
   2241 
   2242     }
   2243 
   2244 #ifdef HAVE_UTIMES
   2245   /* Group all time settings into one call
   2246    * Change times as the last thing to avoid it changing due to metadata changes
   2247    */
   2248 
   2249   mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
   2250   mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
   2251   atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
   2252   atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
   2253 
   2254   if (mtime || mtime_usec || atime || atime_usec)
   2255     {
   2256       if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error))
   2257 	{
   2258 	  status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
   2259 	  res = FALSE;
   2260 	  /* Don't set error multiple times */
   2261 	  error = NULL;
   2262 	}
   2263       else
   2264 	status = G_FILE_ATTRIBUTE_STATUS_SET;
   2265 
   2266       if (mtime)
   2267 	mtime->status = status;
   2268       if (mtime_usec)
   2269 	mtime_usec->status = status;
   2270       if (atime)
   2271 	atime->status = status;
   2272       if (atime_usec)
   2273 	atime_usec->status = status;
   2274     }
   2275 #endif
   2276 
   2277   /* xattrs are handled by default callback */
   2278 
   2279 
   2280   /*  SELinux context */
   2281 #ifdef HAVE_SELINUX
   2282   if (is_selinux_enabled ()) {
   2283     value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
   2284     if (value)
   2285     {
   2286       if (!set_selinux_context (filename, value, error))
   2287         {
   2288           value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
   2289           res = FALSE;
   2290           /* Don't set error multiple times */
   2291           error = NULL;
   2292         }
   2293       else
   2294         value->status = G_FILE_ATTRIBUTE_STATUS_SET;
   2295     }
   2296   }
   2297 #endif
   2298 
   2299   return res;
   2300 }
   2301