glocalfileenumerator.c 11 KB
Newer Older
1 2 3 4 5 6 7
/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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 11 12 13 14 15
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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 19 20
 *
 * Author: Alexander Larsson <alexl@redhat.com>
 */

21
#include "config.h"
22 23 24 25

#include <glib.h>
#include <glocalfileenumerator.h>
#include <glocalfileinfo.h>
26
#include <glocalfile.h>
27
#include <gioerror.h>
28 29
#include <string.h>
#include <stdlib.h>
30 31
#include "glibintl.h"

32

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#define CHUNK_SIZE 1000

#ifdef G_OS_WIN32
#define USE_GDIR
#endif

#ifndef USE_GDIR

#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

typedef struct {
  char *name;
  long inode;
48
  GFileType type;
49 50 51
} DirEntry;

#endif
52 53 54 55 56 57

struct _GLocalFileEnumerator
{
  GFileEnumerator parent;

  GFileAttributeMatcher *matcher;
58
  GFileAttributeMatcher *reduced_matcher;
59 60 61 62 63 64 65
  char *filename;
  char *attributes;
  GFileQueryInfoFlags flags;

  gboolean got_parent_info;
  GLocalParentFileInfo parent_info;
  
66 67 68 69 70 71 72 73 74
#ifdef USE_GDIR
  GDir *dir;
#else
  DIR *dir;
  DirEntry *entries;
  int entries_pos;
  gboolean at_end;
#endif
  
75 76 77
  gboolean follow_symlinks;
};

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 82 83 84 85 86 87 88

static GFileInfo *g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
						     GCancellable     *cancellable,
						     GError          **error);
static gboolean   g_local_file_enumerator_close     (GFileEnumerator  *enumerator,
						     GCancellable     *cancellable,
						     GError          **error);


89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static void
free_entries (GLocalFileEnumerator *local)
{
#ifndef USE_GDIR
  int i;

  if (local->entries != NULL)
    {
      for (i = 0; local->entries[i].name != NULL; i++)
	g_free (local->entries[i].name);
      
      g_free (local->entries);
    }
#endif
}

105 106 107 108 109 110 111
static void
g_local_file_enumerator_finalize (GObject *object)
{
  GLocalFileEnumerator *local;

  local = G_LOCAL_FILE_ENUMERATOR (object);

112 113
  if (local->got_parent_info)
    _g_local_file_info_free_parent_info (&local->parent_info);
114 115
  g_free (local->filename);
  g_file_attribute_matcher_unref (local->matcher);
116
  g_file_attribute_matcher_unref (local->reduced_matcher);
117 118
  if (local->dir)
    {
119
#ifdef USE_GDIR
120
      g_dir_close (local->dir);
121 122 123
#else
      closedir (local->dir);
#endif      
124 125
      local->dir = NULL;
    }
126 127

  free_entries (local);
128 129

  G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize (object);
130 131 132 133 134 135 136 137 138 139 140 141
}


static void
g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
  
  gobject_class->finalize = g_local_file_enumerator_finalize;

  enumerator_class->next_file = g_local_file_enumerator_next_file;
142
  enumerator_class->close_fn = g_local_file_enumerator_close;
143 144 145 146 147 148 149
}

static void
g_local_file_enumerator_init (GLocalFileEnumerator *local)
{
}

150
#ifdef USE_GDIR
151 152
static void
convert_file_to_io_error (GError **error,
153
			  GError  *file_error)
