Coverage Report

Created: 2025-07-11 06:47

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