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.1 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>
27 #ifdef HAVE_SYS_TIME_H
30 #include <sys/types.h>
40 #include <selinux/selinux.h>
45 #if defined HAVE_SYS_XATTR_H
46 #include <sys/xattr.h>
47 #elif defined HAVE_ATTR_XATTR_H
48 #include <attr/xattr.h>
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 <glib/gstdioprivate.h>
57 #include <gfileattribute-priv.h>
58 #include <gfileinfo-priv.h>
63 #include "glib-unix.h"
66 #include "glib-private.h"
68 #include "thumbnail-verify.h"
80 #define X_OK 0 /* not really */
83 #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
86 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
89 #define S_IXUSR _S_IEXEC
93 #include "glocalfileinfo.h"
95 #include "gthemedicon.h"
96 #include "gcontenttypeprivate.h"
100 struct ThumbMD5Context {
103 unsigned char in[64];
113 G_LOCK_DEFINE_STATIC (uid_cache);
114 static GHashTable *uid_cache = NULL;
116 G_LOCK_DEFINE_STATIC (gid_cache);
117 static GHashTable *gid_cache = NULL;
119 #endif /* !G_OS_WIN32 */
122 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
126 g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL);
128 #if defined (G_OS_WIN32)
129 sec = statbuf->st_mtim.tv_sec;
130 usec = statbuf->st_mtim.tv_nsec / 1000;
132 sec = _g_stat_mtime (statbuf);
133 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
134 usec = statbuf->st_mtimensec / 1000;
135 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
136 usec = _g_stat_mtim_nsec (statbuf) / 1000;
142 return g_strdup_printf ("%lu:%lu", sec, usec);
146 _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
150 ino = statbuf->file_index;
152 ino = _g_stat_ino (statbuf);
154 return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
155 (guint64) _g_stat_dev (statbuf),
160 _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
162 return g_strdup_printf ("l%" G_GUINT64_FORMAT,
163 (guint64) _g_stat_dev (statbuf));
166 #if defined (S_ISLNK) || defined (G_OS_WIN32)
169 read_link (const gchar *full_name)
171 #if defined (HAVE_READLINK)
176 buffer = g_malloc (size);
182 read_size = readlink (full_name, buffer, size);
188 if ((gsize) read_size < size)
190 buffer[read_size] = 0;
194 buffer = g_realloc (buffer, size);
196 #elif defined (G_OS_WIN32)
200 read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, NULL, 0, &buffer, TRUE);
203 else if (read_size == 0)
212 #endif /* S_ISLNK || G_OS_WIN32 */
215 /* Get the SELinux security context */
217 get_selinux_context (const char *path,
219 GFileAttributeMatcher *attribute_matcher,
220 gboolean follow_symlinks)
224 if (!_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
227 if (is_selinux_enabled ())
231 if (lgetfilecon_raw (path, &context) < 0)
236 if (getfilecon_raw (path, &context) < 0)
242 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
251 /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
252 * (Mac) getxattr(..., XATTR_NOFOLLOW)
254 #ifdef HAVE_XATTR_NOFOLLOW
255 #define g_fgetxattr(fd,name,value,size) fgetxattr(fd,name,value,size,0,0)
256 #define g_flistxattr(fd,name,size) flistxattr(fd,name,size,0)
257 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
259 #define g_fgetxattr fgetxattr
260 #define g_flistxattr flistxattr
261 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
265 g_getxattr (const char *path, const char *name, void *value, size_t size,
266 gboolean follow_symlinks)
268 #ifdef HAVE_XATTR_NOFOLLOW
269 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
272 return getxattr (path, name, value, size);
274 return lgetxattr (path, name, value, size);
279 g_listxattr(const char *path, char *namebuf, size_t size,
280 gboolean follow_symlinks)
282 #ifdef HAVE_XATTR_NOFOLLOW
283 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
286 return listxattr (path, namebuf, size);
288 return llistxattr (path, namebuf, size);
295 return c >= 32 && c <= 126 && c != '\\';
299 name_is_valid (const char *str)
303 if (!valid_char (*str++))
310 hex_escape_buffer (const char *str,
312 gboolean *free_return)
314 size_t num_invalid, i;
315 char *escaped_str, *p;
317 static char *hex_digits = "0123456789abcdef";
320 for (i = 0; i < len; i++)
322 if (!valid_char (str[i]))
326 if (num_invalid == 0)
328 *free_return = FALSE;
332 escaped_str = g_malloc (len + num_invalid*3 + 1);
335 for (i = 0; i < len; i++)
337 if (valid_char (str[i]))
344 *p++ = hex_digits[(c >> 4) & 0xf];
345 *p++ = hex_digits[c & 0xf];
355 hex_escape_string (const char *str,
356 gboolean *free_return)
358 return hex_escape_buffer (str, strlen (str), free_return);
362 hex_unescape_string (const char *str,
364 gboolean *free_return)
367 char *unescaped_str, *p;
373 if (strchr (str, '\\') == NULL)
377 *free_return = FALSE;
381 unescaped_str = g_malloc (len + 1);
384 for (i = 0; i < len; i++)
386 if (str[i] == '\\' &&
391 (g_ascii_xdigit_value (str[i+2]) << 4) |
392 g_ascii_xdigit_value (str[i+3]);
400 *out_len = p - unescaped_str;
404 return unescaped_str;
408 escape_xattr (GFileInfo *info,
409 const char *gio_attr, /* gio attribute name */
410 const char *value, /* Is zero terminated */
411 size_t len /* not including zero termination */)
414 gboolean free_escaped_val;
416 escaped_val = hex_escape_buffer (value, len, &free_escaped_val);
418 g_file_info_set_attribute_string (info, gio_attr, escaped_val);
420 if (free_escaped_val)
421 g_free (escaped_val);
425 get_one_xattr (const char *path,
427 const char *gio_attr,
429 gboolean follow_symlinks)
436 len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
442 else if (len == -1 && errsv == ERANGE)
444 len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
449 value_p = g_malloc (len+1);
451 len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
465 escape_xattr (info, gio_attr, value_p, len);
467 if (value_p != value)
471 #endif /* defined HAVE_XATTR */
474 get_xattrs (const char *path,
477 GFileAttributeMatcher *matcher,
478 gboolean follow_symlinks)
483 gssize list_res_size;
486 const char *attr, *attr2;
489 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
491 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
497 list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
499 if (list_res_size == -1 ||
503 list_size = list_res_size;
504 list = g_malloc (list_size);
508 list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
511 if (list_res_size == -1 && errsv == ERANGE)
513 list_size = list_size * 2;
514 list = g_realloc (list, list_size);
518 if (list_res_size == -1)
525 while (list_res_size > 0)
527 if ((user && g_str_has_prefix (attr, "user.")) ||
528 (!user && !g_str_has_prefix (attr, "user.")))
530 char *escaped_attr, *gio_attr;
531 gboolean free_escaped_attr;
535 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
536 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
540 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
541 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
544 if (free_escaped_attr)
545 g_free (escaped_attr);
547 get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
552 len = strlen (attr) + 1;
554 list_res_size -= len;
561 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
563 char *unescaped_attribute, *a;
564 gboolean free_unescaped_attribute;
566 attr2 = strchr (attr, ':');
569 attr2 += 2; /* Skip '::' */
570 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
572 a = g_strconcat ("user.", unescaped_attribute, NULL);
574 a = unescaped_attribute;
576 get_one_xattr (path, info, attr, a, follow_symlinks);
581 if (free_unescaped_attribute)
582 g_free (unescaped_attribute);
586 #endif /* defined HAVE_XATTR */
591 get_one_xattr_from_fd (int fd,
593 const char *gio_attr,
601 len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
607 else if (len == -1 && errsv == ERANGE)
609 len = g_fgetxattr (fd, xattr, NULL, 0);
614 value_p = g_malloc (len + 1);
616 len = g_fgetxattr (fd, xattr, value_p, len);
630 escape_xattr (info, gio_attr, value_p, len);
632 if (value_p != value)
635 #endif /* defined HAVE_XATTR */
638 get_xattrs_from_fd (int fd,
641 GFileAttributeMatcher *matcher)
646 gssize list_res_size;
649 const char *attr, *attr2;
652 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
654 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
660 list_res_size = g_flistxattr (fd, NULL, 0);
662 if (list_res_size == -1 ||
666 list_size = list_res_size;
667 list = g_malloc (list_size);
671 list_res_size = g_flistxattr (fd, list, list_size);
674 if (list_res_size == -1 && errsv == ERANGE)
676 list_size = list_size * 2;
677 list = g_realloc (list, list_size);
681 if (list_res_size == -1)
688 while (list_res_size > 0)
690 if ((user && g_str_has_prefix (attr, "user.")) ||
691 (!user && !g_str_has_prefix (attr, "user.")))
693 char *escaped_attr, *gio_attr;
694 gboolean free_escaped_attr;
698 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
699 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
703 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
704 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
707 if (free_escaped_attr)
708 g_free (escaped_attr);
710 get_one_xattr_from_fd (fd, info, gio_attr, attr);
714 len = strlen (attr) + 1;
716 list_res_size -= len;
723 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
725 char *unescaped_attribute, *a;
726 gboolean free_unescaped_attribute;
728 attr2 = strchr (attr, ':');
731 attr2++; /* Skip ':' */
732 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
734 a = g_strconcat ("user.", unescaped_attribute, NULL);
736 a = unescaped_attribute;
738 get_one_xattr_from_fd (fd, info, attr, a);
743 if (free_unescaped_attribute)
744 g_free (unescaped_attribute);
748 #endif /* defined HAVE_XATTR */
753 set_xattr (char *filename,
754 const char *escaped_attribute,
755 const GFileAttributeValue *attr_value,
758 char *attribute, *value;
759 gboolean free_attribute, free_value;
760 int val_len, res, errsv;
764 if (attr_value == NULL)
766 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
767 _("Attribute value must be non-NULL"));
771 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
773 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
774 _("Invalid attribute type (string expected)"));
778 if (!name_is_valid (escaped_attribute))
780 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
781 _("Invalid extended attribute name"));
785 if (g_str_has_prefix (escaped_attribute, "xattr::"))
787 escaped_attribute += strlen ("xattr::");
792 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
793 escaped_attribute += strlen ("xattr-sys::");
797 attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
798 value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
801 a = g_strconcat ("user.", attribute, NULL);
805 res = g_setxattr (filename, a, value, val_len);
819 g_set_error (error, G_IO_ERROR,
820 g_io_error_from_errno (errsv),
821 _("Error setting extended attribute “%s”: %s"),
822 escaped_attribute, g_strerror (errsv));
833 _g_local_file_info_get_parent_info (const char *dir,
834 GFileAttributeMatcher *attribute_matcher,
835 GLocalParentFileInfo *parent_info)
840 parent_info->extra_data = NULL;
841 parent_info->free_extra_data = NULL;
842 parent_info->writable = FALSE;
843 parent_info->is_sticky = FALSE;
844 parent_info->has_trash_dir = FALSE;
845 parent_info->device = 0;
846 parent_info->inode = 0;
848 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) ||
849 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) ||
850 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH) ||
851 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
853 /* FIXME: Windows: The underlying _waccess() call in the C
854 * library is mostly pointless as it only looks at the READONLY
855 * FAT-style attribute of the file, it doesn't check the ACL at
858 parent_info->writable = (g_access (dir, W_OK) == 0);
860 res = g_stat (dir, &statbuf);
863 * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
864 * renamed or deleted only by the owner of the file, by the owner of the directory, and
865 * by a privileged process.
870 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
872 parent_info->is_sticky = FALSE;
874 parent_info->owner = statbuf.st_uid;
875 parent_info->device = statbuf.st_dev;
876 parent_info->inode = statbuf.st_ino;
877 /* No need to find trash dir if it's not writable anyway */
878 if (parent_info->writable &&
879 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
880 parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev);
886 _g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
888 if (parent_info->extra_data &&
889 parent_info->free_extra_data)
890 parent_info->free_extra_data (parent_info->extra_data);
894 get_access_rights (GFileAttributeMatcher *attribute_matcher,
897 GLocalFileStat *statbuf,
898 GLocalParentFileInfo *parent_info)
900 /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
901 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
902 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ))
903 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ,
904 g_access (path, R_OK) == 0);
906 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
907 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE))
908 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE,
909 g_access (path, W_OK) == 0);
911 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
912 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE))
913 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE,
914 g_access (path, X_OK) == 0);
922 if (parent_info->writable)
927 if (parent_info->is_sticky)
929 uid_t uid = geteuid ();
931 if (uid == _g_stat_uid (statbuf) ||
932 uid == (uid_t) parent_info->owner ||
941 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME))
942 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME,
945 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE))
946 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
949 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
950 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
951 writable && parent_info->has_trash_dir);
956 set_info_from_stat (GFileInfo *info,
957 GLocalFileStat *statbuf,
958 GFileAttributeMatcher *attribute_matcher)
962 file_type = G_FILE_TYPE_UNKNOWN;
964 if (S_ISREG (_g_stat_mode (statbuf)))
965 file_type = G_FILE_TYPE_REGULAR;
966 else if (S_ISDIR (_g_stat_mode (statbuf)))
967 file_type = G_FILE_TYPE_DIRECTORY;
969 else if (S_ISCHR (_g_stat_mode (statbuf)) ||
970 S_ISBLK (_g_stat_mode (statbuf)) ||
971 S_ISFIFO (_g_stat_mode (statbuf))
973 || S_ISSOCK (_g_stat_mode (statbuf))
976 file_type = G_FILE_TYPE_SPECIAL;
979 else if (S_ISLNK (_g_stat_mode (statbuf)))
980 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
981 #elif defined (G_OS_WIN32)
982 else if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK ||
983 statbuf->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
984 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
987 g_file_info_set_file_type (info, file_type);
988 g_file_info_set_size (info, _g_stat_size (statbuf));
990 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, _g_stat_dev (statbuf));
991 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, _g_stat_nlink (statbuf));
993 /* Pointless setting these on Windows even if they exist in the struct */
994 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, _g_stat_ino (statbuf));
995 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, _g_stat_uid (statbuf));
996 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, _g_stat_gid (statbuf));
997 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, _g_stat_rdev (statbuf));
999 /* Mostly pointless on Windows.
1000 * Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks.
1002 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, _g_stat_mode (statbuf));
1003 #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
1004 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, _g_stat_blksize (statbuf));
1006 #if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
1007 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, _g_stat_blocks (statbuf));
1008 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
1009 _g_stat_blocks (statbuf) * G_GUINT64_CONSTANT (512));
1010 #elif defined (G_OS_WIN32)
1011 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
1012 statbuf->allocated_size);
1016 #if defined (G_OS_WIN32)
1017 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtim.tv_sec);
1018 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000);
1019 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atim.tv_sec);
1020 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
1022 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, _g_stat_mtime (statbuf));
1023 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
1024 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000);
1025 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
1026 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, _g_stat_mtim_nsec (statbuf) / 1000);
1029 if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_ATIME))
1031 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, _g_stat_atime (statbuf));
1032 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
1033 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
1034 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
1035 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, _g_stat_atim_nsec (statbuf) / 1000);
1041 /* Microsoft uses st_ctime for file creation time,
1042 * instead of file change time:
1043 * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions#generic-text-routine-mappings
1044 * Thank you, Microsoft!
1046 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, _g_stat_ctime (statbuf));
1047 #if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
1048 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
1049 #elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
1050 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, _g_stat_ctim_nsec (statbuf) / 1000);
1054 #if defined (HAVE_STATX)
1055 if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_BTIME))
1057 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->stx_btime.tv_sec);
1058 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->stx_btime.tv_nsec / 1000);
1060 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
1061 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
1062 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtimensec / 1000);
1063 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
1064 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim.tv_sec);
1065 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtim.tv_nsec / 1000);
1066 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME)
1067 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
1068 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM)
1069 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim);
1070 #elif defined (G_OS_WIN32)
1071 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_ctim.tv_sec);
1072 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_ctim.tv_nsec / 1000);
1075 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1076 G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
1078 char *etag = _g_local_file_info_create_etag (statbuf);
1079 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ETAG_VALUE, etag);
1083 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1084 G_FILE_ATTRIBUTE_ID_ID_FILE))
1086 char *id = _g_local_file_info_create_file_id (statbuf);
1087 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILE, id);
1091 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1092 G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
1094 char *id = _g_local_file_info_create_fs_id (statbuf);
1095 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM, id);
1103 make_valid_utf8 (const char *name)
1106 const gchar *remainder, *invalid;
1107 gsize remaining_bytes, valid_bytes;
1111 remaining_bytes = strlen (name);
1113 while (remaining_bytes != 0)
1115 if (g_utf8_validate_len (remainder, remaining_bytes, &invalid))
1117 valid_bytes = invalid - remainder;
1120 string = g_string_sized_new (remaining_bytes);
1122 g_string_append_len (string, remainder, valid_bytes);
1123 /* append U+FFFD REPLACEMENT CHARACTER */
1124 g_string_append (string, "\357\277\275");
1126 remaining_bytes -= valid_bytes + 1;
1127 remainder = invalid + 1;
1131 return g_strdup (name);
1133 g_string_append (string, remainder);
1135 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
1137 return g_string_free (string, FALSE);
1141 convert_pwd_string_to_utf8 (char *pwd_str)
1145 if (!g_utf8_validate (pwd_str, -1, NULL))
1147 utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
1148 if (utf8_string == NULL)
1149 utf8_string = make_valid_utf8 (pwd_str);
1152 utf8_string = g_strdup (pwd_str);
1158 uid_data_free (UidData *data)
1160 g_free (data->user_name);
1161 g_free (data->real_name);
1165 /* called with lock held */
1167 lookup_uid_data (uid_t uid)
1171 struct passwd pwbuf;
1172 struct passwd *pwbufp;
1174 char *gecos, *comma;
1177 if (uid_cache == NULL)
1178 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
1180 data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
1185 data = g_new0 (UidData, 1);
1187 #if defined(HAVE_GETPWUID_R)
1188 getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
1190 pwbufp = getpwuid (uid);
1195 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
1196 data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
1199 gecos = pwbufp->pw_gecos;
1203 comma = strchr (gecos, ',');
1206 data->real_name = convert_pwd_string_to_utf8 (gecos);
1211 /* Default fallbacks */
1212 if (data->real_name == NULL)
1214 if (data->user_name != NULL)
1215 data->real_name = g_strdup (data->user_name);
1217 data->real_name = g_strdup_printf ("user #%d", (int)uid);
1220 if (data->user_name == NULL)
1221 data->user_name = g_strdup_printf ("%d", (int)uid);
1223 g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
1229 get_username_from_uid (uid_t uid)
1235 data = lookup_uid_data (uid);
1236 res = g_strdup (data->user_name);
1237 G_UNLOCK (uid_cache);
1243 get_realname_from_uid (uid_t uid)
1249 data = lookup_uid_data (uid);
1250 res = g_strdup (data->real_name);
1251 G_UNLOCK (uid_cache);
1256 /* called with lock held */
1258 lookup_gid_name (gid_t gid)
1261 #if defined (HAVE_GETGRGID_R)
1265 struct group *gbufp;
1267 if (gid_cache == NULL)
1268 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
1270 name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
1275 #if defined (HAVE_GETGRGID_R)
1276 getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
1278 gbufp = getgrgid (gid);
1281 if (gbufp != NULL &&
1282 gbufp->gr_name != NULL &&
1283 gbufp->gr_name[0] != 0)
1284 name = convert_pwd_string_to_utf8 (gbufp->gr_name);
1286 name = g_strdup_printf("%d", (int)gid);
1288 g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
1294 get_groupname_from_gid (gid_t gid)
1300 name = lookup_gid_name (gid);
1301 res = g_strdup (name);
1302 G_UNLOCK (gid_cache);
1306 #endif /* !G_OS_WIN32 */
1309 get_content_type (const char *basename,
1311 GLocalFileStat *statbuf,
1312 gboolean is_symlink,
1313 gboolean symlink_broken,
1314 GFileQueryInfoFlags flags,
1318 (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
1319 return g_content_type_from_mime_type ("inode/symlink");
1320 else if (statbuf != NULL && S_ISDIR(_g_stat_mode (statbuf)))
1321 return g_content_type_from_mime_type ("inode/directory");
1323 else if (statbuf != NULL && S_ISCHR(_g_stat_mode (statbuf)))
1324 return g_content_type_from_mime_type ("inode/chardevice");
1325 else if (statbuf != NULL && S_ISBLK(_g_stat_mode (statbuf)))
1326 return g_content_type_from_mime_type ("inode/blockdevice");
1327 else if (statbuf != NULL && S_ISFIFO(_g_stat_mode (statbuf)))
1328 return g_content_type_from_mime_type ("inode/fifo");
1329 else if (statbuf != NULL && S_ISREG(_g_stat_mode (statbuf)) && _g_stat_size (statbuf) == 0)
1331 /* Don't sniff zero-length files in order to avoid reading files
1332 * that appear normal but are not (eg: files in /proc and /sys)
1334 * Note that we need to return text/plain here so that
1335 * newly-created text files are opened by the text editor.
1336 * See https://bugzilla.gnome.org/show_bug.cgi?id=755795
1338 return g_content_type_from_mime_type ("text/plain");
1342 else if (statbuf != NULL && S_ISSOCK(_g_stat_mode (statbuf)))
1343 return g_content_type_from_mime_type ("inode/socket");
1348 gboolean result_uncertain;
1350 content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
1352 #if !defined(G_OS_WIN32) && !defined(HAVE_COCOA)
1353 if (!fast && result_uncertain && path != NULL)
1355 guchar sniff_buffer[4096];
1359 sniff_length = _g_unix_content_type_get_sniff_len ();
1360 if (sniff_length > 4096)
1361 sniff_length = 4096;
1364 fd = g_open (path, O_RDONLY | O_NOATIME, 0);
1366 if (fd < 0 && errsv == EPERM)
1368 fd = g_open (path, O_RDONLY, 0);
1374 res = read (fd, sniff_buffer, sniff_length);
1375 (void) g_close (fd, NULL);
1378 g_free (content_type);
1379 content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
1385 return content_type;
1390 /* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
1392 get_thumbnail_attributes (const char *path,
1394 const GLocalFileStat *stat_buf)
1396 GChecksum *checksum;
1401 uri = g_filename_to_uri (path, NULL, NULL);
1403 checksum = g_checksum_new (G_CHECKSUM_MD5);
1404 g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
1406 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
1407 g_checksum_free (checksum);
1409 filename = g_build_filename (g_get_user_cache_dir (),
1410 "thumbnails", "large", basename,
1413 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1415 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
1416 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1417 thumbnail_verify (filename, uri, stat_buf));
1422 filename = g_build_filename (g_get_user_cache_dir (),
1423 "thumbnails", "normal", basename,
1426 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1428 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, filename);
1429 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1430 thumbnail_verify (filename, uri, stat_buf));
1435 filename = g_build_filename (g_get_user_cache_dir (),
1436 "thumbnails", "fail",
1437 "gnome-thumbnail-factory",
1441 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1443 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
1444 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1445 thumbnail_verify (filename, uri, stat_buf));
1456 win32_get_file_user_info (const gchar *filename,
1461 PSECURITY_DESCRIPTOR psd = NULL;
1462 DWORD sd_size = 0; /* first call calculates the size required */
1464 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1465 if ((GetFileSecurityW (wfilename,
1466 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1469 &sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
1470 (psd = g_try_malloc (sd_size)) != NULL &&
1471 GetFileSecurityW (wfilename,
1472 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1479 SID_NAME_USE name_use = 0; /* don't care? */
1480 wchar_t *name = NULL;
1481 wchar_t *domain = NULL;
1483 DWORD domain_len = 0;
1484 /* get the user name */
1488 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
1490 if (!LookupAccountSidW (NULL, /* local machine */
1493 domain, &domain_len, /* no domain info yet */
1494 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1496 name = g_try_malloc (name_len * sizeof (wchar_t));
1497 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1498 if (name && domain &&
1499 LookupAccountSidW (NULL, /* local machine */
1502 domain, &domain_len, /* no domain info yet */
1505 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1511 /* get the group name */
1515 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
1517 if (!LookupAccountSidW (NULL, /* local machine */
1520 domain, &domain_len, /* no domain info yet */
1521 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1523 name = g_try_malloc (name_len * sizeof (wchar_t));
1524 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1525 if (name && domain &&
1526 LookupAccountSidW (NULL, /* local machine */
1529 domain, &domain_len, /* no domain info yet */
1532 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1538 /* TODO: get real name */
1544 #endif /* G_OS_WIN32 */
1547 /* support for '.hidden' files */
1548 G_LOCK_DEFINE_STATIC (hidden_cache);
1549 static GHashTable *hidden_cache;
1550 static GSource *hidden_cache_source = NULL; /* Under the hidden_cache lock */
1551 static guint hidden_cache_ttl_secs = 5;
1552 static guint hidden_cache_ttl_jitter_secs = 2;
1556 GHashTable *hidden_files;
1557 gint64 timestamp_secs;
1561 remove_from_hidden_cache (gpointer user_data)
1563 HiddenCacheData *data;
1564 GHashTableIter iter;
1566 gint64 timestamp_secs;
1568 G_LOCK (hidden_cache);
1569 timestamp_secs = g_source_get_time (hidden_cache_source) / G_USEC_PER_SEC;
1571 g_hash_table_iter_init (&iter, hidden_cache);
1572 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
1574 if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
1575 g_hash_table_iter_remove (&iter);
1578 if (g_hash_table_size (hidden_cache) == 0)
1580 g_clear_pointer (&hidden_cache_source, g_source_unref);
1581 retval = G_SOURCE_REMOVE;
1584 retval = G_SOURCE_CONTINUE;
1586 G_UNLOCK (hidden_cache);
1592 read_hidden_file (const gchar *dirname)
1594 gchar *contents = NULL;
1597 filename = g_build_path ("/", dirname, ".hidden", NULL);
1598 (void) g_file_get_contents (filename, &contents, NULL, NULL);
1601 if (contents != NULL)
1607 table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1609 lines = g_strsplit (contents, "\n", 0);
1612 for (i = 0; lines[i]; i++)
1613 /* hash table takes the individual strings... */
1614 g_hash_table_add (table, lines[i]);
1616 /* ... so we only free the container. */
1626 free_hidden_file_data (gpointer user_data)
1628 HiddenCacheData *data = user_data;
1630 g_clear_pointer (&data->hidden_files, g_hash_table_unref);
1635 file_is_hidden (const gchar *path,
1636 const gchar *basename)
1638 HiddenCacheData *data;
1643 dirname = g_path_get_dirname (path);
1645 G_LOCK (hidden_cache);
1647 if G_UNLIKELY (hidden_cache == NULL)
1648 hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1649 g_free, free_hidden_file_data);
1651 if (!g_hash_table_lookup_extended (hidden_cache, dirname,
1652 NULL, (gpointer *) &data))
1656 data = g_new0 (HiddenCacheData, 1);
1657 data->hidden_files = table = read_hidden_file (dirname);
1658 data->timestamp_secs = g_get_monotonic_time () / G_USEC_PER_SEC;
1660 g_hash_table_insert (hidden_cache,
1661 mydirname = g_strdup (dirname),
1664 if (!hidden_cache_source)
1666 hidden_cache_source =
1667 g_timeout_source_new_seconds (hidden_cache_ttl_secs +
1668 hidden_cache_ttl_jitter_secs);
1669 g_source_set_priority (hidden_cache_source, G_PRIORITY_DEFAULT);
1670 g_source_set_name (hidden_cache_source,
1671 "[gio] remove_from_hidden_cache");
1672 g_source_set_callback (hidden_cache_source,
1673 remove_from_hidden_cache,
1675 g_source_attach (hidden_cache_source,
1676 GLIB_PRIVATE_CALL (g_get_worker_context) ());
1680 table = data->hidden_files;
1682 result = table != NULL && g_hash_table_contains (table, basename);
1684 G_UNLOCK (hidden_cache);
1690 #endif /* !G_OS_WIN32 */
1693 _g_local_file_info_get_nostat (GFileInfo *info,
1694 const char *basename,
1696 GFileAttributeMatcher *attribute_matcher)
1698 g_file_info_set_name (info, basename);
1700 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1701 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
1703 char *display_name = g_filename_display_basename (path);
1705 /* look for U+FFFD REPLACEMENT CHARACTER */
1706 if (strstr (display_name, "\357\277\275") != NULL)
1708 char *p = display_name;
1709 display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1712 g_file_info_set_display_name (info, display_name);
1713 g_free (display_name);
1716 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1717 G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
1719 char *edit_name = g_filename_display_basename (path);
1720 g_file_info_set_edit_name (info, edit_name);
1725 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1726 G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
1728 char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1730 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
1736 get_icon_name (const char *path,
1737 gboolean use_symbolic,
1738 gboolean *with_fallbacks_out)
1740 const char *name = NULL;
1741 gboolean with_fallbacks = TRUE;
1743 if (g_strcmp0 (path, g_get_home_dir ()) == 0)
1745 name = use_symbolic ? "user-home-symbolic" : "user-home";
1746 with_fallbacks = FALSE;
1748 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
1750 name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
1751 with_fallbacks = FALSE;
1753 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
1755 name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
1757 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
1759 name = use_symbolic ? "folder-download-symbolic" : "folder-download";
1761 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
1763 name = use_symbolic ? "folder-music-symbolic" : "folder-music";
1765 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
1767 name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
1769 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
1771 name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
1773 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
1775 name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
1777 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
1779 name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
1786 if (with_fallbacks_out != NULL)
1787 *with_fallbacks_out = with_fallbacks;
1793 get_icon (const char *path,
1794 const char *content_type,
1795 gboolean use_symbolic)
1798 const char *icon_name;
1799 gboolean with_fallbacks;
1801 icon_name = get_icon_name (path, use_symbolic, &with_fallbacks);
1802 if (icon_name != NULL)
1805 icon = g_themed_icon_new_with_default_fallbacks (icon_name);
1807 icon = g_themed_icon_new (icon_name);
1812 icon = g_content_type_get_symbolic_icon (content_type);
1814 icon = g_content_type_get_icon (content_type);
1821 _g_local_file_info_get (const char *basename,
1823 GFileAttributeMatcher *attribute_matcher,
1824 GFileQueryInfoFlags flags,
1825 GLocalParentFileInfo *parent_info,
1829 GLocalFileStat statbuf;
1830 GLocalFileStat statbuf2;
1833 gboolean is_symlink, symlink_broken;
1834 char *symlink_target;
1839 info = g_file_info_new ();
1841 /* Make sure we don't set any unwanted attributes */
1842 g_file_info_set_attribute_mask (info, attribute_matcher);
1844 _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
1846 if (attribute_matcher == NULL)
1848 g_file_info_unset_attribute_mask (info);
1852 res = g_local_file_lstat (path,
1853 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
1854 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
1861 /* Don't bail out if we get Permission denied (SELinux?) */
1862 if (errsv != EACCES)
1864 char *display_name = g_filename_display_name (path);
1865 g_object_unref (info);
1866 g_set_error (error, G_IO_ERROR,
1867 g_io_error_from_errno (errsv),
1868 _("Error when getting information for file “%s”: %s"),
1869 display_name, g_strerror (errsv));
1870 g_free (display_name);
1875 /* Even if stat() fails, try to get as much as other attributes possible */
1876 stat_ok = res != -1;
1879 device = _g_stat_dev (&statbuf);
1884 is_symlink = stat_ok && S_ISLNK (_g_stat_mode (&statbuf));
1885 #elif defined (G_OS_WIN32)
1886 /* glib already checked the FILE_ATTRIBUTE_REPARSE_POINT for us */
1887 is_symlink = stat_ok &&
1888 (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
1889 statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT);
1893 symlink_broken = FALSE;
1897 g_file_info_set_is_symlink (info, TRUE);
1899 /* Unless NOFOLLOW was set we default to following symlinks */
1900 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
1902 res = g_local_file_stat (path,
1903 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
1904 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
1907 /* Report broken links as symlinks */
1914 symlink_broken = TRUE;
1919 set_info_from_stat (info, &statbuf, attribute_matcher);
1922 if (stat_ok && _g_local_file_is_lost_found_dir (path, _g_stat_dev (&statbuf)))
1923 g_file_info_set_is_hidden (info, TRUE);
1927 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1928 G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
1930 if (basename != NULL &&
1931 (basename[0] == '.' ||
1932 file_is_hidden (path, basename)))
1933 g_file_info_set_is_hidden (info, TRUE);
1936 if (basename != NULL && basename[strlen (basename) -1] == '~' &&
1937 (stat_ok && S_ISREG (_g_stat_mode (&statbuf))))
1938 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE);
1940 if (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN)
1941 g_file_info_set_is_hidden (info, TRUE);
1943 if (statbuf.attributes & FILE_ATTRIBUTE_ARCHIVE)
1944 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE, TRUE);
1946 if (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM)
1947 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM, TRUE);
1949 if (statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
1950 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_MOUNTPOINT, TRUE);
1952 if (statbuf.reparse_tag != 0)
1953 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_REPARSE_POINT_TAG, statbuf.reparse_tag);
1956 symlink_target = NULL;
1959 #if defined (S_ISLNK) || defined (G_OS_WIN32)
1960 symlink_target = read_link (path);
1962 if (symlink_target &&
1963 _g_file_attribute_matcher_matches_id (attribute_matcher,
1964 G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
1965 g_file_info_set_symlink_target (info, symlink_target);
1968 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1969 G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
1970 _g_file_attribute_matcher_matches_id (attribute_matcher,
1971 G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
1972 _g_file_attribute_matcher_matches_id (attribute_matcher,
1973 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
1975 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
1979 g_file_info_set_content_type (info, content_type);
1981 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1982 G_FILE_ATTRIBUTE_ID_STANDARD_ICON)
1983 || _g_file_attribute_matcher_matches_id (attribute_matcher,
1984 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
1988 /* non symbolic icon */
1989 icon = get_icon (path, content_type, FALSE);
1992 g_file_info_set_icon (info, icon);
1993 g_object_unref (icon);
1997 icon = get_icon (path, content_type, TRUE);
2000 g_file_info_set_symbolic_icon (info, icon);
2001 g_object_unref (icon);
2006 g_free (content_type);
2010 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2011 G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
2013 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
2017 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
2018 g_free (content_type);
2022 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2023 G_FILE_ATTRIBUTE_ID_OWNER_USER))
2028 win32_get_file_user_info (path, NULL, &name, NULL);
2031 name = get_username_from_uid (_g_stat_uid (&statbuf));
2034 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
2038 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2039 G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
2043 win32_get_file_user_info (path, NULL, NULL, &name);
2046 name = get_realname_from_uid (_g_stat_uid (&statbuf));
2049 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
2053 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2054 G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
2058 win32_get_file_user_info (path, &name, NULL, NULL);
2061 name = get_groupname_from_gid (_g_stat_gid (&statbuf));
2064 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
2068 if (stat_ok && parent_info && parent_info->device != 0 &&
2069 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) &&
2070 (_g_stat_dev (&statbuf) != parent_info->device || _g_stat_ino (&statbuf) == parent_info->inode))
2071 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE);
2074 get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
2077 get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2079 get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2080 get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2082 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2083 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH) ||
2084 _g_file_attribute_matcher_matches_id (attribute_matcher,
2085 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID) ||
2086 _g_file_attribute_matcher_matches_id (attribute_matcher,
2087 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED))
2090 get_thumbnail_attributes (path, info, &statbuf);
2092 get_thumbnail_attributes (path, info, NULL);
2095 vfs = g_vfs_get_default ();
2096 class = G_VFS_GET_CLASS (vfs);
2097 if (class->local_file_add_info)
2099 class->local_file_add_info (vfs,
2105 &parent_info->extra_data,
2106 &parent_info->free_extra_data);
2109 g_file_info_unset_attribute_mask (info);
2111 g_free (symlink_target);
2117 _g_local_file_info_get_from_fd (int fd,
2118 const char *attributes,
2121 GLocalFileStat stat_buf;
2122 GFileAttributeMatcher *matcher;
2125 if (g_local_file_fstat (fd,
2126 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
2127 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
2132 g_set_error (error, G_IO_ERROR,
2133 g_io_error_from_errno (errsv),
2134 _("Error when getting information for file descriptor: %s"),
2135 g_strerror (errsv));
2139 info = g_file_info_new ();
2141 matcher = g_file_attribute_matcher_new (attributes);
2143 /* Make sure we don't set any unwanted attributes */
2144 g_file_info_set_attribute_mask (info, matcher);
2146 set_info_from_stat (info, &stat_buf, matcher);
2149 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
2150 is_selinux_enabled ())
2153 if (fgetfilecon_raw (fd, &context) >= 0)
2155 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
2161 get_xattrs_from_fd (fd, TRUE, info, matcher);
2162 get_xattrs_from_fd (fd, FALSE, info, matcher);
2164 g_file_attribute_matcher_unref (matcher);
2166 g_file_info_unset_attribute_mask (info);
2172 get_uint32 (const GFileAttributeValue *value,
2176 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
2178 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2179 _("Invalid attribute type (uint32 expected)"));
2183 *val_out = value->u.uint32;
2188 #if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2190 get_uint64 (const GFileAttributeValue *value,
2194 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
2196 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2197 _("Invalid attribute type (uint64 expected)"));
2201 *val_out = value->u.uint64;
2207 #if defined(HAVE_SYMLINK)
2209 get_byte_string (const GFileAttributeValue *value,
2210 const char **val_out,
2213 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
2215 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2216 _("Invalid attribute type (byte string expected)"));
2220 *val_out = value->u.string;
2228 get_string (const GFileAttributeValue *value,
2229 const char **val_out,
2232 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
2234 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2235 _("Invalid attribute type (byte string expected)"));
2239 *val_out = value->u.string;
2246 set_unix_mode (char *filename,
2247 GFileQueryInfoFlags flags,
2248 const GFileAttributeValue *value,
2254 if (!get_uint32 (value, &val, error))
2257 #if defined (HAVE_SYMLINK) || defined (G_OS_WIN32)
2258 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
2260 res = lchmod (filename, val);
2262 gboolean is_symlink;
2264 struct stat statbuf;
2265 /* Calling chmod on a symlink changes permissions on the symlink.
2266 * We don't want to do this, so we need to check for a symlink */
2267 res = g_lstat (filename, &statbuf);
2268 is_symlink = (res == 0 && S_ISLNK (statbuf.st_mode));
2270 /* FIXME: implement lchmod for W32, should be doable */
2271 GWin32PrivateStat statbuf;
2273 res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (filename, &statbuf);
2274 is_symlink = (res == 0 &&
2275 (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
2276 statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT));
2280 g_set_error_literal (error, G_IO_ERROR,
2281 G_IO_ERROR_NOT_SUPPORTED,
2282 _("Cannot set permissions on symlinks"));
2286 res = g_chmod (filename, val);
2290 res = g_chmod (filename, val);
2296 g_set_error (error, G_IO_ERROR,
2297 g_io_error_from_errno (errsv),
2298 _("Error setting permissions: %s"),
2299 g_strerror (errsv));
2307 set_unix_uid_gid (char *filename,
2308 const GFileAttributeValue *uid_value,
2309 const GFileAttributeValue *gid_value,
2310 GFileQueryInfoFlags flags,
2320 if (!get_uint32 (uid_value, &val, error))
2329 if (!get_uint32 (gid_value, &val, error))
2337 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
2338 res = lchown (filename, uid, gid);
2341 res = chown (filename, uid, gid);
2347 g_set_error (error, G_IO_ERROR,
2348 g_io_error_from_errno (errsv),
2349 _("Error setting owner: %s"),
2350 g_strerror (errsv));
2359 set_symlink (char *filename,
2360 const GFileAttributeValue *value,
2364 struct stat statbuf;
2366 if (!get_byte_string (value, &val, error))
2371 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2372 _("symlink must be non-NULL"));
2376 if (g_lstat (filename, &statbuf))
2380 g_set_error (error, G_IO_ERROR,
2381 g_io_error_from_errno (errsv),
2382 _("Error setting symlink: %s"),
2383 g_strerror (errsv));
2387 if (!S_ISLNK (statbuf.st_mode))
2389 g_set_error_literal (error, G_IO_ERROR,
2390 G_IO_ERROR_NOT_SYMBOLIC_LINK,
2391 _("Error setting symlink: file is not a symlink"));
2395 if (g_unlink (filename))
2399 g_set_error (error, G_IO_ERROR,
2400 g_io_error_from_errno (errsv),
2401 _("Error setting symlink: %s"),
2402 g_strerror (errsv));
2406 if (symlink (filename, val) != 0)
2410 g_set_error (error, G_IO_ERROR,
2411 g_io_error_from_errno (errsv),
2412 _("Error setting symlink: %s"),
2413 g_strerror (errsv));
2421 #if defined (G_OS_WIN32)
2423 * https://support.microsoft.com/en-ca/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime
2424 * FT = UT * 10000000 + 116444736000000000.
2425 * Converts unix epoch time (a signed 64-bit integer) to FILETIME.
2426 * Can optionally use a more precise timestamp that has
2427 * a fraction of a second expressed in nanoseconds.
2428 * UT must be between January 1st of year 1601 and December 31st of year 30827.
2429 * nsec must be non-negative and < 1000000000.
2430 * Returns TRUE if conversion succeeded, FALSE otherwise.
2432 * The function that does the reverse can be found in
2436 _g_win32_unix_time_to_filetime (gint64 ut,
2442 /* 1 unit of FILETIME is 100ns */
2443 const gint64 hundreds_of_usec_per_sec = 10000000;
2444 /* The difference between January 1, 1601 UTC (FILETIME epoch) and UNIX epoch
2445 * in hundreds of nanoseconds.
2447 const gint64 filetime_unix_epoch_offset = 116444736000000000;
2448 /* This is the maximum timestamp that SYSTEMTIME can
2449 * represent (last millisecond of the year 30827).
2450 * Since FILETIME and SYSTEMTIME are both used on Windows,
2451 * we use this as a limit (FILETIME can support slightly
2452 * larger interval, up to year 30828).
2454 const gint64 max_systemtime = 0x7fff35f4f06c58f0;
2456 g_return_val_if_fail (ft != NULL, FALSE);
2457 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2461 g_set_error (error, G_IO_ERROR,
2462 G_IO_ERROR_INVALID_DATA,
2463 _("Extra nanoseconds %d for UNIX timestamp %lld are negative"),
2468 if (nsec >= hundreds_of_usec_per_sec * 100)
2470 g_set_error (error, G_IO_ERROR,
2471 G_IO_ERROR_INVALID_DATA,
2472 _("Extra nanoseconds %d for UNIX timestamp %lld reach 1 second"),
2477 if (ut >= (G_MAXINT64 / hundreds_of_usec_per_sec) ||
2478 (ut * hundreds_of_usec_per_sec) >= (G_MAXINT64 - filetime_unix_epoch_offset))
2480 g_set_error (error, G_IO_ERROR,
2481 G_IO_ERROR_INVALID_DATA,
2482 _("UNIX timestamp %lld does not fit into 64 bits"),
2487 result = ut * hundreds_of_usec_per_sec + filetime_unix_epoch_offset + nsec / 100;
2489 if (result >= max_systemtime || result < 0)
2491 g_set_error (error, G_IO_ERROR,
2492 G_IO_ERROR_INVALID_DATA,
2493 _("UNIX timestamp %lld is outside of the range supported by Windows"),
2498 ft->dwLowDateTime = (DWORD) (result);
2499 ft->dwHighDateTime = (DWORD) (result >> 32);
2505 set_mtime_atime (const char *filename,
2506 const GFileAttributeValue *mtime_value,
2507 const GFileAttributeValue *mtime_usec_value,
2508 const GFileAttributeValue *atime_value,
2509 const GFileAttributeValue *atime_usec_value,
2514 guint32 val_usec = 0;
2515 gunichar2 *filename_utf16;
2516 SECURITY_ATTRIBUTES sec = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
2520 FILETIME *p_mtime = NULL;
2521 FILETIME *p_atime = NULL;
2527 if (!get_uint64 (atime_value, &val, error))
2530 if (atime_usec_value &&
2531 !get_uint32 (atime_usec_value, &val_usec, error))
2533 if (!_g_win32_unix_time_to_filetime (val, val_usec, &atime, error))
2541 if (!get_uint64 (mtime_value, &val, error))
2544 if (mtime_usec_value &&
2545 !get_uint32 (mtime_usec_value, &val_usec, error))
2547 if (!_g_win32_unix_time_to_filetime (val, val_usec, &mtime, error))
2552 filename_utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2554 if (filename_utf16 == NULL)
2556 g_prefix_error (error,
2557 _("File name “%s” cannot be converted to UTF-16"),
2562 file_handle = CreateFileW (filename_utf16,
2563 FILE_WRITE_ATTRIBUTES,
2564 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
2567 FILE_FLAG_BACKUP_SEMANTICS,
2569 gle = GetLastError ();
2570 g_clear_pointer (&filename_utf16, g_free);
2572 if (file_handle == INVALID_HANDLE_VALUE)
2574 g_set_error (error, G_IO_ERROR,
2575 g_io_error_from_errno (gle),
2576 _("File “%s” cannot be opened: Windows Error %lu"),
2582 res = SetFileTime (file_handle, NULL, p_atime, p_mtime);
2583 gle = GetLastError ();
2584 CloseHandle (file_handle);
2587 g_set_error (error, G_IO_ERROR,
2588 g_io_error_from_errno (gle),
2589 _("Error setting modification or access time for file “%s”: %lu"),
2594 #elif defined (HAVE_UTIMES)
2596 lazy_stat (char *filename,
2597 struct stat *statbuf,
2598 gboolean *called_stat)
2605 res = g_stat (filename, statbuf);
2608 *called_stat = TRUE;
2615 set_mtime_atime (char *filename,
2616 const GFileAttributeValue *mtime_value,
2617 const GFileAttributeValue *mtime_usec_value,
2618 const GFileAttributeValue *atime_value,
2619 const GFileAttributeValue *atime_usec_value,
2624 guint32 val_usec = 0;
2625 struct stat statbuf;
2626 gboolean got_stat = FALSE;
2627 struct timeval times[2] = { {0, 0}, {0, 0} };
2632 if (!get_uint64 (atime_value, &val, error))
2634 times[0].tv_sec = val;
2638 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2640 times[0].tv_sec = statbuf.st_mtime;
2641 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2642 times[0].tv_usec = statbuf.st_atimensec / 1000;
2643 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2644 times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
2649 if (atime_usec_value)
2651 if (!get_uint32 (atime_usec_value, &val_usec, error))
2653 times[0].tv_usec = val_usec;
2659 if (!get_uint64 (mtime_value, &val, error))
2661 times[1].tv_sec = val;
2665 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2667 times[1].tv_sec = statbuf.st_mtime;
2668 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2669 times[1].tv_usec = statbuf.st_mtimensec / 1000;
2670 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2671 times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
2676 if (mtime_usec_value)
2678 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2680 times[1].tv_usec = val_usec;
2683 res = utimes (filename, times);
2688 g_set_error (error, G_IO_ERROR,
2689 g_io_error_from_errno (errsv),
2690 _("Error setting modification or access time: %s"),
2691 g_strerror (errsv));
2701 set_selinux_context (char *filename,
2702 const GFileAttributeValue *value,
2707 if (!get_string (value, &val, error))
2712 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2713 _("SELinux context must be non-NULL"));
2717 if (!is_selinux_enabled ())
2719 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2720 _("SELinux is not enabled on this system"));
2724 if (setfilecon_raw (filename, val) < 0)
2728 g_set_error (error, G_IO_ERROR,
2729 g_io_error_from_errno (errsv),
2730 _("Error setting SELinux context: %s"),
2731 g_strerror (errsv));
2741 _g_local_file_info_set_attribute (char *filename,
2742 const char *attribute,
2743 GFileAttributeType type,
2745 GFileQueryInfoFlags flags,
2746 GCancellable *cancellable,
2749 GFileAttributeValue value = { 0 };
2753 _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
2755 if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
2756 return set_unix_mode (filename, flags, &value, error);
2759 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
2760 return set_unix_uid_gid (filename, &value, NULL, flags, error);
2761 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
2762 return set_unix_uid_gid (filename, NULL, &value, flags, error);
2766 else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
2767 return set_symlink (filename, &value, error);
2770 #if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2771 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
2772 return set_mtime_atime (filename, &value, NULL, NULL, NULL, error);
2773 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
2774 return set_mtime_atime (filename, NULL, &value, NULL, NULL, error);
2775 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
2776 return set_mtime_atime (filename, NULL, NULL, &value, NULL, error);
2777 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
2778 return set_mtime_atime (filename, NULL, NULL, NULL, &value, error);
2782 else if (g_str_has_prefix (attribute, "xattr::"))
2783 return set_xattr (filename, attribute, &value, error);
2784 else if (g_str_has_prefix (attribute, "xattr-sys::"))
2785 return set_xattr (filename, attribute, &value, error);
2789 else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
2790 return set_selinux_context (filename, &value, error);
2793 vfs = g_vfs_get_default ();
2794 class = G_VFS_GET_CLASS (vfs);
2795 if (class->local_file_set_attributes)
2799 info = g_file_info_new ();
2800 g_file_info_set_attribute (info,
2804 if (!class->local_file_set_attributes (vfs, filename,
2809 g_object_unref (info);
2813 if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
2815 g_object_unref (info);
2819 g_object_unref (info);
2822 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
2823 _("Setting attribute %s not supported"), attribute);
2828 _g_local_file_info_set_attributes (char *filename,
2830 GFileQueryInfoFlags flags,
2831 GCancellable *cancellable,
2834 GFileAttributeValue *value;
2836 GFileAttributeValue *uid, *gid;
2838 #if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2839 GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
2841 #if defined (G_OS_UNIX) || defined (G_OS_WIN32)
2842 GFileAttributeStatus status;
2848 /* Handles setting multiple specified data in a single set, and takes care
2849 of ordering restrictions when setting attributes */
2853 /* Set symlink first, since this recreates the file */
2855 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
2858 if (!set_symlink (filename, value, error))
2860 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2862 /* Don't set error multiple times */
2866 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2872 /* Group uid and gid setting into one call
2873 * Change ownership before permissions, since ownership changes can
2874 change permissions (e.g. setuid)
2876 uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
2877 gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
2881 if (!set_unix_uid_gid (filename, uid, gid, flags, error))
2883 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2885 /* Don't set error multiple times */
2889 status = G_FILE_ATTRIBUTE_STATUS_SET;
2891 uid->status = status;
2893 gid->status = status;
2897 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
2900 if (!set_unix_mode (filename, flags, value, error))
2902 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2904 /* Don't set error multiple times */
2908 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2912 #if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2913 /* Group all time settings into one call
2914 * Change times as the last thing to avoid it changing due to metadata changes
2917 mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2918 mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
2919 atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2920 atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
2922 if (mtime || mtime_usec || atime || atime_usec)
2924 if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error))
2926 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2928 /* Don't set error multiple times */
2932 status = G_FILE_ATTRIBUTE_STATUS_SET;
2935 mtime->status = status;
2937 mtime_usec->status = status;
2939 atime->status = status;
2941 atime_usec->status = status;
2945 /* xattrs are handled by default callback */
2948 /* SELinux context */
2950 if (is_selinux_enabled ()) {
2951 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
2954 if (!set_selinux_context (filename, value, error))
2956 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2958 /* Don't set error multiple times */
2962 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2967 vfs = g_vfs_get_default ();
2968 class = G_VFS_GET_CLASS (vfs);
2969 if (class->local_file_set_attributes)
2971 if (!class->local_file_set_attributes (vfs, filename,
2977 /* Don't set error multiple times */