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 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23#include "config.h"
24
25#include <glib.h>
26
27#ifdef HAVE_SYS_TIME_H
28#include <sys/time.h>
29#endif
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <string.h>
33#include <fcntl.h>
34#include <errno.h>
35#ifdef G_OS_UNIX
36#include <grp.h>
37#include <pwd.h>
38#endif
39#ifdef HAVE_SELINUX
40#include <selinux/selinux.h>
41#endif
42
43#ifdef HAVE_XATTR
44
45#if defined HAVE_SYS_XATTR_H
46 #include <sys/xattr.h>
47#elif defined HAVE_ATTR_XATTR_H
48 #include <attr/xattr.h>
49#else
50 #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
51#endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
52
53#endif /* HAVE_XATTR */
54
55#include <glib/gstdio.h>
56#include <glib/gstdioprivate.h>
57#include <gfileattribute-priv.h>
58#include <gfileinfo-priv.h>
59#include <gvfs.h>
60
61#ifdef G_OS_UNIX
62#include <unistd.h>
63#include "glib-unix.h"
64#endif
65
66#include "glib-private.h"
67
68#include "thumbnail-verify.h"
69
70#ifdef G_OS_WIN32
71#include <windows.h>
72#include <io.h>
73#ifndef W_OK
74#define W_OK 2
75#endif
76#ifndef R_OK
77#define R_OK 4
78#endif
79#ifndef X_OK
80#define X_OK 0 /* not really */
81#endif
82#ifndef S_ISREG
83#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
84#endif
85#ifndef S_ISDIR
86#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
87#endif
88#ifndef S_IXUSR
89#define S_IXUSR _S_IEXEC
90#endif
91#endif
92
93#include "glocalfileinfo.h"
94#include "gioerror.h"
95#include "gthemedicon.h"
96#include "gcontenttypeprivate.h"
97#include "glibintl.h"
98
99
100struct ThumbMD5Context {
101 guint32 buf[4];
102 guint32 bits[2];
103 unsigned char in[64];
104};
105
106#ifndef G_OS_WIN32
107
108typedef struct {
109 char *user_name;
110 char *real_name;
111} UidData;
112
113G_LOCK_DEFINE_STATIC (uid_cache);
114static GHashTable *uid_cache = NULL;
115
116G_LOCK_DEFINE_STATIC (gid_cache);
117static GHashTable *gid_cache = NULL;
118
119#endif /* !G_OS_WIN32 */
120
121char *
122_g_local_file_info_create_etag (GLocalFileStat *statbuf)
123{
124 glong sec, usec;
125
126 g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL);
127
128#if defined (G_OS_WIN32)
129 sec = statbuf->st_mtim.tv_sec;
130 usec = statbuf->st_mtim.tv_nsec / 1000;
131#else
132 sec = _g_stat_mtime (buf: statbuf);
133#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
134 usec = statbuf->st_mtimensec / 1000;
135#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
136 usec = _g_stat_mtim_nsec (buf: statbuf) / 1000;
137#else
138 usec = 0;
139#endif
140#endif
141
142 return g_strdup_printf (format: "%lu:%lu", sec, usec);
143}
144
145static char *
146_g_local_file_info_create_file_id (GLocalFileStat *statbuf)
147{
148 guint64 ino;
149#ifdef G_OS_WIN32
150 ino = statbuf->file_index;
151#else
152 ino = _g_stat_ino (buf: statbuf);
153#endif
154 return g_strdup_printf (format: "l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
155 (guint64) _g_stat_dev (buf: statbuf),
156 ino);
157}
158
159static char *
160_g_local_file_info_create_fs_id (GLocalFileStat *statbuf)
161{
162 return g_strdup_printf (format: "l%" G_GUINT64_FORMAT,
163 (guint64) _g_stat_dev (buf: statbuf));
164}
165
166#if defined (S_ISLNK) || defined (G_OS_WIN32)
167
168static gchar *
169read_link (const gchar *full_name)
170{
171#if defined (HAVE_READLINK)
172 gchar *buffer;
173 gsize size;
174
175 size = 256;
176 buffer = g_malloc (n_bytes: size);
177
178 while (1)
179 {
180 gssize read_size;
181
182 read_size = readlink (path: full_name, buf: buffer, len: size);
183 if (read_size < 0)
184 {
185 g_free (mem: buffer);
186 return NULL;
187 }
188 if ((gsize) read_size < size)
189 {
190 buffer[read_size] = 0;
191 return buffer;
192 }
193 size *= 2;
194 buffer = g_realloc (mem: buffer, n_bytes: size);
195 }
196#elif defined (G_OS_WIN32)
197 gchar *buffer;
198 int read_size;
199
200 read_size = GLIB_PRIVATE_CALL (g_win32_readlink_utf8) (full_name, NULL, 0, &buffer, TRUE);
201 if (read_size < 0)
202 return NULL;
203 else if (read_size == 0)
204 return strdup ("");
205 else
206 return buffer;
207#else
208 return NULL;
209#endif
210}
211
212#endif /* S_ISLNK || G_OS_WIN32 */
213
214#ifdef HAVE_SELINUX
215/* Get the SELinux security context */
216static void
217get_selinux_context (const char *path,
218 GFileInfo *info,
219 GFileAttributeMatcher *attribute_matcher,
220 gboolean follow_symlinks)
221{
222 char *context;
223
224 if (!_g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT))
225 return;
226
227 if (is_selinux_enabled ())
228 {
229 if (follow_symlinks)
230 {
231 if (lgetfilecon_raw (path, con: &context) < 0)
232 return;
233 }
234 else
235 {
236 if (getfilecon_raw (path, con: &context) < 0)
237 return;
238 }
239
240 if (context)
241 {
242 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, attr_value: context);
243 freecon (con: context);
244 }
245 }
246}
247#endif
248
249#ifdef HAVE_XATTR
250
251/* Wrappers to hide away differences between (Linux) getxattr/lgetxattr and
252 * (Mac) getxattr(..., XATTR_NOFOLLOW)
253 */
254#ifdef HAVE_XATTR_NOFOLLOW
255#define g_fgetxattr(fd,name,value,size) fgetxattr(fd,name,value,size,0,0)
256#define g_flistxattr(fd,name,size) flistxattr(fd,name,size,0)
257#define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0,0)
258#else
259#define g_fgetxattr fgetxattr
260#define g_flistxattr flistxattr
261#define g_setxattr(path,name,value,size) setxattr(path,name,value,size,0)
262#endif
263
264static gssize
265g_getxattr (const char *path, const char *name, void *value, size_t size,
266 gboolean follow_symlinks)
267{
268#ifdef HAVE_XATTR_NOFOLLOW
269 return getxattr (path, name, value, size, 0, follow_symlinks ? 0 : XATTR_NOFOLLOW);
270#else
271 if (follow_symlinks)
272 return getxattr (path: path, name: name, value: value, size: size);
273 else
274 return lgetxattr (path: path, name: name, value: value, size: size);
275#endif
276}
277
278static gssize
279g_listxattr(const char *path, char *namebuf, size_t size,
280 gboolean follow_symlinks)
281{
282#ifdef HAVE_XATTR_NOFOLLOW
283 return listxattr (path, namebuf, size, follow_symlinks ? 0 : XATTR_NOFOLLOW);
284#else
285 if (follow_symlinks)
286 return listxattr (path: path, list: namebuf, size: size);
287 else
288 return llistxattr (path: path, list: namebuf, size: size);
289#endif
290}
291
292static gboolean
293valid_char (char c)
294{
295 return c >= 32 && c <= 126 && c != '\\';
296}
297
298static gboolean
299name_is_valid (const char *str)
300{
301 while (*str)
302 {
303 if (!valid_char (c: *str++))
304 return FALSE;
305 }
306 return TRUE;
307}
308
309static char *
310hex_escape_buffer (const char *str,
311 size_t len,
312 gboolean *free_return)
313{
314 size_t num_invalid, i;
315 char *escaped_str, *p;
316 unsigned char c;
317 static char *hex_digits = "0123456789abcdef";
318
319 num_invalid = 0;
320 for (i = 0; i < len; i++)
321 {
322 if (!valid_char (c: str[i]))
323 num_invalid++;
324 }
325
326 if (num_invalid == 0)
327 {
328 *free_return = FALSE;
329 return (char *)str;
330 }
331
332 escaped_str = g_malloc (n_bytes: len + num_invalid*3 + 1);
333
334 p = escaped_str;
335 for (i = 0; i < len; i++)
336 {
337 if (valid_char (c: str[i]))
338 *p++ = str[i];
339 else
340 {
341 c = str[i];
342 *p++ = '\\';
343 *p++ = 'x';
344 *p++ = hex_digits[(c >> 4) & 0xf];
345 *p++ = hex_digits[c & 0xf];
346 }
347 }
348 *p = 0;
349
350 *free_return = TRUE;
351 return escaped_str;
352}
353
354static char *
355hex_escape_string (const char *str,
356 gboolean *free_return)
357{
358 return hex_escape_buffer (str, len: strlen (s: str), free_return);
359}
360
361static char *
362hex_unescape_string (const char *str,
363 int *out_len,
364 gboolean *free_return)
365{
366 int i;
367 char *unescaped_str, *p;
368 unsigned char c;
369 int len;
370
371 len = strlen (s: str);
372
373 if (strchr (s: str, c: '\\') == NULL)
374 {
375 if (out_len)
376 *out_len = len;
377 *free_return = FALSE;
378 return (char *)str;
379 }
380
381 unescaped_str = g_malloc (n_bytes: len + 1);
382
383 p = unescaped_str;
384 for (i = 0; i < len; i++)
385 {
386 if (str[i] == '\\' &&
387 str[i+1] == 'x' &&
388 len - i >= 4)
389 {
390 c =
391 (g_ascii_xdigit_value (c: str[i+2]) << 4) |
392 g_ascii_xdigit_value (c: str[i+3]);
393 *p++ = c;
394 i += 3;
395 }
396 else
397 *p++ = str[i];
398 }
399 if (out_len)
400 *out_len = p - unescaped_str;
401 *p++ = 0;
402
403 *free_return = TRUE;
404 return unescaped_str;
405}
406
407static void
408escape_xattr (GFileInfo *info,
409 const char *gio_attr, /* gio attribute name */
410 const char *value, /* Is zero terminated */
411 size_t len /* not including zero termination */)
412{
413 char *escaped_val;
414 gboolean free_escaped_val;
415
416 escaped_val = hex_escape_buffer (str: value, len, free_return: &free_escaped_val);
417
418 g_file_info_set_attribute_string (info, attribute: gio_attr, attr_value: escaped_val);
419
420 if (free_escaped_val)
421 g_free (mem: escaped_val);
422}
423
424static void
425get_one_xattr (const char *path,
426 GFileInfo *info,
427 const char *gio_attr,
428 const char *xattr,
429 gboolean follow_symlinks)
430{
431 char value[64];
432 char *value_p;
433 gssize len;
434 int errsv;
435
436 len = g_getxattr (path, name: xattr, value, size: sizeof (value)-1, follow_symlinks);
437 errsv = errno;
438
439 value_p = NULL;
440 if (len >= 0)
441 value_p = value;
442 else if (len == -1 && errsv == ERANGE)
443 {
444 len = g_getxattr (path, name: xattr, NULL, size: 0, follow_symlinks);
445
446 if (len < 0)
447 return;
448
449 value_p = g_malloc (n_bytes: len+1);
450
451 len = g_getxattr (path, name: xattr, value: value_p, size: len, follow_symlinks);
452
453 if (len < 0)
454 {
455 g_free (mem: value_p);
456 return;
457 }
458 }
459 else
460 return;
461
462 /* Null terminate */
463 value_p[len] = 0;
464
465 escape_xattr (info, gio_attr, value: value_p, len);
466
467 if (value_p != value)
468 g_free (mem: value_p);
469}
470
471#endif /* defined HAVE_XATTR */
472
473static void
474get_xattrs (const char *path,
475 gboolean user,
476 GFileInfo *info,
477 GFileAttributeMatcher *matcher,
478 gboolean follow_symlinks)
479{
480#ifdef HAVE_XATTR
481 gboolean all;
482 gsize list_size;
483 gssize list_res_size;
484 size_t len;
485 char *list;
486 const char *attr, *attr2;
487
488 if (user)
489 all = g_file_attribute_matcher_enumerate_namespace (matcher, ns: "xattr");
490 else
491 all = g_file_attribute_matcher_enumerate_namespace (matcher, ns: "xattr-sys");
492
493 if (all)
494 {
495 int errsv;
496
497 list_res_size = g_listxattr (path, NULL, size: 0, follow_symlinks);
498
499 if (list_res_size == -1 ||
500 list_res_size == 0)
501 return;
502
503 list_size = list_res_size;
504 list = g_malloc (n_bytes: list_size);
505
506 retry:
507
508 list_res_size = g_listxattr (path, namebuf: list, size: list_size, follow_symlinks);
509 errsv = errno;
510
511 if (list_res_size == -1 && errsv == ERANGE)
512 {
513 list_size = list_size * 2;
514 list = g_realloc (mem: list, n_bytes: list_size);
515 goto retry;
516 }
517
518 if (list_res_size == -1)
519 {
520 g_free (mem: list);
521 return;
522 }
523
524 attr = list;
525 while (list_res_size > 0)
526 {
527 if ((user && g_str_has_prefix (str: attr, prefix: "user.")) ||
528 (!user && !g_str_has_prefix (str: attr, prefix: "user.")))
529 {
530 char *escaped_attr, *gio_attr;
531 gboolean free_escaped_attr;
532
533 if (user)
534 {
535 escaped_attr = hex_escape_string (str: attr + 5, free_return: &free_escaped_attr);
536 gio_attr = g_strconcat (string1: "xattr::", escaped_attr, NULL);
537 }
538 else
539 {
540 escaped_attr = hex_escape_string (str: attr, free_return: &free_escaped_attr);
541 gio_attr = g_strconcat (string1: "xattr-sys::", escaped_attr, NULL);
542 }
543
544 if (free_escaped_attr)
545 g_free (mem: escaped_attr);
546
547 get_one_xattr (path, info, gio_attr, xattr: attr, follow_symlinks);
548
549 g_free (mem: gio_attr);
550 }
551
552 len = strlen (s: attr) + 1;
553 attr += len;
554 list_res_size -= len;
555 }
556
557 g_free (mem: list);
558 }
559 else
560 {
561 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
562 {
563 char *unescaped_attribute, *a;
564 gboolean free_unescaped_attribute;
565
566 attr2 = strchr (s: attr, c: ':');
567 if (attr2)
568 {
569 attr2 += 2; /* Skip '::' */
570 unescaped_attribute = hex_unescape_string (str: attr2, NULL, free_return: &free_unescaped_attribute);
571 if (user)
572 a = g_strconcat (string1: "user.", unescaped_attribute, NULL);
573 else
574 a = unescaped_attribute;
575
576 get_one_xattr (path, info, gio_attr: attr, xattr: a, follow_symlinks);
577
578 if (user)
579 g_free (mem: a);
580
581 if (free_unescaped_attribute)
582 g_free (mem: unescaped_attribute);
583 }
584 }
585 }
586#endif /* defined HAVE_XATTR */
587}
588
589#ifdef HAVE_XATTR
590static void
591get_one_xattr_from_fd (int fd,
592 GFileInfo *info,
593 const char *gio_attr,
594 const char *xattr)
595{
596 char value[64];
597 char *value_p;
598 gssize len;
599 int errsv;
600
601 len = g_fgetxattr (fd: fd, name: xattr, value: value, size: sizeof (value) - 1);
602 errsv = errno;
603
604 value_p = NULL;
605 if (len >= 0)
606 value_p = value;
607 else if (len == -1 && errsv == ERANGE)
608 {
609 len = g_fgetxattr (fd: fd, name: xattr, NULL, size: 0);
610
611 if (len < 0)
612 return;
613
614 value_p = g_malloc (n_bytes: len + 1);
615
616 len = g_fgetxattr (fd: fd, name: xattr, value: value_p, size: len);
617
618 if (len < 0)
619 {
620 g_free (mem: value_p);
621 return;
622 }
623 }
624 else
625 return;
626
627 /* Null terminate */
628 value_p[len] = 0;
629
630 escape_xattr (info, gio_attr, value: value_p, len);
631
632 if (value_p != value)
633 g_free (mem: value_p);
634}
635#endif /* defined HAVE_XATTR */
636
637static void
638get_xattrs_from_fd (int fd,
639 gboolean user,
640 GFileInfo *info,
641 GFileAttributeMatcher *matcher)
642{
643#ifdef HAVE_XATTR
644 gboolean all;
645 gsize list_size;
646 gssize list_res_size;
647 size_t len;
648 char *list;
649 const char *attr, *attr2;
650
651 if (user)
652 all = g_file_attribute_matcher_enumerate_namespace (matcher, ns: "xattr");
653 else
654 all = g_file_attribute_matcher_enumerate_namespace (matcher, ns: "xattr-sys");
655
656 if (all)
657 {
658 int errsv;
659
660 list_res_size = g_flistxattr (fd: fd, NULL, size: 0);
661
662 if (list_res_size == -1 ||
663 list_res_size == 0)
664 return;
665
666 list_size = list_res_size;
667 list = g_malloc (n_bytes: list_size);
668
669 retry:
670
671 list_res_size = g_flistxattr (fd: fd, list: list, size: list_size);
672 errsv = errno;
673
674 if (list_res_size == -1 && errsv == ERANGE)
675 {
676 list_size = list_size * 2;
677 list = g_realloc (mem: list, n_bytes: list_size);
678 goto retry;
679 }
680
681 if (list_res_size == -1)
682 {
683 g_free (mem: list);
684 return;
685 }
686
687 attr = list;
688 while (list_res_size > 0)
689 {
690 if ((user && g_str_has_prefix (str: attr, prefix: "user.")) ||
691 (!user && !g_str_has_prefix (str: attr, prefix: "user.")))
692 {
693 char *escaped_attr, *gio_attr;
694 gboolean free_escaped_attr;
695
696 if (user)
697 {
698 escaped_attr = hex_escape_string (str: attr + 5, free_return: &free_escaped_attr);
699 gio_attr = g_strconcat (string1: "xattr::", escaped_attr, NULL);
700 }
701 else
702 {
703 escaped_attr = hex_escape_string (str: attr, free_return: &free_escaped_attr);
704 gio_attr = g_strconcat (string1: "xattr-sys::", escaped_attr, NULL);
705 }
706
707 if (free_escaped_attr)
708 g_free (mem: escaped_attr);
709
710 get_one_xattr_from_fd (fd, info, gio_attr, xattr: attr);
711 g_free (mem: gio_attr);
712 }
713
714 len = strlen (s: attr) + 1;
715 attr += len;
716 list_res_size -= len;
717 }
718
719 g_free (mem: list);
720 }
721 else
722 {
723 while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
724 {
725 char *unescaped_attribute, *a;
726 gboolean free_unescaped_attribute;
727
728 attr2 = strchr (s: attr, c: ':');
729 if (attr2)
730 {
731 attr2++; /* Skip ':' */
732 unescaped_attribute = hex_unescape_string (str: attr2, NULL, free_return: &free_unescaped_attribute);
733 if (user)
734 a = g_strconcat (string1: "user.", unescaped_attribute, NULL);
735 else
736 a = unescaped_attribute;
737
738 get_one_xattr_from_fd (fd, info, gio_attr: attr, xattr: a);
739
740 if (user)
741 g_free (mem: a);
742
743 if (free_unescaped_attribute)
744 g_free (mem: unescaped_attribute);
745 }
746 }
747 }
748#endif /* defined HAVE_XATTR */
749}
750
751#ifdef HAVE_XATTR
752static gboolean
753set_xattr (char *filename,
754 const char *escaped_attribute,
755 const GFileAttributeValue *attr_value,
756 GError **error)
757{
758 char *attribute, *value;
759 gboolean free_attribute, free_value;
760 int val_len, res, errsv;
761 gboolean is_user;
762 char *a;
763
764 if (attr_value == NULL)
765 {
766 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
767 _("Attribute value must be non-NULL"));
768 return FALSE;
769 }
770
771 if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
772 {
773 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
774 _("Invalid attribute type (string expected)"));
775 return FALSE;
776 }
777
778 if (!name_is_valid (str: escaped_attribute))
779 {
780 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
781 _("Invalid extended attribute name"));
782 return FALSE;
783 }
784
785 if (g_str_has_prefix (str: escaped_attribute, prefix: "xattr::"))
786 {
787 escaped_attribute += strlen (s: "xattr::");
788 is_user = TRUE;
789 }
790 else
791 {
792 g_warn_if_fail (g_str_has_prefix (escaped_attribute, "xattr-sys::"));
793 escaped_attribute += strlen (s: "xattr-sys::");
794 is_user = FALSE;
795 }
796
797 attribute = hex_unescape_string (str: escaped_attribute, NULL, free_return: &free_attribute);
798 value = hex_unescape_string (str: attr_value->u.string, out_len: &val_len, free_return: &free_value);
799
800 if (is_user)
801 a = g_strconcat (string1: "user.", attribute, NULL);
802 else
803 a = attribute;
804
805 res = g_setxattr (filename, a, value, val_len);
806 errsv = errno;
807
808 if (is_user)
809 g_free (mem: a);
810
811 if (free_attribute)
812 g_free (mem: attribute);
813
814 if (free_value)
815 g_free (mem: value);
816
817 if (res == -1)
818 {
819 g_set_error (err: error, G_IO_ERROR,
820 code: g_io_error_from_errno (err_no: errsv),
821 _("Error setting extended attribute “%s”: %s"),
822 escaped_attribute, g_strerror (errnum: errsv));
823 return FALSE;
824 }
825
826 return TRUE;
827}
828
829#endif
830
831
832void
833_g_local_file_info_get_parent_info (const char *dir,
834 GFileAttributeMatcher *attribute_matcher,
835 GLocalParentFileInfo *parent_info)
836{
837 GStatBuf statbuf;
838 int res;
839
840 parent_info->extra_data = NULL;
841 parent_info->free_extra_data = NULL;
842 parent_info->writable = FALSE;
843 parent_info->is_sticky = FALSE;
844 parent_info->has_trash_dir = FALSE;
845 parent_info->device = 0;
846 parent_info->inode = 0;
847
848 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) ||
849 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) ||
850 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH) ||
851 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT))
852 {
853 /* FIXME: Windows: The underlying _waccess() call in the C
854 * library is mostly pointless as it only looks at the READONLY
855 * FAT-style attribute of the file, it doesn't check the ACL at
856 * all.
857 */
858 parent_info->writable = (g_access (filename: dir, W_OK) == 0);
859
860 res = g_stat (file: dir, buf: &statbuf);
861
862 /*
863 * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
864 * renamed or deleted only by the owner of the file, by the owner of the directory, and
865 * by a privileged process.
866 */
867 if (res == 0)
868 {
869#ifdef S_ISVTX
870 parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
871#else
872 parent_info->is_sticky = FALSE;
873#endif
874 parent_info->owner = statbuf.st_uid;
875 parent_info->device = statbuf.st_dev;
876 parent_info->inode = statbuf.st_ino;
877 /* No need to find trash dir if it's not writable anyway */
878 if (parent_info->writable &&
879 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
880 parent_info->has_trash_dir = _g_local_file_has_trash_dir (dirname: dir, dir_dev: statbuf.st_dev);
881 }
882 }
883}
884
885void
886_g_local_file_info_free_parent_info (GLocalParentFileInfo *parent_info)
887{
888 if (parent_info->extra_data &&
889 parent_info->free_extra_data)
890 parent_info->free_extra_data (parent_info->extra_data);
891}
892
893static void
894get_access_rights (GFileAttributeMatcher *attribute_matcher,
895 GFileInfo *info,
896 const gchar *path,
897 GLocalFileStat *statbuf,
898 GLocalParentFileInfo *parent_info)
899{
900 /* FIXME: Windows: The underlyin _waccess() is mostly pointless */
901 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
902 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ))
903 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ,
904 attr_value: g_access (filename: path, R_OK) == 0);
905
906 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
907 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE))
908 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE,
909 attr_value: g_access (filename: path, W_OK) == 0);
910
911 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
912 G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE))
913 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE,
914 attr_value: g_access (filename: path, X_OK) == 0);
915
916
917 if (parent_info)
918 {
919 gboolean writable;
920
921 writable = FALSE;
922 if (parent_info->writable)
923 {
924#ifdef G_OS_WIN32
925 writable = TRUE;
926#else
927 if (parent_info->is_sticky)
928 {
929 uid_t uid = geteuid ();
930
931 if (uid == _g_stat_uid (buf: statbuf) ||
932 uid == (uid_t) parent_info->owner ||
933 uid == 0)
934 writable = TRUE;
935 }
936 else
937 writable = TRUE;
938#endif
939 }
940
941 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME))
942 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME,
943 attr_value: writable);
944
945 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE))
946 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
947 attr_value: writable);
948
949 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
950 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
951 attr_value: writable && parent_info->has_trash_dir);
952 }
953}
954
955static void
956set_info_from_stat (GFileInfo *info,
957 GLocalFileStat *statbuf,
958 GFileAttributeMatcher *attribute_matcher)
959{
960 GFileType file_type;
961
962 file_type = G_FILE_TYPE_UNKNOWN;
963
964 if (S_ISREG (_g_stat_mode (statbuf)))
965 file_type = G_FILE_TYPE_REGULAR;
966 else if (S_ISDIR (_g_stat_mode (statbuf)))
967 file_type = G_FILE_TYPE_DIRECTORY;
968#ifndef G_OS_WIN32
969 else if (S_ISCHR (_g_stat_mode (statbuf)) ||
970 S_ISBLK (_g_stat_mode (statbuf)) ||
971 S_ISFIFO (_g_stat_mode (statbuf))
972#ifdef S_ISSOCK
973 || S_ISSOCK (_g_stat_mode (statbuf))
974#endif
975 )
976 file_type = G_FILE_TYPE_SPECIAL;
977#endif
978#ifdef S_ISLNK
979 else if (S_ISLNK (_g_stat_mode (statbuf)))
980 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
981#elif defined (G_OS_WIN32)
982 else if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK ||
983 statbuf->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
984 file_type = G_FILE_TYPE_SYMBOLIC_LINK;
985#endif
986
987 g_file_info_set_file_type (info, type: file_type);
988 g_file_info_set_size (info, size: _g_stat_size (buf: statbuf));
989
990 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, attr_value: _g_stat_dev (buf: statbuf));
991 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, attr_value: _g_stat_nlink (buf: statbuf));
992#ifndef G_OS_WIN32
993 /* Pointless setting these on Windows even if they exist in the struct */
994 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, attr_value: _g_stat_ino (buf: statbuf));
995 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, attr_value: _g_stat_uid (buf: statbuf));
996 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, attr_value: _g_stat_gid (buf: statbuf));
997 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, attr_value: _g_stat_rdev (buf: statbuf));
998#endif
999 /* Mostly pointless on Windows.
1000 * Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks.
1001 */
1002 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, attr_value: _g_stat_mode (buf: statbuf));
1003#if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
1004 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, attr_value: _g_stat_blksize (buf: statbuf));
1005#endif
1006#if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
1007 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, attr_value: _g_stat_blocks (buf: statbuf));
1008 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
1009 attr_value: _g_stat_blocks (buf: statbuf) * G_GUINT64_CONSTANT (512));
1010#elif defined (G_OS_WIN32)
1011 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE,
1012 statbuf->allocated_size);
1013
1014#endif
1015
1016#if defined (G_OS_WIN32)
1017 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtim.tv_sec);
1018 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000);
1019 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atim.tv_sec);
1020 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
1021#else
1022 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, attr_value: _g_stat_mtime (buf: statbuf));
1023#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
1024 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000);
1025#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
1026 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, attr_value: _g_stat_mtim_nsec (buf: statbuf) / 1000);
1027#endif
1028
1029 if (_g_stat_has_field (buf: statbuf, field: G_LOCAL_FILE_STAT_FIELD_ATIME))
1030 {
1031 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, attr_value: _g_stat_atime (buf: statbuf));
1032#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
1033 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
1034#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
1035 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, attr_value: _g_stat_atim_nsec (buf: statbuf) / 1000);
1036#endif
1037 }
1038#endif
1039
1040#ifndef G_OS_WIN32
1041 /* Microsoft uses st_ctime for file creation time,
1042 * instead of file change time:
1043 * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions#generic-text-routine-mappings
1044 * Thank you, Microsoft!
1045 */
1046 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, attr_value: _g_stat_ctime (buf: statbuf));
1047#if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
1048 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
1049#elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
1050 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, attr_value: _g_stat_ctim_nsec (buf: statbuf) / 1000);
1051#endif
1052#endif
1053
1054#if defined (HAVE_STATX)
1055 if (_g_stat_has_field (buf: statbuf, field: G_LOCAL_FILE_STAT_FIELD_BTIME))
1056 {
1057 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, attr_value: statbuf->stx_btime.tv_sec);
1058 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, attr_value: statbuf->stx_btime.tv_nsec / 1000);
1059 }
1060#elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
1061 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
1062 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtimensec / 1000);
1063#elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
1064 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim.tv_sec);
1065 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtim.tv_nsec / 1000);
1066#elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME)
1067 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
1068#elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM)
1069 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtim);
1070#elif defined (G_OS_WIN32)
1071 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_ctim.tv_sec);
1072 _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_ctim.tv_nsec / 1000);
1073#endif
1074
1075 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1076 G_FILE_ATTRIBUTE_ID_ETAG_VALUE))
1077 {
1078 char *etag = _g_local_file_info_create_etag (statbuf);
1079 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ETAG_VALUE, attr_value: etag);
1080 g_free (mem: etag);
1081 }
1082
1083 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1084 G_FILE_ATTRIBUTE_ID_ID_FILE))
1085 {
1086 char *id = _g_local_file_info_create_file_id (statbuf);
1087 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILE, attr_value: id);
1088 g_free (mem: id);
1089 }
1090
1091 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1092 G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM))
1093 {
1094 char *id = _g_local_file_info_create_fs_id (statbuf);
1095 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_ID_FILESYSTEM, attr_value: id);
1096 g_free (mem: id);
1097 }
1098}
1099
1100#ifndef G_OS_WIN32
1101
1102static char *
1103make_valid_utf8 (const char *name)
1104{
1105 GString *string;
1106 const gchar *remainder, *invalid;
1107 gsize remaining_bytes, valid_bytes;
1108
1109 string = NULL;
1110 remainder = name;
1111 remaining_bytes = strlen (s: name);
1112
1113 while (remaining_bytes != 0)
1114 {
1115 if (g_utf8_validate_len (str: remainder, max_len: remaining_bytes, end: &invalid))
1116 break;
1117 valid_bytes = invalid - remainder;
1118
1119 if (string == NULL)
1120 string = g_string_sized_new (dfl_size: remaining_bytes);
1121
1122 g_string_append_len (string, val: remainder, len: valid_bytes);
1123 /* append U+FFFD REPLACEMENT CHARACTER */
1124 g_string_append (string, val: "\357\277\275");
1125
1126 remaining_bytes -= valid_bytes + 1;
1127 remainder = invalid + 1;
1128 }
1129
1130 if (string == NULL)
1131 return g_strdup (str: name);
1132
1133 g_string_append (string, val: remainder);
1134
1135 g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
1136
1137 return g_string_free (string, FALSE);
1138}
1139
1140static char *
1141convert_pwd_string_to_utf8 (char *pwd_str)
1142{
1143 char *utf8_string;
1144
1145 if (!g_utf8_validate (str: pwd_str, max_len: -1, NULL))
1146 {
1147 utf8_string = g_locale_to_utf8 (opsysstring: pwd_str, len: -1, NULL, NULL, NULL);
1148 if (utf8_string == NULL)
1149 utf8_string = make_valid_utf8 (name: pwd_str);
1150 }
1151 else
1152 utf8_string = g_strdup (str: pwd_str);
1153
1154 return utf8_string;
1155}
1156
1157static void
1158uid_data_free (UidData *data)
1159{
1160 g_free (mem: data->user_name);
1161 g_free (mem: data->real_name);
1162 g_free (mem: data);
1163}
1164
1165/* called with lock held */
1166static UidData *
1167lookup_uid_data (uid_t uid)
1168{
1169 UidData *data;
1170 char buffer[4096];
1171 struct passwd pwbuf;
1172 struct passwd *pwbufp;
1173#ifndef __BIONIC__
1174 char *gecos, *comma;
1175#endif
1176
1177 if (uid_cache == NULL)
1178 uid_cache = g_hash_table_new_full (NULL, NULL, NULL, value_destroy_func: (GDestroyNotify)uid_data_free);
1179
1180 data = g_hash_table_lookup (hash_table: uid_cache, GINT_TO_POINTER (uid));
1181
1182 if (data)
1183 return data;
1184
1185 data = g_new0 (UidData, 1);
1186
1187#if defined(HAVE_GETPWUID_R)
1188 getpwuid_r (uid: uid, resultbuf: &pwbuf, buffer: buffer, buflen: sizeof(buffer), result: &pwbufp);
1189#else
1190 pwbufp = getpwuid (uid);
1191#endif
1192
1193 if (pwbufp != NULL)
1194 {
1195 if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
1196 data->user_name = convert_pwd_string_to_utf8 (pwd_str: pwbufp->pw_name);
1197
1198#ifndef __BIONIC__
1199 gecos = pwbufp->pw_gecos;
1200
1201 if (gecos)
1202 {
1203 comma = strchr (s: gecos, c: ',');
1204 if (comma)
1205 *comma = 0;
1206 data->real_name = convert_pwd_string_to_utf8 (pwd_str: gecos);
1207 }
1208#endif
1209 }
1210
1211 /* Default fallbacks */
1212 if (data->real_name == NULL)
1213 {
1214 if (data->user_name != NULL)
1215 data->real_name = g_strdup (str: data->user_name);
1216 else
1217 data->real_name = g_strdup_printf (format: "user #%d", (int)uid);
1218 }
1219
1220 if (data->user_name == NULL)
1221 data->user_name = g_strdup_printf (format: "%d", (int)uid);
1222
1223 g_hash_table_replace (hash_table: uid_cache, GINT_TO_POINTER (uid), value: data);
1224
1225 return data;
1226}
1227
1228static char *
1229get_username_from_uid (uid_t uid)
1230{
1231 char *res;
1232 UidData *data;
1233
1234 G_LOCK (uid_cache);
1235 data = lookup_uid_data (uid);
1236 res = g_strdup (str: data->user_name);
1237 G_UNLOCK (uid_cache);
1238
1239 return res;
1240}
1241
1242static char *
1243get_realname_from_uid (uid_t uid)
1244{
1245 char *res;
1246 UidData *data;
1247
1248 G_LOCK (uid_cache);
1249 data = lookup_uid_data (uid);
1250 res = g_strdup (str: data->real_name);
1251 G_UNLOCK (uid_cache);
1252
1253 return res;
1254}
1255
1256/* called with lock held */
1257static char *
1258lookup_gid_name (gid_t gid)
1259{
1260 char *name;
1261#if defined (HAVE_GETGRGID_R)
1262 char buffer[4096];
1263 struct group gbuf;
1264#endif
1265 struct group *gbufp;
1266
1267 if (gid_cache == NULL)
1268 gid_cache = g_hash_table_new_full (NULL, NULL, NULL, value_destroy_func: (GDestroyNotify)g_free);
1269
1270 name = g_hash_table_lookup (hash_table: gid_cache, GINT_TO_POINTER (gid));
1271
1272 if (name)
1273 return name;
1274
1275#if defined (HAVE_GETGRGID_R)
1276 getgrgid_r (gid: gid, resultbuf: &gbuf, buffer: buffer, buflen: sizeof(buffer), result: &gbufp);
1277#else
1278 gbufp = getgrgid (gid);
1279#endif
1280
1281 if (gbufp != NULL &&
1282 gbufp->gr_name != NULL &&
1283 gbufp->gr_name[0] != 0)
1284 name = convert_pwd_string_to_utf8 (pwd_str: gbufp->gr_name);
1285 else
1286 name = g_strdup_printf(format: "%d", (int)gid);
1287
1288 g_hash_table_replace (hash_table: gid_cache, GINT_TO_POINTER (gid), value: name);
1289
1290 return name;
1291}
1292
1293static char *
1294get_groupname_from_gid (gid_t gid)
1295{
1296 char *res;
1297 char *name;
1298
1299 G_LOCK (gid_cache);
1300 name = lookup_gid_name (gid);
1301 res = g_strdup (str: name);
1302 G_UNLOCK (gid_cache);
1303 return res;
1304}
1305
1306#endif /* !G_OS_WIN32 */
1307
1308static char *
1309get_content_type (const char *basename,
1310 const char *path,
1311 GLocalFileStat *statbuf,
1312 gboolean is_symlink,
1313 gboolean symlink_broken,
1314 GFileQueryInfoFlags flags,
1315 gboolean fast)
1316{
1317 if (is_symlink &&
1318 (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
1319 return g_content_type_from_mime_type (mime_type: "inode/symlink");
1320 else if (statbuf != NULL && S_ISDIR(_g_stat_mode (statbuf)))
1321 return g_content_type_from_mime_type (mime_type: "inode/directory");
1322#ifndef G_OS_WIN32
1323 else if (statbuf != NULL && S_ISCHR(_g_stat_mode (statbuf)))
1324 return g_content_type_from_mime_type (mime_type: "inode/chardevice");
1325 else if (statbuf != NULL && S_ISBLK(_g_stat_mode (statbuf)))
1326 return g_content_type_from_mime_type (mime_type: "inode/blockdevice");
1327 else if (statbuf != NULL && S_ISFIFO(_g_stat_mode (statbuf)))
1328 return g_content_type_from_mime_type (mime_type: "inode/fifo");
1329 else if (statbuf != NULL && S_ISREG(_g_stat_mode (statbuf)) && _g_stat_size (buf: statbuf) == 0)
1330 {
1331 /* Don't sniff zero-length files in order to avoid reading files
1332 * that appear normal but are not (eg: files in /proc and /sys)
1333 *
1334 * Note that we need to return text/plain here so that
1335 * newly-created text files are opened by the text editor.
1336 * See https://bugzilla.gnome.org/show_bug.cgi?id=755795
1337 */
1338 return g_content_type_from_mime_type (mime_type: "text/plain");
1339 }
1340#endif
1341#ifdef S_ISSOCK
1342 else if (statbuf != NULL && S_ISSOCK(_g_stat_mode (statbuf)))
1343 return g_content_type_from_mime_type (mime_type: "inode/socket");
1344#endif
1345 else
1346 {
1347 char *content_type;
1348 gboolean result_uncertain;
1349
1350 content_type = g_content_type_guess (filename: basename, NULL, data_size: 0, result_uncertain: &result_uncertain);
1351
1352#if !defined(G_OS_WIN32) && !defined(HAVE_COCOA)
1353 if (!fast && result_uncertain && path != NULL)
1354 {
1355 guchar sniff_buffer[4096];
1356 gsize sniff_length;
1357 int fd, errsv;
1358
1359 sniff_length = _g_unix_content_type_get_sniff_len ();
1360 if (sniff_length > 4096)
1361 sniff_length = 4096;
1362
1363#ifdef O_NOATIME
1364 fd = g_open (file: path, O_RDONLY | O_NOATIME, 0);
1365 errsv = errno;
1366 if (fd < 0 && errsv == EPERM)
1367#endif
1368 fd = g_open (file: path, O_RDONLY, 0);
1369
1370 if (fd != -1)
1371 {
1372 gssize res;
1373
1374 res = read (fd: fd, buf: sniff_buffer, nbytes: sniff_length);
1375 (void) g_close (fd, NULL);
1376 if (res >= 0)
1377 {
1378 g_free (mem: content_type);
1379 content_type = g_content_type_guess (filename: basename, data: sniff_buffer, data_size: res, NULL);
1380 }
1381 }
1382 }
1383#endif
1384
1385 return content_type;
1386 }
1387
1388}
1389
1390/* @stat_buf is the pre-calculated result of stat(path), or %NULL if that failed. */
1391static void
1392get_thumbnail_attributes (const char *path,
1393 GFileInfo *info,
1394 const GLocalFileStat *stat_buf)
1395{
1396 GChecksum *checksum;
1397 char *uri;
1398 char *filename;
1399 char *basename;
1400
1401 uri = g_filename_to_uri (filename: path, NULL, NULL);
1402
1403 checksum = g_checksum_new (checksum_type: G_CHECKSUM_MD5);
1404 g_checksum_update (checksum, data: (const guchar *) uri, length: strlen (s: uri));
1405
1406 basename = g_strconcat (string1: g_checksum_get_string (checksum), ".png", NULL);
1407 g_checksum_free (checksum);
1408
1409 filename = g_build_filename (first_element: g_get_user_cache_dir (),
1410 "thumbnails", "large", basename,
1411 NULL);
1412
1413 if (g_file_test (filename, test: G_FILE_TEST_IS_REGULAR))
1414 {
1415 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, attr_value: filename);
1416 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1417 attr_value: thumbnail_verify (thumbnail_path: filename, file_uri: uri, file_stat_buf: stat_buf));
1418 }
1419 else
1420 {
1421 g_free (mem: filename);
1422 filename = g_build_filename (first_element: g_get_user_cache_dir (),
1423 "thumbnails", "normal", basename,
1424 NULL);
1425
1426 if (g_file_test (filename, test: G_FILE_TEST_IS_REGULAR))
1427 {
1428 _g_file_info_set_attribute_byte_string_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH, attr_value: filename);
1429 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1430 attr_value: thumbnail_verify (thumbnail_path: filename, file_uri: uri, file_stat_buf: stat_buf));
1431 }
1432 else
1433 {
1434 g_free (mem: filename);
1435 filename = g_build_filename (first_element: g_get_user_cache_dir (),
1436 "thumbnails", "fail",
1437 "gnome-thumbnail-factory",
1438 basename,
1439 NULL);
1440
1441 if (g_file_test (filename, test: G_FILE_TEST_IS_REGULAR))
1442 {
1443 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED, TRUE);
1444 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID,
1445 attr_value: thumbnail_verify (thumbnail_path: filename, file_uri: uri, file_stat_buf: stat_buf));
1446 }
1447 }
1448 }
1449 g_free (mem: basename);
1450 g_free (mem: filename);
1451 g_free (mem: uri);
1452}
1453
1454#ifdef G_OS_WIN32
1455static void
1456win32_get_file_user_info (const gchar *filename,
1457 gchar **group_name,
1458 gchar **user_name,
1459 gchar **real_name)
1460{
1461 PSECURITY_DESCRIPTOR psd = NULL;
1462 DWORD sd_size = 0; /* first call calculates the size required */
1463
1464 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1465 if ((GetFileSecurityW (wfilename,
1466 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1467 NULL,
1468 sd_size,
1469 &sd_size) || (ERROR_INSUFFICIENT_BUFFER == GetLastError())) &&
1470 (psd = g_try_malloc (sd_size)) != NULL &&
1471 GetFileSecurityW (wfilename,
1472 GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
1473 psd,
1474 sd_size,
1475 &sd_size))
1476 {
1477 PSID psid = 0;
1478 BOOL defaulted;
1479 SID_NAME_USE name_use = 0; /* don't care? */
1480 wchar_t *name = NULL;
1481 wchar_t *domain = NULL;
1482 DWORD name_len = 0;
1483 DWORD domain_len = 0;
1484 /* get the user name */
1485 do {
1486 if (!user_name)
1487 break;
1488 if (!GetSecurityDescriptorOwner (psd, &psid, &defaulted))
1489 break;
1490 if (!LookupAccountSidW (NULL, /* local machine */
1491 psid,
1492 name, &name_len,
1493 domain, &domain_len, /* no domain info yet */
1494 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1495 break;
1496 name = g_try_malloc (name_len * sizeof (wchar_t));
1497 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1498 if (name && domain &&
1499 LookupAccountSidW (NULL, /* local machine */
1500 psid,
1501 name, &name_len,
1502 domain, &domain_len, /* no domain info yet */
1503 &name_use))
1504 {
1505 *user_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1506 }
1507 g_free (name);
1508 g_free (domain);
1509 } while (FALSE);
1510
1511 /* get the group name */
1512 do {
1513 if (!group_name)
1514 break;
1515 if (!GetSecurityDescriptorGroup (psd, &psid, &defaulted))
1516 break;
1517 if (!LookupAccountSidW (NULL, /* local machine */
1518 psid,
1519 name, &name_len,
1520 domain, &domain_len, /* no domain info yet */
1521 &name_use) && (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
1522 break;
1523 name = g_try_malloc (name_len * sizeof (wchar_t));
1524 domain = g_try_malloc (domain_len * sizeof (wchar_t));
1525 if (name && domain &&
1526 LookupAccountSidW (NULL, /* local machine */
1527 psid,
1528 name, &name_len,
1529 domain, &domain_len, /* no domain info yet */
1530 &name_use))
1531 {
1532 *group_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
1533 }
1534 g_free (name);
1535 g_free (domain);
1536 } while (FALSE);
1537
1538 /* TODO: get real name */
1539
1540 g_free (psd);
1541 }
1542 g_free (wfilename);
1543}
1544#endif /* G_OS_WIN32 */
1545
1546#ifndef G_OS_WIN32
1547/* support for '.hidden' files */
1548G_LOCK_DEFINE_STATIC (hidden_cache);
1549static GHashTable *hidden_cache;
1550static GSource *hidden_cache_source = NULL; /* Under the hidden_cache lock */
1551static guint hidden_cache_ttl_secs = 5;
1552static guint hidden_cache_ttl_jitter_secs = 2;
1553
1554typedef struct
1555{
1556 GHashTable *hidden_files;
1557 gint64 timestamp_secs;
1558} HiddenCacheData;
1559
1560static gboolean
1561remove_from_hidden_cache (gpointer user_data)
1562{
1563 HiddenCacheData *data;
1564 GHashTableIter iter;
1565 gboolean retval;
1566 gint64 timestamp_secs;
1567
1568 G_LOCK (hidden_cache);
1569 timestamp_secs = g_source_get_time (source: hidden_cache_source) / G_USEC_PER_SEC;
1570
1571 g_hash_table_iter_init (iter: &iter, hash_table: hidden_cache);
1572 while (g_hash_table_iter_next (iter: &iter, NULL, value: (gpointer *) &data))
1573 {
1574 if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
1575 g_hash_table_iter_remove (iter: &iter);
1576 }
1577
1578 if (g_hash_table_size (hash_table: hidden_cache) == 0)
1579 {
1580 g_clear_pointer (&hidden_cache_source, g_source_unref);
1581 retval = G_SOURCE_REMOVE;
1582 }
1583 else
1584 retval = G_SOURCE_CONTINUE;
1585
1586 G_UNLOCK (hidden_cache);
1587
1588 return retval;
1589}
1590
1591static GHashTable *
1592read_hidden_file (const gchar *dirname)
1593{
1594 gchar *contents = NULL;
1595 gchar *filename;
1596
1597 filename = g_build_path (separator: "/", first_element: dirname, ".hidden", NULL);
1598 (void) g_file_get_contents (filename, contents: &contents, NULL, NULL);
1599 g_free (mem: filename);
1600
1601 if (contents != NULL)
1602 {
1603 GHashTable *table;
1604 gchar **lines;
1605 gint i;
1606
1607 table = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
1608
1609 lines = g_strsplit (string: contents, delimiter: "\n", max_tokens: 0);
1610 g_free (mem: contents);
1611
1612 for (i = 0; lines[i]; i++)
1613 /* hash table takes the individual strings... */
1614 g_hash_table_add (hash_table: table, key: lines[i]);
1615
1616 /* ... so we only free the container. */
1617 g_free (mem: lines);
1618
1619 return table;
1620 }
1621 else
1622 return NULL;
1623}
1624
1625static void
1626free_hidden_file_data (gpointer user_data)
1627{
1628 HiddenCacheData *data = user_data;
1629
1630 g_clear_pointer (&data->hidden_files, g_hash_table_unref);
1631 g_free (mem: data);
1632}
1633
1634static gboolean
1635file_is_hidden (const gchar *path,
1636 const gchar *basename)
1637{
1638 HiddenCacheData *data;
1639 gboolean result;
1640 gchar *dirname;
1641 gpointer table;
1642
1643 dirname = g_path_get_dirname (file_name: path);
1644
1645 G_LOCK (hidden_cache);
1646
1647 if G_UNLIKELY (hidden_cache == NULL)
1648 hidden_cache = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal,
1649 key_destroy_func: g_free, value_destroy_func: free_hidden_file_data);
1650
1651 if (!g_hash_table_lookup_extended (hash_table: hidden_cache, lookup_key: dirname,
1652 NULL, value: (gpointer *) &data))
1653 {
1654 gchar *mydirname;
1655
1656 data = g_new0 (HiddenCacheData, 1);
1657 data->hidden_files = table = read_hidden_file (dirname);
1658 data->timestamp_secs = g_get_monotonic_time () / G_USEC_PER_SEC;
1659
1660 g_hash_table_insert (hash_table: hidden_cache,
1661 key: mydirname = g_strdup (str: dirname),
1662 value: data);
1663
1664 if (!hidden_cache_source)
1665 {
1666 hidden_cache_source =
1667 g_timeout_source_new_seconds (interval: hidden_cache_ttl_secs +
1668 hidden_cache_ttl_jitter_secs);
1669 g_source_set_priority (source: hidden_cache_source, G_PRIORITY_DEFAULT);
1670 g_source_set_name (source: hidden_cache_source,
1671 name: "[gio] remove_from_hidden_cache");
1672 g_source_set_callback (source: hidden_cache_source,
1673 func: remove_from_hidden_cache,
1674 NULL, NULL);
1675 g_source_attach (source: hidden_cache_source,
1676 GLIB_PRIVATE_CALL (g_get_worker_context) ());
1677 }
1678 }
1679 else
1680 table = data->hidden_files;
1681
1682 result = table != NULL && g_hash_table_contains (hash_table: table, key: basename);
1683
1684 G_UNLOCK (hidden_cache);
1685
1686 g_free (mem: dirname);
1687
1688 return result;
1689}
1690#endif /* !G_OS_WIN32 */
1691
1692void
1693_g_local_file_info_get_nostat (GFileInfo *info,
1694 const char *basename,
1695 const char *path,
1696 GFileAttributeMatcher *attribute_matcher)
1697{
1698 g_file_info_set_name (info, name: basename);
1699
1700 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1701 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
1702 {
1703 char *display_name = g_filename_display_basename (filename: path);
1704
1705 /* look for U+FFFD REPLACEMENT CHARACTER */
1706 if (strstr (haystack: display_name, needle: "\357\277\275") != NULL)
1707 {
1708 char *p = display_name;
1709 display_name = g_strconcat (string1: display_name, _(" (invalid encoding)"), NULL);
1710 g_free (mem: p);
1711 }
1712 g_file_info_set_display_name (info, display_name);
1713 g_free (mem: display_name);
1714 }
1715
1716 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1717 G_FILE_ATTRIBUTE_ID_STANDARD_EDIT_NAME))
1718 {
1719 char *edit_name = g_filename_display_basename (filename: path);
1720 g_file_info_set_edit_name (info, edit_name);
1721 g_free (mem: edit_name);
1722 }
1723
1724
1725 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1726 G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME))
1727 {
1728 char *copy_name = g_filename_to_utf8 (opsysstring: basename, len: -1, NULL, NULL, NULL);
1729 if (copy_name)
1730 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_COPY_NAME, attr_value: copy_name);
1731 g_free (mem: copy_name);
1732 }
1733}
1734
1735static const char *
1736get_icon_name (const char *path,
1737 gboolean use_symbolic,
1738 gboolean *with_fallbacks_out)
1739{
1740 const char *name = NULL;
1741 gboolean with_fallbacks = TRUE;
1742
1743 if (g_strcmp0 (str1: path, str2: g_get_home_dir ()) == 0)
1744 {
1745 name = use_symbolic ? "user-home-symbolic" : "user-home";
1746 with_fallbacks = FALSE;
1747 }
1748 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_DESKTOP)) == 0)
1749 {
1750 name = use_symbolic ? "user-desktop-symbolic" : "user-desktop";
1751 with_fallbacks = FALSE;
1752 }
1753 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_DOCUMENTS)) == 0)
1754 {
1755 name = use_symbolic ? "folder-documents-symbolic" : "folder-documents";
1756 }
1757 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_DOWNLOAD)) == 0)
1758 {
1759 name = use_symbolic ? "folder-download-symbolic" : "folder-download";
1760 }
1761 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_MUSIC)) == 0)
1762 {
1763 name = use_symbolic ? "folder-music-symbolic" : "folder-music";
1764 }
1765 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_PICTURES)) == 0)
1766 {
1767 name = use_symbolic ? "folder-pictures-symbolic" : "folder-pictures";
1768 }
1769 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
1770 {
1771 name = use_symbolic ? "folder-publicshare-symbolic" : "folder-publicshare";
1772 }
1773 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_TEMPLATES)) == 0)
1774 {
1775 name = use_symbolic ? "folder-templates-symbolic" : "folder-templates";
1776 }
1777 else if (g_strcmp0 (str1: path, str2: g_get_user_special_dir (directory: G_USER_DIRECTORY_VIDEOS)) == 0)
1778 {
1779 name = use_symbolic ? "folder-videos-symbolic" : "folder-videos";
1780 }
1781 else
1782 {
1783 name = NULL;
1784 }
1785
1786 if (with_fallbacks_out != NULL)
1787 *with_fallbacks_out = with_fallbacks;
1788
1789 return name;
1790}
1791
1792static GIcon *
1793get_icon (const char *path,
1794 const char *content_type,
1795 gboolean use_symbolic)
1796{
1797 GIcon *icon = NULL;
1798 const char *icon_name;
1799 gboolean with_fallbacks;
1800
1801 icon_name = get_icon_name (path, use_symbolic, with_fallbacks_out: &with_fallbacks);
1802 if (icon_name != NULL)
1803 {
1804 if (with_fallbacks)
1805 icon = g_themed_icon_new_with_default_fallbacks (iconname: icon_name);
1806 else
1807 icon = g_themed_icon_new (iconname: icon_name);
1808 }
1809 else
1810 {
1811 if (use_symbolic)
1812 icon = g_content_type_get_symbolic_icon (type: content_type);
1813 else
1814 icon = g_content_type_get_icon (type: content_type);
1815 }
1816
1817 return icon;
1818}
1819
1820GFileInfo *
1821_g_local_file_info_get (const char *basename,
1822 const char *path,
1823 GFileAttributeMatcher *attribute_matcher,
1824 GFileQueryInfoFlags flags,
1825 GLocalParentFileInfo *parent_info,
1826 GError **error)
1827{
1828 GFileInfo *info;
1829 GLocalFileStat statbuf;
1830 GLocalFileStat statbuf2;
1831 int res;
1832 gboolean stat_ok;
1833 gboolean is_symlink, symlink_broken;
1834 char *symlink_target;
1835 GVfs *vfs;
1836 GVfsClass *class;
1837 guint64 device;
1838
1839 info = g_file_info_new ();
1840
1841 /* Make sure we don't set any unwanted attributes */
1842 g_file_info_set_attribute_mask (info, mask: attribute_matcher);
1843
1844 _g_local_file_info_get_nostat (info, basename, path, attribute_matcher);
1845
1846 if (attribute_matcher == NULL)
1847 {
1848 g_file_info_unset_attribute_mask (info);
1849 return info;
1850 }
1851
1852 res = g_local_file_lstat (path,
1853 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
1854 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
1855 stat_buf: &statbuf);
1856
1857 if (res == -1)
1858 {
1859 int errsv = errno;
1860
1861 /* Don't bail out if we get Permission denied (SELinux?) */
1862 if (errsv != EACCES)
1863 {
1864 char *display_name = g_filename_display_name (filename: path);
1865 g_object_unref (object: info);
1866 g_set_error (err: error, G_IO_ERROR,
1867 code: g_io_error_from_errno (err_no: errsv),
1868 _("Error when getting information for file “%s”: %s"),
1869 display_name, g_strerror (errnum: errsv));
1870 g_free (mem: display_name);
1871 return NULL;
1872 }
1873 }
1874
1875 /* Even if stat() fails, try to get as much as other attributes possible */
1876 stat_ok = res != -1;
1877
1878 if (stat_ok)
1879 device = _g_stat_dev (buf: &statbuf);
1880 else
1881 device = 0;
1882
1883#ifdef S_ISLNK
1884 is_symlink = stat_ok && S_ISLNK (_g_stat_mode (&statbuf));
1885#elif defined (G_OS_WIN32)
1886 /* glib already checked the FILE_ATTRIBUTE_REPARSE_POINT for us */
1887 is_symlink = stat_ok &&
1888 (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
1889 statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT);
1890#else
1891 is_symlink = FALSE;
1892#endif
1893 symlink_broken = FALSE;
1894
1895 if (is_symlink)
1896 {
1897 g_file_info_set_is_symlink (info, TRUE);
1898
1899 /* Unless NOFOLLOW was set we default to following symlinks */
1900 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
1901 {
1902 res = g_local_file_stat (path,
1903 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
1904 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
1905 stat_buf: &statbuf2);
1906
1907 /* Report broken links as symlinks */
1908 if (res != -1)
1909 {
1910 statbuf = statbuf2;
1911 stat_ok = TRUE;
1912 }
1913 else
1914 symlink_broken = TRUE;
1915 }
1916 }
1917
1918 if (stat_ok)
1919 set_info_from_stat (info, statbuf: &statbuf, attribute_matcher);
1920
1921#ifdef G_OS_UNIX
1922 if (stat_ok && _g_local_file_is_lost_found_dir (path, path_dev: _g_stat_dev (buf: &statbuf)))
1923 g_file_info_set_is_hidden (info, TRUE);
1924#endif
1925
1926#ifndef G_OS_WIN32
1927 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1928 G_FILE_ATTRIBUTE_ID_STANDARD_IS_HIDDEN))
1929 {
1930 if (basename != NULL &&
1931 (basename[0] == '.' ||
1932 file_is_hidden (path, basename)))
1933 g_file_info_set_is_hidden (info, TRUE);
1934 }
1935
1936 if (basename != NULL && basename[strlen (s: basename) -1] == '~' &&
1937 (stat_ok && S_ISREG (_g_stat_mode (&statbuf))))
1938 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE);
1939#else
1940 if (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN)
1941 g_file_info_set_is_hidden (info, TRUE);
1942
1943 if (statbuf.attributes & FILE_ATTRIBUTE_ARCHIVE)
1944 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_ARCHIVE, TRUE);
1945
1946 if (statbuf.attributes & FILE_ATTRIBUTE_SYSTEM)
1947 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_SYSTEM, TRUE);
1948
1949 if (statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
1950 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_IS_MOUNTPOINT, TRUE);
1951
1952 if (statbuf.reparse_tag != 0)
1953 _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_DOS_REPARSE_POINT_TAG, statbuf.reparse_tag);
1954#endif
1955
1956 symlink_target = NULL;
1957 if (is_symlink)
1958 {
1959#if defined (S_ISLNK) || defined (G_OS_WIN32)
1960 symlink_target = read_link (full_name: path);
1961#endif
1962 if (symlink_target &&
1963 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1964 G_FILE_ATTRIBUTE_ID_STANDARD_SYMLINK_TARGET))
1965 g_file_info_set_symlink_target (info, symlink_target);
1966 }
1967
1968 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1969 G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
1970 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1971 G_FILE_ATTRIBUTE_ID_STANDARD_ICON) ||
1972 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1973 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
1974 {
1975 char *content_type = get_content_type (basename, path, statbuf: stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, FALSE);
1976
1977 if (content_type)
1978 {
1979 g_file_info_set_content_type (info, content_type);
1980
1981 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1982 G_FILE_ATTRIBUTE_ID_STANDARD_ICON)
1983 || _g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
1984 G_FILE_ATTRIBUTE_ID_STANDARD_SYMBOLIC_ICON))
1985 {
1986 GIcon *icon;
1987
1988 /* non symbolic icon */
1989 icon = get_icon (path, content_type, FALSE);
1990 if (icon != NULL)
1991 {
1992 g_file_info_set_icon (info, icon);
1993 g_object_unref (object: icon);
1994 }
1995
1996 /* symbolic icon */
1997 icon = get_icon (path, content_type, TRUE);
1998 if (icon != NULL)
1999 {
2000 g_file_info_set_symbolic_icon (info, icon);
2001 g_object_unref (object: icon);
2002 }
2003
2004 }
2005
2006 g_free (mem: content_type);
2007 }
2008 }
2009
2010 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2011 G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))
2012 {
2013 char *content_type = get_content_type (basename, path, statbuf: stat_ok ? &statbuf : NULL, is_symlink, symlink_broken, flags, TRUE);
2014
2015 if (content_type)
2016 {
2017 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, attr_value: content_type);
2018 g_free (mem: content_type);
2019 }
2020 }
2021
2022 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2023 G_FILE_ATTRIBUTE_ID_OWNER_USER))
2024 {
2025 char *name = NULL;
2026
2027#ifdef G_OS_WIN32
2028 win32_get_file_user_info (path, NULL, &name, NULL);
2029#else
2030 if (stat_ok)
2031 name = get_username_from_uid (uid: _g_stat_uid (buf: &statbuf));
2032#endif
2033 if (name)
2034 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, attr_value: name);
2035 g_free (mem: name);
2036 }
2037
2038 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2039 G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL))
2040 {
2041 char *name = NULL;
2042#ifdef G_OS_WIN32
2043 win32_get_file_user_info (path, NULL, NULL, &name);
2044#else
2045 if (stat_ok)
2046 name = get_realname_from_uid (uid: _g_stat_uid (buf: &statbuf));
2047#endif
2048 if (name)
2049 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, attr_value: name);
2050 g_free (mem: name);
2051 }
2052
2053 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2054 G_FILE_ATTRIBUTE_ID_OWNER_GROUP))
2055 {
2056 char *name = NULL;
2057#ifdef G_OS_WIN32
2058 win32_get_file_user_info (path, &name, NULL, NULL);
2059#else
2060 if (stat_ok)
2061 name = get_groupname_from_gid (gid: _g_stat_gid (buf: &statbuf));
2062#endif
2063 if (name)
2064 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, attr_value: name);
2065 g_free (mem: name);
2066 }
2067
2068 if (stat_ok && parent_info && parent_info->device != 0 &&
2069 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) &&
2070 (_g_stat_dev (buf: &statbuf) != parent_info->device || _g_stat_ino (buf: &statbuf) == parent_info->inode))
2071 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE);
2072
2073 if (stat_ok)
2074 get_access_rights (attribute_matcher, info, path, statbuf: &statbuf, parent_info);
2075
2076#ifdef HAVE_SELINUX
2077 get_selinux_context (path, info, attribute_matcher, follow_symlinks: (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2078#endif
2079 get_xattrs (path, TRUE, info, matcher: attribute_matcher, follow_symlinks: (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2080 get_xattrs (path, FALSE, info, matcher: attribute_matcher, follow_symlinks: (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
2081
2082 if (_g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2083 G_FILE_ATTRIBUTE_ID_THUMBNAIL_PATH) ||
2084 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2085 G_FILE_ATTRIBUTE_ID_THUMBNAIL_IS_VALID) ||
2086 _g_file_attribute_matcher_matches_id (matcher: attribute_matcher,
2087 G_FILE_ATTRIBUTE_ID_THUMBNAILING_FAILED))
2088 {
2089 if (stat_ok)
2090 get_thumbnail_attributes (path, info, stat_buf: &statbuf);
2091 else
2092 get_thumbnail_attributes (path, info, NULL);
2093 }
2094
2095 vfs = g_vfs_get_default ();
2096 class = G_VFS_GET_CLASS (vfs);
2097 if (class->local_file_add_info)
2098 {
2099 class->local_file_add_info (vfs,
2100 path,
2101 device,
2102 attribute_matcher,
2103 info,
2104 NULL,
2105 &parent_info->extra_data,
2106 &parent_info->free_extra_data);
2107 }
2108
2109 g_file_info_unset_attribute_mask (info);
2110
2111 g_free (mem: symlink_target);
2112
2113 return info;
2114}
2115
2116GFileInfo *
2117_g_local_file_info_get_from_fd (int fd,
2118 const char *attributes,
2119 GError **error)
2120{
2121 GLocalFileStat stat_buf;
2122 GFileAttributeMatcher *matcher;
2123 GFileInfo *info;
2124
2125 if (g_local_file_fstat (fd,
2126 G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME,
2127 G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME) & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
2128 stat_buf: &stat_buf) == -1)
2129 {
2130 int errsv = errno;
2131
2132 g_set_error (err: error, G_IO_ERROR,
2133 code: g_io_error_from_errno (err_no: errsv),
2134 _("Error when getting information for file descriptor: %s"),
2135 g_strerror (errnum: errsv));
2136 return NULL;
2137 }
2138
2139 info = g_file_info_new ();
2140
2141 matcher = g_file_attribute_matcher_new (attributes);
2142
2143 /* Make sure we don't set any unwanted attributes */
2144 g_file_info_set_attribute_mask (info, mask: matcher);
2145
2146 set_info_from_stat (info, statbuf: &stat_buf, attribute_matcher: matcher);
2147
2148#ifdef HAVE_SELINUX
2149 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT) &&
2150 is_selinux_enabled ())
2151 {
2152 char *context;
2153 if (fgetfilecon_raw (fd, con: &context) >= 0)
2154 {
2155 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_SELINUX_CONTEXT, attr_value: context);
2156 freecon (con: context);
2157 }
2158 }
2159#endif
2160
2161 get_xattrs_from_fd (fd, TRUE, info, matcher);
2162 get_xattrs_from_fd (fd, FALSE, info, matcher);
2163
2164 g_file_attribute_matcher_unref (matcher);
2165
2166 g_file_info_unset_attribute_mask (info);
2167
2168 return info;
2169}
2170
2171static gboolean
2172get_uint32 (const GFileAttributeValue *value,
2173 guint32 *val_out,
2174 GError **error)
2175{
2176 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
2177 {
2178 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2179 _("Invalid attribute type (uint32 expected)"));
2180 return FALSE;
2181 }
2182
2183 *val_out = value->u.uint32;
2184
2185 return TRUE;
2186}
2187
2188#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2189static gboolean
2190get_uint64 (const GFileAttributeValue *value,
2191 guint64 *val_out,
2192 GError **error)
2193{
2194 if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
2195 {
2196 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2197 _("Invalid attribute type (uint64 expected)"));
2198 return FALSE;
2199 }
2200
2201 *val_out = value->u.uint64;
2202
2203 return TRUE;
2204}
2205#endif
2206
2207#if defined(HAVE_SYMLINK)
2208static gboolean
2209get_byte_string (const GFileAttributeValue *value,
2210 const char **val_out,
2211 GError **error)
2212{
2213 if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
2214 {
2215 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2216 _("Invalid attribute type (byte string expected)"));
2217 return FALSE;
2218 }
2219
2220 *val_out = value->u.string;
2221
2222 return TRUE;
2223}
2224#endif
2225
2226#ifdef HAVE_SELINUX
2227static gboolean
2228get_string (const GFileAttributeValue *value,
2229 const char **val_out,
2230 GError **error)
2231{
2232 if (value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
2233 {
2234 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2235 _("Invalid attribute type (byte string expected)"));
2236 return FALSE;
2237 }
2238
2239 *val_out = value->u.string;
2240
2241 return TRUE;
2242}
2243#endif
2244
2245static gboolean
2246set_unix_mode (char *filename,
2247 GFileQueryInfoFlags flags,
2248 const GFileAttributeValue *value,
2249 GError **error)
2250{
2251 guint32 val = 0;
2252 int res = 0;
2253
2254 if (!get_uint32 (value, val_out: &val, error))
2255 return FALSE;
2256
2257#if defined (HAVE_SYMLINK) || defined (G_OS_WIN32)
2258 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) {
2259#ifdef HAVE_LCHMOD
2260 res = lchmod (filename, val);
2261#else
2262 gboolean is_symlink;
2263#ifndef G_OS_WIN32
2264 struct stat statbuf;
2265 /* Calling chmod on a symlink changes permissions on the symlink.
2266 * We don't want to do this, so we need to check for a symlink */
2267 res = g_lstat (file: filename, buf: &statbuf);
2268 is_symlink = (res == 0 && S_ISLNK (statbuf.st_mode));
2269#else
2270 /* FIXME: implement lchmod for W32, should be doable */
2271 GWin32PrivateStat statbuf;
2272
2273 res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (filename, &statbuf);
2274 is_symlink = (res == 0 &&
2275 (statbuf.reparse_tag == IO_REPARSE_TAG_SYMLINK ||
2276 statbuf.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT));
2277#endif
2278 if (is_symlink)
2279 {
2280 g_set_error_literal (err: error, G_IO_ERROR,
2281 code: G_IO_ERROR_NOT_SUPPORTED,
2282 _("Cannot set permissions on symlinks"));
2283 return FALSE;
2284 }
2285 else if (res == 0)
2286 res = g_chmod (file: filename, mode: val);
2287#endif
2288 } else
2289#endif
2290 res = g_chmod (file: filename, mode: val);
2291
2292 if (res == -1)
2293 {
2294 int errsv = errno;
2295
2296 g_set_error (err: error, G_IO_ERROR,
2297 code: g_io_error_from_errno (err_no: errsv),
2298 _("Error setting permissions: %s"),
2299 g_strerror (errnum: errsv));
2300 return FALSE;
2301 }
2302 return TRUE;
2303}
2304
2305#ifdef G_OS_UNIX
2306static gboolean
2307set_unix_uid_gid (char *filename,
2308 const GFileAttributeValue *uid_value,
2309 const GFileAttributeValue *gid_value,
2310 GFileQueryInfoFlags flags,
2311 GError **error)
2312{
2313 int res;
2314 guint32 val = 0;
2315 uid_t uid;
2316 gid_t gid;
2317
2318 if (uid_value)
2319 {
2320 if (!get_uint32 (value: uid_value, val_out: &val, error))
2321 return FALSE;
2322 uid = val;
2323 }
2324 else
2325 uid = -1;
2326
2327 if (gid_value)
2328 {
2329 if (!get_uint32 (value: gid_value, val_out: &val, error))
2330 return FALSE;
2331 gid = val;
2332 }
2333 else
2334 gid = -1;
2335
2336#ifdef HAVE_LCHOWN
2337 if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
2338 res = lchown (file: filename, owner: uid, group: gid);
2339 else
2340#endif
2341 res = chown (file: filename, owner: uid, group: gid);
2342
2343 if (res == -1)
2344 {
2345 int errsv = errno;
2346
2347 g_set_error (err: error, G_IO_ERROR,
2348 code: g_io_error_from_errno (err_no: errsv),
2349 _("Error setting owner: %s"),
2350 g_strerror (errnum: errsv));
2351 return FALSE;
2352 }
2353 return TRUE;
2354}
2355#endif
2356
2357#ifdef HAVE_SYMLINK
2358static gboolean
2359set_symlink (char *filename,
2360 const GFileAttributeValue *value,
2361 GError **error)
2362{
2363 const char *val;
2364 struct stat statbuf;
2365
2366 if (!get_byte_string (value, val_out: &val, error))
2367 return FALSE;
2368
2369 if (val == NULL)
2370 {
2371 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2372 _("symlink must be non-NULL"));
2373 return FALSE;
2374 }
2375
2376 if (g_lstat (file: filename, buf: &statbuf))
2377 {
2378 int errsv = errno;
2379
2380 g_set_error (err: error, G_IO_ERROR,
2381 code: g_io_error_from_errno (err_no: errsv),
2382 _("Error setting symlink: %s"),
2383 g_strerror (errnum: errsv));
2384 return FALSE;
2385 }
2386
2387 if (!S_ISLNK (statbuf.st_mode))
2388 {
2389 g_set_error_literal (err: error, G_IO_ERROR,
2390 code: G_IO_ERROR_NOT_SYMBOLIC_LINK,
2391 _("Error setting symlink: file is not a symlink"));
2392 return FALSE;
2393 }
2394
2395 if (g_unlink (filename))
2396 {
2397 int errsv = errno;
2398
2399 g_set_error (err: error, G_IO_ERROR,
2400 code: g_io_error_from_errno (err_no: errsv),
2401 _("Error setting symlink: %s"),
2402 g_strerror (errnum: errsv));
2403 return FALSE;
2404 }
2405
2406 if (symlink (from: filename, to: val) != 0)
2407 {
2408 int errsv = errno;
2409
2410 g_set_error (err: error, G_IO_ERROR,
2411 code: g_io_error_from_errno (err_no: errsv),
2412 _("Error setting symlink: %s"),
2413 g_strerror (errnum: errsv));
2414 return FALSE;
2415 }
2416
2417 return TRUE;
2418}
2419#endif
2420
2421#if defined (G_OS_WIN32)
2422/* From
2423 * https://support.microsoft.com/en-ca/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime
2424 * FT = UT * 10000000 + 116444736000000000.
2425 * Converts unix epoch time (a signed 64-bit integer) to FILETIME.
2426 * Can optionally use a more precise timestamp that has
2427 * a fraction of a second expressed in nanoseconds.
2428 * UT must be between January 1st of year 1601 and December 31st of year 30827.
2429 * nsec must be non-negative and < 1000000000.
2430 * Returns TRUE if conversion succeeded, FALSE otherwise.
2431 *
2432 * The function that does the reverse can be found in
2433 * glib/gstdio.c.
2434 */
2435static gboolean
2436_g_win32_unix_time_to_filetime (gint64 ut,
2437 gint32 nsec,
2438 FILETIME *ft,
2439 GError **error)
2440{
2441 gint64 result;
2442 /* 1 unit of FILETIME is 100ns */
2443 const gint64 hundreds_of_usec_per_sec = 10000000;
2444 /* The difference between January 1, 1601 UTC (FILETIME epoch) and UNIX epoch
2445 * in hundreds of nanoseconds.
2446 */
2447 const gint64 filetime_unix_epoch_offset = 116444736000000000;
2448 /* This is the maximum timestamp that SYSTEMTIME can
2449 * represent (last millisecond of the year 30827).
2450 * Since FILETIME and SYSTEMTIME are both used on Windows,
2451 * we use this as a limit (FILETIME can support slightly
2452 * larger interval, up to year 30828).
2453 */
2454 const gint64 max_systemtime = 0x7fff35f4f06c58f0;
2455
2456 g_return_val_if_fail (ft != NULL, FALSE);
2457 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2458
2459 if (nsec < 0)
2460 {
2461 g_set_error (error, G_IO_ERROR,
2462 G_IO_ERROR_INVALID_DATA,
2463 _("Extra nanoseconds %d for UNIX timestamp %lld are negative"),
2464 nsec, ut);
2465 return FALSE;
2466 }
2467
2468 if (nsec >= hundreds_of_usec_per_sec * 100)
2469 {
2470 g_set_error (error, G_IO_ERROR,
2471 G_IO_ERROR_INVALID_DATA,
2472 _("Extra nanoseconds %d for UNIX timestamp %lld reach 1 second"),
2473 nsec, ut);
2474 return FALSE;
2475 }
2476
2477 if (ut >= (G_MAXINT64 / hundreds_of_usec_per_sec) ||
2478 (ut * hundreds_of_usec_per_sec) >= (G_MAXINT64 - filetime_unix_epoch_offset))
2479 {
2480 g_set_error (error, G_IO_ERROR,
2481 G_IO_ERROR_INVALID_DATA,
2482 _("UNIX timestamp %lld does not fit into 64 bits"),
2483 ut);
2484 return FALSE;
2485 }
2486
2487 result = ut * hundreds_of_usec_per_sec + filetime_unix_epoch_offset + nsec / 100;
2488
2489 if (result >= max_systemtime || result < 0)
2490 {
2491 g_set_error (error, G_IO_ERROR,
2492 G_IO_ERROR_INVALID_DATA,
2493 _("UNIX timestamp %lld is outside of the range supported by Windows"),
2494 ut);
2495 return FALSE;
2496 }
2497
2498 ft->dwLowDateTime = (DWORD) (result);
2499 ft->dwHighDateTime = (DWORD) (result >> 32);
2500
2501 return TRUE;
2502}
2503
2504static gboolean
2505set_mtime_atime (const char *filename,
2506 const GFileAttributeValue *mtime_value,
2507 const GFileAttributeValue *mtime_usec_value,
2508 const GFileAttributeValue *atime_value,
2509 const GFileAttributeValue *atime_usec_value,
2510 GError **error)
2511{
2512 BOOL res;
2513 guint64 val = 0;
2514 guint32 val_usec = 0;
2515 gunichar2 *filename_utf16;
2516 SECURITY_ATTRIBUTES sec = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
2517 HANDLE file_handle;
2518 FILETIME mtime;
2519 FILETIME atime;
2520 FILETIME *p_mtime = NULL;
2521 FILETIME *p_atime = NULL;
2522 DWORD gle;
2523
2524 /* ATIME */
2525 if (atime_value)
2526 {
2527 if (!get_uint64 (atime_value, &val, error))
2528 return FALSE;
2529 val_usec = 0;
2530 if (atime_usec_value &&
2531 !get_uint32 (atime_usec_value, &val_usec, error))
2532 return FALSE;
2533 if (!_g_win32_unix_time_to_filetime (val, val_usec, &atime, error))
2534 return FALSE;
2535 p_atime = &atime;
2536 }
2537
2538 /* MTIME */
2539 if (mtime_value)
2540 {
2541 if (!get_uint64 (mtime_value, &val, error))
2542 return FALSE;
2543 val_usec = 0;
2544 if (mtime_usec_value &&
2545 !get_uint32 (mtime_usec_value, &val_usec, error))
2546 return FALSE;
2547 if (!_g_win32_unix_time_to_filetime (val, val_usec, &mtime, error))
2548 return FALSE;
2549 p_mtime = &mtime;
2550 }
2551
2552 filename_utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2553
2554 if (filename_utf16 == NULL)
2555 {
2556 g_prefix_error (error,
2557 _("File name “%s” cannot be converted to UTF-16"),
2558 filename);
2559 return FALSE;
2560 }
2561
2562 file_handle = CreateFileW (filename_utf16,
2563 FILE_WRITE_ATTRIBUTES,
2564 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
2565 &sec,
2566 OPEN_EXISTING,
2567 FILE_FLAG_BACKUP_SEMANTICS,
2568 NULL);
2569 gle = GetLastError ();
2570 g_clear_pointer (&filename_utf16, g_free);
2571
2572 if (file_handle == INVALID_HANDLE_VALUE)
2573 {
2574 g_set_error (error, G_IO_ERROR,
2575 g_io_error_from_errno (gle),
2576 _("File “%s” cannot be opened: Windows Error %lu"),
2577 filename, gle);
2578
2579 return FALSE;
2580 }
2581
2582 res = SetFileTime (file_handle, NULL, p_atime, p_mtime);
2583 gle = GetLastError ();
2584 CloseHandle (file_handle);
2585
2586 if (!res)
2587 g_set_error (error, G_IO_ERROR,
2588 g_io_error_from_errno (gle),
2589 _("Error setting modification or access time for file “%s”: %lu"),
2590 filename, gle);
2591
2592 return res;
2593}
2594#elif defined (HAVE_UTIMES)
2595static int
2596lazy_stat (char *filename,
2597 struct stat *statbuf,
2598 gboolean *called_stat)
2599{
2600 int res;
2601
2602 if (*called_stat)
2603 return 0;
2604
2605 res = g_stat (file: filename, buf: statbuf);
2606
2607 if (res == 0)
2608 *called_stat = TRUE;
2609
2610 return res;
2611}
2612
2613
2614static gboolean
2615set_mtime_atime (char *filename,
2616 const GFileAttributeValue *mtime_value,
2617 const GFileAttributeValue *mtime_usec_value,
2618 const GFileAttributeValue *atime_value,
2619 const GFileAttributeValue *atime_usec_value,
2620 GError **error)
2621{
2622 int res;
2623 guint64 val = 0;
2624 guint32 val_usec = 0;
2625 struct stat statbuf;
2626 gboolean got_stat = FALSE;
2627 struct timeval times[2] = { {0, 0}, {0, 0} };
2628
2629 /* ATIME */
2630 if (atime_value)
2631 {
2632 if (!get_uint64 (value: atime_value, val_out: &val, error))
2633 return FALSE;
2634 times[0].tv_sec = val;
2635 }
2636 else
2637 {
2638 if (lazy_stat (filename, statbuf: &statbuf, called_stat: &got_stat) == 0)
2639 {
2640 times[0].tv_sec = statbuf.st_mtime;
2641#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
2642 times[0].tv_usec = statbuf.st_atimensec / 1000;
2643#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
2644 times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
2645#endif
2646 }
2647 }
2648
2649 if (atime_usec_value)
2650 {
2651 if (!get_uint32 (value: atime_usec_value, val_out: &val_usec, error))
2652 return FALSE;
2653 times[0].tv_usec = val_usec;
2654 }
2655
2656 /* MTIME */
2657 if (mtime_value)
2658 {
2659 if (!get_uint64 (value: mtime_value, val_out: &val, error))
2660 return FALSE;
2661 times[1].tv_sec = val;
2662 }
2663 else
2664 {
2665 if (lazy_stat (filename, statbuf: &statbuf, called_stat: &got_stat) == 0)
2666 {
2667 times[1].tv_sec = statbuf.st_mtime;
2668#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
2669 times[1].tv_usec = statbuf.st_mtimensec / 1000;
2670#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
2671 times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
2672#endif
2673 }
2674 }
2675
2676 if (mtime_usec_value)
2677 {
2678 if (!get_uint32 (value: mtime_usec_value, val_out: &val_usec, error))
2679 return FALSE;
2680 times[1].tv_usec = val_usec;
2681 }
2682
2683 res = utimes (file: filename, tvp: times);
2684 if (res == -1)
2685 {
2686 int errsv = errno;
2687
2688 g_set_error (err: error, G_IO_ERROR,
2689 code: g_io_error_from_errno (err_no: errsv),
2690 _("Error setting modification or access time: %s"),
2691 g_strerror (errnum: errsv));
2692 return FALSE;
2693 }
2694 return TRUE;
2695}
2696#endif
2697
2698
2699#ifdef HAVE_SELINUX
2700static gboolean
2701set_selinux_context (char *filename,
2702 const GFileAttributeValue *value,
2703 GError **error)
2704{
2705 const char *val;
2706
2707 if (!get_string (value, val_out: &val, error))
2708 return FALSE;
2709
2710 if (val == NULL)
2711 {
2712 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2713 _("SELinux context must be non-NULL"));
2714 return FALSE;
2715 }
2716
2717 if (!is_selinux_enabled ())
2718 {
2719 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
2720 _("SELinux is not enabled on this system"));
2721 return FALSE;
2722 }
2723
2724 if (setfilecon_raw (path: filename, con: val) < 0)
2725 {
2726 int errsv = errno;
2727
2728 g_set_error (err: error, G_IO_ERROR,
2729 code: g_io_error_from_errno (err_no: errsv),
2730 _("Error setting SELinux context: %s"),
2731 g_strerror (errnum: errsv));
2732 return FALSE;
2733 }
2734
2735 return TRUE;
2736}
2737#endif
2738
2739
2740gboolean
2741_g_local_file_info_set_attribute (char *filename,
2742 const char *attribute,
2743 GFileAttributeType type,
2744 gpointer value_p,
2745 GFileQueryInfoFlags flags,
2746 GCancellable *cancellable,
2747 GError **error)
2748{
2749 GFileAttributeValue value = { 0 };
2750 GVfsClass *class;
2751 GVfs *vfs;
2752
2753 _g_file_attribute_value_set_from_pointer (attr: &value, type, value_p, FALSE);
2754
2755 if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
2756 return set_unix_mode (filename, flags, value: &value, error);
2757
2758#ifdef G_OS_UNIX
2759 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
2760 return set_unix_uid_gid (filename, uid_value: &value, NULL, flags, error);
2761 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
2762 return set_unix_uid_gid (filename, NULL, gid_value: &value, flags, error);
2763#endif
2764
2765#ifdef HAVE_SYMLINK
2766 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) == 0)
2767 return set_symlink (filename, value: &value, error);
2768#endif
2769
2770#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2771 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
2772 return set_mtime_atime (filename, mtime_value: &value, NULL, NULL, NULL, error);
2773 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
2774 return set_mtime_atime (filename, NULL, mtime_usec_value: &value, NULL, NULL, error);
2775 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
2776 return set_mtime_atime (filename, NULL, NULL, atime_value: &value, NULL, error);
2777 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
2778 return set_mtime_atime (filename, NULL, NULL, NULL, atime_usec_value: &value, error);
2779#endif
2780
2781#ifdef HAVE_XATTR
2782 else if (g_str_has_prefix (str: attribute, prefix: "xattr::"))
2783 return set_xattr (filename, escaped_attribute: attribute, attr_value: &value, error);
2784 else if (g_str_has_prefix (str: attribute, prefix: "xattr-sys::"))
2785 return set_xattr (filename, escaped_attribute: attribute, attr_value: &value, error);
2786#endif
2787
2788#ifdef HAVE_SELINUX
2789 else if (strcmp (s1: attribute, G_FILE_ATTRIBUTE_SELINUX_CONTEXT) == 0)
2790 return set_selinux_context (filename, value: &value, error);
2791#endif
2792
2793 vfs = g_vfs_get_default ();
2794 class = G_VFS_GET_CLASS (vfs);
2795 if (class->local_file_set_attributes)
2796 {
2797 GFileInfo *info;
2798
2799 info = g_file_info_new ();
2800 g_file_info_set_attribute (info,
2801 attribute,
2802 type,
2803 value_p);
2804 if (!class->local_file_set_attributes (vfs, filename,
2805 info,
2806 flags, cancellable,
2807 error))
2808 {
2809 g_object_unref (object: info);
2810 return FALSE;
2811 }
2812
2813 if (g_file_info_get_attribute_status (info, attribute) == G_FILE_ATTRIBUTE_STATUS_SET)
2814 {
2815 g_object_unref (object: info);
2816 return TRUE;
2817 }
2818
2819 g_object_unref (object: info);
2820 }
2821
2822 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
2823 _("Setting attribute %s not supported"), attribute);
2824 return FALSE;
2825}
2826
2827gboolean
2828_g_local_file_info_set_attributes (char *filename,
2829 GFileInfo *info,
2830 GFileQueryInfoFlags flags,
2831 GCancellable *cancellable,
2832 GError **error)
2833{
2834 GFileAttributeValue *value;
2835#ifdef G_OS_UNIX
2836 GFileAttributeValue *uid, *gid;
2837#endif
2838#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2839 GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
2840#endif
2841#if defined (G_OS_UNIX) || defined (G_OS_WIN32)
2842 GFileAttributeStatus status;
2843#endif
2844 gboolean res;
2845 GVfsClass *class;
2846 GVfs *vfs;
2847
2848 /* Handles setting multiple specified data in a single set, and takes care
2849 of ordering restrictions when setting attributes */
2850
2851 res = TRUE;
2852
2853 /* Set symlink first, since this recreates the file */
2854#ifdef HAVE_SYMLINK
2855 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
2856 if (value)
2857 {
2858 if (!set_symlink (filename, value, error))
2859 {
2860 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2861 res = FALSE;
2862 /* Don't set error multiple times */
2863 error = NULL;
2864 }
2865 else
2866 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2867
2868 }
2869#endif
2870
2871#ifdef G_OS_UNIX
2872 /* Group uid and gid setting into one call
2873 * Change ownership before permissions, since ownership changes can
2874 change permissions (e.g. setuid)
2875 */
2876 uid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_UID);
2877 gid = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_GID);
2878
2879 if (uid || gid)
2880 {
2881 if (!set_unix_uid_gid (filename, uid_value: uid, gid_value: gid, flags, error))
2882 {
2883 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2884 res = FALSE;
2885 /* Don't set error multiple times */
2886 error = NULL;
2887 }
2888 else
2889 status = G_FILE_ATTRIBUTE_STATUS_SET;
2890 if (uid)
2891 uid->status = status;
2892 if (gid)
2893 gid->status = status;
2894 }
2895#endif
2896
2897 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_UNIX_MODE);
2898 if (value)
2899 {
2900 if (!set_unix_mode (filename, flags, value, error))
2901 {
2902 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2903 res = FALSE;
2904 /* Don't set error multiple times */
2905 error = NULL;
2906 }
2907 else
2908 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2909
2910 }
2911
2912#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
2913 /* Group all time settings into one call
2914 * Change times as the last thing to avoid it changing due to metadata changes
2915 */
2916
2917 mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2918 mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
2919 atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2920 atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
2921
2922 if (mtime || mtime_usec || atime || atime_usec)
2923 {
2924 if (!set_mtime_atime (filename, mtime_value: mtime, mtime_usec_value: mtime_usec, atime_value: atime, atime_usec_value: atime_usec, error))
2925 {
2926 status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2927 res = FALSE;
2928 /* Don't set error multiple times */
2929 error = NULL;
2930 }
2931 else
2932 status = G_FILE_ATTRIBUTE_STATUS_SET;
2933
2934 if (mtime)
2935 mtime->status = status;
2936 if (mtime_usec)
2937 mtime_usec->status = status;
2938 if (atime)
2939 atime->status = status;
2940 if (atime_usec)
2941 atime_usec->status = status;
2942 }
2943#endif
2944
2945 /* xattrs are handled by default callback */
2946
2947
2948 /* SELinux context */
2949#ifdef HAVE_SELINUX
2950 if (is_selinux_enabled ()) {
2951 value = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
2952 if (value)
2953 {
2954 if (!set_selinux_context (filename, value, error))
2955 {
2956 value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
2957 res = FALSE;
2958 /* Don't set error multiple times */
2959 error = NULL;
2960 }
2961 else
2962 value->status = G_FILE_ATTRIBUTE_STATUS_SET;
2963 }
2964 }
2965#endif
2966
2967 vfs = g_vfs_get_default ();
2968 class = G_VFS_GET_CLASS (vfs);
2969 if (class->local_file_set_attributes)
2970 {
2971 if (!class->local_file_set_attributes (vfs, filename,
2972 info,
2973 flags, cancellable,
2974 error))
2975 {
2976 res = FALSE;
2977 /* Don't set error multiple times */
2978 error = NULL;
2979 }
2980 }
2981
2982 return res;
2983}
2984

source code of gtk/subprojects/glib/gio/glocalfileinfo.c