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