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