154 155 156 157 158 159 160 161
{
  int new_code;

  if (file_error == NULL)
    return;
  
  new_code = G_IO_ERROR_FAILED;
  
162 163 164 165 166 167 168 169 170 171 172 173 174
  if (file_error->domain == G_FILE_ERROR) 
    {
      switch (file_error->code) 
        {
        case G_FILE_ERROR_NOENT:
          new_code = G_IO_ERROR_NOT_FOUND;
          break;
        case G_FILE_ERROR_ACCES:
          new_code = G_IO_ERROR_PERMISSION_DENIED;
          break;
        case G_FILE_ERROR_NOTDIR:
          new_code = G_IO_ERROR_NOT_DIRECTORY;
          break;
175 176 177
        case G_FILE_ERROR_MFILE:
          new_code = G_IO_ERROR_TOO_MANY_OPEN_FILES;
          break;
178 179 180
        default:
          break;
        }
181 182
    }
  
183 184 185
  g_set_error_literal (error, G_IO_ERROR,
                       new_code,
                       file_error->message);
186
}
187 188 189 190 191 192 193 194 195 196 197 198 199
#else
static GFileAttributeMatcher *
g_file_attribute_matcher_subtract_attributes (GFileAttributeMatcher *matcher,
                                              const char *           attributes)
{
  GFileAttributeMatcher *result, *tmp;

  tmp = g_file_attribute_matcher_new (attributes);
  result = g_file_attribute_matcher_subtract (matcher, tmp);
  g_file_attribute_matcher_unref (tmp);

  return result;
}
200
#endif
201 202

GFileEnumerator *
203
_g_local_file_enumerator_new (GLocalFile *file,
204 205 206 207
			      const char           *attributes,
			      GFileQueryInfoFlags   flags,
			      GCancellable         *cancellable,
			      GError              **error)
208 209
{
  GLocalFileEnumerator *local;
210
  char *filename = g_file_get_path (G_FILE (file));
211

212 213 214 215
#ifdef USE_GDIR
  GError *dir_error;
  GDir *dir;
  
216 217
  dir_error = NULL;
  dir = g_dir_open (filename, 0, error != NULL ? &dir_error : NULL);
218 219
  if (dir == NULL) 
    {
220 221 222 223 224
      if (error != NULL)
	{
	  convert_file_to_io_error (error, dir_error);
	  g_error_free (dir_error);
	}
225
      g_free (filename);
226 227
      return NULL;
    }
228 229 230 231 232 233 234
#else
  DIR *dir;
  int errsv;

  dir = opendir (filename);
  if (dir == NULL)
    {
235
      gchar *utf8_filename;
236 237
      errsv = errno;

238 239 240 241 242 243
      utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
      g_set_error (error, G_IO_ERROR,
                   g_io_error_from_errno (errsv),
                   "Error opening directory '%s': %s",
                   utf8_filename, g_strerror (errsv));
      g_free (utf8_filename);
244
      g_free (filename);
245 246 247 248
      return NULL;
    }

#endif
249
  
250 251 252
  local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR,
                        "container", file,
                        NULL);
253 254

  local->dir = dir;
255
  local->filename = filename;
256
  local->matcher = g_file_attribute_matcher_new (attributes);
257 258
#ifndef USE_GDIR
  local->reduced_matcher = g_file_attribute_matcher_subtract_attributes (local->matcher,
259 260
                                                                         G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES","
                                                                         "standard::type");
261
#endif
262 263 264 265 266
  local->flags = flags;
  
  return G_FILE_ENUMERATOR (local);
}

