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 #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 gchar *utf8_filename; 236 errsv = errno; 237 238 utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); 239 g_set_error (error, G_IO_ERROR, 240 g_io_error_from_errno (errsv), 241 "Error opening directory '%s': %s", 242 utf8_filename, g_strerror (errsv)); 243 g_free (utf8_filename); 244 g_free (filename); 245 return NULL; 246 } 247 248 #endif 249 250 local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR, 251 "container", file, 252 NULL); 253 254 local->dir = dir; 255 local->filename = filename; 256 local->matcher = g_file_attribute_matcher_new (attributes); 257 #ifndef USE_GDIR 258 local->reduced_matcher = g_file_attribute_matcher_subtract_attributes (local->matcher, 259 G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES"," 260 "standard::type"); 261 #endif 262 local->flags = flags; 263 264 return G_FILE_ENUMERATOR (local); 265 } 266 267 #ifndef USE_GDIR 268 static int 269 sort_by_inode (const void *_a, const void *_b) 270 { 271 const DirEntry *a, *b; 272 273 a = _a; 274 b = _b; 275 return a->inode - b->inode; 276 } 277 278 #ifdef HAVE_STRUCT_DIRENT_D_TYPE 279 static GFileType 280 file_type_from_dirent (char d_type) 281 { 282 switch (d_type) 283 { 284 case DT_BLK: 285 case DT_CHR: 286 case DT_FIFO: 287 case DT_SOCK: 288 return G_FILE_TYPE_SPECIAL; 289 case DT_DIR: 290 return G_FILE_TYPE_DIRECTORY; 291 case DT_LNK: 292 return G_FILE_TYPE_SYMBOLIC_LINK; 293 case DT_REG: 294 return G_FILE_TYPE_REGULAR; 295 case DT_UNKNOWN: 296 default: 297 return G_FILE_TYPE_UNKNOWN; 298 } 299 } 300 #endif 301 302 static const char * 303 next_file_helper (GLocalFileEnumerator *local, GFileType *file_type) 304 { 305 struct dirent *entry; 306 const char *filename; 307 int i; 308 309 if (local->at_end) 310 return NULL; 311 312 if (local->entries == NULL || 313 (local->entries[local->entries_pos].name == NULL)) 314 { 315 if (local->entries == NULL) 316 local->entries = g_new (DirEntry, CHUNK_SIZE + 1); 317 else 318 { 319 /* Restart by clearing old names */ 320 for (i = 0; local->entries[i].name != NULL; i++) 321 g_free (local->entries[i].name); 322 } 323 324 for (i = 0; i < CHUNK_SIZE; i++) 325 { 326 entry = readdir (local->dir); 327 while (entry 328 && (0 == strcmp (entry->d_name, ".") || 329 0 == strcmp (entry->d_name, ".."))) 330 entry = readdir (local->dir); 331 332 if (entry) 333 { 334 local->entries[i].name = g_strdup (entry->d_name); 335 local->entries[i].inode = entry->d_ino; 336 #if HAVE_STRUCT_DIRENT_D_TYPE 337 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 } 342 else 343 break; 344 } 345 local->entries[i].name = NULL; 346 local->entries_pos = 0; 347 348 qsort (local->entries, i, sizeof (DirEntry), sort_by_inode); 349 } 350 351 filename = local->entries[local->entries_pos].name; 352 if (filename == NULL) 353 local->at_end = TRUE; 354 355 *file_type = local->entries[local->entries_pos].type; 356 357 local->entries_pos++; 358 359 return filename; 360 } 361 362 #endif 363 364 static GFileInfo * 365 g_local_file_enumerator_next_file (GFileEnumerator *enumerator, 366 GCancellable *cancellable, 367 GError **error) 368 { 369 GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator); 370 const char *filename; 371 char *path; 372 GFileInfo *info; 373 GError *my_error; 374 GFileType file_type; 375 376 if (!local->got_parent_info) 377 { 378 _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info); 379 local->got_parent_info = TRUE; 380 } 381 382 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 filename = next_file_helper (local, &file_type); 389 #endif 390 391 if (filename == NULL) 392 return NULL; 393 394 my_error = NULL; 395 path = g_build_filename (local->filename, filename, NULL); 396 if (file_type == G_FILE_TYPE_UNKNOWN || 397 (file_type == G_FILE_TYPE_SYMBOLIC_LINK && !(local->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))) 398 { 399 info = _g_local_file_info_get (filename, path, 400 local->matcher, 401 local->flags, 402 &local->parent_info, 403 &my_error); 404 } 405 else 406 { 407 info = _g_local_file_info_get (filename, path, 408 local->reduced_matcher, 409 local->flags, 410 &local->parent_info, 411 &my_error); 412 if (info) 413 { 414 _g_local_file_info_get_nostat (info, filename, path, local->matcher); 415 g_file_info_set_file_type (info, file_type); 416 if (file_type == G_FILE_TYPE_SYMBOLIC_LINK) 417 g_file_info_set_is_symlink (info, TRUE); 418 } 419 } 420 g_free (path); 421 422 if (info == NULL) 423 { 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 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) 429 { 430 g_error_free (my_error); 431 goto next_file; 432 } 433 else 434 g_propagate_error (error, my_error); 435 } 436 437 return info; 438 } 439 440 static gboolean 441 g_local_file_enumerator_close (GFileEnumerator *enumerator, 442 GCancellable *cancellable, 443 GError **error) 444 { 445 GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator); 446 447 if (local->dir) 448 { 449 #ifdef USE_GDIR 450 g_dir_close (local->dir); 451 #else 452 closedir (local->dir); 453 #endif 454 local->dir = NULL; 455 } 456 457 return TRUE; 458 } 459