1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
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, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
22 * Author: Alexander Larsson <alexl@redhat.com>
27 #ifdef HAVE_SYS_TIME_H
30 #include <sys/types.h>
46 #include <selinux/selinux.h>
51 #if defined HAVE_SYS_XATTR_H
52 #include <sys/xattr.h>
53 #elif defined HAVE_ATTR_XATTR_H
54 #include <attr/xattr.h>
56 #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
57 #endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
59 #endif /* HAVE_XATTR */
61 #include <glib/gstdio.h>
62 #include <gfileattribute-priv.h>
63 #include <gfileinfo-priv.h>
78 #define X_OK 0 /* not really */
81 #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
84 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
87 #define S_IXUSR _S_IEXEC
91 #include "glocalfileinfo.h"
93 #include "gthemedicon.h"
94 #include "gcontenttype.h"
95 #include "gcontenttypeprivate.h"
101 #define _g_stat_struct stat
104 struct ThumbMD5Context {
107 unsigned char in[64];
117 G_LOCK_DEFINE_STATIC (uid_cache);
118 static GHashTable *uid_cache = NULL;
120 G_LOCK_DEFINE_STATIC (gid_cache);
121 static GHashTable *gid_cache = NULL;
123 #endif /* !G_OS_WIN32 */
126 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
130 tv.tv_sec = statbuf->st_mtime;
131 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
132 tv.tv_usec = statbuf->st_mtimensec / 1000;
133 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
134 tv.tv_usec = statbuf->st_mtim.tv_nsec / 1000;
139 return g_strdup_printf ("%lu:%lu", tv.tv_sec, tv.tv_usec);
143 _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
145 return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
146 (guint64) statbuf->st_dev,
147 (guint64) statbuf->st_ino);
151 _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
153 return g_strdup_printf ("l%" G_GUINT64_FORMAT,
154 (guint64) statbuf->st_dev);
161 read_link (const gchar *full_name)
168 buffer = g_malloc (size);
174 read_size = readlink (full_name, buffer, size);
180 if (read_size < size)
182 buffer[read_size] = 0;
186 buffer = g_realloc (buffer, size);
196 /* Get the SELinux security context */
198 get_selinux_context (const char *path,
200 GFileAttributeMatcher *attribute_matcher,
201 gboolean follow_symlinks)
205 if (!_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
208 if (is_selinux_enabled ())
212 if (lgetfilecon_raw (path, &context) < 0)
217 if (getfilecon_raw (path, &context) < 0)
223 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
232 /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
233 * (Mac) getxattr(..., XATTR_NOFOLLOW)
235 #ifdef HAVE_XATTR_NOFOLLOW
236 #define g_fgetxattr(fd,name,value,size) fgetxattr(fd,name,value,size,0,0)
237 #define g_flistxattr(fd,name,size) flistxattr(fd,name,size,0)
238 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
240 #define g_fgetxattr fgetxattr
241 #define g_flistxattr flistxattr
242 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
246 g_getxattr (const char *path, const char *name, void *value, size_t size,
247 gboolean follow_symlinks)
249 #ifdef HAVE_XATTR_NOFOLLOW
250 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
253 return getxattr (path, name, value, size);
255 return lgetxattr (path, name, value, size);
260 g_listxattr(const char *path, char *namebuf, size_t size,
261 gboolean follow_symlinks)
263 #ifdef HAVE_XATTR_NOFOLLOW
264 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
267 return listxattr (path, namebuf, size);
269 return llistxattr (path, namebuf, size);
276 return c >= 32 && c <= 126 && c != '\\';
280 name_is_valid (const char *str)
284 if (!valid_char (*str++))
291 hex_escape_string (const char *str,
292 gboolean *free_return)
295 char *escaped_str, *p;
297 static char *hex_digits = "0123456789abcdef";
303 for (i = 0; i < len; i++)
305 if (!valid_char (str[i]))
309 if (num_invalid == 0)
311 *free_return = FALSE;
315 escaped_str = g_malloc (len + num_invalid*3 + 1);
318 for (i = 0; i < len; i++)
320 if (valid_char (str[i]))
327 *p++ = hex_digits[(c >> 4) & 0xf];
328 *p++ = hex_digits[c & 0xf];
338 hex_unescape_string (const char *str,
340 gboolean *free_return)
343 char *unescaped_str, *p;
349 if (strchr (str, '\\') == NULL)
353 *free_return = FALSE;
357 unescaped_str = g_malloc (len + 1);
360 for (i = 0; i < len; i++)
362 if (str[i] == '\\' &&
367 (g_ascii_xdigit_value (str[i+2]) << 4) |
368 g_ascii_xdigit_value (str[i+3]);
378 *out_len = p - unescaped_str;
380 return unescaped_str;
384 escape_xattr (GFileInfo *info,
385 const char *gio_attr, /* gio attribute name */
386 const char *value, /* Is zero terminated */
387 size_t len /* not including zero termination */)
390 gboolean free_escaped_val;
392 escaped_val = hex_escape_string (value, &free_escaped_val);
394 g_file_info_set_attribute_string (info, gio_attr, escaped_val);
396 if (free_escaped_val)
397 g_free (escaped_val);
401 get_one_xattr (const char *path,
403 const char *gio_attr,
405 gboolean follow_symlinks)
411 len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
416 else if (len == -1 && errno == ERANGE)
418 len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
423 value_p = g_malloc (len+1);
425 len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
439 escape_xattr (info, gio_attr, value_p, len);
441 if (value_p != value)
445 #endif /* defined HAVE_XATTR */
448 get_xattrs (const char *path,
451 GFileAttributeMatcher *matcher,
452 gboolean follow_symlinks)
457 ssize_t list_res_size;
460 const char *attr, *attr2;
463 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
465 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
469 list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
471 if (list_res_size == -1 ||
475 list_size = list_res_size;
476 list = g_malloc (list_size);
480 list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
482 if (list_res_size == -1 && errno == ERANGE)
484 list_size = list_size * 2;
485 list = g_realloc (list, list_size);
489 if (list_res_size == -1)
493 while (list_res_size > 0)
495 if ((user && g_str_has_prefix (attr, "user.")) ||
496 (!user && !g_str_has_prefix (attr, "user.")))
498 char *escaped_attr, *gio_attr;
499 gboolean free_escaped_attr;
503 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
504 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
508 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
509 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
512 if (free_escaped_attr)
513 g_free (escaped_attr);
515 get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
520 len = strlen (attr) + 1;
522 list_res_size -= len;
529 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
531 char *unescaped_attribute, *a;
532 gboolean free_unescaped_attribute;
534 attr2 = strchr (attr, ':');
537 attr2 += 2; /* Skip '::' */
538 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
540 a = g_strconcat ("user.", unescaped_attribute, NULL);
542 a = unescaped_attribute;
544 get_one_xattr (path, info, attr, a, follow_symlinks);
549 if (free_unescaped_attribute)
550 g_free (unescaped_attribute);
554 #endif /* defined HAVE_XATTR */
559 get_one_xattr_from_fd (int fd,
561 const char *gio_attr,
568 len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
573 else if (len == -1 && errno == ERANGE)
575 len = g_fgetxattr (fd, xattr, NULL, 0);
580 value_p = g_malloc (len + 1);
582 len = g_fgetxattr (fd, xattr, value_p, len);
596 escape_xattr (info, gio_attr, value_p, len);
598 if (value_p != value)
601 #endif /* defined HAVE_XATTR */
604 get_xattrs_from_fd (int fd,
607 GFileAttributeMatcher *matcher)
612 ssize_t list_res_size;
615 const char *attr, *attr2;
618 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
620 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
624 list_res_size = g_flistxattr (fd, NULL, 0);
626 if (list_res_size == -1 ||
630 list_size = list_res_size;
631 list = g_malloc (list_size);
635 list_res_size = g_flistxattr (fd, list, list_size);
637 if (list_res_size == -1 && errno == ERANGE)
639 list_size = list_size * 2;
640 list = g_realloc (list, list_size);
644 if (list_res_size == -1)
648 while (list_res_size > 0)
650 if ((user && g_str_has_prefix (attr, "user.")) ||
651 (!user && !g_str_has_prefix (attr, "user.")))
653 char *escaped_attr, *gio_attr;
654 gboolean free_escaped_attr;
658 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
659 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
663 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
664 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
667 if (free_escaped_attr)
668 g_free (escaped_attr);
670 get_one_xattr_from_fd (fd, info, gio_attr, attr);
673 len = strlen (attr) + 1;
675 list_res_size -= len;
682 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
684 char *unescaped_attribute, *a;
685 gboolean free_unescaped_attribute;
687 attr2 = strchr (attr, ':');
690 attr2++; /* Skip ':' */
691 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
693 a = g_strconcat ("user.", unescaped_attribute, NULL);
695 a = unescaped_attribute;
697 get_one_xattr_from_fd (fd, info, attr, a);
702 if (free_unescaped_attribute)
703 g_free (unescaped_attribute);
707 #endif /* defined HAVE_XATTR */
712 set_xattr (char *filename,
713 const char *escaped_attribute,
714 const GFileAttributeValue *attr_value,
717 char *attribute, *value;
718 gboolean free_attribute, free_value;
719 int val_len, res, errsv;
723 if (attr_value == NULL)
725 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
726 _("Attribute value must be non-NULL"));
730 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
732 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
733 _("Invalid attribute type (string expected)"));
737 if (!name_is_valid (escaped_attribute))
739 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
740 _("Invalid extended attribute name"));
744 if (g_str_has_prefix (escaped_attribute, "xattr::"))
746 escaped_attribute += strlen ("xattr::");
751 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
752 escaped_attribute += strlen ("xattr-sys::");
756 attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
757 value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
760 a = g_strconcat ("user.", attribute, NULL);
764 res = g_setxattr (filename, a, value, val_len);
778 g_set_error (error, G_IO_ERROR,
779 g_io_error_from_errno (errsv),
780 _("Error setting extended attribute '%s': %s"),
781 escaped_attribute, g_strerror (errsv));
792 _g_local_file_info_get_parent_info (const char *dir,
793 GFileAttributeMatcher *attribute_matcher,
794 GLocalParentFileInfo *parent_info)
796 struct _g_stat_struct statbuf;
799 parent_info->extra_data = NULL;
800 parent_info->free_extra_data = NULL;
801 parent_info->writable = FALSE;
802 parent_info->is_sticky = FALSE;
803 parent_info->has_trash_dir = FALSE;
804 parent_info->device = 0;
806 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) ||
807 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) ||
808 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH) ||
809 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
811 /* FIXME: Windows: The underlying _waccess() call in the C
812 * library is mostly pointless as it only looks at the READONLY
813 * FAT-style attribute of the file, it doesn't check the ACL at
816 parent_info->writable = (g_access (dir, W_OK) == 0);
818 res = g_stat (dir, &statbuf);
821 * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
822 * renamed or deleted only by the owner of the file, by the owner of the directory, and
823 * by a privileged process.
828 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
830 parent_info->is_sticky = FALSE;
832 parent_info->owner = statbuf.st_uid;
833 parent_info->device = statbuf.st_dev;
834 /* No need to find trash dir if it's not writable anyway */
835 if (parent_info->writable &&
836 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
837 parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev);
843 _g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
845 if (parent_info->extra_data &&
846 parent_info->free_extra_data)
847 parent_info->free_extra_data (parent_info->extra_data);
851 get_access_rights (GFileAttributeMatcher *attribute_matcher,
854 GLocalFileStat *statbuf,
855 GLocalParentFileInfo *parent_info)
857 /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
858 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
859 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ))
860 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ,
861 g_access (path, R_OK) == 0);
863 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
864 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE))
865 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE,
866 g_access (path, W_OK) == 0);
868 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
869 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE))
870 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE,
871 g_access (path, X_OK) == 0);
879 if (parent_info->writable)
881 if (parent_info->is_sticky)
884 uid_t uid = geteuid ();
886 if (uid == statbuf->st_uid ||
887 uid == parent_info->owner ||
896 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME))
897 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME,
900 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE))
901 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
904 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
905 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
906 writable && parent_info->has_trash_dir);
911 set_info_from_stat (GFileInfo *info,
912 GLocalFileStat *statbuf,
913 GFileAttributeMatcher *attribute_matcher)
917 file_type = G_FILE_TYPE_UNKNOWN;
919 if (S_ISREG (statbuf->st_mode))
920 file_type = G_FILE_TYPE_REGULAR;
921 else if (S_ISDIR (statbuf->st_mode))
922 file_type = G_FILE_TYPE_DIRECTORY;
924 else if (S_ISCHR (statbuf->st_mode) ||
925 S_ISBLK (statbuf->st_mode) ||
926 S_ISFIFO (statbuf->st_mode)
928 || S_ISSOCK (statbuf->st_mode)
931 file_type = G_FILE_TYPE_SPECIAL;
934 else if (S_ISLNK (statbuf->st_mode))
935 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
938 g_file_info_set_file_type (info, file_type);
939 g_file_info_set_size (info, statbuf->st_size);
941 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, statbuf->st_dev);
943 /* Pointless setting these on Windows even if they exist in the struct */
944 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, statbuf->st_ino);
945 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, statbuf->st_nlink);
946 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, statbuf->st_uid);
947 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, statbuf->st_gid);
948 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, statbuf->st_rdev);
950 /* FIXME: st_mode is mostly pointless on Windows, too. Set the attribute or not? */
951 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, statbuf->st_mode);
952 #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
953 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, statbuf->st_blksize);
955 #if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
956 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, statbuf->st_blocks);
957 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
958 statbuf->st_blocks * G_GUINT64_CONSTANT (512));
961 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtime);
962 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
963 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000);
964 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
965 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000);
968 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atime);
969 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
970 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
971 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
972 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
975 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, statbuf->st_ctime);
976 #if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
977 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
978 #elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
979 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000);
982 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
983 G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
985 char *etag = _g_local_file_info_create_etag (statbuf);
986 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ETAG_VALUE, etag);
990 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
991 G_FILE_ATTRIBUTE_ID_ID_FILE))
993 char *id = _g_local_file_info_create_file_id (statbuf);
994 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILE, id);
998 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
999 G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
1001 char *id = _g_local_file_info_create_fs_id (statbuf);
1002 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM, id);
1010 make_valid_utf8 (const char *name)
1013 const gchar *remainder, *invalid;
1014 gint remaining_bytes, valid_bytes;
1018 remaining_bytes = strlen (name);
1020 while (remaining_bytes != 0)
1022 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
1024 valid_bytes = invalid - remainder;
1027 string = g_string_sized_new (remaining_bytes);
1029 g_string_append_len (string, remainder, valid_bytes);
1030 /* append U+FFFD REPLACEMENT CHARACTER */
1031 g_string_append (string, "\357\277\275");
1033 remaining_bytes -= valid_bytes + 1;
1034 remainder = invalid + 1;
1038 return g_strdup (name);
1040 g_string_append (string, remainder);
1042 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
1044 return g_string_free (string, FALSE);
1048 convert_pwd_string_to_utf8 (char *pwd_str)
1052 if (!g_utf8_validate (pwd_str, -1, NULL))
1054 utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
1055 if (utf8_string == NULL)
1056 utf8_string = make_valid_utf8 (pwd_str);
1059 utf8_string = g_strdup (pwd_str);
1065 uid_data_free (UidData *data)
1067 g_free (data->user_name);
1068 g_free (data->real_name);
1072 /* called with lock held */
1074 lookup_uid_data (uid_t uid)
1078 struct passwd pwbuf;
1079 struct passwd *pwbufp;
1080 char *gecos, *comma;
1082 if (uid_cache == NULL)
1083 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
1085 data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
1090 data = g_new0 (UidData, 1);
1092 #if defined(HAVE_POSIX_GETPWUID_R)
1093 getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
1094 #elif defined(HAVE_NONPOSIX_GETPWUID_R)
1095 pwbufp = getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer));
1097 pwbufp = getpwuid (uid);
1102 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
1103 data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
1105 gecos = pwbufp->pw_gecos;
1109 comma = strchr (gecos, ',');
1112 data->real_name = convert_pwd_string_to_utf8 (gecos);
1116 /* Default fallbacks */
1117 if (data->real_name == NULL)
1119 if (data->user_name != NULL)
1120 data->real_name = g_strdup (data->user_name);
1122 data->real_name = g_strdup_printf ("user #%d", (int)uid);
1125 if (data->user_name == NULL)
1126 data->user_name = g_strdup_printf ("%d", (int)uid);
1128 g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
1134 get_username_from_uid (uid_t uid)
1140 data = lookup_uid_data (uid);
1141 res = g_strdup (data->user_name);
1142 G_UNLOCK (uid_cache);
1148 get_realname_from_uid (uid_t uid)
1154 data = lookup_uid_data (uid);
1155 res = g_strdup (data->real_name);
1156 G_UNLOCK (uid_cache);
1161 /* called with lock held */
1163 lookup_gid_name (gid_t gid)
1168 struct group *gbufp;
1170 if (gid_cache == NULL)
1171 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
1173 name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
1178 #if defined (HAVE_POSIX_GETGRGID_R)
1179 getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
1180 #elif defined (HAVE_NONPOSIX_GETGRGID_R)
1181 gbufp = getgrgid_r (gid, &gbuf, buffer, sizeof(buffer));
1183 gbufp = getgrgid (gid);
1186 if (gbufp != NULL &&
1187 gbufp->gr_name != NULL &&
1188 gbufp->gr_name[0] != 0)
1189 name = convert_pwd_string_to_utf8 (gbufp->gr_name);
1191 name = g_strdup_printf("%d", (int)gid);
1193 g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
1199 get_groupname_from_gid (gid_t gid)
1205 name = lookup_gid_name (gid);
1206 res = g_strdup (name);
1207 G_UNLOCK (gid_cache);
1211 #endif /* !G_OS_WIN32 */
1214 get_content_type (const char *basename,
1216 GLocalFileStat *statbuf,
1217 gboolean is_symlink,
1218 gboolean symlink_broken,
1219 GFileQueryInfoFlags flags,
1223 (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
1224 return g_strdup ("inode/symlink");
1225 else if (S_ISDIR(statbuf->st_mode))
1226 return g_strdup ("inode/directory");
1228 else if (S_ISCHR(statbuf->st_mode))
1229 return g_strdup ("inode/chardevice");
1230 else if (S_ISBLK(statbuf->st_mode))
1231 return g_strdup ("inode/blockdevice");
1232 else if (S_ISFIFO(statbuf->st_mode))
1233 return g_strdup ("inode/fifo");
1236 else if (S_ISSOCK(statbuf->st_mode))
1237 return g_strdup ("inode/socket");
1242 gboolean result_uncertain;
1244 content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
1247 if (!fast && result_uncertain && path != NULL)
1249 guchar sniff_buffer[4096];
1253 sniff_length = _g_unix_content_type_get_sniff_len ();
1254 if (sniff_length > 4096)
1255 sniff_length = 4096;
1258 fd = open (path, O_RDONLY | O_NOATIME);
1259 if (fd < 0 && errno == EPERM)
1261 fd = open (path, O_RDONLY);
1267 res = read (fd, sniff_buffer, sniff_length);
1271 g_free (content_type);
1272 content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
1278 return content_type;
1284 get_thumbnail_attributes (const char *path,
1287 GChecksum *checksum;
1292 uri = g_filename_to_uri (path, NULL, NULL);
1294 checksum = g_checksum_new (G_CHECKSUM_MD5);
1295 g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
1299 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
1300 g_checksum_free (checksum);
1302 filename = g_build_filename (g_get_home_dir (),
1303 ".thumbnails", "normal", basename,
1306 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1307 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
1311 filename = g_build_filename (g_get_home_dir (),
1312 ".thumbnails", "fail",
1313 "gnome-thumbnail-factory",
1317 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1318 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
1326 win32_get_file_user_info (const gchar *filename,
1331 PSECURITY_DESCRIPTOR psd = NULL;
1332 DWORD sd_size = 0; /* first call calculates the size required */
1334 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1335 if ((GetFileSecurityW (wfilename,
1336 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1339 &sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
1340 (psd = g_try_malloc (sd_size)) != NULL &&
1341 GetFileSecurityW (wfilename,
1342 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1349 SID_NAME_USE name_use = 0; /* don't care? */
1350 wchar_t *name = NULL;
1351 wchar_t *domain = NULL;
1353 DWORD domain_len = 0;
1354 /* get the user name */
1358 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
1360 if (!LookupAccountSidW (NULL, /* local machine */
1363 domain, &domain_len, /* no domain info yet */
1364 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1366 name = g_try_malloc (name_len * sizeof (wchar_t));
1367 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1368 if (name && domain &&
1369 LookupAccountSidW (NULL, /* local machine */
1372 domain, &domain_len, /* no domain info yet */
1375 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1381 /* get the group name */
1385 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
1387 if (!LookupAccountSidW (NULL, /* local machine */
1390 domain, &domain_len, /* no domain info yet */
1391 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1393 name = g_try_malloc (name_len * sizeof (wchar_t));
1394 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1395 if (name && domain &&
1396 LookupAccountSidW (NULL, /* local machine */
1399 domain, &domain_len, /* no domain info yet */
1402 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1408 /* TODO: get real name */
1414 #endif /* G_OS_WIN32 */
1417 _g_local_file_info_get (const char *basename,
1419 GFileAttributeMatcher *attribute_matcher,
1420 GFileQueryInfoFlags flags,
1421 GLocalParentFileInfo *parent_info,
1425 GLocalFileStat statbuf;
1427 struct stat statbuf2;
1430 gboolean is_symlink, symlink_broken;
1432 DWORD dos_attributes;
1434 char *symlink_target;
1439 info = g_file_info_new ();
1441 /* Make sure we don't set any unwanted attributes */
1442 g_file_info_set_attribute_mask (info, attribute_matcher);
1444 g_file_info_set_name (info, basename);
1446 /* Avoid stat in trivial case */
1447 if (attribute_matcher == NULL)
1451 res = g_lstat (path, &statbuf);
1454 wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, error);
1459 g_object_unref (info);
1463 len = wcslen (wpath);
1464 while (len > 0 && G_IS_DIR_SEPARATOR (wpath[len-1]))
1467 (!g_path_is_absolute (path) || len > g_path_skip_root (path) - path))
1470 res = _wstati64 (wpath, &statbuf);
1471 dos_attributes = GetFileAttributesW (wpath);
1480 char *display_name = g_filename_display_name (path);
1481 g_object_unref (info);
1482 g_set_error (error, G_IO_ERROR,
1483 g_io_error_from_errno (errsv),
1484 _("Error stating file '%s': %s"),
1485 display_name, g_strerror (errsv));
1486 g_free (display_name);
1490 device = statbuf.st_dev;
1493 is_symlink = S_ISLNK (statbuf.st_mode);
1497 symlink_broken = FALSE;
1501 g_file_info_set_is_symlink (info, TRUE);
1503 /* Unless NOFOLLOW was set we default to following symlinks */
1504 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
1506 res = stat (path, &statbuf2);
1508 /* Report broken links as symlinks */
1512 symlink_broken = TRUE;
1517 set_info_from_stat (info, &statbuf, attribute_matcher);
1520 if (basename != NULL && basename[0] == '.')
1521 g_file_info_set_is_hidden (info, TRUE);
1523 if (basename != NULL && basename[strlen (basename) -1] == '~' &&
1524 S_ISREG (statbuf.st_mode))
1525 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE);
1527 if (dos_attributes & FILE_ATTRIBUTE_HIDDEN)
1528 g_file_info_set_is_hidden (info, TRUE);
1530 if (dos_attributes & FILE_ATTRIBUTE_ARCHIVE)
1531 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE, TRUE);
1533 if (dos_attributes & FILE_ATTRIBUTE_SYSTEM)
1534 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM, TRUE);
1537 symlink_target = NULL;
1541 symlink_target = read_link (path);
1542 if (symlink_target &&
1543 _g_file_attribute_matcher_matches_id (attribute_matcher,
1544 G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
1545 g_file_info_set_symlink_target (info, symlink_target);
1548 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1549 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
1551 char *display_name = g_filename_display_basename (path);
1553 /* look for U+FFFD REPLACEMENT CHARACTER */
1554 if (strstr (display_name, "\357\277\275") != NULL)
1556 char *p = display_name;
1557 display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1560 g_file_info_set_display_name (info, display_name);
1561 g_free (display_name);
1564 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1565 G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
1567 char *edit_name = g_filename_display_basename (path);
1568 g_file_info_set_edit_name (info, edit_name);
1573 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1574 G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
1576 char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1578 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
1582 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1583 G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
1584 _g_file_attribute_matcher_matches_id (attribute_matcher,
1585 G_FILE_ATTRIBUTE_ID_STANDARD_ICON))
1587 char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, FALSE);
1591 g_file_info_set_content_type (info, content_type);
1593 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1594 G_FILE_ATTRIBUTE_ID_STANDARD_ICON))
1598 if (strcmp (path, g_get_home_dir ()) == 0)
1599 icon = g_themed_icon_new ("user-home");
1600 else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
1601 icon = g_themed_icon_new ("user-desktop");
1602 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
1603 icon = g_themed_icon_new_with_default_fallbacks ("folder-documents");
1604 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
1605 icon = g_themed_icon_new_with_default_fallbacks ("folder-download");
1606 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
1607 icon = g_themed_icon_new_with_default_fallbacks ("folder-music");
1608 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
1609 icon = g_themed_icon_new_with_default_fallbacks ("folder-pictures");
1610 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
1611 icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare");
1612 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
1613 icon = g_themed_icon_new_with_default_fallbacks ("folder-templates");
1614 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
1615 icon = g_themed_icon_new_with_default_fallbacks ("folder-videos");
1618 icon = g_content_type_get_icon (content_type);
1619 if (G_IS_THEMED_ICON (icon))
1621 const char *type_icon = NULL;
1623 if (S_ISDIR (statbuf.st_mode))
1624 type_icon = "folder";
1626 g_themed_icon_append_name (G_THEMED_ICON (icon), type_icon);
1632 g_file_info_set_icon (info, icon);
1633 g_object_unref (icon);
1637 g_free (content_type);
1641 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1642 G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
1644 char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, TRUE);
1648 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
1649 g_free (content_type);
1653 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1654 G_FILE_ATTRIBUTE_ID_OWNER_USER))
1659 win32_get_file_user_info (path, NULL, &name, NULL);
1661 name = get_username_from_uid (statbuf.st_uid);
1664 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
1668 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1669 G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
1673 win32_get_file_user_info (path, NULL, NULL, &name);
1675 name = get_realname_from_uid (statbuf.st_uid);
1678 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
1682 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1683 G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
1687 win32_get_file_user_info (path, &name, NULL, NULL);
1689 name = get_groupname_from_gid (statbuf.st_gid);
1692 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
1696 if (parent_info && parent_info->device != 0 &&
1697 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) &&
1698 statbuf.st_dev != parent_info->device)
1699 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE);
1701 get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
1704 get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
1706 get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
1707 get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
1709 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1710 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH))
1711 get_thumbnail_attributes (path, info);
1713 vfs = g_vfs_get_default ();
1714 class = G_VFS_GET_CLASS (vfs);
1715 if (class->local_file_add_info)
1717 class->local_file_add_info (vfs,
1723 &parent_info->extra_data,
1724 &parent_info->free_extra_data);
1727 g_file_info_unset_attribute_mask (info);
1729 g_free (symlink_target);
1735 _g_local_file_info_get_from_fd (int fd,
1736 const char *attributes,
1739 GLocalFileStat stat_buf;
1740 GFileAttributeMatcher *matcher;
1744 #define FSTAT _fstati64
1749 if (FSTAT (fd, &stat_buf) == -1)
1753 g_set_error (error, G_IO_ERROR,
1754 g_io_error_from_errno (errsv),
1755 _("Error stating file descriptor: %s"),
1756 g_strerror (errsv));
1760 info = g_file_info_new ();
1762 matcher = g_file_attribute_matcher_new (attributes);
1764 /* Make sure we don't set any unwanted attributes */
1765 g_file_info_set_attribute_mask (info, matcher);
1767 set_info_from_stat (info, &stat_buf, matcher);
1770 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
1771 is_selinux_enabled ())
1774 if (fgetfilecon_raw (fd, &context) >= 0)
1776 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
1782 get_xattrs_from_fd (fd, TRUE, info, matcher);
1783 get_xattrs_from_fd (fd, FALSE, info, matcher);
1785 g_file_attribute_matcher_unref (matcher);
1787 g_file_info_unset_attribute_mask (info);
1793 get_uint32 (const GFileAttributeValue *value,
1797 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
1799 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1800 _("Invalid attribute type (uint32 expected)"));
1804 *val_out = value->u.uint32;
1811 get_uint64 (const GFileAttributeValue *value,
1815 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
1817 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1818 _("Invalid attribute type (uint64 expected)"));
1822 *val_out = value->u.uint64;
1828 #if defined(HAVE_SYMLINK)
1830 get_byte_string (const GFileAttributeValue *value,
1831 const char **val_out,
1834 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
1836 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1837 _("Invalid attribute type (byte string expected)"));
1841 *val_out = value->u.string;
1849 get_string (const GFileAttributeValue *value,
1850 const char **val_out,
1853 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
1855 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1856 _("Invalid attribute type (byte string expected)"));
1860 *val_out = value->u.string;
1867 set_unix_mode (char *filename,
1868 GFileQueryInfoFlags flags,
1869 const GFileAttributeValue *value,
1875 if (!get_uint32 (value, &val, error))
1879 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
1881 res = lchmod (filename, val);
1883 struct stat statbuf;
1884 /* Calling chmod on a symlink changes permissions on the symlink.
1885 * We don't want to do this, so we need to check for a symlink */
1886 res = g_lstat (filename, &statbuf);
1887 if (res == 0 && S_ISLNK (statbuf.st_mode))
1889 g_set_error_literal (error, G_IO_ERROR,
1890 G_IO_ERROR_NOT_SUPPORTED,
1891 _("Cannot set permissions on symlinks"));
1895 res = g_chmod (filename, val);
1899 res = g_chmod (filename, val);
1905 g_set_error (error, G_IO_ERROR,
1906 g_io_error_from_errno (errsv),
1907 _("Error setting permissions: %s"),
1908 g_strerror (errsv));
1916 set_unix_uid_gid (char *filename,
1917 const GFileAttributeValue *uid_value,
1918 const GFileAttributeValue *gid_value,
1919 GFileQueryInfoFlags flags,
1929 if (!get_uint32 (uid_value, &val, error))
1938 if (!get_uint32 (gid_value, &val, error))
1946 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
1947 res = lchown (filename, uid, gid);
1950 res = chown (filename, uid, gid);
1956 g_set_error (error, G_IO_ERROR,
1957 g_io_error_from_errno (errsv),
1958 _("Error setting owner: %s"),
1959 g_strerror (errsv));
1968 set_symlink (char *filename,
1969 const GFileAttributeValue *value,
1973 struct stat statbuf;
1975 if (!get_byte_string (value, &val, error))
1980 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1981 _("symlink must be non-NULL"));
1985 if (g_lstat (filename, &statbuf))
1989 g_set_error (error, G_IO_ERROR,
1990 g_io_error_from_errno (errsv),
1991 _("Error setting symlink: %s"),
1992 g_strerror (errsv));
1996 if (!S_ISLNK (statbuf.st_mode))
1998 g_set_error_literal (error, G_IO_ERROR,
1999 G_IO_ERROR_NOT_SYMBOLIC_LINK,
2000 _("Error setting symlink: file is not a symlink"));
2004 if (g_unlink (filename))
2008 g_set_error (error, G_IO_ERROR,
2009 g_io_error_from_errno (errsv),
2010 _("Error setting symlink: %s"),
2011 g_strerror (errsv));
2015 if (symlink (filename, val) != 0)
2019 g_set_error (error, G_IO_ERROR,
2020 g_io_error_from_errno (errsv),
2021 _("Error setting symlink: %s"),
2022 g_strerror (errsv));
2032 lazy_stat (char *filename,
2033 struct stat *statbuf,
2034 gboolean *called_stat)
2041 res = g_stat (filename, statbuf);
2044 *called_stat = TRUE;
2051 set_mtime_atime (char *filename,
2052 const GFileAttributeValue *mtime_value,
2053 const GFileAttributeValue *mtime_usec_value,
2054 const GFileAttributeValue *atime_value,
2055 const GFileAttributeValue *atime_usec_value,
2061 struct stat statbuf;
2062 gboolean got_stat = FALSE;
2063 struct timeval times[2] = { {0, 0}, {0, 0} };
2068 if (!get_uint64 (atime_value, &val, error))
2070 times[0].tv_sec = val;
2074 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2076 times[0].tv_sec = statbuf.st_mtime;
2077 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2078 times[0].tv_usec = statbuf.st_atimensec / 1000;
2079 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2080 times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
2085 if (atime_usec_value)
2087 if (!get_uint32 (atime_usec_value, &val_usec, error))
2089 times[0].tv_usec = val_usec;
2095 if (!get_uint64 (mtime_value, &val, error))
2097 times[1].tv_sec = val;
2101 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2103 times[1].tv_sec = statbuf.st_mtime;
2104 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2105 times[1].tv_usec = statbuf.st_mtimensec / 1000;
2106 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2107 times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
2112 if (mtime_usec_value)
2114 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2116 times[1].tv_usec = val_usec;
2119 res = utimes (filename, times);
2124 g_set_error (error, G_IO_ERROR,
2125 g_io_error_from_errno (errsv),
2126 _("Error setting modification or access time: %s"),
2127 g_strerror (errsv));
2137 set_selinux_context (char *filename,
2138 const GFileAttributeValue *value,
2143 if (!get_string (value, &val, error))
2148 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2149 _("SELinux context must be non-NULL"));
2153 if (is_selinux_enabled ()) {
2154 security_context_t val_s;
2156 val_s = g_strdup (val);
2158 if (setfilecon_raw (filename, val_s) < 0)
2162 g_set_error (error, G_IO_ERROR,
2163 g_io_error_from_errno (errsv),
2164 _("Error setting SELinux context: %s"),
2165 g_strerror (errsv));
2170 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2171 _("SELinux is not enabled on this system"));
2181 _g_local_file_info_set_attribute (char *filename,
2182 const char *attribute,
2183 GFileAttributeType type,
2185 GFileQueryInfoFlags flags,
2186 GCancellable *cancellable,
2189 GFileAttributeValue value = { 0 };
2193 _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
2195 if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
2196 return set_unix_mode (filename, flags, &value, error);
2199 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
2200 return set_unix_uid_gid (filename, &value, NULL, flags, error);
2201 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
2202 return set_unix_uid_gid (filename, NULL, &value, flags, error);
2206 else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
2207 return set_symlink (filename, &value, error);
2211 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
2212 return set_mtime_atime (filename, &value, NULL, NULL, NULL, error);
2213 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
2214 return set_mtime_atime (filename, NULL, &value, NULL, NULL, error);
2215 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
2216 return set_mtime_atime (filename, NULL, NULL, &value, NULL, error);
2217 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
2218 return set_mtime_atime (filename, NULL, NULL, NULL, &value, error);
2222 else if (g_str_has_prefix (attribute, "xattr::"))
2223 return set_xattr (filename, attribute, &value, error);
2224 else if (g_str_has_prefix (attribute, "xattr-sys::"))
2225 return set_xattr (filename, attribute, &value, error);
2229 else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
2230 return set_selinux_context (filename, &value, error);
2233 vfs = g_vfs_get_default ();
2234 class = G_VFS_GET_CLASS (vfs);
2235 if (class->local_file_set_attributes)
2239 info = g_file_info_new ();
2240 g_file_info_set_attribute (info,
2244 if (!class->local_file_set_attributes (vfs, filename,
2249 g_object_unref (info);
2253 if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
2255 g_object_unref (info);
2259 g_object_unref (info);
2262 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
2263 _("Setting attribute %s not supported"), attribute);
2268 _g_local_file_info_set_attributes (char *filename,
2270 GFileQueryInfoFlags flags,
2271 GCancellable *cancellable,
2274 GFileAttributeValue *value;
2276 GFileAttributeValue *uid, *gid;
2279 GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
2281 #if defined (HAVE_CHOWN) || defined (HAVE_UTIMES)
2282 GFileAttributeStatus status;
2288 /* Handles setting multiple specified data in a single set, and takes care
2289 of ordering restrictions when setting attributes */
2293 /* Set symlink first, since this recreates the file */
2295 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
2298 if (!set_symlink (filename, value, error))
2300 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2302 /* Don't set error multiple times */
2306 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2312 /* Group uid and gid setting into one call
2313 * Change ownership before permissions, since ownership changes can
2314 change permissions (e.g. setuid)
2316 uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
2317 gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
2321 if (!set_unix_uid_gid (filename, uid, gid, flags, error))
2323 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2325 /* Don't set error multiple times */
2329 status = G_FILE_ATTRIBUTE_STATUS_SET;
2331 uid->status = status;
2333 gid->status = status;
2337 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
2340 if (!set_unix_mode (filename, flags, value, error))
2342 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2344 /* Don't set error multiple times */
2348 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2353 /* Group all time settings into one call
2354 * Change times as the last thing to avoid it changing due to metadata changes
2357 mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2358 mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
2359 atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2360 atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
2362 if (mtime || mtime_usec || atime || atime_usec)
2364 if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error))
2366 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2368 /* Don't set error multiple times */
2372 status = G_FILE_ATTRIBUTE_STATUS_SET;
2375 mtime->status = status;
2377 mtime_usec->status = status;
2379 atime->status = status;
2381 atime_usec->status = status;
2385 /* xattrs are handled by default callback */
2388 /* SELinux context */
2390 if (is_selinux_enabled ()) {
2391 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
2394 if (!set_selinux_context (filename, value, error))
2396 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2398 /* Don't set error multiple times */
2402 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2407 vfs = g_vfs_get_default ();
2408 class = G_VFS_GET_CLASS (vfs);
2409 if (class->local_file_set_attributes)
2411 if (!class->local_file_set_attributes (vfs, filename,
2417 /* Don't set error multiple times */