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 * SPDX-License-Identifier: LGPL-2.1-or-later
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General
20 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 * Author: Alexander Larsson <alexl@redhat.com>
29 #ifdef HAVE_SYS_TIME_H
32 #include <sys/types.h>
42 #include <selinux/selinux.h>
47 #if defined HAVE_SYS_XATTR_H
48 #include <sys/xattr.h>
49 #elif defined HAVE_ATTR_XATTR_H
50 #include <attr/xattr.h>
52 #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
53 #endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
55 #endif /* HAVE_XATTR */
57 #include <glib/gstdio.h>
58 #include <glib/gstdioprivate.h>
59 #include <gfileattribute-priv.h>
60 #include <gfileinfo-priv.h>
65 #include "glib-unix.h"
68 #include "glib-private.h"
70 #include "thumbnail-verify.h"
82 #define X_OK 0 /* not really */
85 #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
88 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
91 #define S_IXUSR _S_IEXEC
99 #include "glocalfileinfo.h"
100 #include "gioerror.h"
101 #include "gthemedicon.h"
102 #include "gcontenttypeprivate.h"
103 #include "glibintl.h"
106 struct ThumbMD5Context {
109 unsigned char in[64];
119 G_LOCK_DEFINE_STATIC (uid_cache);
120 static GHashTable *uid_cache = NULL;
122 G_LOCK_DEFINE_STATIC (gid_cache);
123 static GHashTable *gid_cache = NULL;
125 #endif /* !G_OS_WIN32 */
128 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
130 glong sec, usec, nsec;
132 g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL);
134 sec = _g_stat_mtime (statbuf);
135 usec = _g_stat_mtim_nsec (statbuf) / 1000;
136 nsec = _g_stat_mtim_nsec (statbuf);
138 return g_strdup_printf ("%lu:%lu:%lu", sec, usec, nsec);
142 _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
146 ino = statbuf->file_index;
148 ino = _g_stat_ino (statbuf);
150 return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
151 (guint64) _g_stat_dev (statbuf),
156 _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
158 return g_strdup_printf ("l%" G_GUINT64_FORMAT,
159 (guint64) _g_stat_dev (statbuf));
162 #if defined (S_ISLNK) || defined (G_OS_WIN32)
165 read_link (const gchar *full_name)
167 #if defined (HAVE_READLINK)
172 buffer = g_malloc (size);
178 read_size = readlink (full_name, buffer, size);
184 if ((gsize) read_size < size)
186 buffer[read_size] = 0;
190 buffer = g_realloc (buffer, size);
192 #elif defined (G_OS_WIN32)
196 read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, NULL, 0, &buffer, TRUE);
199 else if (read_size == 0)
208 #endif /* S_ISLNK || G_OS_WIN32 */
211 /* Get the SELinux security context */
213 get_selinux_context (const char *path,
215 GFileAttributeMatcher *attribute_matcher,
216 gboolean follow_symlinks)
220 if (!_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
223 if (is_selinux_enabled ())
227 if (lgetfilecon_raw (path, &context) < 0)
232 if (getfilecon_raw (path, &context) < 0)
238 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
247 /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
248 * (Mac) getxattr(..., XATTR_NOFOLLOW)
250 #ifdef HAVE_XATTR_NOFOLLOW
251 #define g_fgetxattr(fd,name,value,size) fgetxattr(fd,name,value,size,0,0)
252 #define g_flistxattr(fd,name,size) flistxattr(fd,name,size,0)
253 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
254 #define g_removexattr(path,name) removexattr(path,name,0)
256 #define g_fgetxattr fgetxattr
257 #define g_flistxattr flistxattr
258 #define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
259 #define g_removexattr(path,name) removexattr(path,name)
263 g_getxattr (const char *path, const char *name, void *value, size_t size,
264 gboolean follow_symlinks)
266 #ifdef HAVE_XATTR_NOFOLLOW
267 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
270 return getxattr (path, name, value, size);
272 return lgetxattr (path, name, value, size);
277 g_listxattr(const char *path, char *namebuf, size_t size,
278 gboolean follow_symlinks)
280 #ifdef HAVE_XATTR_NOFOLLOW
281 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
284 return listxattr (path, namebuf, size);
286 return llistxattr (path, namebuf, size);
293 return c >= 32 && c <= 126 && c != '\\';
297 name_is_valid (const char *str)
301 if (!valid_char (*str++))
308 hex_escape_buffer (const char *str,
310 gboolean *free_return)
312 size_t num_invalid, i;
313 char *escaped_str, *p;
315 static char *hex_digits = "0123456789abcdef";
318 for (i = 0; i < len; i++)
320 if (!valid_char (str[i]))
324 if (num_invalid == 0)
326 *free_return = FALSE;
330 escaped_str = g_malloc (len + num_invalid*3 + 1);
333 for (i = 0; i < len; i++)
335 if (valid_char (str[i]))
342 *p++ = hex_digits[(c >> 4) & 0xf];
343 *p++ = hex_digits[c & 0xf];
353 hex_escape_string (const char *str,
354 gboolean *free_return)
356 return hex_escape_buffer (str, strlen (str), free_return);
360 hex_unescape_string (const char *str,
362 gboolean *free_return)
365 char *unescaped_str, *p;
371 if (strchr (str, '\\') == NULL)
375 *free_return = FALSE;
379 unescaped_str = g_malloc (len + 1);
382 for (i = 0; i < len; i++)
384 if (str[i] == '\\' &&
389 (g_ascii_xdigit_value (str[i+2]) << 4) |
390 g_ascii_xdigit_value (str[i+3]);
398 *out_len = p - unescaped_str;
402 return unescaped_str;
406 escape_xattr (GFileInfo *info,
407 const char *gio_attr, /* gio attribute name */
408 const char *value, /* Is zero terminated */
409 size_t len /* not including zero termination */)
412 gboolean free_escaped_val;
414 escaped_val = hex_escape_buffer (value, len, &free_escaped_val);
416 g_file_info_set_attribute_string (info, gio_attr, escaped_val);
418 if (free_escaped_val)
419 g_free (escaped_val);
423 get_one_xattr (const char *path,
425 const char *gio_attr,
427 gboolean follow_symlinks)
434 len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
440 else if (len == -1 && errsv == ERANGE)
442 len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
447 value_p = g_malloc (len+1);
449 len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
463 escape_xattr (info, gio_attr, value_p, len);
465 if (value_p != value)
469 #endif /* defined HAVE_XATTR */
472 get_xattrs (const char *path,
475 GFileAttributeMatcher *matcher,
476 gboolean follow_symlinks)
481 gssize list_res_size;
484 const char *attr, *attr2;
487 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
489 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
495 list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
497 if (list_res_size == -1 ||
501 list_size = list_res_size;
502 list = g_malloc (list_size);
506 list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
509 if (list_res_size == -1 && errsv == ERANGE)
511 list_size = list_size * 2;
512 list = g_realloc (list, list_size);
516 if (list_res_size == -1)
523 while (list_res_size > 0)
525 if ((user && g_str_has_prefix (attr, "user.")) ||
526 (!user && !g_str_has_prefix (attr, "user.")))
528 char *escaped_attr, *gio_attr;
529 gboolean free_escaped_attr;
533 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
534 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
538 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
539 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
542 if (free_escaped_attr)
543 g_free (escaped_attr);
545 get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
550 len = strlen (attr) + 1;
552 list_res_size -= len;
559 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
561 char *unescaped_attribute, *a;
562 gboolean free_unescaped_attribute;
564 attr2 = strchr (attr, ':');
567 attr2 += 2; /* Skip '::' */
568 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
570 a = g_strconcat ("user.", unescaped_attribute, NULL);
572 a = unescaped_attribute;
574 get_one_xattr (path, info, attr, a, follow_symlinks);
579 if (free_unescaped_attribute)
580 g_free (unescaped_attribute);
584 #endif /* defined HAVE_XATTR */
589 get_one_xattr_from_fd (int fd,
591 const char *gio_attr,
599 len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
605 else if (len == -1 && errsv == ERANGE)
607 len = g_fgetxattr (fd, xattr, NULL, 0);
612 value_p = g_malloc (len + 1);
614 len = g_fgetxattr (fd, xattr, value_p, len);
628 escape_xattr (info, gio_attr, value_p, len);
630 if (value_p != value)
633 #endif /* defined HAVE_XATTR */
636 get_xattrs_from_fd (int fd,
639 GFileAttributeMatcher *matcher)
644 gssize list_res_size;
647 const char *attr, *attr2;
650 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
652 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
658 list_res_size = g_flistxattr (fd, NULL, 0);
660 if (list_res_size == -1 ||
664 list_size = list_res_size;
665 list = g_malloc (list_size);
669 list_res_size = g_flistxattr (fd, list, list_size);
672 if (list_res_size == -1 && errsv == ERANGE)
674 list_size = list_size * 2;
675 list = g_realloc (list, list_size);
679 if (list_res_size == -1)
686 while (list_res_size > 0)
688 if ((user && g_str_has_prefix (attr, "user.")) ||
689 (!user && !g_str_has_prefix (attr, "user.")))
691 char *escaped_attr, *gio_attr;
692 gboolean free_escaped_attr;
696 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
697 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
701 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
702 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
705 if (free_escaped_attr)
706 g_free (escaped_attr);
708 get_one_xattr_from_fd (fd, info, gio_attr, attr);
712 len = strlen (attr) + 1;
714 list_res_size -= len;
721 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
723 char *unescaped_attribute, *a;
724 gboolean free_unescaped_attribute;
726 attr2 = strchr (attr, ':');
729 attr2++; /* Skip ':' */
730 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
732 a = g_strconcat ("user.", unescaped_attribute, NULL);
734 a = unescaped_attribute;
736 get_one_xattr_from_fd (fd, info, attr, a);
741 if (free_unescaped_attribute)
742 g_free (unescaped_attribute);
746 #endif /* defined HAVE_XATTR */
751 set_xattr (char *filename,
752 const char *escaped_attribute,
753 const GFileAttributeValue *attr_value,
756 char *attribute, *value;
757 gboolean free_attribute, free_value;
758 int val_len, res, errsv;
762 if (attr_value == NULL)
764 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
765 _("Attribute value must be non-NULL"));
769 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING && attr_value->type != G_FILE_ATTRIBUTE_TYPE_INVALID)
771 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
772 _("Invalid attribute type (string or invalid expected)"));
776 if (!name_is_valid (escaped_attribute))
778 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
779 _("Invalid extended attribute name"));
783 if (g_str_has_prefix (escaped_attribute, "xattr::"))
785 escaped_attribute += strlen ("xattr::");
790 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
791 escaped_attribute += strlen ("xattr-sys::");
795 attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
798 a = g_strconcat ("user.", attribute, NULL);
802 if (attr_value->type == G_FILE_ATTRIBUTE_TYPE_STRING)
804 value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
805 res = g_setxattr (filename, a, value, val_len);
812 res = g_removexattr (filename, a);
828 g_set_error (error, G_IO_ERROR,
829 g_io_error_from_errno (errsv),
830 _("Error setting extended attribute “%s”: %s"),
831 escaped_attribute, g_strerror (errsv));
842 _g_local_file_info_get_parent_info (const char *dir,
843 GFileAttributeMatcher *attribute_matcher,
844 GLocalParentFileInfo *parent_info)
849 parent_info->extra_data = NULL;
850 parent_info->free_extra_data = NULL;
851 parent_info->writable = FALSE;
852 parent_info->is_sticky = FALSE;
853 parent_info->has_trash_dir = FALSE;
854 parent_info->device = 0;
855 parent_info->inode = 0;
857 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) ||
858 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) ||
859 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH) ||
860 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
862 /* FIXME: Windows: The underlying _waccess() call in the C
863 * library is mostly pointless as it only looks at the READONLY
864 * FAT-style attribute of the file, it doesn't check the ACL at
867 parent_info->writable = (g_access (dir, W_OK) == 0);
869 res = g_stat (dir, &statbuf);
872 * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
873 * renamed or deleted only by the owner of the file, by the owner of the directory, and
874 * by a privileged process.
879 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
881 parent_info->is_sticky = FALSE;
883 parent_info->owner = statbuf.st_uid;
884 parent_info->device = statbuf.st_dev;
885 parent_info->inode = statbuf.st_ino;
886 /* No need to find trash dir if it's not writable anyway */
887 if (parent_info->writable &&
888 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
889 parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev);
895 _g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
897 if (parent_info->extra_data &&
898 parent_info->free_extra_data)
899 parent_info->free_extra_data (parent_info->extra_data);
903 get_access_rights (GFileAttributeMatcher *attribute_matcher,
906 GLocalFileStat *statbuf,
907 GLocalParentFileInfo *parent_info)
909 /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
910 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
911 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ))
912 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ,
913 g_access (path, R_OK) == 0);
915 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
916 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE))
917 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE,
918 g_access (path, W_OK) == 0);
920 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
921 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE))
922 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE,
923 g_access (path, X_OK) == 0);
931 if (parent_info->writable)
936 if (parent_info->is_sticky)
938 uid_t uid = geteuid ();
940 if (uid == _g_stat_uid (statbuf) ||
941 uid == (uid_t) parent_info->owner ||
950 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME))
951 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME,
954 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE))
955 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
958 if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
959 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
960 writable && parent_info->has_trash_dir);
965 set_info_from_stat (GFileInfo *info,
966 GLocalFileStat *statbuf,
967 GFileAttributeMatcher *attribute_matcher)
971 file_type = G_FILE_TYPE_UNKNOWN;
973 if (S_ISREG (_g_stat_mode (statbuf)))
974 file_type = G_FILE_TYPE_REGULAR;
975 else if (S_ISDIR (_g_stat_mode (statbuf)))
976 file_type = G_FILE_TYPE_DIRECTORY;
978 else if (S_ISCHR (_g_stat_mode (statbuf)) ||
979 S_ISBLK (_g_stat_mode (statbuf)) ||
980 S_ISFIFO (_g_stat_mode (statbuf))
982 || S_ISSOCK (_g_stat_mode (statbuf))
985 file_type = G_FILE_TYPE_SPECIAL;
988 else if (S_ISLNK (_g_stat_mode (statbuf)))
989 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
990 #elif defined (G_OS_WIN32)
991 else if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK ||
992 statbuf->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
993 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
996 g_file_info_set_file_type (info, file_type);
997 g_file_info_set_size (info, _g_stat_size (statbuf));
999 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, _g_stat_dev (statbuf));
1000 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, _g_stat_nlink (statbuf));
1002 /* Pointless setting these on Windows even if they exist in the struct */
1003 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, _g_stat_ino (statbuf));
1004 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, _g_stat_uid (statbuf));
1005 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, _g_stat_gid (statbuf));
1006 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, _g_stat_rdev (statbuf));
1008 /* Mostly pointless on Windows.
1009 * Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks.
1011 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, _g_stat_mode (statbuf));
1012 #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
1013 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, _g_stat_blksize (statbuf));
1015 #if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
1016 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, _g_stat_blocks (statbuf));
1017 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
1018 _g_stat_blocks (statbuf) * G_GUINT64_CONSTANT (512));
1019 #elif defined (G_OS_WIN32)
1020 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
1021 statbuf->allocated_size);
1025 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, _g_stat_mtime (statbuf));
1026 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, _g_stat_mtim_nsec (statbuf) / 1000);
1027 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC, _g_stat_mtim_nsec (statbuf));
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 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, _g_stat_atim_nsec (statbuf) / 1000);
1033 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_NSEC, _g_stat_atim_nsec (statbuf));
1037 /* Microsoft uses st_ctime for file creation time,
1038 * instead of file change time:
1039 * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions#generic-text-routine-mappings
1040 * Thank you, Microsoft!
1042 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, _g_stat_ctime (statbuf));
1043 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, _g_stat_ctim_nsec (statbuf) / 1000);
1044 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_NSEC, _g_stat_ctim_nsec (statbuf));
1047 #if defined (HAVE_STATX)
1048 if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_BTIME))
1050 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->stx_btime.tv_sec);
1051 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->stx_btime.tv_nsec / 1000);
1052 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->stx_btime.tv_nsec);
1054 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
1055 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
1056 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtimensec / 1000);
1057 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->st_birthtimensec);
1058 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
1059 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim.tv_sec);
1060 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtim.tv_nsec / 1000);
1061 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->st_birthtim.tv_nsec);
1062 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME)
1063 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
1064 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM)
1065 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim);
1066 #elif defined (G_OS_WIN32)
1067 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_ctim.tv_sec);
1068 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_ctim.tv_nsec / 1000);
1069 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, statbuf->st_ctim.tv_nsec);
1072 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1073 G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
1075 char *etag = _g_local_file_info_create_etag (statbuf);
1076 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ETAG_VALUE, etag);
1080 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1081 G_FILE_ATTRIBUTE_ID_ID_FILE))
1083 char *id = _g_local_file_info_create_file_id (statbuf);
1084 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILE, id);
1088 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1089 G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
1091 char *id = _g_local_file_info_create_fs_id (statbuf);
1092 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM, id);
1100 make_valid_utf8 (const char *name)
1103 const gchar *remainder, *invalid;
1104 gsize remaining_bytes, valid_bytes;
1108 remaining_bytes = strlen (name);
1110 while (remaining_bytes != 0)
1112 if (g_utf8_validate_len (remainder, remaining_bytes, &invalid))
1114 valid_bytes = invalid - remainder;
1117 string = g_string_sized_new (remaining_bytes);
1119 g_string_append_len (string, remainder, valid_bytes);
1120 /* append U+FFFD REPLACEMENT CHARACTER */
1121 g_string_append (string, "\357\277\275");
1123 remaining_bytes -= valid_bytes + 1;
1124 remainder = invalid + 1;
1128 return g_strdup (name);
1130 g_string_append (string, remainder);
1132 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
1134 return g_string_free (string, FALSE);
1138 convert_pwd_string_to_utf8 (char *pwd_str)
1142 if (!g_utf8_validate (pwd_str, -1, NULL))
1144 utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
1145 if (utf8_string == NULL)
1146 utf8_string = make_valid_utf8 (pwd_str);
1149 utf8_string = g_strdup (pwd_str);
1155 uid_data_free (UidData *data)
1157 g_free (data->user_name);
1158 g_free (data->real_name);
1162 /* called with lock held */
1164 lookup_uid_data (uid_t uid)
1168 struct passwd pwbuf;
1169 struct passwd *pwbufp;
1171 char *gecos, *comma;
1174 if (uid_cache == NULL)
1175 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
1177 data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
1182 data = g_new0 (UidData, 1);
1184 #if defined(HAVE_GETPWUID_R)
1185 getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
1187 pwbufp = getpwuid (uid);
1192 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
1193 data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
1196 gecos = pwbufp->pw_gecos;
1200 comma = strchr (gecos, ',');
1203 data->real_name = convert_pwd_string_to_utf8 (gecos);
1208 /* Default fallbacks */
1209 if (data->real_name == NULL)
1211 if (data->user_name != NULL)
1212 data->real_name = g_strdup (data->user_name);
1214 data->real_name = g_strdup_printf ("user #%d", (int)uid);
1217 if (data->user_name == NULL)
1218 data->user_name = g_strdup_printf ("%d", (int)uid);
1220 g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
1226 get_username_from_uid (uid_t uid)
1232 data = lookup_uid_data (uid);
1233 res = g_strdup (data->user_name);
1234 G_UNLOCK (uid_cache);
1240 get_realname_from_uid (uid_t uid)
1246 data = lookup_uid_data (uid);
1247 res = g_strdup (data->real_name);
1248 G_UNLOCK (uid_cache);
1253 /* called with lock held */
1255 lookup_gid_name (gid_t gid)
1258 #if defined (HAVE_GETGRGID_R)
1262 struct group *gbufp;
1264 if (gid_cache == NULL)
1265 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
1267 name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
1272 #if defined (HAVE_GETGRGID_R)
1273 getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
1275 gbufp = getgrgid (gid);
1278 if (gbufp != NULL &&
1279 gbufp->gr_name != NULL &&
1280 gbufp->gr_name[0] != 0)
1281 name = convert_pwd_string_to_utf8 (gbufp->gr_name);
1283 name = g_strdup_printf("%d", (int)gid);
1285 g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
1291 get_groupname_from_gid (gid_t gid)
1297 name = lookup_gid_name (gid);
1298 res = g_strdup (name);
1299 G_UNLOCK (gid_cache);
1303 #endif /* !G_OS_WIN32 */
1306 get_content_type (const char *basename,
1308 GLocalFileStat *statbuf,
1309 gboolean is_symlink,
1310 gboolean symlink_broken,
1311 GFileQueryInfoFlags flags,
1315 (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
1316 return g_content_type_from_mime_type ("inode/symlink");
1317 else if (statbuf != NULL && S_ISDIR(_g_stat_mode (statbuf)))
1318 return g_content_type_from_mime_type ("inode/directory");
1320 else if (statbuf != NULL && S_ISCHR(_g_stat_mode (statbuf)))
1321 return g_content_type_from_mime_type ("inode/chardevice");
1322 else if (statbuf != NULL && S_ISBLK(_g_stat_mode (statbuf)))
1323 return g_content_type_from_mime_type ("inode/blockdevice");
1324 else if (statbuf != NULL && S_ISFIFO(_g_stat_mode (statbuf)))
1325 return g_content_type_from_mime_type ("inode/fifo");
1326 else if (statbuf != NULL && S_ISREG(_g_stat_mode (statbuf)) && _g_stat_size (statbuf) == 0)
1328 /* Don't sniff zero-length files in order to avoid reading files
1329 * that appear normal but are not (eg: files in /proc and /sys)
1331 return g_content_type_from_mime_type ("application/x-zerosize");
1335 else if (statbuf != NULL && S_ISSOCK(_g_stat_mode (statbuf)))
1336 return g_content_type_from_mime_type ("inode/socket");
1341 gboolean result_uncertain;
1343 content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
1345 #if !defined(G_OS_WIN32) && !defined(__APPLE__)
1346 if (!fast && result_uncertain && path != NULL)
1348 /* Sniff the first 16KiB of the file (sometimes less, if xdgmime
1349 * says it doesn’t need so much). Most files need less than 4KiB of
1350 * sniffing, but some disk images need more (see
1351 * https://gitlab.gnome.org/GNOME/glib/-/issues/3186). */
1352 guchar sniff_buffer[16384];
1359 sniff_length = _g_unix_content_type_get_sniff_len ();
1360 if (sniff_length == 0 || sniff_length > sizeof (sniff_buffer))
1361 sniff_length = sizeof (sniff_buffer);
1364 fd = g_open (path, O_RDONLY | O_NOATIME | O_CLOEXEC, 0);
1366 if (fd < 0 && errsv == EPERM)
1368 fd = g_open (path, O_RDONLY | O_CLOEXEC, 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;
1391 THUMBNAIL_SIZE_AUTO,
1392 THUMBNAIL_SIZE_NORMAL,
1393 THUMBNAIL_SIZE_LARGE,
1394 THUMBNAIL_SIZE_XLARGE,
1395 THUMBNAIL_SIZE_XXLARGE,
1396 THUMBNAIL_SIZE_LAST,
1400 get_thumbnail_dirname_from_size (ThumbnailSize size)
1404 case THUMBNAIL_SIZE_AUTO:
1407 case THUMBNAIL_SIZE_NORMAL:
1410 case THUMBNAIL_SIZE_LARGE:
1413 case THUMBNAIL_SIZE_XLARGE:
1416 case THUMBNAIL_SIZE_XXLARGE:
1420 g_assert_not_reached ();
1423 g_return_val_if_reached (NULL);
1426 /* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
1428 get_thumbnail_attributes (const char *path,
1430 const GLocalFileStat *stat_buf,
1433 GChecksum *checksum;
1434 const char *dirname;
1436 char *filename = NULL;
1438 guint32 failed_attr_id;
1439 guint32 is_valid_attr_id;
1440 guint32 path_attr_id;
1444 case THUMBNAIL_SIZE_AUTO:
1445 failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED;
1446 is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID;
1447 path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH;
1449 case THUMBNAIL_SIZE_NORMAL:
1450 failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_NORMAL;
1451 is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_NORMAL;
1452 path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_NORMAL;
1454 case THUMBNAIL_SIZE_LARGE:
1455 failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_LARGE;
1456 is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_LARGE;
1457 path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_LARGE;
1459 case THUMBNAIL_SIZE_XLARGE:
1460 failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XLARGE;
1461 is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XLARGE;
1462 path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XLARGE;
1464 case THUMBNAIL_SIZE_XXLARGE:
1465 failed_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XXLARGE;
1466 is_valid_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XXLARGE;
1467 path_attr_id = G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XXLARGE;
1470 g_assert_not_reached ();
1473 dirname = get_thumbnail_dirname_from_size (size);
1474 uri = g_filename_to_uri (path, NULL, NULL);
1476 checksum = g_checksum_new (G_CHECKSUM_MD5);
1477 g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
1479 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
1480 g_checksum_free (checksum);
1484 filename = g_build_filename (g_get_user_cache_dir (),
1485 "thumbnails", dirname, basename,
1488 if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1489 g_clear_pointer (&filename, g_free);
1495 for (i = THUMBNAIL_SIZE_LAST - 1; i >= 0 ; i--)
1497 filename = g_build_filename (g_get_user_cache_dir (),
1499 get_thumbnail_dirname_from_size (i),
1502 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1505 g_clear_pointer (&filename, g_free);
1511 _g_file_info_set_attribute_byte_string_by_id (info, path_attr_id, filename);
1512 _g_file_info_set_attribute_boolean_by_id (info, is_valid_attr_id,
1513 thumbnail_verify (filename, uri, stat_buf));
1517 filename = g_build_filename (g_get_user_cache_dir (),
1518 "thumbnails", "fail",
1519 "gnome-thumbnail-factory",
1523 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1525 _g_file_info_set_attribute_boolean_by_id (info, failed_attr_id, TRUE);
1526 _g_file_info_set_attribute_boolean_by_id (info, is_valid_attr_id,
1527 thumbnail_verify (filename, uri, stat_buf));
1538 win32_get_file_user_info (const gchar *filename,
1543 PSECURITY_DESCRIPTOR psd = NULL;
1544 DWORD sd_size = 0; /* first call calculates the size required */
1546 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1547 if ((GetFileSecurityW (wfilename,
1548 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1551 &sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
1552 (psd = g_try_malloc (sd_size)) != NULL &&
1553 GetFileSecurityW (wfilename,
1554 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1561 SID_NAME_USE name_use = 0; /* don't care? */
1562 wchar_t *name = NULL;
1563 wchar_t *domain = NULL;
1565 DWORD domain_len = 0;
1566 /* get the user name */
1570 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
1572 if (!LookupAccountSidW (NULL, /* local machine */
1575 domain, &domain_len, /* no domain info yet */
1576 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1578 name = g_try_malloc (name_len * sizeof (wchar_t));
1579 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1580 if (name && domain &&
1581 LookupAccountSidW (NULL, /* local machine */
1584 domain, &domain_len, /* no domain info yet */
1587 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1593 /* get the group name */
1597 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
1599 if (!LookupAccountSidW (NULL, /* local machine */
1602 domain, &domain_len, /* no domain info yet */
1603 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1605 name = g_try_malloc (name_len * sizeof (wchar_t));
1606 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1607 if (name && domain &&
1608 LookupAccountSidW (NULL, /* local machine */
1611 domain, &domain_len, /* no domain info yet */
1614 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1620 /* TODO: get real name */
1626 #endif /* G_OS_WIN32 */
1629 /* support for '.hidden' files */
1630 G_LOCK_DEFINE_STATIC (hidden_cache);
1631 static GHashTable *hidden_cache;
1632 static GSource *hidden_cache_source = NULL; /* Under the hidden_cache lock */
1633 static guint hidden_cache_ttl_secs = 5;
1634 static guint hidden_cache_ttl_jitter_secs = 2;
1638 GHashTable *hidden_files;
1639 gint64 timestamp_secs;
1643 remove_from_hidden_cache (gpointer user_data)
1645 HiddenCacheData *data;
1646 GHashTableIter iter;
1648 gint64 timestamp_secs;
1650 G_LOCK (hidden_cache);
1651 timestamp_secs = g_source_get_time (hidden_cache_source) / G_USEC_PER_SEC;
1653 g_hash_table_iter_init (&iter, hidden_cache);
1654 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
1656 if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
1657 g_hash_table_iter_remove (&iter);
1660 if (g_hash_table_size (hidden_cache) == 0)
1662 g_clear_pointer (&hidden_cache_source, g_source_unref);
1663 retval = G_SOURCE_REMOVE;
1666 retval = G_SOURCE_CONTINUE;
1668 G_UNLOCK (hidden_cache);
1674 read_hidden_file (const gchar *dirname)
1676 gchar *contents = NULL;
1679 filename = g_build_path ("/", dirname, ".hidden", NULL);
1680 (void) g_file_get_contents (filename, &contents, NULL, NULL);
1683 if (contents != NULL)
1689 table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1691 lines = g_strsplit (contents, "\n", 0);
1694 for (i = 0; lines[i]; i++)
1695 /* hash table takes the individual strings... */
1696 g_hash_table_add (table, lines[i]);
1698 /* ... so we only free the container. */
1708 free_hidden_file_data (gpointer user_data)
1710 HiddenCacheData *data = user_data;
1712 g_clear_pointer (&data->hidden_files, g_hash_table_unref);
1717 file_is_hidden (const gchar *path,
1718 const gchar *basename)
1720 HiddenCacheData *data;
1725 dirname = g_path_get_dirname (path);
1727 G_LOCK (hidden_cache);
1729 if G_UNLIKELY (hidden_cache == NULL)
1730 hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1731 g_free, free_hidden_file_data);
1733 if (!g_hash_table_lookup_extended (hidden_cache, dirname,
1734 NULL, (gpointer *) &data))
1736 data = g_new0 (HiddenCacheData, 1);
1737 data->hidden_files = table = read_hidden_file (dirname);
1738 data->timestamp_secs = g_get_monotonic_time () / G_USEC_PER_SEC;
1740 g_hash_table_insert (hidden_cache,
1744 if (!hidden_cache_source)
1746 hidden_cache_source =
1747 g_timeout_source_new_seconds (hidden_cache_ttl_secs +
1748 hidden_cache_ttl_jitter_secs);
1749 g_source_set_priority (hidden_cache_source, G_PRIORITY_DEFAULT);
1750 g_source_set_static_name (hidden_cache_source,
1751 "[gio] remove_from_hidden_cache");
1752 g_source_set_callback (hidden_cache_source,
1753 remove_from_hidden_cache,
1755 g_source_attach (hidden_cache_source,
1756 GLIB_PRIVATE_CALL (g_get_worker_context) ());
1760 table = data->hidden_files;
1762 result = table != NULL && g_hash_table_contains (table, basename);
1764 G_UNLOCK (hidden_cache);
1770 #endif /* !G_OS_WIN32 */
1773 _g_local_file_info_get_nostat (GFileInfo *info,
1774 const char *basename,
1776 GFileAttributeMatcher *attribute_matcher)
1778 g_file_info_set_name (info, basename);
1780 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1781 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
1783 char *display_name = g_filename_display_basename (path);
1785 /* look for U+FFFD REPLACEMENT CHARACTER */
1786 if (strstr (display_name, "\357\277\275") != NULL)
1788 char *p = display_name;
1789 display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1792 g_file_info_set_display_name (info, display_name);
1793 g_free (display_name);
1796 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1797 G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
1799 char *edit_name = g_filename_display_basename (path);
1800 g_file_info_set_edit_name (info, edit_name);
1805 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1806 G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
1808 char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1810 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
1816 get_icon_name (const char *path,
1817 gboolean use_symbolic,
1818 gboolean *with_fallbacks_out)
1820 const char *name = NULL;
1821 gboolean with_fallbacks = TRUE;
1823 if (g_strcmp0 (path, g_get_home_dir ()) == 0)
1825 name = use_symbolic ? "user-home-symbolic" : "user-home";
1826 with_fallbacks = FALSE;
1828 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
1830 name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
1831 with_fallbacks = FALSE;
1833 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
1835 name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
1837 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
1839 name = use_symbolic ? "folder-download-symbolic" : "folder-download";
1841 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
1843 name = use_symbolic ? "folder-music-symbolic" : "folder-music";
1845 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
1847 name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
1849 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
1851 name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
1853 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
1855 name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
1857 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
1859 name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
1866 if (with_fallbacks_out != NULL)
1867 *with_fallbacks_out = with_fallbacks;
1873 get_icon (const char *path,
1874 const char *content_type,
1875 gboolean use_symbolic)
1878 const char *icon_name;
1879 gboolean with_fallbacks;
1881 icon_name = get_icon_name (path, use_symbolic, &with_fallbacks);
1882 if (icon_name != NULL)
1885 icon = g_themed_icon_new_with_default_fallbacks (icon_name);
1887 icon = g_themed_icon_new (icon_name);
1892 icon = g_content_type_get_symbolic_icon (content_type);
1894 icon = g_content_type_get_icon (content_type);
1901 _g_local_file_info_get (const char *basename,
1903 GFileAttributeMatcher *attribute_matcher,
1904 GFileQueryInfoFlags flags,
1905 GLocalParentFileInfo *parent_info,
1909 GLocalFileStat statbuf;
1910 GLocalFileStat statbuf2;
1913 gboolean is_symlink, symlink_broken;
1914 char *symlink_target;
1919 info = g_file_info_new ();
1921 /* Make sure we don't set any unwanted attributes */
1922 g_file_info_set_attribute_mask (info, attribute_matcher);
1924 _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
1926 if (attribute_matcher == NULL)
1928 g_file_info_unset_attribute_mask (info);
1932 res = g_local_file_lstat (path,
1933 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
1934 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
1941 /* Don't bail out if we get Permission denied (SELinux?) */
1942 if (errsv != EACCES)
1944 char *display_name = g_filename_display_name (path);
1945 g_object_unref (info);
1946 g_set_error (error, G_IO_ERROR,
1947 g_io_error_from_errno (errsv),
1948 _("Error when getting information for file “%s”: %s"),
1949 display_name, g_strerror (errsv));
1950 g_free (display_name);
1955 /* Even if stat() fails, try to get as much as other attributes possible */
1956 stat_ok = res != -1;
1959 device = _g_stat_dev (&statbuf);
1964 is_symlink = stat_ok && S_ISLNK (_g_stat_mode (&statbuf));
1965 #elif defined (G_OS_WIN32)
1966 /* glib already checked the FILE_ATTRIBUTE_REPARSE_POINT for us */
1967 is_symlink = stat_ok &&
1968 (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
1969 statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT);
1973 symlink_broken = FALSE;
1977 g_file_info_set_is_symlink (info, TRUE);
1979 /* Unless NOFOLLOW was set we default to following symlinks */
1980 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
1982 res = g_local_file_stat (path,
1983 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
1984 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
1987 /* Report broken links as symlinks */
1994 symlink_broken = TRUE;
1998 g_file_info_set_is_symlink (info, FALSE);
2001 set_info_from_stat (info, &statbuf, attribute_matcher);
2004 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2005 G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
2007 g_file_info_set_is_hidden (info,
2008 (basename != NULL &&
2009 (basename[0] == '.' ||
2010 file_is_hidden (path, basename) ||
2012 _g_local_file_is_lost_found_dir (path, _g_stat_dev (&statbuf))))));
2015 _g_file_info_set_attribute_boolean_by_id (info,
2016 G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP,
2017 basename != NULL && basename[strlen (basename) - 1] == '~' &&
2018 (stat_ok && S_ISREG (_g_stat_mode (&statbuf))));
2020 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, FALSE);
2022 g_file_info_set_is_hidden (info, (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN));
2024 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE,
2025 (statbuf.attributes & FILE_ATTRIBUTE_ARCHIVE));
2027 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM,
2028 (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM));
2030 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_MOUNTPOINT,
2031 (statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT));
2033 if (statbuf.reparse_tag != 0)
2034 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_REPARSE_POINT_TAG, statbuf.reparse_tag);
2036 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, FALSE);
2039 symlink_target = NULL;
2042 #if defined (S_ISLNK) || defined (G_OS_WIN32)
2043 symlink_target = read_link (path);
2045 if (symlink_target &&
2046 _g_file_attribute_matcher_matches_id (attribute_matcher,
2047 G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
2048 g_file_info_set_symlink_target (info, symlink_target);
2051 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2052 G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
2053 _g_file_attribute_matcher_matches_id (attribute_matcher,
2054 G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
2055 _g_file_attribute_matcher_matches_id (attribute_matcher,
2056 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
2058 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
2062 g_file_info_set_content_type (info, content_type);
2064 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2065 G_FILE_ATTRIBUTE_ID_STANDARD_ICON)
2066 || _g_file_attribute_matcher_matches_id (attribute_matcher,
2067 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
2071 /* non symbolic icon */
2072 icon = get_icon (path, content_type, FALSE);
2075 g_file_info_set_icon (info, icon);
2076 g_object_unref (icon);
2080 icon = get_icon (path, content_type, TRUE);
2083 g_file_info_set_symbolic_icon (info, icon);
2084 g_object_unref (icon);
2089 g_free (content_type);
2093 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2094 G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
2096 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
2100 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
2101 g_free (content_type);
2105 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2106 G_FILE_ATTRIBUTE_ID_OWNER_USER))
2111 win32_get_file_user_info (path, NULL, &name, NULL);
2114 name = get_username_from_uid (_g_stat_uid (&statbuf));
2117 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
2121 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2122 G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
2126 win32_get_file_user_info (path, NULL, NULL, &name);
2129 name = get_realname_from_uid (_g_stat_uid (&statbuf));
2132 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
2136 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2137 G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
2141 win32_get_file_user_info (path, &name, NULL, NULL);
2144 name = get_groupname_from_gid (_g_stat_gid (&statbuf));
2147 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
2151 if (stat_ok && parent_info && parent_info->device != 0 &&
2152 _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
2153 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT,
2154 (_g_stat_dev (&statbuf) != parent_info->device || _g_stat_ino (&statbuf) == parent_info->inode));
2157 get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
2160 get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2162 get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2163 get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2165 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2166 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH) ||
2167 _g_file_attribute_matcher_matches_id (attribute_matcher,
2168 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID) ||
2169 _g_file_attribute_matcher_matches_id (attribute_matcher,
2170 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED))
2172 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_AUTO);
2175 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2176 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_NORMAL) ||
2177 _g_file_attribute_matcher_matches_id (attribute_matcher,
2178 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_NORMAL) ||
2179 _g_file_attribute_matcher_matches_id (attribute_matcher,
2180 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_NORMAL))
2182 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_NORMAL);
2185 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2186 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_LARGE) ||
2187 _g_file_attribute_matcher_matches_id (attribute_matcher,
2188 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_LARGE) ||
2189 _g_file_attribute_matcher_matches_id (attribute_matcher,
2190 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_LARGE))
2192 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_LARGE);
2195 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2196 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XLARGE) ||
2197 _g_file_attribute_matcher_matches_id (attribute_matcher,
2198 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XLARGE) ||
2199 _g_file_attribute_matcher_matches_id (attribute_matcher,
2200 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XLARGE))
2202 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_XLARGE);
2205 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2206 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH_XXLARGE) ||
2207 _g_file_attribute_matcher_matches_id (attribute_matcher,
2208 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID_XXLARGE) ||
2209 _g_file_attribute_matcher_matches_id (attribute_matcher,
2210 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED_XXLARGE))
2212 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_XXLARGE);
2215 vfs = g_vfs_get_default ();
2216 class = G_VFS_GET_CLASS (vfs);
2217 if (class->local_file_add_info)
2219 class->local_file_add_info (vfs,
2225 &parent_info->extra_data,
2226 &parent_info->free_extra_data);
2229 g_file_info_unset_attribute_mask (info);
2231 g_free (symlink_target);
2237 _g_local_file_info_get_from_fd (int fd,
2238 const char *attributes,
2241 GLocalFileStat stat_buf;
2242 GFileAttributeMatcher *matcher;
2245 if (g_local_file_fstat (fd,
2246 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
2247 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
2252 g_set_error (error, G_IO_ERROR,
2253 g_io_error_from_errno (errsv),
2254 _("Error when getting information for file descriptor: %s"),
2255 g_strerror (errsv));
2259 info = g_file_info_new ();
2261 matcher = g_file_attribute_matcher_new (attributes);
2263 /* Make sure we don't set any unwanted attributes */
2264 g_file_info_set_attribute_mask (info, matcher);
2266 set_info_from_stat (info, &stat_buf, matcher);
2269 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
2270 is_selinux_enabled ())
2273 if (fgetfilecon_raw (fd, &context) >= 0)
2275 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
2281 get_xattrs_from_fd (fd, TRUE, info, matcher);
2282 get_xattrs_from_fd (fd, FALSE, info, matcher);
2284 g_file_attribute_matcher_unref (matcher);
2286 g_file_info_unset_attribute_mask (info);
2292 get_uint32 (const GFileAttributeValue *value,
2296 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
2298 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2299 _("Invalid attribute type (uint32 expected)"));
2303 *val_out = value->u.uint32;
2308 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
2310 get_uint64 (const GFileAttributeValue *value,
2314 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
2316 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2317 _("Invalid attribute type (uint64 expected)"));
2321 *val_out = value->u.uint64;
2327 #if defined(HAVE_SYMLINK)
2329 get_byte_string (const GFileAttributeValue *value,
2330 const char **val_out,
2333 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
2335 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2336 _("Invalid attribute type (byte string expected)"));
2340 *val_out = value->u.string;
2348 get_string (const GFileAttributeValue *value,
2349 const char **val_out,
2352 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
2354 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2355 _("Invalid attribute type (byte string expected)"));
2359 *val_out = value->u.string;
2366 set_unix_mode (char *filename,
2367 GFileQueryInfoFlags flags,
2368 const GFileAttributeValue *value,
2374 if (!get_uint32 (value, &val, error))
2377 #if defined (HAVE_SYMLINK) || defined (G_OS_WIN32)
2378 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
2380 res = lchmod (filename, val);
2382 gboolean is_symlink;
2384 struct stat statbuf;
2385 /* Calling chmod on a symlink changes permissions on the symlink.
2386 * We don't want to do this, so we need to check for a symlink */
2387 res = g_lstat (filename, &statbuf);
2388 is_symlink = (res == 0 && S_ISLNK (statbuf.st_mode));
2390 /* FIXME: implement lchmod for W32, should be doable */
2391 GWin32PrivateStat statbuf;
2393 res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (filename, &statbuf);
2394 is_symlink = (res == 0 &&
2395 (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
2396 statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT));
2400 g_set_error_literal (error, G_IO_ERROR,
2401 G_IO_ERROR_NOT_SUPPORTED,
2402 _("Cannot set permissions on symlinks"));
2406 res = g_chmod (filename, val);
2410 res = g_chmod (filename, val);
2416 g_set_error (error, G_IO_ERROR,
2417 g_io_error_from_errno (errsv),
2418 _("Error setting permissions: %s"),
2419 g_strerror (errsv));
2427 set_unix_uid_gid (char *filename,
2428 const GFileAttributeValue *uid_value,
2429 const GFileAttributeValue *gid_value,
2430 GFileQueryInfoFlags flags,
2440 if (!get_uint32 (uid_value, &val, error))
2449 if (!get_uint32 (gid_value, &val, error))
2457 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
2458 res = lchown (filename, uid, gid);
2461 res = chown (filename, uid, gid);
2467 g_set_error (error, G_IO_ERROR,
2468 g_io_error_from_errno (errsv),
2469 _("Error setting owner: %s"),
2470 g_strerror (errsv));
2479 set_symlink (char *filename,
2480 const GFileAttributeValue *value,
2484 struct stat statbuf;
2486 if (!get_byte_string (value, &val, error))
2491 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2492 _("symlink must be non-NULL"));
2496 if (g_lstat (filename, &statbuf))
2500 g_set_error (error, G_IO_ERROR,
2501 g_io_error_from_errno (errsv),
2502 _("Error setting symlink: %s"),
2503 g_strerror (errsv));
2507 if (!S_ISLNK (statbuf.st_mode))
2509 g_set_error_literal (error, G_IO_ERROR,
2510 G_IO_ERROR_NOT_SYMBOLIC_LINK,
2511 _("Error setting symlink: file is not a symlink"));
2515 if (g_unlink (filename))
2519 g_set_error (error, G_IO_ERROR,
2520 g_io_error_from_errno (errsv),
2521 _("Error setting symlink: %s"),
2522 g_strerror (errsv));
2526 if (symlink (filename, val) != 0)
2530 g_set_error (error, G_IO_ERROR,
2531 g_io_error_from_errno (errsv),
2532 _("Error setting symlink: %s"),
2533 g_strerror (errsv));
2541 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined(G_OS_WIN32)
2543 lazy_stat (const char *filename,
2545 gboolean *called_stat)
2552 res = g_stat (filename, statbuf);
2555 *called_stat = TRUE;
2561 #if defined (G_OS_WIN32)
2563 * https://support.microsoft.com/en-ca/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime
2564 * FT = UT * 10000000 + 116444736000000000.
2565 * Converts unix epoch time (a signed 64-bit integer) to FILETIME.
2566 * Can optionally use a more precise timestamp that has
2567 * a fraction of a second expressed in nanoseconds.
2568 * UT must be between January 1st of year 1601 and December 31st of year 30827.
2569 * nsec must be non-negative and < 1000000000.
2570 * Returns TRUE if conversion succeeded, FALSE otherwise.
2572 * The function that does the reverse can be found in
2576 _g_win32_unix_time_to_filetime (gint64 ut,
2582 /* 1 unit of FILETIME is 100ns */
2583 const gint64 hundreds_of_nsec_per_sec = 10000000;
2584 /* The difference between January 1, 1601 UTC (FILETIME epoch) and UNIX epoch
2585 * in hundreds of nanoseconds.
2587 const gint64 filetime_unix_epoch_offset = 116444736000000000;
2588 /* This is the maximum timestamp that SYSTEMTIME can
2589 * represent (last millisecond of the year 30827).
2590 * Since FILETIME and SYSTEMTIME are both used on Windows,
2591 * we use this as a limit (FILETIME can support slightly
2592 * larger interval, up to year 30828).
2594 const gint64 max_systemtime = 0x7fff35f4f06c58f0;
2596 g_return_val_if_fail (ft != NULL, FALSE);
2597 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2601 g_set_error (error, G_IO_ERROR,
2602 G_IO_ERROR_INVALID_DATA,
2603 _("Extra nanoseconds %d for UNIX timestamp %lld are negative"),
2608 if (nsec >= hundreds_of_nsec_per_sec * 100)
2610 g_set_error (error, G_IO_ERROR,
2611 G_IO_ERROR_INVALID_DATA,
2612 _("Extra nanoseconds %d for UNIX timestamp %lld reach 1 second"),
2617 if (ut >= (G_MAXINT64 / hundreds_of_nsec_per_sec) ||
2618 (ut * hundreds_of_nsec_per_sec) >= (G_MAXINT64 - filetime_unix_epoch_offset))
2620 g_set_error (error, G_IO_ERROR,
2621 G_IO_ERROR_INVALID_DATA,
2622 _("UNIX timestamp %lld does not fit into 64 bits"),
2627 result = ut * hundreds_of_nsec_per_sec + filetime_unix_epoch_offset + nsec / 100;
2629 if (result >= max_systemtime || result < 0)
2631 g_set_error (error, G_IO_ERROR,
2632 G_IO_ERROR_INVALID_DATA,
2633 _("UNIX timestamp %lld is outside of the range supported by Windows"),
2638 ft->dwLowDateTime = (DWORD) (result);
2639 ft->dwHighDateTime = (DWORD) (result >> 32);
2645 set_mtime_atime (const char *filename,
2646 const GFileAttributeValue *mtime_value,
2647 const GFileAttributeValue *mtime_usec_value,
2648 const GFileAttributeValue *mtime_nsec_value,
2649 const GFileAttributeValue *atime_value,
2650 const GFileAttributeValue *atime_usec_value,
2651 const GFileAttributeValue *atime_nsec_value,
2656 guint32 val_usec = 0;
2657 guint32 val_nsec = 0;
2658 gunichar2 *filename_utf16;
2659 SECURITY_ATTRIBUTES sec = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
2663 FILETIME *p_mtime = NULL;
2664 FILETIME *p_atime = NULL;
2667 gboolean got_stat = FALSE;
2672 if (!get_uint64 (atime_value, &val, error))
2679 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2681 val = statbuf.st_atime;
2682 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2683 val_nsec = statbuf.st_atimensec;
2684 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2685 val_nsec = statbuf.st_atim.tv_nsec;
2690 if (atime_usec_value &&
2691 !get_uint32 (atime_usec_value, &val_usec, error))
2694 /* Convert to nanoseconds. Clamp the usec value if it’s going to overflow,
2695 * as %G_MAXINT32 will trigger a ‘too big’ error in
2696 * _g_win32_unix_time_to_filetime() anyway. */
2697 val_nsec = (val_usec > G_MAXINT32 / 1000) ? G_MAXINT32 : (val_usec * 1000);
2699 if (atime_nsec_value &&
2700 !get_uint32 (atime_nsec_value, &val_nsec, error))
2704 if (!_g_win32_unix_time_to_filetime (val, val_nsec, &atime, error))
2709 if (!_g_win32_unix_time_to_filetime (val, val_usec, &atime, error))
2718 if (!get_uint64 (mtime_value, &val, error))
2725 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2727 val = statbuf.st_mtime;
2728 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2729 val_nsec = statbuf.st_mtimensec;
2730 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2731 val_nsec = statbuf.st_mtim.tv_nsec;
2736 if (mtime_usec_value &&
2737 !get_uint32 (mtime_usec_value, &val_usec, error))
2740 /* Convert to nanoseconds. Clamp the usec value if it’s going to overflow,
2741 * as %G_MAXINT32 will trigger a ‘too big’ error in
2742 * _g_win32_unix_time_to_filetime() anyway. */
2743 val_nsec = (val_usec > G_MAXINT32 / 1000) ? G_MAXINT32 : (val_usec * 1000);
2745 if (mtime_nsec_value &&
2746 !get_uint32 (mtime_nsec_value, &val_nsec, error))
2750 if (!_g_win32_unix_time_to_filetime (val, val_nsec, &mtime, error))
2755 if (!_g_win32_unix_time_to_filetime (val, val_usec, &mtime, error))
2760 filename_utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2762 if (filename_utf16 == NULL)
2764 g_prefix_error (error,
2765 _("File name “%s” cannot be converted to UTF-16"),
2770 file_handle = CreateFileW (filename_utf16,
2771 FILE_WRITE_ATTRIBUTES,
2772 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
2775 FILE_FLAG_BACKUP_SEMANTICS,
2777 gle = GetLastError ();
2778 g_clear_pointer (&filename_utf16, g_free);
2780 if (file_handle == INVALID_HANDLE_VALUE)
2782 g_set_error (error, G_IO_ERROR,
2783 g_io_error_from_errno (gle),
2784 _("File “%s” cannot be opened: Windows Error %lu"),
2790 res = SetFileTime (file_handle, NULL, p_atime, p_mtime);
2791 gle = GetLastError ();
2792 CloseHandle (file_handle);
2795 g_set_error (error, G_IO_ERROR,
2796 g_io_error_from_errno (gle),
2797 _("Error setting modification or access time for file “%s”: %lu"),
2802 #elif defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT)
2804 set_mtime_atime (char *filename,
2805 const GFileAttributeValue *mtime_value,
2806 const GFileAttributeValue *mtime_usec_value,
2807 const GFileAttributeValue *mtime_nsec_value,
2808 const GFileAttributeValue *atime_value,
2809 const GFileAttributeValue *atime_usec_value,
2810 const GFileAttributeValue *atime_nsec_value,
2816 gboolean got_stat = FALSE;
2817 #ifdef HAVE_UTIMENSAT
2818 struct timespec times_n[2] = { {0, 0}, {0, 0} };
2822 if (!get_uint64 (atime_value, &val, error))
2824 times_n[0].tv_sec = val;
2828 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2830 times_n[0].tv_sec = statbuf.st_atime;
2831 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2832 times_n[0].tv_nsec = statbuf.st_atimensec;
2833 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2834 times_n[0].tv_nsec = statbuf.st_atim.tv_nsec;
2839 if (atime_usec_value)
2841 guint32 val_usec = 0;
2843 if (!get_uint32 (atime_usec_value, &val_usec, error))
2846 times_n[0].tv_nsec = val_usec * 1000;
2849 if (atime_nsec_value)
2851 guint32 val_nsec = 0;
2853 if (!get_uint32 (atime_nsec_value, &val_nsec, error))
2855 times_n[0].tv_nsec = val_nsec;
2861 if (!get_uint64 (mtime_value, &val, error))
2863 times_n[1].tv_sec = val;
2867 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2869 times_n[1].tv_sec = statbuf.st_mtime;
2870 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2871 times_n[1].tv_nsec = statbuf.st_mtimensec;
2872 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2873 times_n[1].tv_nsec = statbuf.st_mtim.tv_nsec;
2878 if (mtime_usec_value)
2880 guint32 val_usec = 0;
2882 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2885 times_n[1].tv_nsec = val_usec * 1000;
2888 if (mtime_nsec_value)
2890 guint32 val_nsec = 0;
2892 if (!get_uint32 (mtime_nsec_value, &val_nsec, error))
2894 times_n[1].tv_nsec = val_nsec;
2897 res = utimensat (AT_FDCWD, filename, times_n, 0);
2899 #else /* HAVE_UTIMES */
2901 struct timeval times[2] = { {0, 0}, {0, 0} };
2906 if (!get_uint64 (atime_value, &val, error))
2909 times[0].tv_sec = val;
2913 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2915 times[0].tv_sec = statbuf.st_atime;
2916 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2917 times[0].tv_usec = statbuf.st_atimensec / 1000;
2918 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2919 times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
2924 if (atime_usec_value)
2926 guint32 val_usec = 0;
2928 if (!get_uint32 (atime_usec_value, &val_usec, error))
2931 times[0].tv_usec = val_usec;
2937 if (!get_uint64 (mtime_value, &val, error))
2940 times[1].tv_sec = val;
2944 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2946 times[1].tv_sec = statbuf.st_mtime;
2947 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2948 times[1].tv_usec = statbuf.st_mtimensec / 1000;
2949 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2950 times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
2955 if (mtime_usec_value)
2957 guint32 val_usec = 0;
2959 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2962 times[1].tv_usec = val_usec;
2965 res = utimes (filename, times);
2972 g_set_error (error, G_IO_ERROR,
2973 g_io_error_from_errno (errsv),
2974 _("Error setting modification or access time: %s"),
2975 g_strerror (errsv));
2985 set_selinux_context (char *filename,
2986 const GFileAttributeValue *value,
2991 if (!get_string (value, &val, error))
2996 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2997 _("SELinux context must be non-NULL"));
3001 if (!is_selinux_enabled ())
3003 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
3004 _("SELinux is not enabled on this system"));
3008 if (setfilecon_raw (filename, val) < 0)
3012 g_set_error (error, G_IO_ERROR,
3013 g_io_error_from_errno (errsv),
3014 _("Error setting SELinux context: %s"),
3015 g_strerror (errsv));
3025 _g_local_file_info_set_attribute (char *filename,
3026 const char *attribute,
3027 GFileAttributeType type,
3029 GFileQueryInfoFlags flags,
3030 GCancellable *cancellable,
3033 GFileAttributeValue value = { 0 };
3037 _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
3039 if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
3040 return set_unix_mode (filename, flags, &value, error);
3043 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
3044 return set_unix_uid_gid (filename, &value, NULL, flags, error);
3045 else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
3046 return set_unix_uid_gid (filename, NULL, &value, flags, error);
3050 else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
3051 return set_symlink (filename, &value, error);
3054 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
3055 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
3056 return set_mtime_atime (filename, &value, NULL, NULL, NULL, NULL, NULL, error);
3057 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
3058 return set_mtime_atime (filename, NULL, &value, NULL, NULL, NULL, NULL, error);
3059 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC) == 0)
3060 return set_mtime_atime (filename, NULL, NULL, &value, NULL, NULL, NULL, error);
3061 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
3062 return set_mtime_atime (filename, NULL, NULL, NULL, &value, NULL, NULL, error);
3063 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
3064 return set_mtime_atime (filename, NULL, NULL, NULL, NULL, &value, NULL, error);
3065 else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC) == 0)
3066 return set_mtime_atime (filename, NULL, NULL, NULL, NULL, NULL, &value, error);
3070 else if (g_str_has_prefix (attribute, "xattr::"))
3071 return set_xattr (filename, attribute, &value, error);
3072 else if (g_str_has_prefix (attribute, "xattr-sys::"))
3073 return set_xattr (filename, attribute, &value, error);
3077 else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
3078 return set_selinux_context (filename, &value, error);
3081 vfs = g_vfs_get_default ();
3082 class = G_VFS_GET_CLASS (vfs);
3083 if (class->local_file_set_attributes)
3087 info = g_file_info_new ();
3088 g_file_info_set_attribute (info,
3092 if (!class->local_file_set_attributes (vfs, filename,
3097 g_object_unref (info);
3101 if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
3103 g_object_unref (info);
3107 g_object_unref (info);
3110 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
3111 _("Setting attribute %s not supported"), attribute);
3116 _g_local_file_info_set_attributes (char *filename,
3118 GFileQueryInfoFlags flags,
3119 GCancellable *cancellable,
3122 GFileAttributeValue *value;
3124 GFileAttributeValue *uid, *gid;
3126 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
3127 GFileAttributeValue *mtime, *mtime_usec, *mtime_nsec, *atime, *atime_usec, *atime_nsec;
3129 #if defined (G_OS_UNIX) || defined (G_OS_WIN32)
3130 GFileAttributeStatus status;
3136 /* Handles setting multiple specified data in a single set, and takes care
3137 of ordering restrictions when setting attributes */
3141 /* Set symlink first, since this recreates the file */
3143 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
3146 if (!set_symlink (filename, value, error))
3148 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3150 /* Don't set error multiple times */
3154 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
3160 /* Group uid and gid setting into one call
3161 * Change ownership before permissions, since ownership changes can
3162 change permissions (e.g. setuid)
3164 uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
3165 gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
3169 if (!set_unix_uid_gid (filename, uid, gid, flags, error))
3171 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3173 /* Don't set error multiple times */
3177 status = G_FILE_ATTRIBUTE_STATUS_SET;
3179 uid->status = status;
3181 gid->status = status;
3185 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
3188 if (!set_unix_mode (filename, flags, value, error))
3190 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3192 /* Don't set error multiple times */
3196 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
3200 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
3201 /* Group all time settings into one call
3202 * Change times as the last thing to avoid it changing due to metadata changes
3205 mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
3206 mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
3207 mtime_nsec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
3208 atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
3209 atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
3210 atime_nsec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
3212 if (mtime || mtime_usec || mtime_nsec || atime || atime_usec || atime_nsec)
3214 if (!set_mtime_atime (filename, mtime, mtime_usec, mtime_nsec, atime, atime_usec, atime_nsec, error))
3216 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3218 /* Don't set error multiple times */
3222 status = G_FILE_ATTRIBUTE_STATUS_SET;
3225 mtime->status = status;
3227 mtime_usec->status = status;
3229 mtime_nsec->status = status;
3231 atime->status = status;
3233 atime_usec->status = status;
3235 atime_nsec->status = status;
3239 /* xattrs are handled by default callback */
3242 /* SELinux context */
3244 if (is_selinux_enabled ()) {
3245 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
3248 if (!set_selinux_context (filename, value, error))
3250 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3252 /* Don't set error multiple times */
3256 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
3261 vfs = g_vfs_get_default ();
3262 class = G_VFS_GET_CLASS (vfs);
3263 if (class->local_file_set_attributes)
3265 if (!class->local_file_set_attributes (vfs, filename,
3271 /* Don't set error multiple times */