Add some more cases to the app-id unit tests
[glib.git] / gio / glocalfileinfo.c
blob595d73654f4896e454e39de1399e4d345f407029
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 * Author: Alexander Larsson <alexl@redhat.com>
23 #include "config.h"
25 #include <glib.h>
27 #ifdef HAVE_SYS_TIME_H
28 #include <sys/time.h>
29 #endif
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #ifdef G_OS_UNIX
36 #include <grp.h>
37 #include <pwd.h>
38 #endif
39 #ifdef HAVE_SELINUX
40 #include <selinux/selinux.h>
41 #endif
43 #ifdef HAVE_XATTR
45 #if defined HAVE_SYS_XATTR_H
46 #include <sys/xattr.h>
47 #elif defined HAVE_ATTR_XATTR_H
48 #include <attr/xattr.h>
49 #else
50 #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
51 #endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
53 #endif /* HAVE_XATTR */
55 #include <glib/gstdio.h>
56 #include <gfileattribute-priv.h>
57 #include <gfileinfo-priv.h>
58 #include <gvfs.h>
60 #ifdef G_OS_UNIX
61 #include <unistd.h>
62 #include "glib-unix.h"
63 #include "glib-private.h"
64 #endif
66 #include "thumbnail-verify.h"
68 #ifdef G_OS_WIN32
69 #include <windows.h>
70 #include <io.h>
71 #ifndef W_OK
72 #define W_OK 2
73 #endif
74 #ifndef R_OK
75 #define R_OK 4
76 #endif
77 #ifndef X_OK
78 #define X_OK 0 /* not really */
79 #endif
80 #ifndef S_ISREG
81 #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
82 #endif
83 #ifndef S_ISDIR
84 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
85 #endif
86 #ifndef S_IXUSR
87 #define S_IXUSR _S_IEXEC
88 #endif
89 #endif
91 #include "glocalfileinfo.h"
92 #include "gioerror.h"
93 #include "gthemedicon.h"
94 #include "gcontenttypeprivate.h"
95 #include "glibintl.h"
98 struct ThumbMD5Context {
99 guint32 buf[4];
100 guint32 bits[2];
101 unsigned char in[64];
104 #ifndef G_OS_WIN32
106 typedef struct {
107 char *user_name;
108 char *real_name;
109 } UidData;
111 G_LOCK_DEFINE_STATIC (uid_cache);
112 static GHashTable *uid_cache = NULL;
114 G_LOCK_DEFINE_STATIC (gid_cache);
115 static GHashTable *gid_cache = NULL;
117 #endif /* !G_OS_WIN32 */
119 char *
120 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
122 glong sec, usec;
124 sec = statbuf->st_mtime;
125 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
126 usec = statbuf->st_mtimensec / 1000;
127 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
128 usec = statbuf->st_mtim.tv_nsec / 1000;
129 #else
130 usec = 0;
131 #endif
133 return g_strdup_printf ("%lu:%lu", sec, usec);
136 static char *
137 _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
139 return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
140 (guint64) statbuf->st_dev,
141 (guint64) statbuf->st_ino);
144 static char *
145 _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
147 return g_strdup_printf ("l%" G_GUINT64_FORMAT,
148 (guint64) statbuf->st_dev);
152 #ifdef S_ISLNK
154 static gchar *
155 read_link (const gchar *full_name)
157 #ifdef HAVE_READLINK
158 gchar *buffer;
159 guint size;
161 size = 256;
162 buffer = g_malloc (size);
164 while (1)
166 int read_size;
168 read_size = readlink (full_name, buffer, size);
169 if (read_size < 0)
171 g_free (buffer);
172 return NULL;
174 if (read_size < size)
176 buffer[read_size] = 0;
177 return buffer;
179 size *= 2;
180 buffer = g_realloc (buffer, size);
182 #else
183 return NULL;
184 #endif
187 #endif /* S_ISLNK */
189 #ifdef HAVE_SELINUX
190 /* Get the SELinux security context */
191 static void
192 get_selinux_context (const char *path,
193 GFileInfo *info,
194 GFileAttributeMatcher *attribute_matcher,
195 gboolean follow_symlinks)
197 char *context;
199 if (!_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
200 return;
202 if (is_selinux_enabled ())
204 if (follow_symlinks)
206 if (lgetfilecon_raw (path, &context) < 0)
207 return;
209 else
211 if (getfilecon_raw (path, &context) < 0)
212 return;
215 if (context)
217 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
218 freecon (context);
222 #endif
224 #ifdef HAVE_XATTR
226 /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
227 * (Mac) getxattr(..., XATTR_NOFOLLOW)
229 #ifdef HAVE_XATTR_NOFOLLOW
230 #define g_fgetxattr(fd,name,value,size) fgetxattr(fd,name,value,size,0,0)
231 #define g_flistxattr(fd,name,size) flistxattr(fd,name,size,0)
232 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
233 #else
234 #define g_fgetxattr fgetxattr
235 #define g_flistxattr flistxattr
236 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
237 #endif
239 static ssize_t
240 g_getxattr (const char *path, const char *name, void *value, size_t size,
241 gboolean follow_symlinks)
243 #ifdef HAVE_XATTR_NOFOLLOW
244 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
245 #else
246 if (follow_symlinks)
247 return getxattr (path, name, value, size);
248 else
249 return lgetxattr (path, name, value, size);
250 #endif
253 static ssize_t
254 g_listxattr(const char *path, char *namebuf, size_t size,
255 gboolean follow_symlinks)
257 #ifdef HAVE_XATTR_NOFOLLOW
258 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
259 #else
260 if (follow_symlinks)
261 return listxattr (path, namebuf, size);
262 else
263 return llistxattr (path, namebuf, size);
264 #endif
267 static gboolean
268 valid_char (char c)
270 return c >= 32 && c <= 126 && c != '\\';
273 static gboolean
274 name_is_valid (const char *str)
276 while (*str)
278 if (!valid_char (*str++))
279 return FALSE;
281 return TRUE;
284 static char *
285 hex_escape_string (const char *str,
286 gboolean *free_return)
288 int num_invalid, i;
289 char *escaped_str, *p;
290 unsigned char c;
291 static char *hex_digits = "0123456789abcdef";
292 int len;
294 len = strlen (str);
296 num_invalid = 0;
297 for (i = 0; i < len; i++)
299 if (!valid_char (str[i]))
300 num_invalid++;
303 if (num_invalid == 0)
305 *free_return = FALSE;
306 return (char *)str;
309 escaped_str = g_malloc (len + num_invalid*3 + 1);
311 p = escaped_str;
312 for (i = 0; i < len; i++)
314 if (valid_char (str[i]))
315 *p++ = str[i];
316 else
318 c = str[i];
319 *p++ = '\\';
320 *p++ = 'x';
321 *p++ = hex_digits[(c >> 4) & 0xf];
322 *p++ = hex_digits[c & 0xf];
325 *p = 0;
327 *free_return = TRUE;
328 return escaped_str;
331 static char *
332 hex_unescape_string (const char *str,
333 int *out_len,
334 gboolean *free_return)
336 int i;
337 char *unescaped_str, *p;
338 unsigned char c;
339 int len;
341 len = strlen (str);
343 if (strchr (str, '\\') == NULL)
345 if (out_len)
346 *out_len = len;
347 *free_return = FALSE;
348 return (char *)str;
351 unescaped_str = g_malloc (len + 1);
353 p = unescaped_str;
354 for (i = 0; i < len; i++)
356 if (str[i] == '\\' &&
357 str[i+1] == 'x' &&
358 len - i >= 4)
361 (g_ascii_xdigit_value (str[i+2]) << 4) |
362 g_ascii_xdigit_value (str[i+3]);
363 *p++ = c;
364 i += 3;
366 else
367 *p++ = str[i];
369 *p++ = 0;
371 if (out_len)
372 *out_len = p - unescaped_str;
373 *free_return = TRUE;
374 return unescaped_str;
377 static void
378 escape_xattr (GFileInfo *info,
379 const char *gio_attr, /* gio attribute name */
380 const char *value, /* Is zero terminated */
381 size_t len /* not including zero termination */)
383 char *escaped_val;
384 gboolean free_escaped_val;
386 escaped_val = hex_escape_string (value, &free_escaped_val);
388 g_file_info_set_attribute_string (info, gio_attr, escaped_val);
390 if (free_escaped_val)
391 g_free (escaped_val);
394 static void
395 get_one_xattr (const char *path,
396 GFileInfo *info,
397 const char *gio_attr,
398 const char *xattr,
399 gboolean follow_symlinks)
401 char value[64];
402 char *value_p;
403 ssize_t len;
405 len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
407 value_p = NULL;
408 if (len >= 0)
409 value_p = value;
410 else if (len == -1 && errno == ERANGE)
412 len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
414 if (len < 0)
415 return;
417 value_p = g_malloc (len+1);
419 len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
421 if (len < 0)
423 g_free (value_p);
424 return;
427 else
428 return;
430 /* Null terminate */
431 value_p[len] = 0;
433 escape_xattr (info, gio_attr, value_p, len);
435 if (value_p != value)
436 g_free (value_p);
439 #endif /* defined HAVE_XATTR */
441 static void
442 get_xattrs (const char *path,
443 gboolean user,
444 GFileInfo *info,
445 GFileAttributeMatcher *matcher,
446 gboolean follow_symlinks)
448 #ifdef HAVE_XATTR
449 gboolean all;
450 gsize list_size;
451 ssize_t list_res_size;
452 size_t len;
453 char *list;
454 const char *attr, *attr2;
456 if (user)
457 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
458 else
459 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
461 if (all)
463 list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
465 if (list_res_size == -1 ||
466 list_res_size == 0)
467 return;
469 list_size = list_res_size;
470 list = g_malloc (list_size);
472 retry:
474 list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
476 if (list_res_size == -1 && errno == ERANGE)
478 list_size = list_size * 2;
479 list = g_realloc (list, list_size);
480 goto retry;
483 if (list_res_size == -1)
484 return;
486 attr = list;
487 while (list_res_size > 0)
489 if ((user && g_str_has_prefix (attr, "user.")) ||
490 (!user && !g_str_has_prefix (attr, "user.")))
492 char *escaped_attr, *gio_attr;
493 gboolean free_escaped_attr;
495 if (user)
497 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
498 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
500 else
502 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
503 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
506 if (free_escaped_attr)
507 g_free (escaped_attr);
509 get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
511 g_free (gio_attr);
514 len = strlen (attr) + 1;
515 attr += len;
516 list_res_size -= len;
519 g_free (list);
521 else
523 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
525 char *unescaped_attribute, *a;
526 gboolean free_unescaped_attribute;
528 attr2 = strchr (attr, ':');
529 if (attr2)
531 attr2 += 2; /* Skip '::' */
532 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
533 if (user)
534 a = g_strconcat ("user.", unescaped_attribute, NULL);
535 else
536 a = unescaped_attribute;
538 get_one_xattr (path, info, attr, a, follow_symlinks);
540 if (user)
541 g_free (a);
543 if (free_unescaped_attribute)
544 g_free (unescaped_attribute);
548 #endif /* defined HAVE_XATTR */
551 #ifdef HAVE_XATTR
552 static void
553 get_one_xattr_from_fd (int fd,
554 GFileInfo *info,
555 const char *gio_attr,
556 const char *xattr)
558 char value[64];
559 char *value_p;
560 ssize_t len;
562 len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
564 value_p = NULL;
565 if (len >= 0)
566 value_p = value;
567 else if (len == -1 && errno == ERANGE)
569 len = g_fgetxattr (fd, xattr, NULL, 0);
571 if (len < 0)
572 return;
574 value_p = g_malloc (len + 1);
576 len = g_fgetxattr (fd, xattr, value_p, len);
578 if (len < 0)
580 g_free (value_p);
581 return;
584 else
585 return;
587 /* Null terminate */
588 value_p[len] = 0;
590 escape_xattr (info, gio_attr, value_p, len);
592 if (value_p != value)
593 g_free (value_p);
595 #endif /* defined HAVE_XATTR */
597 static void
598 get_xattrs_from_fd (int fd,
599 gboolean user,
600 GFileInfo *info,
601 GFileAttributeMatcher *matcher)
603 #ifdef HAVE_XATTR
604 gboolean all;
605 gsize list_size;
606 ssize_t list_res_size;
607 size_t len;
608 char *list;
609 const char *attr, *attr2;
611 if (user)
612 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
613 else
614 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
616 if (all)
618 list_res_size = g_flistxattr (fd, NULL, 0);
620 if (list_res_size == -1 ||
621 list_res_size == 0)
622 return;
624 list_size = list_res_size;
625 list = g_malloc (list_size);
627 retry:
629 list_res_size = g_flistxattr (fd, list, list_size);
631 if (list_res_size == -1 && errno == ERANGE)
633 list_size = list_size * 2;
634 list = g_realloc (list, list_size);
635 goto retry;
638 if (list_res_size == -1)
639 return;
641 attr = list;
642 while (list_res_size > 0)
644 if ((user && g_str_has_prefix (attr, "user.")) ||
645 (!user && !g_str_has_prefix (attr, "user.")))
647 char *escaped_attr, *gio_attr;
648 gboolean free_escaped_attr;
650 if (user)
652 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
653 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
655 else
657 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
658 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
661 if (free_escaped_attr)
662 g_free (escaped_attr);
664 get_one_xattr_from_fd (fd, info, gio_attr, attr);
665 g_free (gio_attr);
668 len = strlen (attr) + 1;
669 attr += len;
670 list_res_size -= len;
673 g_free (list);
675 else
677 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
679 char *unescaped_attribute, *a;
680 gboolean free_unescaped_attribute;
682 attr2 = strchr (attr, ':');
683 if (attr2)
685 attr2++; /* Skip ':' */
686 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
687 if (user)
688 a = g_strconcat ("user.", unescaped_attribute, NULL);
689 else
690 a = unescaped_attribute;
692 get_one_xattr_from_fd (fd, info, attr, a);
694 if (user)
695 g_free (a);
697 if (free_unescaped_attribute)
698 g_free (unescaped_attribute);
702 #endif /* defined HAVE_XATTR */
705 #ifdef HAVE_XATTR
706 static gboolean
707 set_xattr (char *filename,
708 const char *escaped_attribute,
709 const GFileAttributeValue *attr_value,
710 GError **error)
712 char *attribute, *value;
713 gboolean free_attribute, free_value;
714 int val_len, res, errsv;
715 gboolean is_user;
716 char *a;
718 if (attr_value == NULL)
720 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
721 _("Attribute value must be non-NULL"));
722 return FALSE;
725 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
727 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
728 _("Invalid attribute type (string expected)"));
729 return FALSE;
732 if (!name_is_valid (escaped_attribute))
734 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
735 _("Invalid extended attribute name"));
736 return FALSE;
739 if (g_str_has_prefix (escaped_attribute, "xattr::"))
741 escaped_attribute += strlen ("xattr::");
742 is_user = TRUE;
744 else
746 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
747 escaped_attribute += strlen ("xattr-sys::");
748 is_user = FALSE;
751 attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
752 value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
754 if (is_user)
755 a = g_strconcat ("user.", attribute, NULL);
756 else
757 a = attribute;
759 res = g_setxattr (filename, a, value, val_len);
760 errsv = errno;
762 if (is_user)
763 g_free (a);
765 if (free_attribute)
766 g_free (attribute);
768 if (free_value)
769 g_free (value);
771 if (res == -1)
773 g_set_error (error, G_IO_ERROR,
774 g_io_error_from_errno (errsv),
775 _("Error setting extended attribute “%s”: %s"),
776 escaped_attribute, g_strerror (errsv));
777 return FALSE;
780 return TRUE;
783 #endif
786 void
787 _g_local_file_info_get_parent_info (const char *dir,
788 GFileAttributeMatcher *attribute_matcher,
789 GLocalParentFileInfo *parent_info)
791 GStatBuf statbuf;
792 int res;
794 parent_info->extra_data = NULL;
795 parent_info->free_extra_data = NULL;
796 parent_info->writable = FALSE;
797 parent_info->is_sticky = FALSE;
798 parent_info->has_trash_dir = FALSE;
799 parent_info->device = 0;
801 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) ||
802 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) ||
803 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH) ||
804 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
806 /* FIXME: Windows: The underlying _waccess() call in the C
807 * library is mostly pointless as it only looks at the READONLY
808 * FAT-style attribute of the file, it doesn't check the ACL at
809 * all.
811 parent_info->writable = (g_access (dir, W_OK) == 0);
813 res = g_stat (dir, &statbuf);
816 * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
817 * renamed or deleted only by the owner of the file, by the owner of the directory, and
818 * by a privileged process.
820 if (res == 0)
822 #ifdef S_ISVTX
823 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
824 #else
825 parent_info->is_sticky = FALSE;
826 #endif
827 parent_info->owner = statbuf.st_uid;
828 parent_info->device = statbuf.st_dev;
829 /* No need to find trash dir if it's not writable anyway */
830 if (parent_info->writable &&
831 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
832 parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev);
837 void
838 _g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
840 if (parent_info->extra_data &&
841 parent_info->free_extra_data)
842 parent_info->free_extra_data (parent_info->extra_data);
845 static void
846 get_access_rights (GFileAttributeMatcher *attribute_matcher,
847 GFileInfo *info,
848 const gchar *path,
849 GLocalFileStat *statbuf,
850 GLocalParentFileInfo *parent_info)
852 /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
853 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
854 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ))
855 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ,
856 g_access (path, R_OK) == 0);
858 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
859 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE))
860 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE,
861 g_access (path, W_OK) == 0);
863 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
864 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE))
865 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE,
866 g_access (path, X_OK) == 0);
869 if (parent_info)
871 gboolean writable;
873 writable = FALSE;
874 if (parent_info->writable)
876 if (parent_info->is_sticky)
878 #ifndef G_OS_WIN32
879 uid_t uid = geteuid ();
881 if (uid == statbuf->st_uid ||
882 uid == parent_info->owner ||
883 uid == 0)
884 #endif
885 writable = TRUE;
887 else
888 writable = TRUE;
891 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME))
892 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME,
893 writable);
895 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE))
896 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
897 writable);
899 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
900 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
901 writable && parent_info->has_trash_dir);
905 static void
906 set_info_from_stat (GFileInfo *info,
907 GLocalFileStat *statbuf,
908 GFileAttributeMatcher *attribute_matcher)
910 GFileType file_type;
912 file_type = G_FILE_TYPE_UNKNOWN;
914 if (S_ISREG (statbuf->st_mode))
915 file_type = G_FILE_TYPE_REGULAR;
916 else if (S_ISDIR (statbuf->st_mode))
917 file_type = G_FILE_TYPE_DIRECTORY;
918 #ifndef G_OS_WIN32
919 else if (S_ISCHR (statbuf->st_mode) ||
920 S_ISBLK (statbuf->st_mode) ||
921 S_ISFIFO (statbuf->st_mode)
922 #ifdef S_ISSOCK
923 || S_ISSOCK (statbuf->st_mode)
924 #endif
926 file_type = G_FILE_TYPE_SPECIAL;
927 #endif
928 #ifdef S_ISLNK
929 else if (S_ISLNK (statbuf->st_mode))
930 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
931 #endif
933 g_file_info_set_file_type (info, file_type);
934 g_file_info_set_size (info, statbuf->st_size);
936 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, statbuf->st_dev);
937 #ifndef G_OS_WIN32
938 /* Pointless setting these on Windows even if they exist in the struct */
939 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, statbuf->st_ino);
940 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, statbuf->st_nlink);
941 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, statbuf->st_uid);
942 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, statbuf->st_gid);
943 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, statbuf->st_rdev);
944 #endif
945 /* FIXME: st_mode is mostly pointless on Windows, too. Set the attribute or not? */
946 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, statbuf->st_mode);
947 #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
948 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, statbuf->st_blksize);
949 #endif
950 #if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
951 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, statbuf->st_blocks);
952 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
953 statbuf->st_blocks * G_GUINT64_CONSTANT (512));
954 #endif
956 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtime);
957 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
958 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000);
959 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
960 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000);
961 #endif
963 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atime);
964 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
965 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
966 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
967 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
968 #endif
970 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, statbuf->st_ctime);
971 #if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
972 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
973 #elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
974 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000);
975 #endif
977 #if defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
978 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
979 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtimensec / 1000);
980 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
981 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim.tv_sec);
982 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtim.tv_nsec / 1000);
983 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME)
984 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
985 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM)
986 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim);
987 #endif
989 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
990 G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
992 char *etag = _g_local_file_info_create_etag (statbuf);
993 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ETAG_VALUE, etag);
994 g_free (etag);
997 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
998 G_FILE_ATTRIBUTE_ID_ID_FILE))
1000 char *id = _g_local_file_info_create_file_id (statbuf);
1001 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILE, id);
1002 g_free (id);
1005 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1006 G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
1008 char *id = _g_local_file_info_create_fs_id (statbuf);
1009 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM, id);
1010 g_free (id);
1014 #ifndef G_OS_WIN32
1016 static char *
1017 make_valid_utf8 (const char *name)
1019 GString *string;
1020 const gchar *remainder, *invalid;
1021 gint remaining_bytes, valid_bytes;
1023 string = NULL;
1024 remainder = name;
1025 remaining_bytes = strlen (name);
1027 while (remaining_bytes != 0)
1029 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
1030 break;
1031 valid_bytes = invalid - remainder;
1033 if (string == NULL)
1034 string = g_string_sized_new (remaining_bytes);
1036 g_string_append_len (string, remainder, valid_bytes);
1037 /* append U+FFFD REPLACEMENT CHARACTER */
1038 g_string_append (string, "\357\277\275");
1040 remaining_bytes -= valid_bytes + 1;
1041 remainder = invalid + 1;
1044 if (string == NULL)
1045 return g_strdup (name);
1047 g_string_append (string, remainder);
1049 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
1051 return g_string_free (string, FALSE);
1054 static char *
1055 convert_pwd_string_to_utf8 (char *pwd_str)
1057 char *utf8_string;
1059 if (!g_utf8_validate (pwd_str, -1, NULL))
1061 utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
1062 if (utf8_string == NULL)
1063 utf8_string = make_valid_utf8 (pwd_str);
1065 else
1066 utf8_string = g_strdup (pwd_str);
1068 return utf8_string;
1071 static void
1072 uid_data_free (UidData *data)
1074 g_free (data->user_name);
1075 g_free (data->real_name);
1076 g_free (data);
1079 /* called with lock held */
1080 static UidData *
1081 lookup_uid_data (uid_t uid)
1083 UidData *data;
1084 char buffer[4096];
1085 struct passwd pwbuf;
1086 struct passwd *pwbufp;
1087 char *gecos, *comma;
1089 if (uid_cache == NULL)
1090 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
1092 data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
1094 if (data)
1095 return data;
1097 data = g_new0 (UidData, 1);
1099 #if defined(HAVE_GETPWUID_R)
1100 getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
1101 #else
1102 pwbufp = getpwuid (uid);
1103 #endif
1105 if (pwbufp != NULL)
1107 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
1108 data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
1110 #ifndef __BIONIC__
1111 gecos = pwbufp->pw_gecos;
1113 if (gecos)
1115 comma = strchr (gecos, ',');
1116 if (comma)
1117 *comma = 0;
1118 data->real_name = convert_pwd_string_to_utf8 (gecos);
1120 #endif
1123 /* Default fallbacks */
1124 if (data->real_name == NULL)
1126 if (data->user_name != NULL)
1127 data->real_name = g_strdup (data->user_name);
1128 else
1129 data->real_name = g_strdup_printf ("user #%d", (int)uid);
1132 if (data->user_name == NULL)
1133 data->user_name = g_strdup_printf ("%d", (int)uid);
1135 g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
1137 return data;
1140 static char *
1141 get_username_from_uid (uid_t uid)
1143 char *res;
1144 UidData *data;
1146 G_LOCK (uid_cache);
1147 data = lookup_uid_data (uid);
1148 res = g_strdup (data->user_name);
1149 G_UNLOCK (uid_cache);
1151 return res;
1154 static char *
1155 get_realname_from_uid (uid_t uid)
1157 char *res;
1158 UidData *data;
1160 G_LOCK (uid_cache);
1161 data = lookup_uid_data (uid);
1162 res = g_strdup (data->real_name);
1163 G_UNLOCK (uid_cache);
1165 return res;
1168 /* called with lock held */
1169 static char *
1170 lookup_gid_name (gid_t gid)
1172 char *name;
1173 char buffer[4096];
1174 struct group gbuf;
1175 struct group *gbufp;
1177 if (gid_cache == NULL)
1178 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
1180 name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
1182 if (name)
1183 return name;
1185 #if defined (HAVE_GETGRGID_R)
1186 getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
1187 #else
1188 gbufp = getgrgid (gid);
1189 #endif
1191 if (gbufp != NULL &&
1192 gbufp->gr_name != NULL &&
1193 gbufp->gr_name[0] != 0)
1194 name = convert_pwd_string_to_utf8 (gbufp->gr_name);
1195 else
1196 name = g_strdup_printf("%d", (int)gid);
1198 g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
1200 return name;
1203 static char *
1204 get_groupname_from_gid (gid_t gid)
1206 char *res;
1207 char *name;
1209 G_LOCK (gid_cache);
1210 name = lookup_gid_name (gid);
1211 res = g_strdup (name);
1212 G_UNLOCK (gid_cache);
1213 return res;
1216 #endif /* !G_OS_WIN32 */
1218 static char *
1219 get_content_type (const char *basename,
1220 const char *path,
1221 GLocalFileStat *statbuf,
1222 gboolean is_symlink,
1223 gboolean symlink_broken,
1224 GFileQueryInfoFlags flags,
1225 gboolean fast)
1227 if (is_symlink &&
1228 (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
1229 return g_content_type_from_mime_type ("inode/symlink");
1230 else if (statbuf != NULL && S_ISDIR(statbuf->st_mode))
1231 return g_content_type_from_mime_type ("inode/directory");
1232 #ifndef G_OS_WIN32
1233 else if (statbuf != NULL && S_ISCHR(statbuf->st_mode))
1234 return g_content_type_from_mime_type ("inode/chardevice");
1235 else if (statbuf != NULL && S_ISBLK(statbuf->st_mode))
1236 return g_content_type_from_mime_type ("inode/blockdevice");
1237 else if (statbuf != NULL && S_ISFIFO(statbuf->st_mode))
1238 return g_content_type_from_mime_type ("inode/fifo");
1239 else if (statbuf != NULL && S_ISREG(statbuf->st_mode) && statbuf->st_size == 0)
1241 /* Don't sniff zero-length files in order to avoid reading files
1242 * that appear normal but are not (eg: files in /proc and /sys)
1244 * Note that we need to return text/plain here so that
1245 * newly-created text files are opened by the text editor.
1246 * See https://bugzilla.gnome.org/show_bug.cgi?id=755795
1248 return g_content_type_from_mime_type ("text/plain");
1250 #endif
1251 #ifdef S_ISSOCK
1252 else if (statbuf != NULL && S_ISSOCK(statbuf->st_mode))
1253 return g_content_type_from_mime_type ("inode/socket");
1254 #endif
1255 else
1257 char *content_type;
1258 gboolean result_uncertain;
1260 content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
1262 #ifndef G_OS_WIN32
1263 if (!fast && result_uncertain && path != NULL)
1265 guchar sniff_buffer[4096];
1266 gsize sniff_length;
1267 int fd;
1269 sniff_length = _g_unix_content_type_get_sniff_len ();
1270 if (sniff_length > 4096)
1271 sniff_length = 4096;
1273 #ifdef O_NOATIME
1274 fd = g_open (path, O_RDONLY | O_NOATIME, 0);
1275 if (fd < 0 && errno == EPERM)
1276 #endif
1277 fd = g_open (path, O_RDONLY, 0);
1279 if (fd != -1)
1281 ssize_t res;
1283 res = read (fd, sniff_buffer, sniff_length);
1284 (void) g_close (fd, NULL);
1285 if (res >= 0)
1287 g_free (content_type);
1288 content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
1292 #endif
1294 return content_type;
1299 /* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
1300 static void
1301 get_thumbnail_attributes (const char *path,
1302 GFileInfo *info,
1303 const GLocalFileStat *stat_buf)
1305 GChecksum *checksum;
1306 char *uri;
1307 char *filename;
1308 char *basename;
1310 uri = g_filename_to_uri (path, NULL, NULL);
1312 checksum = g_checksum_new (G_CHECKSUM_MD5);
1313 g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
1315 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
1316 g_checksum_free (checksum);
1318 filename = g_build_filename (g_get_user_cache_dir (),
1319 "thumbnails", "large", basename,
1320 NULL);
1322 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1324 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
1325 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1326 thumbnail_verify (filename, uri, stat_buf));
1328 else
1330 g_free (filename);
1331 filename = g_build_filename (g_get_user_cache_dir (),
1332 "thumbnails", "normal", basename,
1333 NULL);
1335 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1337 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
1338 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1339 thumbnail_verify (filename, uri, stat_buf));
1341 else
1343 g_free (filename);
1344 filename = g_build_filename (g_get_user_cache_dir (),
1345 "thumbnails", "fail",
1346 "gnome-thumbnail-factory",
1347 basename,
1348 NULL);
1350 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1352 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
1353 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1354 thumbnail_verify (filename, uri, stat_buf));
1358 g_free (basename);
1359 g_free (filename);
1360 g_free (uri);
1363 #ifdef G_OS_WIN32
1364 static void
1365 win32_get_file_user_info (const gchar *filename,
1366 gchar **group_name,
1367 gchar **user_name,
1368 gchar **real_name)
1370 PSECURITY_DESCRIPTOR psd = NULL;
1371 DWORD sd_size = 0; /* first call calculates the size required */
1373 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1374 if ((GetFileSecurityW (wfilename,
1375 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1376 NULL,
1377 sd_size,
1378 &sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
1379 (psd = g_try_malloc (sd_size)) != NULL &&
1380 GetFileSecurityW (wfilename,
1381 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1382 psd,
1383 sd_size,
1384 &sd_size))
1386 PSID psid = 0;
1387 BOOL defaulted;
1388 SID_NAME_USE name_use = 0; /* don't care? */
1389 wchar_t *name = NULL;
1390 wchar_t *domain = NULL;
1391 DWORD name_len = 0;
1392 DWORD domain_len = 0;
1393 /* get the user name */
1394 do {
1395 if (!user_name)
1396 break;
1397 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
1398 break;
1399 if (!LookupAccountSidW (NULL, /* local machine */
1400 psid,
1401 name, &name_len,
1402 domain, &domain_len, /* no domain info yet */
1403 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1404 break;
1405 name = g_try_malloc (name_len * sizeof (wchar_t));
1406 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1407 if (name && domain &&
1408 LookupAccountSidW (NULL, /* local machine */
1409 psid,
1410 name, &name_len,
1411 domain, &domain_len, /* no domain info yet */
1412 &name_use))
1414 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1416 g_free (name);
1417 g_free (domain);
1418 } while (FALSE);
1420 /* get the group name */
1421 do {
1422 if (!group_name)
1423 break;
1424 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
1425 break;
1426 if (!LookupAccountSidW (NULL, /* local machine */
1427 psid,
1428 name, &name_len,
1429 domain, &domain_len, /* no domain info yet */
1430 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1431 break;
1432 name = g_try_malloc (name_len * sizeof (wchar_t));
1433 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1434 if (name && domain &&
1435 LookupAccountSidW (NULL, /* local machine */
1436 psid,
1437 name, &name_len,
1438 domain, &domain_len, /* no domain info yet */
1439 &name_use))
1441 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1443 g_free (name);
1444 g_free (domain);
1445 } while (FALSE);
1447 /* TODO: get real name */
1449 g_free (psd);
1451 g_free (wfilename);
1453 #endif /* G_OS_WIN32 */
1455 #ifndef G_OS_WIN32
1456 /* support for '.hidden' files */
1457 G_LOCK_DEFINE_STATIC (hidden_cache);
1458 static GHashTable *hidden_cache;
1460 static gboolean
1461 remove_from_hidden_cache (gpointer user_data)
1463 G_LOCK (hidden_cache);
1464 g_hash_table_remove (hidden_cache, user_data);
1465 G_UNLOCK (hidden_cache);
1467 return FALSE;
1470 static GHashTable *
1471 read_hidden_file (const gchar *dirname)
1473 gchar *contents = NULL;
1474 gchar *filename;
1476 filename = g_build_path ("/", dirname, ".hidden", NULL);
1477 (void) g_file_get_contents (filename, &contents, NULL, NULL);
1478 g_free (filename);
1480 if (contents != NULL)
1482 GHashTable *table;
1483 gchar **lines;
1484 gint i;
1486 table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1488 lines = g_strsplit (contents, "\n", 0);
1489 g_free (contents);
1491 for (i = 0; lines[i]; i++)
1492 /* hash table takes the individual strings... */
1493 g_hash_table_add (table, lines[i]);
1495 /* ... so we only free the container. */
1496 g_free (lines);
1498 return table;
1500 else
1501 return NULL;
1504 static void
1505 maybe_unref_hash_table (gpointer data)
1507 if (data != NULL)
1508 g_hash_table_unref (data);
1511 static gboolean
1512 file_is_hidden (const gchar *path,
1513 const gchar *basename)
1515 gboolean result;
1516 gchar *dirname;
1517 gpointer table;
1519 dirname = g_path_get_dirname (path);
1521 G_LOCK (hidden_cache);
1523 if G_UNLIKELY (hidden_cache == NULL)
1524 hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1525 g_free, maybe_unref_hash_table);
1527 if (!g_hash_table_lookup_extended (hidden_cache, dirname,
1528 NULL, &table))
1530 gchar *mydirname;
1531 GSource *remove_from_cache_source;
1533 g_hash_table_insert (hidden_cache,
1534 mydirname = g_strdup (dirname),
1535 table = read_hidden_file (dirname));
1537 remove_from_cache_source = g_timeout_source_new_seconds (5);
1538 g_source_set_priority (remove_from_cache_source, G_PRIORITY_DEFAULT);
1539 g_source_set_callback (remove_from_cache_source,
1540 remove_from_hidden_cache,
1541 mydirname,
1542 NULL);
1543 g_source_attach (remove_from_cache_source,
1544 GLIB_PRIVATE_CALL (g_get_worker_context) ());
1545 g_source_unref (remove_from_cache_source);
1548 result = table != NULL && g_hash_table_contains (table, basename);
1550 G_UNLOCK (hidden_cache);
1552 g_free (dirname);
1554 return result;
1556 #endif /* !G_OS_WIN32 */
1558 void
1559 _g_local_file_info_get_nostat (GFileInfo *info,
1560 const char *basename,
1561 const char *path,
1562 GFileAttributeMatcher *attribute_matcher)
1564 g_file_info_set_name (info, basename);
1566 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1567 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
1569 char *display_name = g_filename_display_basename (path);
1571 /* look for U+FFFD REPLACEMENT CHARACTER */
1572 if (strstr (display_name, "\357\277\275") != NULL)
1574 char *p = display_name;
1575 display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1576 g_free (p);
1578 g_file_info_set_display_name (info, display_name);
1579 g_free (display_name);
1582 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1583 G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
1585 char *edit_name = g_filename_display_basename (path);
1586 g_file_info_set_edit_name (info, edit_name);
1587 g_free (edit_name);
1591 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1592 G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
1594 char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1595 if (copy_name)
1596 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
1597 g_free (copy_name);
1601 static const char *
1602 get_icon_name (const char *path,
1603 const char *content_type,
1604 gboolean use_symbolic,
1605 gboolean *with_fallbacks_out)
1607 const char *name = NULL;
1608 gboolean with_fallbacks = TRUE;
1610 if (strcmp (path, g_get_home_dir ()) == 0)
1612 name = use_symbolic ? "user-home-symbolic" : "user-home";
1613 with_fallbacks = FALSE;
1615 else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
1617 name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
1618 with_fallbacks = FALSE;
1620 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
1622 name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
1624 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
1626 name = use_symbolic ? "folder-download-symbolic" : "folder-download";
1628 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
1630 name = use_symbolic ? "folder-music-symbolic" : "folder-music";
1632 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
1634 name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
1636 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
1638 name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
1640 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
1642 name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
1644 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
1646 name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
1648 else if (g_strcmp0 (content_type, "inode/directory") == 0)
1650 name = use_symbolic ? "folder-symbolic" : "folder";
1652 else
1654 name = NULL;
1657 if (with_fallbacks_out != NULL)
1658 *with_fallbacks_out = with_fallbacks;
1660 return name;
1663 static GIcon *
1664 get_icon (const char *path,
1665 const char *content_type,
1666 gboolean use_symbolic)
1668 GIcon *icon = NULL;
1669 const char *icon_name;
1670 gboolean with_fallbacks;
1672 icon_name = get_icon_name (path, content_type, use_symbolic, &with_fallbacks);
1673 if (icon_name != NULL)
1675 if (with_fallbacks)
1676 icon = g_themed_icon_new_with_default_fallbacks (icon_name);
1677 else
1678 icon = g_themed_icon_new (icon_name);
1680 else
1682 if (use_symbolic)
1683 icon = g_content_type_get_symbolic_icon (content_type);
1684 else
1685 icon = g_content_type_get_icon (content_type);
1688 return icon;
1691 GFileInfo *
1692 _g_local_file_info_get (const char *basename,
1693 const char *path,
1694 GFileAttributeMatcher *attribute_matcher,
1695 GFileQueryInfoFlags flags,
1696 GLocalParentFileInfo *parent_info,
1697 GError **error)
1699 GFileInfo *info;
1700 GLocalFileStat statbuf;
1701 #ifdef S_ISLNK
1702 struct stat statbuf2;
1703 #endif
1704 int res;
1705 gboolean stat_ok;
1706 gboolean is_symlink, symlink_broken;
1707 #ifdef G_OS_WIN32
1708 DWORD dos_attributes;
1709 #endif
1710 char *symlink_target;
1711 GVfs *vfs;
1712 GVfsClass *class;
1713 guint64 device;
1715 info = g_file_info_new ();
1717 /* Make sure we don't set any unwanted attributes */
1718 g_file_info_set_attribute_mask (info, attribute_matcher);
1720 _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
1722 if (attribute_matcher == NULL)
1724 g_file_info_unset_attribute_mask (info);
1725 return info;
1728 #ifndef G_OS_WIN32
1729 res = g_lstat (path, &statbuf);
1730 #else
1732 wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, error);
1733 int len;
1735 if (wpath == NULL)
1737 g_object_unref (info);
1738 return NULL;
1741 len = wcslen (wpath);
1742 while (len > 0 && G_IS_DIR_SEPARATOR (wpath[len-1]))
1743 len--;
1744 if (len > 0 &&
1745 (!g_path_is_absolute (path) || len > g_path_skip_root (path) - path))
1746 wpath[len] = '\0';
1748 res = _wstati64 (wpath, &statbuf);
1749 dos_attributes = GetFileAttributesW (wpath);
1751 g_free (wpath);
1753 #endif
1755 if (res == -1)
1757 int errsv = errno;
1759 /* Don't bail out if we get Permission denied (SELinux?) */
1760 if (errsv != EACCES)
1762 char *display_name = g_filename_display_name (path);
1763 g_object_unref (info);
1764 g_set_error (error, G_IO_ERROR,
1765 g_io_error_from_errno (errsv),
1766 _("Error when getting information for file “%s”: %s"),
1767 display_name, g_strerror (errsv));
1768 g_free (display_name);
1769 return NULL;
1773 /* Even if stat() fails, try to get as much as other attributes possible */
1774 stat_ok = res != -1;
1776 if (stat_ok)
1777 device = statbuf.st_dev;
1778 else
1779 device = 0;
1781 #ifdef S_ISLNK
1782 is_symlink = stat_ok && S_ISLNK (statbuf.st_mode);
1783 #else
1784 is_symlink = FALSE;
1785 #endif
1786 symlink_broken = FALSE;
1787 #ifdef S_ISLNK
1788 if (is_symlink)
1790 g_file_info_set_is_symlink (info, TRUE);
1792 /* Unless NOFOLLOW was set we default to following symlinks */
1793 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
1795 res = stat (path, &statbuf2);
1797 /* Report broken links as symlinks */
1798 if (res != -1)
1800 statbuf = statbuf2;
1801 stat_ok = TRUE;
1803 else
1804 symlink_broken = TRUE;
1807 #endif
1809 if (stat_ok)
1810 set_info_from_stat (info, &statbuf, attribute_matcher);
1812 #ifdef G_OS_UNIX
1813 if (stat_ok && _g_local_file_is_lost_found_dir (path, statbuf.st_dev))
1814 g_file_info_set_is_hidden (info, TRUE);
1815 #endif
1817 #ifndef G_OS_WIN32
1818 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1819 G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
1821 if (basename != NULL &&
1822 (basename[0] == '.' ||
1823 file_is_hidden (path, basename)))
1824 g_file_info_set_is_hidden (info, TRUE);
1827 if (basename != NULL && basename[strlen (basename) -1] == '~' &&
1828 (stat_ok && S_ISREG (statbuf.st_mode)))
1829 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE);
1830 #else
1831 if (dos_attributes & FILE_ATTRIBUTE_HIDDEN)
1832 g_file_info_set_is_hidden (info, TRUE);
1834 if (dos_attributes & FILE_ATTRIBUTE_ARCHIVE)
1835 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE, TRUE);
1837 if (dos_attributes & FILE_ATTRIBUTE_SYSTEM)
1838 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM, TRUE);
1839 #endif
1841 symlink_target = NULL;
1842 #ifdef S_ISLNK
1843 if (is_symlink)
1845 symlink_target = read_link (path);
1846 if (symlink_target &&
1847 _g_file_attribute_matcher_matches_id (attribute_matcher,
1848 G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
1849 g_file_info_set_symlink_target (info, symlink_target);
1851 #endif
1852 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1853 G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
1854 _g_file_attribute_matcher_matches_id (attribute_matcher,
1855 G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
1856 _g_file_attribute_matcher_matches_id (attribute_matcher,
1857 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
1859 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
1861 if (content_type)
1863 g_file_info_set_content_type (info, content_type);
1865 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1866 G_FILE_ATTRIBUTE_ID_STANDARD_ICON)
1867 || _g_file_attribute_matcher_matches_id (attribute_matcher,
1868 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
1870 GIcon *icon;
1872 /* non symbolic icon */
1873 icon = get_icon (path, content_type, FALSE);
1874 if (icon != NULL)
1876 g_file_info_set_icon (info, icon);
1877 g_object_unref (icon);
1880 /* symbolic icon */
1881 icon = get_icon (path, content_type, TRUE);
1882 if (icon != NULL)
1884 g_file_info_set_symbolic_icon (info, icon);
1885 g_object_unref (icon);
1890 g_free (content_type);
1894 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1895 G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
1897 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
1899 if (content_type)
1901 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
1902 g_free (content_type);
1906 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1907 G_FILE_ATTRIBUTE_ID_OWNER_USER))
1909 char *name = NULL;
1911 #ifdef G_OS_WIN32
1912 win32_get_file_user_info (path, NULL, &name, NULL);
1913 #else
1914 if (stat_ok)
1915 name = get_username_from_uid (statbuf.st_uid);
1916 #endif
1917 if (name)
1918 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
1919 g_free (name);
1922 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1923 G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
1925 char *name = NULL;
1926 #ifdef G_OS_WIN32
1927 win32_get_file_user_info (path, NULL, NULL, &name);
1928 #else
1929 if (stat_ok)
1930 name = get_realname_from_uid (statbuf.st_uid);
1931 #endif
1932 if (name)
1933 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
1934 g_free (name);
1937 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1938 G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
1940 char *name = NULL;
1941 #ifdef G_OS_WIN32
1942 win32_get_file_user_info (path, &name, NULL, NULL);
1943 #else
1944 if (stat_ok)
1945 name = get_groupname_from_gid (statbuf.st_gid);
1946 #endif
1947 if (name)
1948 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
1949 g_free (name);
1952 if (stat_ok && parent_info && parent_info->device != 0 &&
1953 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) &&
1954 statbuf.st_dev != parent_info->device)
1955 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE);
1957 if (stat_ok)
1958 get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
1960 #ifdef HAVE_SELINUX
1961 get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
1962 #endif
1963 get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
1964 get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
1966 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1967 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH))
1969 if (stat_ok)
1970 get_thumbnail_attributes (path, info, &statbuf);
1971 else
1972 get_thumbnail_attributes (path, info, NULL);
1975 vfs = g_vfs_get_default ();
1976 class = G_VFS_GET_CLASS (vfs);
1977 if (class->local_file_add_info)
1979 class->local_file_add_info (vfs,
1980 path,
1981 device,
1982 attribute_matcher,
1983 info,
1984 NULL,
1985 &parent_info->extra_data,
1986 &parent_info->free_extra_data);
1989 g_file_info_unset_attribute_mask (info);
1991 g_free (symlink_target);
1993 return info;
1996 GFileInfo *
1997 _g_local_file_info_get_from_fd (int fd,
1998 const char *attributes,
1999 GError **error)
2001 GLocalFileStat stat_buf;
2002 GFileAttributeMatcher *matcher;
2003 GFileInfo *info;
2005 #ifdef G_OS_WIN32
2006 #define FSTAT _fstati64
2007 #else
2008 #define FSTAT fstat
2009 #endif
2011 if (FSTAT (fd, &stat_buf) == -1)
2013 int errsv = errno;
2015 g_set_error (error, G_IO_ERROR,
2016 g_io_error_from_errno (errsv),
2017 _("Error when getting information for file descriptor: %s"),
2018 g_strerror (errsv));
2019 return NULL;
2022 info = g_file_info_new ();
2024 matcher = g_file_attribute_matcher_new (attributes);
2026 /* Make sure we don't set any unwanted attributes */
2027 g_file_info_set_attribute_mask (info, matcher);
2029 set_info_from_stat (info, &stat_buf, matcher);
2031 #ifdef HAVE_SELINUX
2032 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
2033 is_selinux_enabled ())
2035 char *context;
2036 if (fgetfilecon_raw (fd, &context) >= 0)
2038 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
2039 freecon (context);
2042 #endif
2044 get_xattrs_from_fd (fd, TRUE, info, matcher);
2045 get_xattrs_from_fd (fd, FALSE, info, matcher);
2047 g_file_attribute_matcher_unref (matcher);
2049 g_file_info_unset_attribute_mask (info);
2051 return info;
2054 static gboolean
2055 get_uint32 (const GFileAttributeValue *value,
2056 guint32 *val_out,
2057 GError **error)
2059 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
2061 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2062 _("Invalid attribute type (uint32 expected)"));
2063 return FALSE;
2066 *val_out = value->u.uint32;
2068 return TRUE;
2071 #ifdef HAVE_UTIMES
2072 static gboolean
2073 get_uint64 (const GFileAttributeValue *value,
2074 guint64 *val_out,
2075 GError **error)
2077 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
2079 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2080 _("Invalid attribute type (uint64 expected)"));
2081 return FALSE;
2084 *val_out = value->u.uint64;
2086 return TRUE;
2088 #endif
2090 #if defined(HAVE_SYMLINK)
2091 static gboolean
2092 get_byte_string (const GFileAttributeValue *value,
2093 const char **val_out,
2094 GError **error)
2096 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
2098 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2099 _("Invalid attribute type (byte string expected)"));
2100 return FALSE;
2103 *val_out = value->u.string;
2105 return TRUE;
2107 #endif
2109 #ifdef HAVE_SELINUX
2110 static gboolean
2111 get_string (const GFileAttributeValue *value,
2112 const char **val_out,
2113 GError **error)
2115 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
2117 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2118 _("Invalid attribute type (byte string expected)"));
2119 return FALSE;
2122 *val_out = value->u.string;
2124 return TRUE;
2126 #endif
2128 static gboolean
2129 set_unix_mode (char *filename,
2130 GFileQueryInfoFlags flags,
2131 const GFileAttributeValue *value,
2132 GError **error)
2134 guint32 val = 0;
2135 int res = 0;
2137 if (!get_uint32 (value, &val, error))
2138 return FALSE;
2140 #ifdef HAVE_SYMLINK
2141 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
2142 #ifdef HAVE_LCHMOD
2143 res = lchmod (filename, val);
2144 #else
2145 struct stat statbuf;
2146 /* Calling chmod on a symlink changes permissions on the symlink.
2147 * We don't want to do this, so we need to check for a symlink */
2148 res = g_lstat (filename, &statbuf);
2149 if (res == 0 && S_ISLNK (statbuf.st_mode))
2151 g_set_error_literal (error, G_IO_ERROR,
2152 G_IO_ERROR_NOT_SUPPORTED,
2153 _("Cannot set permissions on symlinks"));
2154 return FALSE;
2156 else if (res == 0)
2157 res = g_chmod (filename, val);
2158 #endif
2159 } else
2160 #endif
2161 res = g_chmod (filename, val);
2163 if (res == -1)
2165 int errsv = errno;
2167 g_set_error (error, G_IO_ERROR,
2168 g_io_error_from_errno (errsv),
2169 _("Error setting permissions: %s"),
2170 g_strerror (errsv));
2171 return FALSE;
2173 return TRUE;
2176 #ifdef G_OS_UNIX
2177 static gboolean
2178 set_unix_uid_gid (char *filename,
2179 const GFileAttributeValue *uid_value,
2180 const GFileAttributeValue *gid_value,
2181 GFileQueryInfoFlags flags,
2182 GError **error)
2184 int res;
2185 guint32 val = 0;
2186 uid_t uid;
2187 gid_t gid;
2189 if (uid_value)
2191 if (!get_uint32 (uid_value, &val, error))
2192 return FALSE;
2193 uid = val;
2195 else
2196 uid = -1;
2198 if (gid_value)
2200 if (!get_uint32 (gid_value, &val, error))
2201 return FALSE;
2202 gid = val;
2204 else
2205 gid = -1;
2207 #ifdef HAVE_LCHOWN
2208 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
2209 res = lchown (filename, uid, gid);
2210 else
2211 #endif
2212 res = chown (filename, uid, gid);
2214 if (res == -1)
2216 int errsv = errno;
2218 g_set_error (error, G_IO_ERROR,
2219 g_io_error_from_errno (errsv),
2220 _("Error setting owner: %s"),
2221 g_strerror (errsv));
2222 return FALSE;
2224 return TRUE;
2226 #endif
2228 #ifdef HAVE_SYMLINK
2229 static gboolean
2230 set_symlink (char *filename,
2231 const GFileAttributeValue *value,
2232 GError **error)
2234 const char *val;
2235 struct stat statbuf;
2237 if (!get_byte_string (value, &val, error))
2238 return FALSE;
2240 if (val == NULL)
2242 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2243 _("symlink must be non-NULL"));
2244 return FALSE;
2247 if (g_lstat (filename, &statbuf))
2249 int errsv = errno;
2251 g_set_error (error, G_IO_ERROR,
2252 g_io_error_from_errno (errsv),
2253 _("Error setting symlink: %s"),
2254 g_strerror (errsv));
2255 return FALSE;
2258 if (!S_ISLNK (statbuf.st_mode))
2260 g_set_error_literal (error, G_IO_ERROR,
2261 G_IO_ERROR_NOT_SYMBOLIC_LINK,
2262 _("Error setting symlink: file is not a symlink"));
2263 return FALSE;
2266 if (g_unlink (filename))
2268 int errsv = errno;
2270 g_set_error (error, G_IO_ERROR,
2271 g_io_error_from_errno (errsv),
2272 _("Error setting symlink: %s"),
2273 g_strerror (errsv));
2274 return FALSE;
2277 if (symlink (filename, val) != 0)
2279 int errsv = errno;
2281 g_set_error (error, G_IO_ERROR,
2282 g_io_error_from_errno (errsv),
2283 _("Error setting symlink: %s"),
2284 g_strerror (errsv));
2285 return FALSE;
2288 return TRUE;
2290 #endif
2292 #ifdef HAVE_UTIMES
2293 static int
2294 lazy_stat (char *filename,
2295 struct stat *statbuf,
2296 gboolean *called_stat)
2298 int res;
2300 if (*called_stat)
2301 return 0;
2303 res = g_stat (filename, statbuf);
2305 if (res == 0)
2306 *called_stat = TRUE;
2308 return res;
2312 static gboolean
2313 set_mtime_atime (char *filename,
2314 const GFileAttributeValue *mtime_value,
2315 const GFileAttributeValue *mtime_usec_value,
2316 const GFileAttributeValue *atime_value,
2317 const GFileAttributeValue *atime_usec_value,
2318 GError **error)
2320 int res;
2321 guint64 val = 0;
2322 guint32 val_usec = 0;
2323 struct stat statbuf;
2324 gboolean got_stat = FALSE;
2325 struct timeval times[2] = { {0, 0}, {0, 0} };
2327 /* ATIME */
2328 if (atime_value)
2330 if (!get_uint64 (atime_value, &val, error))
2331 return FALSE;
2332 times[0].tv_sec = val;
2334 else
2336 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2338 times[0].tv_sec = statbuf.st_mtime;
2339 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2340 times[0].tv_usec = statbuf.st_atimensec / 1000;
2341 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2342 times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
2343 #endif
2347 if (atime_usec_value)
2349 if (!get_uint32 (atime_usec_value, &val_usec, error))
2350 return FALSE;
2351 times[0].tv_usec = val_usec;
2354 /* MTIME */
2355 if (mtime_value)
2357 if (!get_uint64 (mtime_value, &val, error))
2358 return FALSE;
2359 times[1].tv_sec = val;
2361 else
2363 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2365 times[1].tv_sec = statbuf.st_mtime;
2366 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2367 times[1].tv_usec = statbuf.st_mtimensec / 1000;
2368 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2369 times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
2370 #endif
2374 if (mtime_usec_value)
2376 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2377 return FALSE;
2378 times[1].tv_usec = val_usec;
2381 res = utimes (filename, times);
2382 if (res == -1)
2384 int errsv = errno;
2386 g_set_error (error, G_IO_ERROR,
2387 g_io_error_from_errno (errsv),
2388 _("Error setting modification or access time: %s"),
2389 g_strerror (errsv));
2390 return FALSE;
2392 return TRUE;
2394 #endif
2397 #ifdef HAVE_SELINUX
2398 static gboolean
2399 set_selinux_context (char *filename,
2400 const GFileAttributeValue *value,
2401 GError **error)
2403 const char *val;
2405 if (!get_string (value, &val, error))
2406 return FALSE;
2408 if (val == NULL)
2410 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2411 _("SELinux context must be non-NULL"));
2412 return FALSE;
2415 if (is_selinux_enabled ()) {
2416 security_context_t val_s;
2418 val_s = g_strdup (val);
2420 if (setfilecon_raw (filename, val_s) < 0)
2422 int errsv = errno;
2424 g_set_error (error, G_IO_ERROR,
2425 g_io_error_from_errno (errsv),
2426 _("Error setting SELinux context: %s"),
2427 g_strerror (errsv));
2428 return FALSE;
2430 g_free (val_s);
2431 } else {
2432 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2433 _("SELinux is not enabled on this system"));
2434 return FALSE;
2437 return TRUE;
2439 #endif
2442 gboolean
2443 _g_local_file_info_set_attribute (char *filename,
2444 const char *attribute,
2445 GFileAttributeType type,
2446 gpointer value_p,
2447 GFileQueryInfoFlags flags,
2448 GCancellable *cancellable,
2449 GError **error)
2451 GFileAttributeValue value = { 0 };
2452 GVfsClass *class;
2453 GVfs *vfs;
2455 _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
2457 if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
2458 return set_unix_mode (filename, flags, &value, error);
2460 #ifdef G_OS_UNIX
2461 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
2462 return set_unix_uid_gid (filename, &value, NULL, flags, error);
2463 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
2464 return set_unix_uid_gid (filename, NULL, &value, flags, error);
2465 #endif
2467 #ifdef HAVE_SYMLINK
2468 else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
2469 return set_symlink (filename, &value, error);
2470 #endif
2472 #ifdef HAVE_UTIMES
2473 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
2474 return set_mtime_atime (filename, &value, NULL, NULL, NULL, error);
2475 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
2476 return set_mtime_atime (filename, NULL, &value, NULL, NULL, error);
2477 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
2478 return set_mtime_atime (filename, NULL, NULL, &value, NULL, error);
2479 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
2480 return set_mtime_atime (filename, NULL, NULL, NULL, &value, error);
2481 #endif
2483 #ifdef HAVE_XATTR
2484 else if (g_str_has_prefix (attribute, "xattr::"))
2485 return set_xattr (filename, attribute, &value, error);
2486 else if (g_str_has_prefix (attribute, "xattr-sys::"))
2487 return set_xattr (filename, attribute, &value, error);
2488 #endif
2490 #ifdef HAVE_SELINUX
2491 else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
2492 return set_selinux_context (filename, &value, error);
2493 #endif
2495 vfs = g_vfs_get_default ();
2496 class = G_VFS_GET_CLASS (vfs);
2497 if (class->local_file_set_attributes)
2499 GFileInfo *info;
2501 info = g_file_info_new ();
2502 g_file_info_set_attribute (info,
2503 attribute,
2504 type,
2505 value_p);
2506 if (!class->local_file_set_attributes (vfs, filename,
2507 info,
2508 flags, cancellable,
2509 error))
2511 g_object_unref (info);
2512 return FALSE;
2515 if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
2517 g_object_unref (info);
2518 return TRUE;
2521 g_object_unref (info);
2524 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
2525 _("Setting attribute %s not supported"), attribute);
2526 return FALSE;
2529 gboolean
2530 _g_local_file_info_set_attributes (char *filename,
2531 GFileInfo *info,
2532 GFileQueryInfoFlags flags,
2533 GCancellable *cancellable,
2534 GError **error)
2536 GFileAttributeValue *value;
2537 #ifdef G_OS_UNIX
2538 GFileAttributeValue *uid, *gid;
2539 #ifdef HAVE_UTIMES
2540 GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
2541 #endif
2542 GFileAttributeStatus status;
2543 #endif
2544 gboolean res;
2545 GVfsClass *class;
2546 GVfs *vfs;
2548 /* Handles setting multiple specified data in a single set, and takes care
2549 of ordering restrictions when setting attributes */
2551 res = TRUE;
2553 /* Set symlink first, since this recreates the file */
2554 #ifdef HAVE_SYMLINK
2555 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
2556 if (value)
2558 if (!set_symlink (filename, value, error))
2560 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2561 res = FALSE;
2562 /* Don't set error multiple times */
2563 error = NULL;
2565 else
2566 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2569 #endif
2571 #ifdef G_OS_UNIX
2572 /* Group uid and gid setting into one call
2573 * Change ownership before permissions, since ownership changes can
2574 change permissions (e.g. setuid)
2576 uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
2577 gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
2579 if (uid || gid)
2581 if (!set_unix_uid_gid (filename, uid, gid, flags, error))
2583 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2584 res = FALSE;
2585 /* Don't set error multiple times */
2586 error = NULL;
2588 else
2589 status = G_FILE_ATTRIBUTE_STATUS_SET;
2590 if (uid)
2591 uid->status = status;
2592 if (gid)
2593 gid->status = status;
2595 #endif
2597 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
2598 if (value)
2600 if (!set_unix_mode (filename, flags, value, error))
2602 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2603 res = FALSE;
2604 /* Don't set error multiple times */
2605 error = NULL;
2607 else
2608 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2612 #ifdef HAVE_UTIMES
2613 /* Group all time settings into one call
2614 * Change times as the last thing to avoid it changing due to metadata changes
2617 mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2618 mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
2619 atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2620 atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
2622 if (mtime || mtime_usec || atime || atime_usec)
2624 if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error))
2626 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2627 res = FALSE;
2628 /* Don't set error multiple times */
2629 error = NULL;
2631 else
2632 status = G_FILE_ATTRIBUTE_STATUS_SET;
2634 if (mtime)
2635 mtime->status = status;
2636 if (mtime_usec)
2637 mtime_usec->status = status;
2638 if (atime)
2639 atime->status = status;
2640 if (atime_usec)
2641 atime_usec->status = status;
2643 #endif
2645 /* xattrs are handled by default callback */
2648 /* SELinux context */
2649 #ifdef HAVE_SELINUX
2650 if (is_selinux_enabled ()) {
2651 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
2652 if (value)
2654 if (!set_selinux_context (filename, value, error))
2656 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2657 res = FALSE;
2658 /* Don't set error multiple times */
2659 error = NULL;
2661 else
2662 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2665 #endif
2667 vfs = g_vfs_get_default ();
2668 class = G_VFS_GET_CLASS (vfs);
2669 if (class->local_file_set_attributes)
2671 if (!class->local_file_set_attributes (vfs, filename,
2672 info,
2673 flags, cancellable,
2674 error))
2676 res = FALSE;
2677 /* Don't set error multiple times */
2678 error = NULL;
2682 return res;