/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  | }  |