Coverage Report

Created: 2025-06-13 06:55

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