267 268 269 270 271 272 273 274 275 276 277
#ifndef USE_GDIR
static int
sort_by_inode (const void *_a, const void *_b)
{
  const DirEntry *a, *b;

  a = _a;
  b = _b;
  return a->inode - b->inode;
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
static GFileType
file_type_from_dirent (char d_type)
{
  switch (d_type)
    {
    case DT_BLK:
    case DT_CHR:
    case DT_FIFO:
    case DT_SOCK:
      return G_FILE_TYPE_SPECIAL;
    case DT_DIR:
      return G_FILE_TYPE_DIRECTORY;
    case DT_LNK:
      return G_FILE_TYPE_SYMBOLIC_LINK;
    case DT_REG:
      return G_FILE_TYPE_REGULAR;
    case DT_UNKNOWN:
    default:
      return G_FILE_TYPE_UNKNOWN;
    }
}
#endif

302
static const char *
303
next_file_helper (GLocalFileEnumerator *local, GFileType *file_type)
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
{
  struct dirent *entry;
  const char *filename;
  int i;

  if (local->at_end)
    return NULL;
  
  if (local->entries == NULL ||
      (local->entries[local->entries_pos].name == NULL))
    {
      if (local->entries == NULL)
	local->entries = g_new (DirEntry, CHUNK_SIZE + 1);
      else
	{
	  /* Restart by clearing old names */
	  for (i = 0; local->entries[i].name != NULL; i++)
	    g_free (local->entries[i].name);
	}
      
      for (i = 0; i < CHUNK_SIZE; i++)
	{
	  entry = readdir (local->dir);
	  while (entry 
		 && (0 == strcmp (entry->d_name, ".") ||
		     0 == strcmp (entry->d_name, "..")))
	    entry = readdir (local->dir);

	  if (entry)
	    {
	      local->entries[i].name = g_strdup (entry->d_name);
	      local->entries[i].inode = entry->d_ino;
336 337 338 339 340
#if HAVE_STRUCT_DIRENT_D_TYPE
              local->entries[i].type = file_type_from_dirent (entry->d_type);
#else
              local->entries[i].type = G_FILE_TYPE_UNKNOWN;
#endif
341 342 343 344 345 346 347 348 349 350
	    }
	  else
	    break;
	}
      local->entries[i].name = NULL;
      local->entries_pos = 0;
      
      qsort (local->entries, i, sizeof (DirEntry), sort_by_inode);
    }

351
  filename = local->entries[local->entries_pos].name;
352 353 354
  if (filename == NULL)
    local->at_end = TRUE;
    
355 356 357 358
  *file_type = local->entries[local->entries_pos].type;

  local->entries_pos++;

359 360 361 362 363
  return filename;
}

#endif

364
static GFileInfo *
365
g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
366
				   GCancellable     *cancellable,
367
				   GError          **error)
368 369 370 371 372
{
  GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
  const char *filename;
  char *path;
  GFileInfo *info;
373
  GError *my_error;
374
  GFileType file_type;
375 376 377 378 379 380

  if (!local->got_parent_info)
    {
      _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info);
      local->got_parent_info = TRUE;
    }
Matthias Clasen's avatar
Matthias Clasen committed
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 390
#endif

391 392 393
  if (filename == NULL)
    return NULL;

394
  my_error = NULL;
395
  path = g_build_filename (local->filename, filename, NULL);
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
  if (file_type == G_FILE_TYPE_UNKNOWN ||
      (file_type == G_FILE_TYPE_SYMBOLIC_LINK && !(local->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
    {
      info = _g_local_file_info_get (filename, path,
                                     local->matcher,
                                     local->flags,
                                     &local->parent_info,
                                     &my_error); 
    }
  else
    {
      info = _g_local_file_info_get (filename, path,
                                     local->reduced_matcher,
                                     local->flags,
                                     &local->parent_info,
                                     &my_error); 
      if (info)
        {
414
          _g_local_file_info_get_nostat (info, filename, path, local->matcher);
415 416 417 418 419
          g_file_info_set_file_type (info, file_type);
          if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
            g_file_info_set_is_symlink (info, TRUE);
        }
    }
420
  g_free (path);
421

422 423 424 425 426 427
  if (info == NULL)
    {
      /* Failed to get info */
      /* If the file does not exist there might have been a race where
       * the file was removed between the readdir and the stat, so we
       * ignore the file. */
Matthias Clasen's avatar
Matthias Clasen committed
428
      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
429 430 431 432 433 434 435 436 437 438 439 440
	{
	  g_error_free (my_error);
	  goto next_file;
	}
      else
	g_propagate_error (error, my_error);
    }

  return info;
}

static gboolean
441
g_local_file_enumerator_close (GFileEnumerator  *enumerator,
442 443 444 445 446 447 448
			       GCancellable     *cancellable,
			       GError          **error)
{
  GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);

  if (local->dir)
    {
449
#ifdef USE_GDIR
450
      g_dir_close (local->dir);
451 452 453
#else
      closedir (local->dir);
#endif
454 455 456 457 458
      local->dir = NULL;
    }

  return TRUE;
}