Coverage Report

Created: 2025-07-23 08:13

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