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