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