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