1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
6 *
7 * SPDX-License-Identifier: LGPL-2.1-or-later
8 *
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.
13 *
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.
18 *
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/>.
21 *
22 * Author: Alexander Larsson <alexl@redhat.com>
23 */
24
25 #include "config.h"
26
27 #include <glib.h>
28
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #ifdef G_OS_UNIX
38 #include <grp.h>
39 #include <pwd.h>
40 #endif
41 #ifdef HAVE_SELINUX
42 #include <selinux/selinux.h>
43 #endif
44
45 #ifdef HAVE_XATTR
46
47 #if defined HAVE_SYS_XATTR_H
48 #include <sys/xattr.h>
49 #elif defined HAVE_ATTR_XATTR_H
50 #include <attr/xattr.h>
51 #else
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 */
54
55 #endif /* HAVE_XATTR */
56
57 #include <glib/gstdio.h>
58 #include <glib/gstdioprivate.h>
59 #include <gfileattribute-priv.h>
60 #include <gfileinfo-priv.h>
61 #include <gvfs.h>
62
63 #ifdef G_OS_UNIX
64 #include <unistd.h>
65 #include "glib-unix.h"
66 #endif
67
68 #include "glib-private.h"
69
70 #include "thumbnail-verify.h"
71
72 #ifdef G_OS_WIN32
73 #include <windows.h>
74 #include <io.h>
75 #ifndef W_OK
76 #define W_OK 2
77 #endif
78 #ifndef R_OK
79 #define R_OK 4
80 #endif
81 #ifndef X_OK
82 #define X_OK 0 /* not really */
83 #endif
84 #ifndef S_ISREG
85 #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
86 #endif
87 #ifndef S_ISDIR
88 #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
89 #endif
90 #ifndef S_IXUSR
91 #define S_IXUSR _S_IEXEC
92 #endif
93 #endif
94
95 #ifndef O_CLOEXEC
96 #define O_CLOEXEC 0
97 #endif
98
99 #include "glocalfileinfo.h"
100 #include "gioerror.h"
101 #include "gthemedicon.h"
102 #include "gcontenttypeprivate.h"
103 #include "glibintl.h"
104
105
106 struct ThumbMD5Context {
107 guint32 buf[4];
108 guint32 bits[2];
109 unsigned char in[64];
110 };
111
112 #ifndef G_OS_WIN32
113
114 typedef struct {
115 char *user_name;
116 char *real_name;
117 } UidData;
118
119 G_LOCK_DEFINE_STATIC (uid_cache);
120 static GHashTable *uid_cache = NULL;
121
122 G_LOCK_DEFINE_STATIC (gid_cache);
123 static GHashTable *gid_cache = NULL;
124
125 #endif /* !G_OS_WIN32 */
126
127 char *
128 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
129 {
130 glong sec, usec, nsec;
131
132 g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL);
133
134 sec = _g_stat_mtime (statbuf);
135 usec = _g_stat_mtim_nsec (statbuf) / 1000;
136 nsec = _g_stat_mtim_nsec (statbuf);
137
138 return g_strdup_printf ("%lu:%lu:%lu", sec, usec, nsec);
139 }
140
141 static char *
142 _g_local_file_info_create_file_id (GLocalFileStat *statbuf)
143 {
144 guint64 ino;
145 #ifdef G_OS_WIN32
146 ino = statbuf->file_index;
147 #else
148 ino = _g_stat_ino (statbuf);
149 #endif
150 return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
151 (guint64) _g_stat_dev (statbuf),
152 ino);
153 }
154
155 static char *
156 _g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
157 {
158 return g_strdup_printf ("l%" G_GUINT64_FORMAT,
159 (guint64) _g_stat_dev (statbuf));
160 }
161
162 #if defined (S_ISLNK) || defined (G_OS_WIN32)
163
164 static gchar *
165 read_link (const gchar *full_name)
166 {
167 #if defined (HAVE_READLINK)
168 gchar *buffer;
169 gsize size;
170
171 size = 256;
172 buffer = g_malloc (size);
173
174 while (1)
175 {
176 gssize read_size;
177
178 read_size = readlink (full_name, buffer, size);
179 if (read_size < 0)
180 {
181 g_free (buffer);
182 return NULL;
183 }
184 if ((gsize) read_size < size)
185 {
186 buffer[read_size] = 0;
187 return buffer;
188 }
189 size *= 2;
190 buffer = g_realloc (buffer, size);
191 }
192 #elif defined (G_OS_WIN32)
193 gchar *buffer;
194 int read_size;
195
196 read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, NULL, 0, &buffer, TRUE);
197 if (read_size < 0)
198 return NULL;
199 else if (read_size == 0)
200 return strdup ("");
201 else
202 return buffer;
203 #else
204 return NULL;
205 #endif
206 }
207
208 #endif /* S_ISLNK || G_OS_WIN32 */
209
210 #ifdef HAVE_SELINUX
211 /* Get the SELinux security context */
212 static void
213 get_selinux_context (const char *path,
214 GFileInfo *info,
215 GFileAttributeMatcher *attribute_matcher,
216 gboolean follow_symlinks)
217 {
218 char *context;
219
220 if (!_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
221 return;
222
223 if (is_selinux_enabled ())
224 {
225 if (follow_symlinks)
226 {
227 if (lgetfilecon_raw (path, &context) < 0)
228 return;
229 }
230 else
231 {
232 if (getfilecon_raw (path, &context) < 0)
233 return;
234 }
235
236 if (context)
237 {
238 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
239 freecon (context);
240 }
241 }
242 }
243 #endif
244
245 #ifdef HAVE_XATTR
246
247 /* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
248 * (Mac) getxattr(..., XATTR_NOFOLLOW)
249 */
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)
255 #else
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)
260 #endif
261
262 static gssize
263 g_getxattr (const char *path, const char *name, void *value, size_t size,
264 gboolean follow_symlinks)
265 {
266 #ifdef HAVE_XATTR_NOFOLLOW
267 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
268 #else
269 if (follow_symlinks)
270 return getxattr (path, name, value, size);
271 else
272 return lgetxattr (path, name, value, size);
273 #endif
274 }
275
276 static gssize
277 g_listxattr(const char *path, char *namebuf, size_t size,
278 gboolean follow_symlinks)
279 {
280 #ifdef HAVE_XATTR_NOFOLLOW
281 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
282 #else
283 if (follow_symlinks)
284 return listxattr (path, namebuf, size);
285 else
286 return llistxattr (path, namebuf, size);
287 #endif
288 }
289
290 static gboolean
291 valid_char (char c)
292 {
293 return c >= 32 && c <= 126 && c != '\\';
294 }
295
296 static gboolean
297 name_is_valid (const char *str)
298 {
299 while (*str)
300 {
301 if (!valid_char (*str++))
302 return FALSE;
303 }
304 return TRUE;
305 }
306
307 static char *
308 hex_escape_buffer (const char *str,
309 size_t len,
310 gboolean *free_return)
311 {
312 size_t num_invalid, i;
313 char *escaped_str, *p;
314 unsigned char c;
315 static char *hex_digits = "0123456789abcdef";
316
317 num_invalid = 0;
318 for (i = 0; i < len; i++)
319 {
320 if (!valid_char (str[i]))
321 num_invalid++;
322 }
323
324 if (num_invalid == 0)
325 {
326 *free_return = FALSE;
327 return (char *)str;
328 }
329
330 escaped_str = g_malloc (len + num_invalid*3 + 1);
331
332 p = escaped_str;
333 for (i = 0; i < len; i++)
334 {
335 if (valid_char (str[i]))
336 *p++ = str[i];
337 else
338 {
339 c = str[i];
340 *p++ = '\\';
341 *p++ = 'x';
342 *p++ = hex_digits[(c >> 4) & 0xf];
343 *p++ = hex_digits[c & 0xf];
344 }
345 }
346 *p = 0;
347
348 *free_return = TRUE;
349 return escaped_str;
350 }
351
352 static char *
353 hex_escape_string (const char *str,
354 gboolean *free_return)
355 {
356 return hex_escape_buffer (str, strlen (str), free_return);
357 }
358
359 static char *
360 hex_unescape_string (const char *str,
361 int *out_len,
362 gboolean *free_return)
363 {
364 int i;
365 char *unescaped_str, *p;
366 unsigned char c;
367 int len;
368
369 len = strlen (str);
370
371 if (strchr (str, '\\') == NULL)
372 {
373 if (out_len)
374 *out_len = len;
375 *free_return = FALSE;
376 return (char *)str;
377 }
378
379 unescaped_str = g_malloc (len + 1);
380
381 p = unescaped_str;
382 for (i = 0; i < len; i++)
383 {
384 if (str[i] == '\\' &&
385 str[i+1] == 'x' &&
386 len - i >= 4)
387 {
388 c =
389 (g_ascii_xdigit_value (str[i+2]) << 4) |
390 g_ascii_xdigit_value (str[i+3]);
391 *p++ = c;
392 i += 3;
393 }
394 else
395 *p++ = str[i];
396 }
397 if (out_len)
398 *out_len = p - unescaped_str;
399 *p++ = 0;
400
401 *free_return = TRUE;
402 return unescaped_str;
403 }
404
405 static void
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 */)
410 {
411 char *escaped_val;
412 gboolean free_escaped_val;
413
414 escaped_val = hex_escape_buffer (value, len, &free_escaped_val);
415
416 g_file_info_set_attribute_string (info, gio_attr, escaped_val);
417
418 if (free_escaped_val)
419 g_free (escaped_val);
420 }
421
422 static void
423 get_one_xattr (const char *path,
424 GFileInfo *info,
425 const char *gio_attr,
426 const char *xattr,
427 gboolean follow_symlinks)
428 {
429 char value[64];
430 char *value_p;
431 gssize len;
432 int errsv;
433
434 len = g_getxattr (path, xattr, value, sizeof (value)-1, follow_symlinks);
435 errsv = errno;
436
437 value_p = NULL;
438 if (len >= 0)
439 value_p = value;
440 else if (len == -1 && errsv == ERANGE)
441 {
442 len = g_getxattr (path, xattr, NULL, 0, follow_symlinks);
443
444 if (len < 0)
445 return;
446
447 value_p = g_malloc (len+1);
448
449 len = g_getxattr (path, xattr, value_p, len, follow_symlinks);
450
451 if (len < 0)
452 {
453 g_free (value_p);
454 return;
455 }
456 }
457 else
458 return;
459
460 /* Null terminate */
461 value_p[len] = 0;
462
463 escape_xattr (info, gio_attr, value_p, len);
464
465 if (value_p != value)
466 g_free (value_p);
467 }
468
469 #endif /* defined HAVE_XATTR */
470
471 static void
472 get_xattrs (const char *path,
473 gboolean user,
474 GFileInfo *info,
475 GFileAttributeMatcher *matcher,
476 gboolean follow_symlinks)
477 {
478 #ifdef HAVE_XATTR
479 gboolean all;
480 gsize list_size;
481 gssize list_res_size;
482 size_t len;
483 char *list;
484 const char *attr, *attr2;
485
486 if (user)
487 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
488 else
489 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
490
491 if (all)
492 {
493 int errsv;
494
495 list_res_size = g_listxattr (path, NULL, 0, follow_symlinks);
496
497 if (list_res_size == -1 ||
498 list_res_size == 0)
499 return;
500
501 list_size = list_res_size;
502 list = g_malloc (list_size);
503
504 retry:
505
506 list_res_size = g_listxattr (path, list, list_size, follow_symlinks);
507 errsv = errno;
508
509 if (list_res_size == -1 && errsv == ERANGE)
510 {
511 list_size = list_size * 2;
512 list = g_realloc (list, list_size);
513 goto retry;
514 }
515
516 if (list_res_size == -1)
517 {
518 g_free (list);
519 return;
520 }
521
522 attr = list;
523 while (list_res_size > 0)
524 {
525 if ((user && g_str_has_prefix (attr, "user.")) ||
526 (!user && !g_str_has_prefix (attr, "user.")))
527 {
528 char *escaped_attr, *gio_attr;
529 gboolean free_escaped_attr;
530
531 if (user)
532 {
533 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
534 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
535 }
536 else
537 {
538 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
539 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
540 }
541
542 if (free_escaped_attr)
543 g_free (escaped_attr);
544
545 get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
546
547 g_free (gio_attr);
548 }
549
550 len = strlen (attr) + 1;
551 attr += len;
552 list_res_size -= len;
553 }
554
555 g_free (list);
556 }
557 else
558 {
559 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
560 {
561 char *unescaped_attribute, *a;
562 gboolean free_unescaped_attribute;
563
564 attr2 = strchr (attr, ':');
565 if (attr2)
566 {
567 attr2 += 2; /* Skip '::' */
568 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
569 if (user)
570 a = g_strconcat ("user.", unescaped_attribute, NULL);
571 else
572 a = unescaped_attribute;
573
574 get_one_xattr (path, info, attr, a, follow_symlinks);
575
576 if (user)
577 g_free (a);
578
579 if (free_unescaped_attribute)
580 g_free (unescaped_attribute);
581 }
582 }
583 }
584 #endif /* defined HAVE_XATTR */
585 }
586
587 #ifdef HAVE_XATTR
588 static void
589 get_one_xattr_from_fd (int fd,
590 GFileInfo *info,
591 const char *gio_attr,
592 const char *xattr)
593 {
594 char value[64];
595 char *value_p;
596 gssize len;
597 int errsv;
598
599 len = g_fgetxattr (fd, xattr, value, sizeof (value) - 1);
600 errsv = errno;
601
602 value_p = NULL;
603 if (len >= 0)
604 value_p = value;
605 else if (len == -1 && errsv == ERANGE)
606 {
607 len = g_fgetxattr (fd, xattr, NULL, 0);
608
609 if (len < 0)
610 return;
611
612 value_p = g_malloc (len + 1);
613
614 len = g_fgetxattr (fd, xattr, value_p, len);
615
616 if (len < 0)
617 {
618 g_free (value_p);
619 return;
620 }
621 }
622 else
623 return;
624
625 /* Null terminate */
626 value_p[len] = 0;
627
628 escape_xattr (info, gio_attr, value_p, len);
629
630 if (value_p != value)
631 g_free (value_p);
632 }
633 #endif /* defined HAVE_XATTR */
634
635 static void
636 get_xattrs_from_fd (int fd,
637 gboolean user,
638 GFileInfo *info,
639 GFileAttributeMatcher *matcher)
640 {
641 #ifdef HAVE_XATTR
642 gboolean all;
643 gsize list_size;
644 gssize list_res_size;
645 size_t len;
646 char *list;
647 const char *attr, *attr2;
648
649 if (user)
650 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
651 else
652 all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr-sys");
653
654 if (all)
655 {
656 int errsv;
657
658 list_res_size = g_flistxattr (fd, NULL, 0);
659
660 if (list_res_size == -1 ||
661 list_res_size == 0)
662 return;
663
664 list_size = list_res_size;
665 list = g_malloc (list_size);
666
667 retry:
668
669 list_res_size = g_flistxattr (fd, list, list_size);
670 errsv = errno;
671
672 if (list_res_size == -1 && errsv == ERANGE)
673 {
674 list_size = list_size * 2;
675 list = g_realloc (list, list_size);
676 goto retry;
677 }
678
679 if (list_res_size == -1)
680 {
681 g_free (list);
682 return;
683 }
684
685 attr = list;
686 while (list_res_size > 0)
687 {
688 if ((user && g_str_has_prefix (attr, "user.")) ||
689 (!user && !g_str_has_prefix (attr, "user.")))
690 {
691 char *escaped_attr, *gio_attr;
692 gboolean free_escaped_attr;
693
694 if (user)
695 {
696 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
697 gio_attr = g_strconcat ("xattr::", escaped_attr, NULL);
698 }
699 else
700 {
701 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
702 gio_attr = g_strconcat ("xattr-sys::", escaped_attr, NULL);
703 }
704
705 if (free_escaped_attr)
706 g_free (escaped_attr);
707
708 get_one_xattr_from_fd (fd, info, gio_attr, attr);
709 g_free (gio_attr);
710 }
711
712 len = strlen (attr) + 1;
713 attr += len;
714 list_res_size -= len;
715 }
716
717 g_free (list);
718 }
719 else
720 {
721 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
722 {
723 char *unescaped_attribute, *a;
724 gboolean free_unescaped_attribute;
725
726 attr2 = strchr (attr, ':');
727 if (attr2)
728 {
729 attr2++; /* Skip ':' */
730 unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
731 if (user)
732 a = g_strconcat ("user.", unescaped_attribute, NULL);
733 else
734 a = unescaped_attribute;
735
736 get_one_xattr_from_fd (fd, info, attr, a);
737
738 if (user)
739 g_free (a);
740
741 if (free_unescaped_attribute)
742 g_free (unescaped_attribute);
743 }
744 }
745 }
746 #endif /* defined HAVE_XATTR */
747 }
748
749 #ifdef HAVE_XATTR
750 static gboolean
751 set_xattr (char *filename,
752 const char *escaped_attribute,
753 const GFileAttributeValue *attr_value,
754 GError **error)
755 {
756 char *attribute, *value;
757 gboolean free_attribute, free_value;
758 int val_len, res, errsv;
759 gboolean is_user;
760 char *a;
761
762 if (attr_value == NULL)
763 {
764 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
765 _("Attribute value must be non-NULL"));
766 return FALSE;
767 }
768
769 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING && attr_value->type != G_FILE_ATTRIBUTE_TYPE_INVALID)
770 {
771 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
772 _("Invalid attribute type (string or invalid expected)"));
773 return FALSE;
774 }
775
776 if (!name_is_valid (escaped_attribute))
777 {
778 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
779 _("Invalid extended attribute name"));
780 return FALSE;
781 }
782
783 if (g_str_has_prefix (escaped_attribute, "xattr::"))
784 {
785 escaped_attribute += strlen ("xattr::");
786 is_user = TRUE;
787 }
788 else
789 {
790 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
791 escaped_attribute += strlen ("xattr-sys::");
792 is_user = FALSE;
793 }
794
795 attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
796
797 if (is_user)
798 a = g_strconcat ("user.", attribute, NULL);
799 else
800 a = attribute;
801
802 if (attr_value->type == G_FILE_ATTRIBUTE_TYPE_STRING)
803 {
804 value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
805 res = g_setxattr (filename, a, value, val_len);
806 }
807 else
808 {
809 value = NULL;
810 val_len = 0;
811 free_value = FALSE;
812 res = g_removexattr (filename, a);
813 }
814
815 errsv = errno;
816
817 if (is_user)
818 g_free (a);
819
820 if (free_attribute)
821 g_free (attribute);
822
823 if (free_value)
824 g_free (value);
825
826 if (res == -1)
827 {
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));
832 return FALSE;
833 }
834
835 return TRUE;
836 }
837
838 #endif
839
840
841 void
842 _g_local_file_info_get_parent_info (const char *dir,
843 GFileAttributeMatcher *attribute_matcher,
844 GLocalParentFileInfo *parent_info)
845 {
846 GStatBuf statbuf;
847 int res;
848
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;
856
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))
861 {
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
865 * all.
866 */
867 parent_info->writable = (g_access (dir, W_OK) == 0);
868
869 res = g_stat (dir, &statbuf);
870
871 /*
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.
875 */
876 if (res == 0)
877 {
878 #ifdef S_ISVTX
879 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
880 #else
881 parent_info->is_sticky = FALSE;
882 #endif
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);
890 }
891 }
892 }
893
894 void
895 _g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
896 {
897 if (parent_info->extra_data &&
898 parent_info->free_extra_data)
899 parent_info->free_extra_data (parent_info->extra_data);
900 }
901
902 static void
903 get_access_rights (GFileAttributeMatcher *attribute_matcher,
904 GFileInfo *info,
905 const gchar *path,
906 GLocalFileStat *statbuf,
907 GLocalParentFileInfo *parent_info)
908 {
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);
914
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);
919
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);
924
925
926 if (parent_info)
927 {
928 gboolean writable;
929
930 writable = FALSE;
931 if (parent_info->writable)
932 {
933 #ifdef G_OS_WIN32
934 writable = TRUE;
935 #else
936 if (parent_info->is_sticky)
937 {
938 uid_t uid = geteuid ();
939
940 if (uid == _g_stat_uid (statbuf) ||
941 uid == (uid_t) parent_info->owner ||
942 uid == 0)
943 writable = TRUE;
944 }
945 else
946 writable = TRUE;
947 #endif
948 }
949
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,
952 writable);
953
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,
956 writable);
957
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);
961 }
962 }
963
964 static void
965 set_info_from_stat (GFileInfo *info,
966 GLocalFileStat *statbuf,
967 GFileAttributeMatcher *attribute_matcher)
968 {
969 GFileType file_type;
970
971 file_type = G_FILE_TYPE_UNKNOWN;
972
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;
977 #ifndef G_OS_WIN32
978 else if (S_ISCHR (_g_stat_mode (statbuf)) ||
979 S_ISBLK (_g_stat_mode (statbuf)) ||
980 S_ISFIFO (_g_stat_mode (statbuf))
981 #ifdef S_ISSOCK
982 || S_ISSOCK (_g_stat_mode (statbuf))
983 #endif
984 )
985 file_type = G_FILE_TYPE_SPECIAL;
986 #endif
987 #ifdef S_ISLNK
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;
994 #endif
995
996 g_file_info_set_file_type (info, file_type);
997 g_file_info_set_size (info, _g_stat_size (statbuf));
998
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));
1001 #ifndef G_OS_WIN32
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));
1007 #endif
1008 /* Mostly pointless on Windows.
1009 * Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks.
1010 */
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));
1014 #endif
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);
1022
1023 #endif
1024
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));
1028
1029 if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_ATIME))
1030 {
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));
1034 }
1035
1036 #ifndef G_OS_WIN32
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!
1041 */
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));
1045 #endif
1046
1047 #if defined (HAVE_STATX)
1048 if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_BTIME))
1049 {
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);
1053 }
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);
1070 #endif
1071
1072 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1073 G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
1074 {
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);
1077 g_free (etag);
1078 }
1079
1080 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1081 G_FILE_ATTRIBUTE_ID_ID_FILE))
1082 {
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);
1085 g_free (id);
1086 }
1087
1088 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1089 G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
1090 {
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);
1093 g_free (id);
1094 }
1095 }
1096
1097 #ifndef G_OS_WIN32
1098
1099 static char *
1100 make_valid_utf8 (const char *name)
1101 {
1102 GString *string;
1103 const gchar *remainder, *invalid;
1104 gsize remaining_bytes, valid_bytes;
1105
1106 string = NULL;
1107 remainder = name;
1108 remaining_bytes = strlen (name);
1109
1110 while (remaining_bytes != 0)
1111 {
1112 if (g_utf8_validate_len (remainder, remaining_bytes, &invalid))
1113 break;
1114 valid_bytes = invalid - remainder;
1115
1116 if (string == NULL)
1117 string = g_string_sized_new (remaining_bytes);
1118
1119 g_string_append_len (string, remainder, valid_bytes);
1120 /* append U+FFFD REPLACEMENT CHARACTER */
1121 g_string_append (string, "\357\277\275");
1122
1123 remaining_bytes -= valid_bytes + 1;
1124 remainder = invalid + 1;
1125 }
1126
1127 if (string == NULL)
1128 return g_strdup (name);
1129
1130 g_string_append (string, remainder);
1131
1132 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
1133
1134 return g_string_free (string, FALSE);
1135 }
1136
1137 static char *
1138 convert_pwd_string_to_utf8 (char *pwd_str)
1139 {
1140 char *utf8_string;
1141
1142 if (!g_utf8_validate (pwd_str, -1, NULL))
1143 {
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);
1147 }
1148 else
1149 utf8_string = g_strdup (pwd_str);
1150
1151 return utf8_string;
1152 }
1153
1154 static void
1155 uid_data_free (UidData *data)
1156 {
1157 g_free (data->user_name);
1158 g_free (data->real_name);
1159 g_free (data);
1160 }
1161
1162 /* called with lock held */
1163 static UidData *
1164 lookup_uid_data (uid_t uid)
1165 {
1166 UidData *data;
1167 char buffer[4096];
1168 struct passwd pwbuf;
1169 struct passwd *pwbufp;
1170 #ifndef __BIONIC__
1171 char *gecos, *comma;
1172 #endif
1173
1174 if (uid_cache == NULL)
1175 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
1176
1177 data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
1178
1179 if (data)
1180 return data;
1181
1182 data = g_new0 (UidData, 1);
1183
1184 #if defined(HAVE_GETPWUID_R)
1185 getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
1186 #else
1187 pwbufp = getpwuid (uid);
1188 #endif
1189
1190 if (pwbufp != NULL)
1191 {
1192 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
1193 data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
1194
1195 #ifndef __BIONIC__
1196 gecos = pwbufp->pw_gecos;
1197
1198 if (gecos)
1199 {
1200 comma = strchr (gecos, ',');
1201 if (comma)
1202 *comma = 0;
1203 data->real_name = convert_pwd_string_to_utf8 (gecos);
1204 }
1205 #endif
1206 }
1207
1208 /* Default fallbacks */
1209 if (data->real_name == NULL)
1210 {
1211 if (data->user_name != NULL)
1212 data->real_name = g_strdup (data->user_name);
1213 else
1214 data->real_name = g_strdup_printf ("user #%d", (int)uid);
1215 }
1216
1217 if (data->user_name == NULL)
1218 data->user_name = g_strdup_printf ("%d", (int)uid);
1219
1220 g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
1221
1222 return data;
1223 }
1224
1225 static char *
1226 get_username_from_uid (uid_t uid)
1227 {
1228 char *res;
1229 UidData *data;
1230
1231 G_LOCK (uid_cache);
1232 data = lookup_uid_data (uid);
1233 res = g_strdup (data->user_name);
1234 G_UNLOCK (uid_cache);
1235
1236 return res;
1237 }
1238
1239 static char *
1240 get_realname_from_uid (uid_t uid)
1241 {
1242 char *res;
1243 UidData *data;
1244
1245 G_LOCK (uid_cache);
1246 data = lookup_uid_data (uid);
1247 res = g_strdup (data->real_name);
1248 G_UNLOCK (uid_cache);
1249
1250 return res;
1251 }
1252
1253 /* called with lock held */
1254 static char *
1255 lookup_gid_name (gid_t gid)
1256 {
1257 char *name;
1258 #if defined (HAVE_GETGRGID_R)
1259 char buffer[4096];
1260 struct group gbuf;
1261 #endif
1262 struct group *gbufp;
1263
1264 if (gid_cache == NULL)
1265 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
1266
1267 name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
1268
1269 if (name)
1270 return name;
1271
1272 #if defined (HAVE_GETGRGID_R)
1273 getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
1274 #else
1275 gbufp = getgrgid (gid);
1276 #endif
1277
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);
1282 else
1283 name = g_strdup_printf("%d", (int)gid);
1284
1285 g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
1286
1287 return name;
1288 }
1289
1290 static char *
1291 get_groupname_from_gid (gid_t gid)
1292 {
1293 char *res;
1294 char *name;
1295
1296 G_LOCK (gid_cache);
1297 name = lookup_gid_name (gid);
1298 res = g_strdup (name);
1299 G_UNLOCK (gid_cache);
1300 return res;
1301 }
1302
1303 #endif /* !G_OS_WIN32 */
1304
1305 static char *
1306 get_content_type (const char *basename,
1307 const char *path,
1308 GLocalFileStat *statbuf,
1309 gboolean is_symlink,
1310 gboolean symlink_broken,
1311 GFileQueryInfoFlags flags,
1312 gboolean fast)
1313 {
1314 if (is_symlink &&
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");
1319 #ifndef G_OS_WIN32
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)
1327 {
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)
1330 */
1331 return g_content_type_from_mime_type ("application/x-zerosize");
1332 }
1333 #endif
1334 #ifdef S_ISSOCK
1335 else if (statbuf != NULL && S_ISSOCK(_g_stat_mode (statbuf)))
1336 return g_content_type_from_mime_type ("inode/socket");
1337 #endif
1338 else
1339 {
1340 char *content_type;
1341 gboolean result_uncertain;
1342
1343 content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
1344
1345 #if !defined(G_OS_WIN32) && !defined(__APPLE__)
1346 if (!fast && result_uncertain && path != NULL)
1347 {
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];
1353 gsize sniff_length;
1354 #ifdef O_NOATIME
1355 int errsv;
1356 #endif
1357 int fd;
1358
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);
1362
1363 #ifdef O_NOATIME
1364 fd = g_open (path, O_RDONLY | O_NOATIME | O_CLOEXEC, 0);
1365 errsv = errno;
1366 if (fd < 0 && errsv == EPERM)
1367 #endif
1368 fd = g_open (path, O_RDONLY | O_CLOEXEC, 0);
1369
1370 if (fd != -1)
1371 {
1372 gssize res;
1373
1374 res = read (fd, sniff_buffer, sniff_length);
1375 (void) g_close (fd, NULL);
1376 if (res >= 0)
1377 {
1378 g_free (content_type);
1379 content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
1380 }
1381 }
1382 }
1383 #endif
1384
1385 return content_type;
1386 }
1387
1388 }
1389
1390 typedef enum {
1391 THUMBNAIL_SIZE_AUTO,
1392 THUMBNAIL_SIZE_NORMAL,
1393 THUMBNAIL_SIZE_LARGE,
1394 THUMBNAIL_SIZE_XLARGE,
1395 THUMBNAIL_SIZE_XXLARGE,
1396 THUMBNAIL_SIZE_LAST,
1397 } ThumbnailSize;
1398
1399 static const char *
1400 get_thumbnail_dirname_from_size (ThumbnailSize size)
1401 {
1402 switch (size)
1403 {
1404 case THUMBNAIL_SIZE_AUTO:
1405 return NULL;
1406 break;
1407 case THUMBNAIL_SIZE_NORMAL:
1408 return "normal";
1409 break;
1410 case THUMBNAIL_SIZE_LARGE:
1411 return "large";
1412 break;
1413 case THUMBNAIL_SIZE_XLARGE:
1414 return "x-large";
1415 break;
1416 case THUMBNAIL_SIZE_XXLARGE:
1417 return "xx-large";
1418 break;
1419 default:
1420 g_assert_not_reached ();
1421 }
1422
1423 g_return_val_if_reached (NULL);
1424 }
1425
1426 /* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
1427 static void
1428 get_thumbnail_attributes (const char *path,
1429 GFileInfo *info,
1430 const GLocalFileStat *stat_buf,
1431 ThumbnailSize size)
1432 {
1433 GChecksum *checksum;
1434 const char *dirname;
1435 char *uri;
1436 char *filename = NULL;
1437 char *basename;
1438 guint32 failed_attr_id;
1439 guint32 is_valid_attr_id;
1440 guint32 path_attr_id;
1441
1442 switch (size)
1443 {
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;
1448 break;
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;
1453 break;
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;
1458 break;
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;
1463 break;
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;
1468 break;
1469 default:
1470 g_assert_not_reached ();
1471 }
1472
1473 dirname = get_thumbnail_dirname_from_size (size);
1474 uri = g_filename_to_uri (path, NULL, NULL);
1475
1476 checksum = g_checksum_new (G_CHECKSUM_MD5);
1477 g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
1478
1479 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
1480 g_checksum_free (checksum);
1481
1482 if (dirname)
1483 {
1484 filename = g_build_filename (g_get_user_cache_dir (),
1485 "thumbnails", dirname, basename,
1486 NULL);
1487
1488 if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1489 g_clear_pointer (&filename, g_free);
1490 }
1491 else
1492 {
1493 gssize i;
1494
1495 for (i = THUMBNAIL_SIZE_LAST - 1; i >= 0 ; i--)
1496 {
1497 filename = g_build_filename (g_get_user_cache_dir (),
1498 "thumbnails",
1499 get_thumbnail_dirname_from_size (i),
1500 basename,
1501 NULL);
1502 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1503 break;
1504
1505 g_clear_pointer (&filename, g_free);
1506 }
1507 }
1508
1509 if (filename)
1510 {
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));
1514 }
1515 else
1516 {
1517 filename = g_build_filename (g_get_user_cache_dir (),
1518 "thumbnails", "fail",
1519 "gnome-thumbnail-factory",
1520 basename,
1521 NULL);
1522
1523 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1524 {
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));
1528 }
1529 }
1530
1531 g_free (basename);
1532 g_free (filename);
1533 g_free (uri);
1534 }
1535
1536 #ifdef G_OS_WIN32
1537 static void
1538 win32_get_file_user_info (const gchar *filename,
1539 gchar **group_name,
1540 gchar **user_name,
1541 gchar **real_name)
1542 {
1543 PSECURITY_DESCRIPTOR psd = NULL;
1544 DWORD sd_size = 0; /* first call calculates the size required */
1545
1546 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1547 if ((GetFileSecurityW (wfilename,
1548 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1549 NULL,
1550 sd_size,
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,
1555 psd,
1556 sd_size,
1557 &sd_size))
1558 {
1559 PSID psid = 0;
1560 BOOL defaulted;
1561 SID_NAME_USE name_use = 0; /* don't care? */
1562 wchar_t *name = NULL;
1563 wchar_t *domain = NULL;
1564 DWORD name_len = 0;
1565 DWORD domain_len = 0;
1566 /* get the user name */
1567 do {
1568 if (!user_name)
1569 break;
1570 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
1571 break;
1572 if (!LookupAccountSidW (NULL, /* local machine */
1573 psid,
1574 name, &name_len,
1575 domain, &domain_len, /* no domain info yet */
1576 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1577 break;
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 */
1582 psid,
1583 name, &name_len,
1584 domain, &domain_len, /* no domain info yet */
1585 &name_use))
1586 {
1587 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1588 }
1589 g_free (name);
1590 g_free (domain);
1591 } while (FALSE);
1592
1593 /* get the group name */
1594 do {
1595 if (!group_name)
1596 break;
1597 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
1598 break;
1599 if (!LookupAccountSidW (NULL, /* local machine */
1600 psid,
1601 name, &name_len,
1602 domain, &domain_len, /* no domain info yet */
1603 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1604 break;
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 */
1609 psid,
1610 name, &name_len,
1611 domain, &domain_len, /* no domain info yet */
1612 &name_use))
1613 {
1614 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1615 }
1616 g_free (name);
1617 g_free (domain);
1618 } while (FALSE);
1619
1620 /* TODO: get real name */
1621
1622 g_free (psd);
1623 }
1624 g_free (wfilename);
1625 }
1626 #endif /* G_OS_WIN32 */
1627
1628 #ifndef 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;
1635
1636 typedef struct
1637 {
1638 GHashTable *hidden_files;
1639 gint64 timestamp_secs;
1640 } HiddenCacheData;
1641
1642 static gboolean
1643 remove_from_hidden_cache (gpointer user_data)
1644 {
1645 HiddenCacheData *data;
1646 GHashTableIter iter;
1647 gboolean retval;
1648 gint64 timestamp_secs;
1649
1650 G_LOCK (hidden_cache);
1651 timestamp_secs = g_source_get_time (hidden_cache_source) / G_USEC_PER_SEC;
1652
1653 g_hash_table_iter_init (&iter, hidden_cache);
1654 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
1655 {
1656 if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
1657 g_hash_table_iter_remove (&iter);
1658 }
1659
1660 if (g_hash_table_size (hidden_cache) == 0)
1661 {
1662 g_clear_pointer (&hidden_cache_source, g_source_unref);
1663 retval = G_SOURCE_REMOVE;
1664 }
1665 else
1666 retval = G_SOURCE_CONTINUE;
1667
1668 G_UNLOCK (hidden_cache);
1669
1670 return retval;
1671 }
1672
1673 static GHashTable *
1674 read_hidden_file (const gchar *dirname)
1675 {
1676 gchar *contents = NULL;
1677 gchar *filename;
1678
1679 filename = g_build_path ("/", dirname, ".hidden", NULL);
1680 (void) g_file_get_contents (filename, &contents, NULL, NULL);
1681 g_free (filename);
1682
1683 if (contents != NULL)
1684 {
1685 GHashTable *table;
1686 gchar **lines;
1687 gint i;
1688
1689 table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1690
1691 lines = g_strsplit (contents, "\n", 0);
1692 g_free (contents);
1693
1694 for (i = 0; lines[i]; i++)
1695 /* hash table takes the individual strings... */
1696 g_hash_table_add (table, lines[i]);
1697
1698 /* ... so we only free the container. */
1699 g_free (lines);
1700
1701 return table;
1702 }
1703 else
1704 return NULL;
1705 }
1706
1707 static void
1708 free_hidden_file_data (gpointer user_data)
1709 {
1710 HiddenCacheData *data = user_data;
1711
1712 g_clear_pointer (&data->hidden_files, g_hash_table_unref);
1713 g_free (data);
1714 }
1715
1716 static gboolean
1717 file_is_hidden (const gchar *path,
1718 const gchar *basename)
1719 {
1720 HiddenCacheData *data;
1721 gboolean result;
1722 gchar *dirname;
1723 gpointer table;
1724
1725 dirname = g_path_get_dirname (path);
1726
1727 G_LOCK (hidden_cache);
1728
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);
1732
1733 if (!g_hash_table_lookup_extended (hidden_cache, dirname,
1734 NULL, (gpointer *) &data))
1735 {
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;
1739
1740 g_hash_table_insert (hidden_cache,
1741 g_strdup (dirname),
1742 data);
1743
1744 if (!hidden_cache_source)
1745 {
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,
1754 NULL, NULL);
1755 g_source_attach (hidden_cache_source,
1756 GLIB_PRIVATE_CALL (g_get_worker_context) ());
1757 }
1758 }
1759 else
1760 table = data->hidden_files;
1761
1762 result = table != NULL && g_hash_table_contains (table, basename);
1763
1764 G_UNLOCK (hidden_cache);
1765
1766 g_free (dirname);
1767
1768 return result;
1769 }
1770 #endif /* !G_OS_WIN32 */
1771
1772 void
1773 _g_local_file_info_get_nostat (GFileInfo *info,
1774 const char *basename,
1775 const char *path,
1776 GFileAttributeMatcher *attribute_matcher)
1777 {
1778 g_file_info_set_name (info, basename);
1779
1780 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1781 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
1782 {
1783 char *display_name = g_filename_display_basename (path);
1784
1785 /* look for U+FFFD REPLACEMENT CHARACTER */
1786 if (strstr (display_name, "\357\277\275") != NULL)
1787 {
1788 char *p = display_name;
1789 display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
1790 g_free (p);
1791 }
1792 g_file_info_set_display_name (info, display_name);
1793 g_free (display_name);
1794 }
1795
1796 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1797 G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
1798 {
1799 char *edit_name = g_filename_display_basename (path);
1800 g_file_info_set_edit_name (info, edit_name);
1801 g_free (edit_name);
1802 }
1803
1804
1805 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
1806 G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
1807 {
1808 char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1809 if (copy_name)
1810 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, copy_name);
1811 g_free (copy_name);
1812 }
1813 }
1814
1815 static const char *
1816 get_icon_name (const char *path,
1817 gboolean use_symbolic,
1818 gboolean *with_fallbacks_out)
1819 {
1820 const char *name = NULL;
1821 gboolean with_fallbacks = TRUE;
1822
1823 if (g_strcmp0 (path, g_get_home_dir ()) == 0)
1824 {
1825 name = use_symbolic ? "user-home-symbolic" : "user-home";
1826 with_fallbacks = FALSE;
1827 }
1828 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
1829 {
1830 name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
1831 with_fallbacks = FALSE;
1832 }
1833 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
1834 {
1835 name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
1836 }
1837 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
1838 {
1839 name = use_symbolic ? "folder-download-symbolic" : "folder-download";
1840 }
1841 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
1842 {
1843 name = use_symbolic ? "folder-music-symbolic" : "folder-music";
1844 }
1845 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
1846 {
1847 name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
1848 }
1849 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
1850 {
1851 name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
1852 }
1853 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
1854 {
1855 name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
1856 }
1857 else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
1858 {
1859 name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
1860 }
1861 else
1862 {
1863 name = NULL;
1864 }
1865
1866 if (with_fallbacks_out != NULL)
1867 *with_fallbacks_out = with_fallbacks;
1868
1869 return name;
1870 }
1871
1872 static GIcon *
1873 get_icon (const char *path,
1874 const char *content_type,
1875 gboolean use_symbolic)
1876 {
1877 GIcon *icon = NULL;
1878 const char *icon_name;
1879 gboolean with_fallbacks;
1880
1881 icon_name = get_icon_name (path, use_symbolic, &with_fallbacks);
1882 if (icon_name != NULL)
1883 {
1884 if (with_fallbacks)
1885 icon = g_themed_icon_new_with_default_fallbacks (icon_name);
1886 else
1887 icon = g_themed_icon_new (icon_name);
1888 }
1889 else
1890 {
1891 if (use_symbolic)
1892 icon = g_content_type_get_symbolic_icon (content_type);
1893 else
1894 icon = g_content_type_get_icon (content_type);
1895 }
1896
1897 return icon;
1898 }
1899
1900 GFileInfo *
1901 _g_local_file_info_get (const char *basename,
1902 const char *path,
1903 GFileAttributeMatcher *attribute_matcher,
1904 GFileQueryInfoFlags flags,
1905 GLocalParentFileInfo *parent_info,
1906 GError **error)
1907 {
1908 GFileInfo *info;
1909 GLocalFileStat statbuf;
1910 GLocalFileStat statbuf2;
1911 int res;
1912 gboolean stat_ok;
1913 gboolean is_symlink, symlink_broken;
1914 char *symlink_target;
1915 GVfs *vfs;
1916 GVfsClass *class;
1917 guint64 device;
1918
1919 info = g_file_info_new ();
1920
1921 /* Make sure we don't set any unwanted attributes */
1922 g_file_info_set_attribute_mask (info, attribute_matcher);
1923
1924 _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
1925
1926 if (attribute_matcher == NULL)
1927 {
1928 g_file_info_unset_attribute_mask (info);
1929 return info;
1930 }
1931
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),
1935 &statbuf);
1936
1937 if (res == -1)
1938 {
1939 int errsv = errno;
1940
1941 /* Don't bail out if we get Permission denied (SELinux?) */
1942 if (errsv != EACCES)
1943 {
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);
1951 return NULL;
1952 }
1953 }
1954
1955 /* Even if stat() fails, try to get as much as other attributes possible */
1956 stat_ok = res != -1;
1957
1958 if (stat_ok)
1959 device = _g_stat_dev (&statbuf);
1960 else
1961 device = 0;
1962
1963 #ifdef S_ISLNK
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);
1970 #else
1971 is_symlink = FALSE;
1972 #endif
1973 symlink_broken = FALSE;
1974
1975 if (is_symlink)
1976 {
1977 g_file_info_set_is_symlink (info, TRUE);
1978
1979 /* Unless NOFOLLOW was set we default to following symlinks */
1980 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
1981 {
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),
1985 &statbuf2);
1986
1987 /* Report broken links as symlinks */
1988 if (res != -1)
1989 {
1990 statbuf = statbuf2;
1991 stat_ok = TRUE;
1992 }
1993 else
1994 symlink_broken = TRUE;
1995 }
1996 }
1997 else
1998 g_file_info_set_is_symlink (info, FALSE);
1999
2000 if (stat_ok)
2001 set_info_from_stat (info, &statbuf, attribute_matcher);
2002
2003 #ifndef G_OS_WIN32
2004 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2005 G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
2006 {
2007 g_file_info_set_is_hidden (info,
2008 (basename != NULL &&
2009 (basename[0] == '.' ||
2010 file_is_hidden (path, basename) ||
2011 (stat_ok &&
2012 _g_local_file_is_lost_found_dir (path, _g_stat_dev (&statbuf))))));
2013 }
2014
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))));
2019 #else
2020 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, FALSE);
2021
2022 g_file_info_set_is_hidden (info, (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN));
2023
2024 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE,
2025 (statbuf.attributes & FILE_ATTRIBUTE_ARCHIVE));
2026
2027 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM,
2028 (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM));
2029
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));
2032
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);
2035
2036 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, FALSE);
2037 #endif
2038
2039 symlink_target = NULL;
2040 if (is_symlink)
2041 {
2042 #if defined (S_ISLNK) || defined (G_OS_WIN32)
2043 symlink_target = read_link (path);
2044 #endif
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);
2049 }
2050
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))
2057 {
2058 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
2059
2060 if (content_type)
2061 {
2062 g_file_info_set_content_type (info, content_type);
2063
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))
2068 {
2069 GIcon *icon;
2070
2071 /* non symbolic icon */
2072 icon = get_icon (path, content_type, FALSE);
2073 if (icon != NULL)
2074 {
2075 g_file_info_set_icon (info, icon);
2076 g_object_unref (icon);
2077 }
2078
2079 /* symbolic icon */
2080 icon = get_icon (path, content_type, TRUE);
2081 if (icon != NULL)
2082 {
2083 g_file_info_set_symbolic_icon (info, icon);
2084 g_object_unref (icon);
2085 }
2086
2087 }
2088
2089 g_free (content_type);
2090 }
2091 }
2092
2093 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2094 G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
2095 {
2096 char *content_type = get_content_type (basename, path, stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
2097
2098 if (content_type)
2099 {
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);
2102 }
2103 }
2104
2105 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2106 G_FILE_ATTRIBUTE_ID_OWNER_USER))
2107 {
2108 char *name = NULL;
2109
2110 #ifdef G_OS_WIN32
2111 win32_get_file_user_info (path, NULL, &name, NULL);
2112 #else
2113 if (stat_ok)
2114 name = get_username_from_uid (_g_stat_uid (&statbuf));
2115 #endif
2116 if (name)
2117 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name);
2118 g_free (name);
2119 }
2120
2121 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2122 G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
2123 {
2124 char *name = NULL;
2125 #ifdef G_OS_WIN32
2126 win32_get_file_user_info (path, NULL, NULL, &name);
2127 #else
2128 if (stat_ok)
2129 name = get_realname_from_uid (_g_stat_uid (&statbuf));
2130 #endif
2131 if (name)
2132 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name);
2133 g_free (name);
2134 }
2135
2136 if (_g_file_attribute_matcher_matches_id (attribute_matcher,
2137 G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
2138 {
2139 char *name = NULL;
2140 #ifdef G_OS_WIN32
2141 win32_get_file_user_info (path, &name, NULL, NULL);
2142 #else
2143 if (stat_ok)
2144 name = get_groupname_from_gid (_g_stat_gid (&statbuf));
2145 #endif
2146 if (name)
2147 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name);
2148 g_free (name);
2149 }
2150
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));
2155
2156 if (stat_ok)
2157 get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
2158
2159 #ifdef HAVE_SELINUX
2160 get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2161 #endif
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);
2164
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))
2171 {
2172 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_AUTO);
2173 }
2174
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))
2181 {
2182 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_NORMAL);
2183 }
2184
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))
2191 {
2192 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_LARGE);
2193 }
2194
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))
2201 {
2202 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_XLARGE);
2203 }
2204
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))
2211 {
2212 get_thumbnail_attributes (path, info, stat_ok ? &statbuf : NULL, THUMBNAIL_SIZE_XXLARGE);
2213 }
2214
2215 vfs = g_vfs_get_default ();
2216 class = G_VFS_GET_CLASS (vfs);
2217 if (class->local_file_add_info)
2218 {
2219 class->local_file_add_info (vfs,
2220 path,
2221 device,
2222 attribute_matcher,
2223 info,
2224 NULL,
2225 &parent_info->extra_data,
2226 &parent_info->free_extra_data);
2227 }
2228
2229 g_file_info_unset_attribute_mask (info);
2230
2231 g_free (symlink_target);
2232
2233 return info;
2234 }
2235
2236 GFileInfo *
2237 _g_local_file_info_get_from_fd (int fd,
2238 const char *attributes,
2239 GError **error)
2240 {
2241 GLocalFileStat stat_buf;
2242 GFileAttributeMatcher *matcher;
2243 GFileInfo *info;
2244
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),
2248 &stat_buf) == -1)
2249 {
2250 int errsv = errno;
2251
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));
2256 return NULL;
2257 }
2258
2259 info = g_file_info_new ();
2260
2261 matcher = g_file_attribute_matcher_new (attributes);
2262
2263 /* Make sure we don't set any unwanted attributes */
2264 g_file_info_set_attribute_mask (info, matcher);
2265
2266 set_info_from_stat (info, &stat_buf, matcher);
2267
2268 #ifdef HAVE_SELINUX
2269 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
2270 is_selinux_enabled ())
2271 {
2272 char *context;
2273 if (fgetfilecon_raw (fd, &context) >= 0)
2274 {
2275 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, context);
2276 freecon (context);
2277 }
2278 }
2279 #endif
2280
2281 get_xattrs_from_fd (fd, TRUE, info, matcher);
2282 get_xattrs_from_fd (fd, FALSE, info, matcher);
2283
2284 g_file_attribute_matcher_unref (matcher);
2285
2286 g_file_info_unset_attribute_mask (info);
2287
2288 return info;
2289 }
2290
2291 static gboolean
2292 get_uint32 (const GFileAttributeValue *value,
2293 guint32 *val_out,
2294 GError **error)
2295 {
2296 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
2297 {
2298 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2299 _("Invalid attribute type (uint32 expected)"));
2300 return FALSE;
2301 }
2302
2303 *val_out = value->u.uint32;
2304
2305 return TRUE;
2306 }
2307
2308 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
2309 static gboolean
2310 get_uint64 (const GFileAttributeValue *value,
2311 guint64 *val_out,
2312 GError **error)
2313 {
2314 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
2315 {
2316 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2317 _("Invalid attribute type (uint64 expected)"));
2318 return FALSE;
2319 }
2320
2321 *val_out = value->u.uint64;
2322
2323 return TRUE;
2324 }
2325 #endif
2326
2327 #if defined(HAVE_SYMLINK)
2328 static gboolean
2329 get_byte_string (const GFileAttributeValue *value,
2330 const char **val_out,
2331 GError **error)
2332 {
2333 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
2334 {
2335 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2336 _("Invalid attribute type (byte string expected)"));
2337 return FALSE;
2338 }
2339
2340 *val_out = value->u.string;
2341
2342 return TRUE;
2343 }
2344 #endif
2345
2346 #ifdef HAVE_SELINUX
2347 static gboolean
2348 get_string (const GFileAttributeValue *value,
2349 const char **val_out,
2350 GError **error)
2351 {
2352 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
2353 {
2354 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2355 _("Invalid attribute type (byte string expected)"));
2356 return FALSE;
2357 }
2358
2359 *val_out = value->u.string;
2360
2361 return TRUE;
2362 }
2363 #endif
2364
2365 static gboolean
2366 set_unix_mode (char *filename,
2367 GFileQueryInfoFlags flags,
2368 const GFileAttributeValue *value,
2369 GError **error)
2370 {
2371 guint32 val = 0;
2372 int res = 0;
2373
2374 if (!get_uint32 (value, &val, error))
2375 return FALSE;
2376
2377 #if defined (HAVE_SYMLINK) || defined (G_OS_WIN32)
2378 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
2379 #ifdef HAVE_LCHMOD
2380 res = lchmod (filename, val);
2381 #else
2382 gboolean is_symlink;
2383 #ifndef G_OS_WIN32
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));
2389 #else
2390 /* FIXME: implement lchmod for W32, should be doable */
2391 GWin32PrivateStat statbuf;
2392
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));
2397 #endif
2398 if (is_symlink)
2399 {
2400 g_set_error_literal (error, G_IO_ERROR,
2401 G_IO_ERROR_NOT_SUPPORTED,
2402 _("Cannot set permissions on symlinks"));
2403 return FALSE;
2404 }
2405 else if (res == 0)
2406 res = g_chmod (filename, val);
2407 #endif
2408 } else
2409 #endif
2410 res = g_chmod (filename, val);
2411
2412 if (res == -1)
2413 {
2414 int errsv = errno;
2415
2416 g_set_error (error, G_IO_ERROR,
2417 g_io_error_from_errno (errsv),
2418 _("Error setting permissions: %s"),
2419 g_strerror (errsv));
2420 return FALSE;
2421 }
2422 return TRUE;
2423 }
2424
2425 #ifdef G_OS_UNIX
2426 static gboolean
2427 set_unix_uid_gid (char *filename,
2428 const GFileAttributeValue *uid_value,
2429 const GFileAttributeValue *gid_value,
2430 GFileQueryInfoFlags flags,
2431 GError **error)
2432 {
2433 int res;
2434 guint32 val = 0;
2435 uid_t uid;
2436 gid_t gid;
2437
2438 if (uid_value)
2439 {
2440 if (!get_uint32 (uid_value, &val, error))
2441 return FALSE;
2442 uid = val;
2443 }
2444 else
2445 uid = -1;
2446
2447 if (gid_value)
2448 {
2449 if (!get_uint32 (gid_value, &val, error))
2450 return FALSE;
2451 gid = val;
2452 }
2453 else
2454 gid = -1;
2455
2456 #ifdef HAVE_LCHOWN
2457 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
2458 res = lchown (filename, uid, gid);
2459 else
2460 #endif
2461 res = chown (filename, uid, gid);
2462
2463 if (res == -1)
2464 {
2465 int errsv = errno;
2466
2467 g_set_error (error, G_IO_ERROR,
2468 g_io_error_from_errno (errsv),
2469 _("Error setting owner: %s"),
2470 g_strerror (errsv));
2471 return FALSE;
2472 }
2473 return TRUE;
2474 }
2475 #endif
2476
2477 #ifdef HAVE_SYMLINK
2478 static gboolean
2479 set_symlink (char *filename,
2480 const GFileAttributeValue *value,
2481 GError **error)
2482 {
2483 const char *val;
2484 struct stat statbuf;
2485
2486 if (!get_byte_string (value, &val, error))
2487 return FALSE;
2488
2489 if (val == NULL)
2490 {
2491 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2492 _("symlink must be non-NULL"));
2493 return FALSE;
2494 }
2495
2496 if (g_lstat (filename, &statbuf))
2497 {
2498 int errsv = errno;
2499
2500 g_set_error (error, G_IO_ERROR,
2501 g_io_error_from_errno (errsv),
2502 _("Error setting symlink: %s"),
2503 g_strerror (errsv));
2504 return FALSE;
2505 }
2506
2507 if (!S_ISLNK (statbuf.st_mode))
2508 {
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"));
2512 return FALSE;
2513 }
2514
2515 if (g_unlink (filename))
2516 {
2517 int errsv = errno;
2518
2519 g_set_error (error, G_IO_ERROR,
2520 g_io_error_from_errno (errsv),
2521 _("Error setting symlink: %s"),
2522 g_strerror (errsv));
2523 return FALSE;
2524 }
2525
2526 if (symlink (filename, val) != 0)
2527 {
2528 int errsv = errno;
2529
2530 g_set_error (error, G_IO_ERROR,
2531 g_io_error_from_errno (errsv),
2532 _("Error setting symlink: %s"),
2533 g_strerror (errsv));
2534 return FALSE;
2535 }
2536
2537 return TRUE;
2538 }
2539 #endif
2540
2541 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined(G_OS_WIN32)
2542 static int
2543 lazy_stat (const char *filename,
2544 GStatBuf *statbuf,
2545 gboolean *called_stat)
2546 {
2547 int res;
2548
2549 if (*called_stat)
2550 return 0;
2551
2552 res = g_stat (filename, statbuf);
2553
2554 if (res == 0)
2555 *called_stat = TRUE;
2556
2557 return res;
2558 }
2559 #endif
2560
2561 #if defined (G_OS_WIN32)
2562 /* From
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.
2571 *
2572 * The function that does the reverse can be found in
2573 * glib/gstdio.c.
2574 */
2575 static gboolean
2576 _g_win32_unix_time_to_filetime (gint64 ut,
2577 gint32 nsec,
2578 FILETIME *ft,
2579 GError **error)
2580 {
2581 gint64 result;
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.
2586 */
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).
2593 */
2594 const gint64 max_systemtime = 0x7fff35f4f06c58f0;
2595
2596 g_return_val_if_fail (ft != NULL, FALSE);
2597 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2598
2599 if (nsec < 0)
2600 {
2601 g_set_error (error, G_IO_ERROR,
2602 G_IO_ERROR_INVALID_DATA,
2603 _("Extra nanoseconds %d for UNIX timestamp %lld are negative"),
2604 nsec, ut);
2605 return FALSE;
2606 }
2607
2608 if (nsec >= hundreds_of_nsec_per_sec * 100)
2609 {
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"),
2613 nsec, ut);
2614 return FALSE;
2615 }
2616
2617 if (ut >= (G_MAXINT64 / hundreds_of_nsec_per_sec) ||
2618 (ut * hundreds_of_nsec_per_sec) >= (G_MAXINT64 - filetime_unix_epoch_offset))
2619 {
2620 g_set_error (error, G_IO_ERROR,
2621 G_IO_ERROR_INVALID_DATA,
2622 _("UNIX timestamp %lld does not fit into 64 bits"),
2623 ut);
2624 return FALSE;
2625 }
2626
2627 result = ut * hundreds_of_nsec_per_sec + filetime_unix_epoch_offset + nsec / 100;
2628
2629 if (result >= max_systemtime || result < 0)
2630 {
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"),
2634 ut);
2635 return FALSE;
2636 }
2637
2638 ft->dwLowDateTime = (DWORD) (result);
2639 ft->dwHighDateTime = (DWORD) (result >> 32);
2640
2641 return TRUE;
2642 }
2643
2644 static gboolean
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,
2652 GError **error)
2653 {
2654 BOOL res;
2655 guint64 val = 0;
2656 guint32 val_usec = 0;
2657 guint32 val_nsec = 0;
2658 gunichar2 *filename_utf16;
2659 SECURITY_ATTRIBUTES sec = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
2660 HANDLE file_handle;
2661 FILETIME mtime;
2662 FILETIME atime;
2663 FILETIME *p_mtime = NULL;
2664 FILETIME *p_atime = NULL;
2665 DWORD gle;
2666 GStatBuf statbuf;
2667 gboolean got_stat = FALSE;
2668
2669 /* ATIME */
2670 if (atime_value)
2671 {
2672 if (!get_uint64 (atime_value, &val, error))
2673 return FALSE;
2674 val_usec = 0;
2675 val_nsec = 0;
2676 }
2677 else
2678 {
2679 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2680 {
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;
2686 #endif
2687 }
2688 }
2689
2690 if (atime_usec_value &&
2691 !get_uint32 (atime_usec_value, &val_usec, error))
2692 return FALSE;
2693
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);
2698
2699 if (atime_nsec_value &&
2700 !get_uint32 (atime_nsec_value, &val_nsec, error))
2701 return FALSE;
2702 if (val_nsec > 0)
2703 {
2704 if (!_g_win32_unix_time_to_filetime (val, val_nsec, &atime, error))
2705 return FALSE;
2706 }
2707 else
2708 {
2709 if (!_g_win32_unix_time_to_filetime (val, val_usec, &atime, error))
2710 return FALSE;
2711 }
2712
2713 p_atime = &atime;
2714
2715 /* MTIME */
2716 if (mtime_value)
2717 {
2718 if (!get_uint64 (mtime_value, &val, error))
2719 return FALSE;
2720 val_usec = 0;
2721 val_nsec = 0;
2722 }
2723 else
2724 {
2725 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2726 {
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;
2732 #endif
2733 }
2734 }
2735
2736 if (mtime_usec_value &&
2737 !get_uint32 (mtime_usec_value, &val_usec, error))
2738 return FALSE;
2739
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);
2744
2745 if (mtime_nsec_value &&
2746 !get_uint32 (mtime_nsec_value, &val_nsec, error))
2747 return FALSE;
2748 if (val_nsec > 0)
2749 {
2750 if (!_g_win32_unix_time_to_filetime (val, val_nsec, &mtime, error))
2751 return FALSE;
2752 }
2753 else
2754 {
2755 if (!_g_win32_unix_time_to_filetime (val, val_usec, &mtime, error))
2756 return FALSE;
2757 }
2758 p_mtime = &mtime;
2759
2760 filename_utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2761
2762 if (filename_utf16 == NULL)
2763 {
2764 g_prefix_error (error,
2765 _("File name “%s” cannot be converted to UTF-16"),
2766 filename);
2767 return FALSE;
2768 }
2769
2770 file_handle = CreateFileW (filename_utf16,
2771 FILE_WRITE_ATTRIBUTES,
2772 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
2773 &sec,
2774 OPEN_EXISTING,
2775 FILE_FLAG_BACKUP_SEMANTICS,
2776 NULL);
2777 gle = GetLastError ();
2778 g_clear_pointer (&filename_utf16, g_free);
2779
2780 if (file_handle == INVALID_HANDLE_VALUE)
2781 {
2782 g_set_error (error, G_IO_ERROR,
2783 g_io_error_from_errno (gle),
2784 _("File “%s” cannot be opened: Windows Error %lu"),
2785 filename, gle);
2786
2787 return FALSE;
2788 }
2789
2790 res = SetFileTime (file_handle, NULL, p_atime, p_mtime);
2791 gle = GetLastError ();
2792 CloseHandle (file_handle);
2793
2794 if (!res)
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"),
2798 filename, gle);
2799
2800 return res;
2801 }
2802 #elif defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT)
2803 static gboolean
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,
2811 GError **error)
2812 {
2813 int res;
2814 guint64 val = 0;
2815 GStatBuf statbuf;
2816 gboolean got_stat = FALSE;
2817 #ifdef HAVE_UTIMENSAT
2818 struct timespec times_n[2] = { {0, 0}, {0, 0} };
2819 /* ATIME */
2820 if (atime_value)
2821 {
2822 if (!get_uint64 (atime_value, &val, error))
2823 return FALSE;
2824 times_n[0].tv_sec = val;
2825 }
2826 else
2827 {
2828 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2829 {
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;
2835 #endif
2836 }
2837 }
2838
2839 if (atime_usec_value)
2840 {
2841 guint32 val_usec = 0;
2842
2843 if (!get_uint32 (atime_usec_value, &val_usec, error))
2844 return FALSE;
2845
2846 times_n[0].tv_nsec = val_usec * 1000;
2847 }
2848
2849 if (atime_nsec_value)
2850 {
2851 guint32 val_nsec = 0;
2852
2853 if (!get_uint32 (atime_nsec_value, &val_nsec, error))
2854 return FALSE;
2855 times_n[0].tv_nsec = val_nsec;
2856 }
2857
2858 /* MTIME */
2859 if (mtime_value)
2860 {
2861 if (!get_uint64 (mtime_value, &val, error))
2862 return FALSE;
2863 times_n[1].tv_sec = val;
2864 }
2865 else
2866 {
2867 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2868 {
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;
2874 #endif
2875 }
2876 }
2877
2878 if (mtime_usec_value)
2879 {
2880 guint32 val_usec = 0;
2881
2882 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2883 return FALSE;
2884
2885 times_n[1].tv_nsec = val_usec * 1000;
2886 }
2887
2888 if (mtime_nsec_value)
2889 {
2890 guint32 val_nsec = 0;
2891
2892 if (!get_uint32 (mtime_nsec_value, &val_nsec, error))
2893 return FALSE;
2894 times_n[1].tv_nsec = val_nsec;
2895 }
2896
2897 res = utimensat (AT_FDCWD, filename, times_n, 0);
2898
2899 #else /* HAVE_UTIMES */
2900
2901 struct timeval times[2] = { {0, 0}, {0, 0} };
2902
2903 /* ATIME */
2904 if (atime_value)
2905 {
2906 if (!get_uint64 (atime_value, &val, error))
2907 return FALSE;
2908
2909 times[0].tv_sec = val;
2910 }
2911 else
2912 {
2913 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2914 {
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;
2920 #endif
2921 }
2922 }
2923
2924 if (atime_usec_value)
2925 {
2926 guint32 val_usec = 0;
2927
2928 if (!get_uint32 (atime_usec_value, &val_usec, error))
2929 return FALSE;
2930
2931 times[0].tv_usec = val_usec;
2932 }
2933
2934 /* MTIME */
2935 if (mtime_value)
2936 {
2937 if (!get_uint64 (mtime_value, &val, error))
2938 return FALSE;
2939
2940 times[1].tv_sec = val;
2941 }
2942 else
2943 {
2944 if (lazy_stat (filename, &statbuf, &got_stat) == 0)
2945 {
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;
2951 #endif
2952 }
2953 }
2954
2955 if (mtime_usec_value)
2956 {
2957 guint32 val_usec = 0;
2958
2959 if (!get_uint32 (mtime_usec_value, &val_usec, error))
2960 return FALSE;
2961
2962 times[1].tv_usec = val_usec;
2963 }
2964
2965 res = utimes (filename, times);
2966 #endif
2967
2968 if (res == -1)
2969 {
2970 int errsv = errno;
2971
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));
2976 return FALSE;
2977 }
2978 return TRUE;
2979 }
2980 #endif
2981
2982
2983 #ifdef HAVE_SELINUX
2984 static gboolean
2985 set_selinux_context (char *filename,
2986 const GFileAttributeValue *value,
2987 GError **error)
2988 {
2989 const char *val;
2990
2991 if (!get_string (value, &val, error))
2992 return FALSE;
2993
2994 if (val == NULL)
2995 {
2996 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
2997 _("SELinux context must be non-NULL"));
2998 return FALSE;
2999 }
3000
3001 if (!is_selinux_enabled ())
3002 {
3003 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
3004 _("SELinux is not enabled on this system"));
3005 return FALSE;
3006 }
3007
3008 if (setfilecon_raw (filename, val) < 0)
3009 {
3010 int errsv = errno;
3011
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));
3016 return FALSE;
3017 }
3018
3019 return TRUE;
3020 }
3021 #endif
3022
3023
3024 gboolean
3025 _g_local_file_info_set_attribute (char *filename,
3026 const char *attribute,
3027 GFileAttributeType type,
3028 gpointer value_p,
3029 GFileQueryInfoFlags flags,
3030 GCancellable *cancellable,
3031 GError **error)
3032 {
3033 GFileAttributeValue value = { 0 };
3034 GVfsClass *class;
3035 GVfs *vfs;
3036
3037 _g_file_attribute_value_set_from_pointer (&value, type, value_p, FALSE);
3038
3039 if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
3040 return set_unix_mode (filename, flags, &value, error);
3041
3042 #ifdef G_OS_UNIX
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);
3047 #endif
3048
3049 #ifdef HAVE_SYMLINK
3050 else if (strcmp (attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
3051 return set_symlink (filename, &value, error);
3052 #endif
3053
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);
3067 #endif
3068
3069 #ifdef HAVE_XATTR
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);
3074 #endif
3075
3076 #ifdef HAVE_SELINUX
3077 else if (strcmp (attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
3078 return set_selinux_context (filename, &value, error);
3079 #endif
3080
3081 vfs = g_vfs_get_default ();
3082 class = G_VFS_GET_CLASS (vfs);
3083 if (class->local_file_set_attributes)
3084 {
3085 GFileInfo *info;
3086
3087 info = g_file_info_new ();
3088 g_file_info_set_attribute (info,
3089 attribute,
3090 type,
3091 value_p);
3092 if (!class->local_file_set_attributes (vfs, filename,
3093 info,
3094 flags, cancellable,
3095 error))
3096 {
3097 g_object_unref (info);
3098 return FALSE;
3099 }
3100
3101 if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
3102 {
3103 g_object_unref (info);
3104 return TRUE;
3105 }
3106
3107 g_object_unref (info);
3108 }
3109
3110 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
3111 _("Setting attribute %s not supported"), attribute);
3112 return FALSE;
3113 }
3114
3115 gboolean
3116 _g_local_file_info_set_attributes (char *filename,
3117 GFileInfo *info,
3118 GFileQueryInfoFlags flags,
3119 GCancellable *cancellable,
3120 GError **error)
3121 {
3122 GFileAttributeValue *value;
3123 #ifdef G_OS_UNIX
3124 GFileAttributeValue *uid, *gid;
3125 #endif
3126 #if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
3127 GFileAttributeValue *mtime, *mtime_usec, *mtime_nsec, *atime, *atime_usec, *atime_nsec;
3128 #endif
3129 #if defined (G_OS_UNIX) || defined (G_OS_WIN32)
3130 GFileAttributeStatus status;
3131 #endif
3132 gboolean res;
3133 GVfsClass *class;
3134 GVfs *vfs;
3135
3136 /* Handles setting multiple specified data in a single set, and takes care
3137 of ordering restrictions when setting attributes */
3138
3139 res = TRUE;
3140
3141 /* Set symlink first, since this recreates the file */
3142 #ifdef HAVE_SYMLINK
3143 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
3144 if (value)
3145 {
3146 if (!set_symlink (filename, value, error))
3147 {
3148 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3149 res = FALSE;
3150 /* Don't set error multiple times */
3151 error = NULL;
3152 }
3153 else
3154 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
3155
3156 }
3157 #endif
3158
3159 #ifdef G_OS_UNIX
3160 /* Group uid and gid setting into one call
3161 * Change ownership before permissions, since ownership changes can
3162 change permissions (e.g. setuid)
3163 */
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);
3166
3167 if (uid || gid)
3168 {
3169 if (!set_unix_uid_gid (filename, uid, gid, flags, error))
3170 {
3171 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3172 res = FALSE;
3173 /* Don't set error multiple times */
3174 error = NULL;
3175 }
3176 else
3177 status = G_FILE_ATTRIBUTE_STATUS_SET;
3178 if (uid)
3179 uid->status = status;
3180 if (gid)
3181 gid->status = status;
3182 }
3183 #endif
3184
3185 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
3186 if (value)
3187 {
3188 if (!set_unix_mode (filename, flags, value, error))
3189 {
3190 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3191 res = FALSE;
3192 /* Don't set error multiple times */
3193 error = NULL;
3194 }
3195 else
3196 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
3197
3198 }
3199
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
3203 */
3204
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);
3211
3212 if (mtime || mtime_usec || mtime_nsec || atime || atime_usec || atime_nsec)
3213 {
3214 if (!set_mtime_atime (filename, mtime, mtime_usec, mtime_nsec, atime, atime_usec, atime_nsec, error))
3215 {
3216 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3217 res = FALSE;
3218 /* Don't set error multiple times */
3219 error = NULL;
3220 }
3221 else
3222 status = G_FILE_ATTRIBUTE_STATUS_SET;
3223
3224 if (mtime)
3225 mtime->status = status;
3226 if (mtime_usec)
3227 mtime_usec->status = status;
3228 if (mtime_nsec)
3229 mtime_nsec->status = status;
3230 if (atime)
3231 atime->status = status;
3232 if (atime_usec)
3233 atime_usec->status = status;
3234 if (atime_nsec)
3235 atime_nsec->status = status;
3236 }
3237 #endif
3238
3239 /* xattrs are handled by default callback */
3240
3241
3242 /* SELinux context */
3243 #ifdef HAVE_SELINUX
3244 if (is_selinux_enabled ()) {
3245 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
3246 if (value)
3247 {
3248 if (!set_selinux_context (filename, value, error))
3249 {
3250 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
3251 res = FALSE;
3252 /* Don't set error multiple times */
3253 error = NULL;
3254 }
3255 else
3256 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
3257 }
3258 }
3259 #endif
3260
3261 vfs = g_vfs_get_default ();
3262 class = G_VFS_GET_CLASS (vfs);
3263 if (class->local_file_set_attributes)
3264 {
3265 if (!class->local_file_set_attributes (vfs, filename,
3266 info,
3267 flags, cancellable,
3268 error))
3269 {
3270 res = FALSE;
3271 /* Don't set error multiple times */
3272 error = NULL;
3273 }
3274 }
3275
3276 return res;
3277 }