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