subprocess: Init and clear the mutex
[platform/upstream/glib.git] / gio / glocalfileenumerator.c
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 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 #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 {
92 #ifndef USE_GDIR
93   int i;
94
95   if (local->entries != NULL)
96     {
97       for (i = 0; local->entries[i].name != NULL; i++)
98         g_free (local->entries[i].name);
99       
100       g_free (local->entries);
101     }
102 #endif
103 }
104
105 static void
106 g_local_file_enumerator_finalize (GObject *object)
107 {
108   GLocalFileEnumerator *local;
109
110   local = G_LOCAL_FILE_ENUMERATOR (object);
111
112   if (local->got_parent_info)
113     _g_local_file_info_free_parent_info (&local->parent_info);
114   g_free (local->filename);
115   g_file_attribute_matcher_unref (local->matcher);
116   g_file_attribute_matcher_unref (local->reduced_matcher);
117   if (local->dir)
118     {
119 #ifdef USE_GDIR
120       g_dir_close (local->dir);
121 #else
122       closedir (local->dir);
123 #endif      
124       local->dir = NULL;
125     }
126
127   free_entries (local);
128
129   G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize (object);
130 }
131
132
133 static void
134 g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass)
135 {
136   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
137   GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
138   
139   gobject_class->finalize = g_local_file_enumerator_finalize;
140
141   enumerator_class->next_file = g_local_file_enumerator_next_file;
142   enumerator_class->close_fn = g_local_file_enumerator_close;
143 }
144
145 static void
146 g_local_file_enumerator_init (GLocalFileEnumerator *local)
147 {
148 }
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 {
192   GFileAttributeMatcher *result, *tmp;
193
194   tmp = g_file_attribute_matcher_new (attributes);
195   result = g_file_attribute_matcher_subtract (matcher, tmp);
196   g_file_attribute_matcher_unref (tmp);
197
198   return result;
199 }
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 {
209   GLocalFileEnumerator *local;
210   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   DIR *dir;
230   int errsv;
231
232   dir = opendir (filename);
233   if (dir == NULL)
234     {
235       errsv = errno;
236
237       g_set_error_literal (error, G_IO_ERROR,
238                            g_io_error_from_errno (errsv),
239                            g_strerror (errsv));
240       g_free (filename);
241       return NULL;
242     }
243
244 #endif
245   
246   local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR,
247                         "container", file,
248                         NULL);
249
250   local->dir = dir;
251   local->filename = filename;
252   local->matcher = g_file_attribute_matcher_new (attributes);
253 #ifndef USE_GDIR
254   local->reduced_matcher = g_file_attribute_matcher_subtract_attributes (local->matcher,
255                                                                          G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES","
256                                                                          "standard::type");
257 #endif
258   local->flags = flags;
259   
260   return G_FILE_ENUMERATOR (local);
261 }
262
263 #ifndef USE_GDIR
264 static int
265 sort_by_inode (const void *_a, const void *_b)
266 {
267   const DirEntry *a, *b;
268
269   a = _a;
270   b = _b;
271   return a->inode - b->inode;
272 }
273
274 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
275 static GFileType
276 file_type_from_dirent (char d_type)
277 {
278   switch (d_type)
279     {
280     case DT_BLK:
281     case DT_CHR:
282     case DT_FIFO:
283     case DT_SOCK:
284       return G_FILE_TYPE_SPECIAL;
285     case DT_DIR:
286       return G_FILE_TYPE_DIRECTORY;
287     case DT_LNK:
288       return G_FILE_TYPE_SYMBOLIC_LINK;
289     case DT_REG:
290       return G_FILE_TYPE_REGULAR;
291     case DT_UNKNOWN:
292     default:
293       return G_FILE_TYPE_UNKNOWN;
294     }
295 }
296 #endif
297
298 static const char *
299 next_file_helper (GLocalFileEnumerator *local, GFileType *file_type)
300 {
301   struct dirent *entry;
302   const char *filename;
303   int i;
304
305   if (local->at_end)
306     return NULL;
307   
308   if (local->entries == NULL ||
309       (local->entries[local->entries_pos].name == NULL))
310     {
311       if (local->entries == NULL)
312         local->entries = g_new (DirEntry, CHUNK_SIZE + 1);
313       else
314         {
315           /* Restart by clearing old names */
316           for (i = 0; local->entries[i].name != NULL; i++)
317             g_free (local->entries[i].name);
318         }
319       
320       for (i = 0; i < CHUNK_SIZE; i++)
321         {
322           entry = readdir (local->dir);
323           while (entry 
324                  && (0 == strcmp (entry->d_name, ".") ||
325                      0 == strcmp (entry->d_name, "..")))
326             entry = readdir (local->dir);
327
328           if (entry)
329             {
330               local->entries[i].name = g_strdup (entry->d_name);
331               local->entries[i].inode = entry->d_ino;
332 #if HAVE_STRUCT_DIRENT_D_TYPE
333               local->entries[i].type = file_type_from_dirent (entry->d_type);
334 #else
335               local->entries[i].type = G_FILE_TYPE_UNKNOWN;
336 #endif
337             }
338           else
339             break;
340         }
341       local->entries[i].name = NULL;
342       local->entries_pos = 0;
343       
344       qsort (local->entries, i, sizeof (DirEntry), sort_by_inode);
345     }
346
347   filename = local->entries[local->entries_pos].name;
348   if (filename == NULL)
349     local->at_end = TRUE;
350     
351   *file_type = local->entries[local->entries_pos].type;
352
353   local->entries_pos++;
354
355   return filename;
356 }
357
358 #endif
359
360 static GFileInfo *
361 g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
362                                    GCancellable     *cancellable,
363                                    GError          **error)
364 {
365   GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
366   const char *filename;
367   char *path;
368   GFileInfo *info;
369   GError *my_error;
370   GFileType file_type;
371
372   if (!local->got_parent_info)
373     {
374       _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info);
375       local->got_parent_info = TRUE;
376     }
377
378  next_file:
379
380 #ifdef USE_GDIR
381   filename = g_dir_read_name (local->dir);
382   file_type = G_FILE_TYPE_UNKNOWN;
383 #else
384   filename = next_file_helper (local, &file_type);
385 #endif
386
387   if (filename == NULL)
388     return NULL;
389
390   my_error = NULL;
391   path = g_build_filename (local->filename, filename, NULL);
392   if (file_type == G_FILE_TYPE_UNKNOWN ||
393       (file_type == G_FILE_TYPE_SYMBOLIC_LINK && !(local->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
394     {
395       info = _g_local_file_info_get (filename, path,
396                                      local->matcher,
397                                      local->flags,
398                                      &local->parent_info,
399                                      &my_error); 
400     }
401   else
402     {
403       info = _g_local_file_info_get (filename, path,
404                                      local->reduced_matcher,
405                                      local->flags,
406                                      &local->parent_info,
407                                      &my_error); 
408       if (info)
409         {
410           _g_local_file_info_get_nostat (info, filename, path, local->matcher);
411           g_file_info_set_file_type (info, file_type);
412           if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
413             g_file_info_set_is_symlink (info, TRUE);
414         }
415     }
416   g_free (path);
417
418   if (info == NULL)
419     {
420       /* Failed to get info */
421       /* If the file does not exist there might have been a race where
422        * the file was removed between the readdir and the stat, so we
423        * ignore the file. */
424       if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
425         {
426           g_error_free (my_error);
427           goto next_file;
428         }
429       else
430         g_propagate_error (error, my_error);
431     }
432
433   return info;
434 }
435
436 static gboolean
437 g_local_file_enumerator_close (GFileEnumerator  *enumerator,
438                                GCancellable     *cancellable,
439                                GError          **error)
440 {
441   GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
442
443   if (local->dir)
444     {
445 #ifdef USE_GDIR
446       g_dir_close (local->dir);
447 #else
448       closedir (local->dir);
449 #endif
450       local->dir = NULL;
451     }
452
453   return TRUE;
454 }