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