Coverage Report

Created: 2025-07-01 07:09

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