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, write to the 17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 18 * Boston, MA 02111-1307, USA. 19 * 20 * Author: Alexander Larsson <alexl (at) redhat.com> 21 */ 22 23 #include "config.h" 24 25 #include <glib.h> 26 #include <glocalfileenumerator.h> 27 #include <glocalfileinfo.h> 28 #include <glocalfile.h> 29 #include <gioerror.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include "glibintl.h" 33 34 #include "gioalias.h" 35 36 #define CHUNK_SIZE 1000 37 38 /* TODO: 39 * It would be nice to use the dirent->d_type to check file type without 40 * needing to stat each files on linux and other systems that support it. 41 * (question: does that following symlink or not?) 42 */ 43 44 #ifdef G_OS_WIN32 45 #define USE_GDIR 46 #endif 47 48 #ifndef USE_GDIR 49 50 #include <sys/types.h> 51 #include <dirent.h> 52 #include <errno.h> 53 54 typedef struct { 55 char *name; 56 long inode; 57 } DirEntry; 58 59 #endif 60 61 struct _GLocalFileEnumerator 62 { 63 GFileEnumerator parent; 64 65 GFileAttributeMatcher *matcher; 66 char *filename; 67 char *attributes; 68 GFileQueryInfoFlags flags; 69 70 gboolean got_parent_info; 71 GLocalParentFileInfo parent_info; 72 73 #ifdef USE_GDIR 74 GDir *dir; 75 #else 76 DIR *dir; 77 DirEntry *entries; 78 int entries_pos; 79 gboolean at_end; 80 #endif 81 82 gboolean follow_symlinks; 83 }; 84 85 #define g_local_file_enumerator_get_type _g_local_file_enumerator_get_type 86 G_DEFINE_TYPE (GLocalFileEnumerator, g_local_file_enumerator, G_TYPE_FILE_ENUMERATOR); 87 88 static GFileInfo *g_local_file_enumerator_next_file (GFileEnumerator *enumerator, 89 GCancellable *cancellable, 90 GError **error); 91 static gboolean g_local_file_enumerator_close (GFileEnumerator *enumerator, 92 GCancellable *cancellable, 93 GError **error); 94 95 96 static void 97 free_entries (GLocalFileEnumerator *local) 98 { 99 #ifndef USE_GDIR 100 int i; 101 102 if (local->entries != NULL) 103 { 104 for (i = 0; local->entries[i].name != NULL; i++) 105 g_free (local->entries[i].name); 106 107 g_free (local->entries); 108 } 109 #endif 110 } 111 112 static void 113 g_local_file_enumerator_finalize (GObject *object) 114 { 115 GLocalFileEnumerator *local; 116 117 local = G_LOCAL_FILE_ENUMERATOR (object); 118 119 g_free (local->filename); 120 g_file_attribute_matcher_unref (local->matcher); 121 if (local->dir) 122 { 123 #ifdef USE_GDIR 124 g_dir_close (local->dir); 125 #else 126 closedir (local->dir); 127 #endif 128 local->dir = NULL; 129 } 130 131 free_entries (local); 132 133 G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize (object); 134 } 135 136 137 static void 138 g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass) 139 { 140 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 141 GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass); 142 143 gobject_class->finalize = g_local_file_enumerator_finalize; 144 145 enumerator_class->next_file = g_local_file_enumerator_next_file; 146 enumerator_class->close_fn = g_local_file_enumerator_close; 147 } 148 149 static void 150 g_local_file_enumerator_init (GLocalFileEnumerator *local) 151 { 152 } 153 154 #ifdef USE_GDIR 155 static void 156 convert_file_to_io_error (GError **error, 157 GError *file_error) 158 { 159 int new_code; 160 161 if (file_error == NULL) 162 return; 163 164 new_code = G_IO_ERROR_FAILED; 165 166 if (file_error->domain == G_FILE_ERROR) 167 { 168 switch (file_error->code) 169 { 170 case G_FILE_ERROR_NOENT: 171 new_code = G_IO_ERROR_NOT_FOUND; 172 break; 173 case G_FILE_ERROR_ACCES: 174 new_code = G_IO_ERROR_PERMISSION_DENIED; 175 break; 176 case G_FILE_ERROR_NOTDIR: 177 new_code = G_IO_ERROR_NOT_DIRECTORY; 178 break; 179 case G_FILE_ERROR_MFILE: 180 new_code = G_IO_ERROR_TOO_MANY_OPEN_FILES; 181 break; 182 default: 183 break; 184 } 185 } 186 187 g_set_error_literal (error, G_IO_ERROR, 188 new_code, 189 file_error->message); 190 } 191 #endif 192 193 GFileEnumerator * 194 _g_local_file_enumerator_new (GLocalFile *file, 195 const char *attributes, 196 GFileQueryInfoFlags flags, 197 GCancellable *cancellable, 198 GError **error) 199 { 200 GLocalFileEnumerator *local; 201 char *filename = g_file_get_path (G_FILE (file)); 202 203 #ifdef USE_GDIR 204 GError *dir_error; 205 GDir *dir; 206 207 dir_error = NULL; 208 dir = g_dir_open (filename, 0, error != NULL ? &dir_error : NULL); 209 if (dir == NULL) 210 { 211 if (error != NULL) 212 { 213 convert_file_to_io_error (error, dir_error); 214 g_error_free (dir_error); 215 } 216 g_free (filename); 217 return NULL; 218 } 219 #else 220 DIR *dir; 221 int errsv; 222 223 dir = opendir (filename); 224 if (dir == NULL) 225 { 226 errsv = errno; 227 228 g_set_error_literal (error, G_IO_ERROR, 229 g_io_error_from_errno (errsv), 230 g_strerror (errsv)); 231 g_free (filename); 232 return NULL; 233 } 234 235 #endif 236 237 local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR, 238 "container", file, 239 NULL); 240 241 local->dir = dir; 242 local->filename = filename; 243 local->matcher = g_file_attribute_matcher_new (attributes); 244 local->flags = flags; 245 246 return G_FILE_ENUMERATOR (local); 247 } 248 249 #ifndef USE_GDIR 250 static int 251 sort_by_inode (const void *_a, const void *_b) 252 { 253 const DirEntry *a, *b; 254 255 a = _a; 256 b = _b; 257 return a->inode - b->inode; 258 } 259 260 static const char * 261 next_file_helper (GLocalFileEnumerator *local) 262 { 263 struct dirent *entry; 264 const char *filename; 265 int i; 266 267 if (local->at_end) 268 return NULL; 269 270 if (local->entries == NULL || 271 (local->entries[local->entries_pos].name == NULL)) 272 { 273 if (local->entries == NULL) 274 local->entries = g_new (DirEntry, CHUNK_SIZE + 1); 275 else 276 { 277 /* Restart by clearing old names */ 278 for (i = 0; local->entries[i].name != NULL; i++) 279 g_free (local->entries[i].name); 280 } 281 282 for (i = 0; i < CHUNK_SIZE; i++) 283 { 284 entry = readdir (local->dir); 285 while (entry 286 && (0 == strcmp (entry->d_name, ".") || 287 0 == strcmp (entry->d_name, ".."))) 288 entry = readdir (local->dir); 289 290 if (entry) 291 { 292 local->entries[i].name = g_strdup (entry->d_name); 293 local->entries[i].inode = entry->d_ino; 294 } 295 else 296 break; 297 } 298 local->entries[i].name = NULL; 299 local->entries_pos = 0; 300 301 qsort (local->entries, i, sizeof (DirEntry), sort_by_inode); 302 } 303 304 filename = local->entries[local->entries_pos++].name; 305 if (filename == NULL) 306 local->at_end = TRUE; 307 308 return filename; 309 } 310 311 #endif 312 313 static GFileInfo * 314 g_local_file_enumerator_next_file (GFileEnumerator *enumerator, 315 GCancellable *cancellable, 316 GError **error) 317 { 318 GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator); 319 const char *filename; 320 char *path; 321 GFileInfo *info; 322 GError *my_error; 323 324 if (!local->got_parent_info) 325 { 326 _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info); 327 local->got_parent_info = TRUE; 328 } 329 330 next_file: 331 332 #ifdef USE_GDIR 333 filename = g_dir_read_name (local->dir); 334 #else 335 filename = next_file_helper (local); 336 #endif 337 338 if (filename == NULL) 339 return NULL; 340 341 my_error = NULL; 342 path = g_build_filename (local->filename, filename, NULL); 343 info = _g_local_file_info_get (filename, path, 344 local->matcher, 345 local->flags, 346 &local->parent_info, 347 &my_error); 348 g_free (path); 349 350 if (info == NULL) 351 { 352 /* Failed to get info */ 353 /* If the file does not exist there might have been a race where 354 * the file was removed between the readdir and the stat, so we 355 * ignore the file. */ 356 if (my_error->domain == G_IO_ERROR && 357 my_error->code == G_IO_ERROR_NOT_FOUND) 358 { 359 g_error_free (my_error); 360 goto next_file; 361 } 362 else 363 g_propagate_error (error, my_error); 364 } 365 366 return info; 367 } 368 369 static gboolean 370 g_local_file_enumerator_close (GFileEnumerator *enumerator, 371 GCancellable *cancellable, 372 GError **error) 373 { 374 GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator); 375 376 if (local->dir) 377 { 378 #ifdef USE_GDIR 379 g_dir_close (local->dir); 380 #else 381 closedir (local->dir); 382 #endif 383 local->dir = NULL; 384 } 385 386 return TRUE; 387 } 388 389 390