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