Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/glocalfileenumerator.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 * 
3
 * Copyright (C) 2006-2007 Red Hat, Inc.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Author: Alexander Larsson <alexl@redhat.com>
19
 */
20
21
#include "config.h"
22
23
#include <glib.h>
24
#include <glocalfileenumerator.h>
25
#include <glocalfileinfo.h>
26
#include <glocalfile.h>
27
#include <gioerror.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include "glibintl.h"
31
32
33
0
#define CHUNK_SIZE 1000
34
35
#ifdef G_OS_WIN32
36
#define USE_GDIR
37
#endif
38
39
#ifndef USE_GDIR
40
41
#include <sys/types.h>
42
#include <dirent.h>
43
#include <errno.h>
44
45
typedef struct {
46
  char *name;
47
  long inode;
48
  GFileType type;
49
} DirEntry;
50
51
#endif
52
53
struct _GLocalFileEnumerator
54
{
55
  GFileEnumerator parent;
56
57
  GFileAttributeMatcher *matcher;
58
  GFileAttributeMatcher *reduced_matcher;
59
  char *filename;
60
  char *attributes;
61
  GFileQueryInfoFlags flags;
62
63
  gboolean got_parent_info;
64
  GLocalParentFileInfo parent_info;
65
  
66
#ifdef USE_GDIR
67
  GDir *dir;
68
#else
69
  DIR *dir;
70
  DirEntry *entries;
71
  int entries_pos;
72
  gboolean at_end;
73
#endif
74
  
75
  gboolean follow_symlinks;
76
};
77
78
#define g_local_file_enumerator_get_type _g_local_file_enumerator_get_type
79
G_DEFINE_TYPE (GLocalFileEnumerator, g_local_file_enumerator, G_TYPE_FILE_ENUMERATOR)
80
81
static GFileInfo *g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
82
                 GCancellable     *cancellable,
83
                 GError          **error);
84
static gboolean   g_local_file_enumerator_close     (GFileEnumerator  *enumerator,
85
                 GCancellable     *cancellable,
86
                 GError          **error);
87
88
89
static void
90
free_entries (GLocalFileEnumerator *local)
91
0
{
92
0
#ifndef USE_GDIR
93
0
  int i;
94
95
0
  if (local->entries != NULL)
96
0
    {
97
0
      for (i = 0; local->entries[i].name != NULL; i++)
98
0
  g_free (local->entries[i].name);
99
      
100
0
      g_free (local->entries);
101
0
    }
102
0
#endif
103
0
}
104
105
static void
106
g_local_file_enumerator_finalize (GObject *object)
107
0
{
108
0
  GLocalFileEnumerator *local;
109
110
0
  local = G_LOCAL_FILE_ENUMERATOR (object);
111
112
0
  if (local->got_parent_info)
113
0
    _g_local_file_info_free_parent_info (&local->parent_info);
114
0
  g_free (local->filename);
115
0
  g_file_attribute_matcher_unref (local->matcher);
116
0
  g_file_attribute_matcher_unref (local->reduced_matcher);
117
0
  if (local->dir)
118
0
    {
119
#ifdef USE_GDIR
120
      g_dir_close (local->dir);
121
#else
122
0
      closedir (local->dir);
123
0
#endif      
124
0
      local->dir = NULL;
125
0
    }
126
127
0
  free_entries (local);
128
129
0
  G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize (object);
130
0
}
131
132
133
static void
134
g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass)
135
0
{
136
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
137
0
  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
138
  
139
0
  gobject_class->finalize = g_local_file_enumerator_finalize;
140
141
0
  enumerator_class->next_file = g_local_file_enumerator_next_file;
142
0
  enumerator_class->close_fn = g_local_file_enumerator_close;
143
0
}
144
145
static void
146
g_local_file_enumerator_init (GLocalFileEnumerator *local)
147
0
{
148
0
}
149
150
#ifdef USE_GDIR
151
static void
152
convert_file_to_io_error (GError **error,
153
        GError  *file_error)
154
{
155
  int new_code;
156
157
  if (file_error == NULL)
158
    return;
159
  
160
  new_code = G_IO_ERROR_FAILED;
161
  
162
  if (file_error->domain == G_FILE_ERROR) 
163
    {
164
      switch (file_error->code) 
165
        {
166
        case G_FILE_ERROR_NOENT:
167
          new_code = G_IO_ERROR_NOT_FOUND;
168
          break;
169
        case G_FILE_ERROR_ACCES:
170
          new_code = G_IO_ERROR_PERMISSION_DENIED;
171
          break;
172
        case G_FILE_ERROR_NOTDIR:
173
          new_code = G_IO_ERROR_NOT_DIRECTORY;
174
          break;
175
        case G_FILE_ERROR_MFILE:
176
          new_code = G_IO_ERROR_TOO_MANY_OPEN_FILES;
177
          break;
178
        default:
179
          break;
180
        }
181
    }
182
  
183
  g_set_error_literal (error, G_IO_ERROR,
184
                       new_code,
185
                       file_error->message);
186
}
187
#else
188
static GFileAttributeMatcher *
189
g_file_attribute_matcher_subtract_attributes (GFileAttributeMatcher *matcher,
190
                                              const char *           attributes)
