LCOV - code coverage report
Current view: top level - gio - glocalfileenumerator.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 93.9 % 132 124
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 14 14
Branches: - 0 0

             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                 :             : }
        

Generated by: LCOV version 2.0-1