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