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