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