Coverage Report

Created: 2026-04-01 06:53

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