191
0
{
192
0
  GFileAttributeMatcher *result, *tmp;
193
194
0
  tmp = g_file_attribute_matcher_new (attributes);
195
0
  result = g_file_attribute_matcher_subtract (matcher, tmp);
196
0
  g_file_attribute_matcher_unref (tmp);
197
198
0
  return result;
199
0
}
200
#endif
201
202
GFileEnumerator *
203
_g_local_file_enumerator_new (GLocalFile *file,
204
            const char           *attributes,
205
            GFileQueryInfoFlags   flags,
206
            GCancellable         *cancellable,
207
            GError              **error)
208
0
{
209
0
  GLocalFileEnumerator *local;
210
0
  char *filename = g_file_get_path (G_FILE (file));
211
212
#ifdef USE_GDIR
213
  GError *dir_error;
214
  GDir *dir;
215
  
216
  dir_error = NULL;
217
  dir = g_dir_open (filename, 0, error != NULL ? &dir_error : NULL);
218
  if (dir == NULL) 
219
    {
220
      if (error != NULL)
221
  {
222
    convert_file_to_io_error (error, dir_error);
223
    g_error_free (dir_error);
224
  }
225
      g_free (filename);
226
      return NULL;
227
    }
228
#else
229
0
  DIR *dir;
230
0
  int errsv;
231
232
0
  dir = opendir (filename);
233
0
  if (dir == NULL)
234
0
    {
235
0
      gchar *utf8_filename;
236
0
      errsv = errno;
237
238
0
      utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
239
0
      g_set_error (error, G_IO_ERROR,
240
0
                   g_io_error_from_errno (errsv),
241
0
                   "Error opening directory '%s': %s",
242
0
                   utf8_filename, g_strerror (errsv));
243
0
      g_free (utf8_filename);
244
0
      g_free (filename);
245
0
      return NULL;
246
0
    }
247
248
0
#endif
249
  
250
0
  local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR,
251
0
                        "container", file,
252
0
                        NULL);
253
254
0
  local->dir = dir;
255
0
  local->filename = filename;
256
0
  local->matcher = g_file_attribute_matcher_new (attributes);
257
0
#ifndef USE_GDIR
258
0
  local->reduced_matcher = g_file_attribute_matcher_subtract_attributes (local->matcher,
259
0
                                                                         G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES","
260
0
                                                                         "standard::type");
261
0
#endif
262
0
  local->flags = flags;
263
  
264
0
  return G_FILE_ENUMERATOR (local);
