LCOV - code coverage report
Current view: top level - glib/gio - glocalfileenumerator.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 124 132 93.9 %
Date: 2024-04-23 05:16:05 Functions: 14 14 100.0 %
Branches: 54 65 83.1 %

           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                 :        198 : sort_by_inode (const void *_a, const void *_b)
     273                 :            : {
     274                 :            :   const DirEntry *a, *b;
     275                 :            : 
     276                 :        198 :   a = _a;
     277                 :        198 :   b = _b;
     278                 :        198 :   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 1.14