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