265
0
}
266
267
#ifndef USE_GDIR
268
static int
269
sort_by_inode (const void *_a, const void *_b)
270
0
{
271
0
  const DirEntry *a, *b;
272
273
0
  a = _a;
274
0
  b = _b;
275
0
  return a->inode - b->inode;
276
0
}
277
278
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
279
static GFileType
280
file_type_from_dirent (char d_type)
281
0
{
282
0
  switch (d_type)
283
0
    {
284
0
    case DT_BLK:
285
0
    case DT_CHR:
286
0
    case DT_FIFO:
287
0
    case DT_SOCK:
288
0
      return G_FILE_TYPE_SPECIAL;
289
0
    case DT_DIR:
290
0
      return G_FILE_TYPE_DIRECTORY;
291
0
    case DT_LNK:
292
0
      return G_FILE_TYPE_SYMBOLIC_LINK;
293
0
    case DT_REG:
294
0
      return G_FILE_TYPE_REGULAR;
295
0
    case DT_UNKNOWN:
296
0
    default:
297
0
      return G_FILE_TYPE_UNKNOWN;
298
0
    }
299
0
}
300
#endif
301
302
static const char *
303
next_file_helper (GLocalFileEnumerator *local, GFileType *file_type)
304
0
{
305
0
  struct dirent *entry;
306
0
  const char *filename;
307
0
  int i;
308
309
0
  if (local->at_end)
310
0
    return NULL;
311
  
312
0
  if (local->entries == NULL ||
313
0
      (local->entries[local->entries_pos].name == NULL))
314
0
    {
315
0
      if (local->entries == NULL)
316
0
  local->entries = g_new (DirEntry, CHUNK_SIZE + 1);
317
0
      else
318
0
  {
319
    /* Restart by clearing old names */
320
0
    for (i = 0; local->entries[i].name != NULL; i++)
321
0
      g_free (local->entries[i].name);
322
0
  }
323
      
324
0
      for (i = 0; i < CHUNK_SIZE; i++)
325
0
  {
326
0
    entry = readdir (local->dir);
327
0
    while (entry 
328
0
     && (0 == strcmp (entry->d_name, ".") ||
329
0
         0 == strcmp (entry->d_name, "..")))
330
0
      entry = readdir (local->dir);
331
332
0
    if (entry)
333
0
      {
334
0
        local->entries[i].name = g_strdup (entry->d_name);
335
0
        local->entries[i].inode = entry->d_ino;
336
0
#if HAVE_STRUCT_DIRENT_D_TYPE
337
0
              local->entries[i].type = file_type_from_dirent (entry->d_type);
338
#else
339
              local->entries[i].type = G_FILE_TYPE_UNKNOWN;
340
#endif
341
0
      }
342
0
    else
343
0
      break;
344
0
  }
345
0
      local->entries[i].name = NULL;
346
0
      local->entries_pos = 0;
347
      
348
0
      qsort (local->entries, i, sizeof (DirEntry), sort_by_inode);
349
0
    }
350
351
0
  filename = local->entries[local->entries_pos].name;
352
0
  if (filename == NULL)
353
0
    local->at_end = TRUE;
354
    
355
0
  *file_type = local->entries[local->entries_pos].type;
356
357
0
  local->entries_pos++;
358
359
0
  return filename;
360
0
}
361
362
#endif
363
364
static GFileInfo *
365
g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
366
           GCancellable     *cancellable,
367
           GError          **error)
368
0
{
369
0
  GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
370
0
  const char *filename;
371
0
  char *path;
372
0
  GFileInfo *info;
373
0
  GError *my_error;
374
0
  GFileType file_type;
375
376
0
  if (!local->got_parent_info)
377
0
    {
378
0
      _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info);
379
0
      local->got_parent_info = TRUE;
380
0
    }
381
382
0
 next_file:
383
384
#ifdef USE_GDIR
385
  filename = g_dir_read_name (local->dir);
386
  file_type = G_FILE_TYPE_UNKNOWN;
387
#else
388
0
  filename = next_file_helper (local, &file_type);
389
0
#endif
390
391
0
  if (filename == NULL)
392
0
    return NULL;
393
394
0
  my_error = NULL;
395
0
  path = g_build_filename (local->filename, filename, NULL);
396
0
  if (file_type == G_FILE_TYPE_UNKNOWN ||
397
0
      (file_type == G_FILE_TYPE_SYMBOLIC_LINK && !(local->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
398
0
    {
399
0
      info = _g_local_file_info_get (filename, path,
400
0
                                     local->matcher,
401
0
                                     local->flags,
402
0
                                     &local->parent_info,
403
0
                                     &my_error); 
404
0
    }
405
0
  else
406
0
    {
407
0
      info = _g_local_file_info_get (filename, path,
408
0
                                     local->reduced_matcher,
409
0
                                     local->flags,
410
0
                                     &local->parent_info,
411
0
                                     &my_error); 
412
0
      if (info)
413
0
        {
414
0
          _g_local_file_info_get_nostat (info, filename, path, local->matcher);
415
0
          g_file_info_set_file_type (info, file_type);
416
0
          if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
417
0
            g_file_info_set_is_symlink (info, TRUE);
418
0
        }
419
0
    }
420
0
  g_free (path);
421
422
0
  if (info == NULL)
423
0
    {
424
      /* Failed to get info */
425
      /* If the file does not exist there might have been a race where
426
       * the file was removed between the readdir and the stat, so we
427
       * ignore the file. */
428
0
      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
429
0
  {
430
0
    g_error_free (my_error);
431
0
    goto next_file;
432
0
  }
433
0
      else
434
0
  g_propagate_error (error, my_error);
435
0
    }
436
437
0
  return info;
438
0
}
439
440
static gboolean
441
g_local_file_enumerator_close (GFileEnumerator  *enumerator,
442
             GCancellable     *cancellable,
443
             GError          **error)
444
0
{
445
0
  GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
446
447
0
  if (local->dir)
448
0
    {
449
#ifdef USE_GDIR
450
      g_dir_close (local->dir);
451
#else
452
0
      closedir (local->dir);
453
0
#endif
454
0
      local->dir = NULL;
455
0
    }
456
457
0
  return TRUE;
458
0
}