Blame lib/fts-cycle.c

Packit 709fb3
/* Detect cycles in file tree walks.
Packit 709fb3
Packit 709fb3
   Copyright (C) 2003-2006, 2009-2017 Free Software Foundation, Inc.
Packit 709fb3
Packit 709fb3
   Written by Jim Meyering.
Packit 709fb3
Packit 709fb3
   This program is free software: you can redistribute it and/or modify
Packit 709fb3
   it under the terms of the GNU General Public License as published by
Packit 709fb3
   the Free Software Foundation; either version 3 of the License, or
Packit 709fb3
   (at your option) any later version.
Packit 709fb3
Packit 709fb3
   This program is distributed in the hope that it will be useful,
Packit 709fb3
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 709fb3
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 709fb3
   GNU General Public License for more details.
Packit 709fb3
Packit 709fb3
   You should have received a copy of the GNU General Public License
Packit 709fb3
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 709fb3
Packit 709fb3
#include "cycle-check.h"
Packit 709fb3
#include "hash.h"
Packit 709fb3
Packit 709fb3
/* Use each of these to map a device/inode pair to an FTSENT.  */
Packit 709fb3
struct Active_dir
Packit 709fb3
{
Packit 709fb3
  dev_t dev;
Packit 709fb3
  ino_t ino;
Packit 709fb3
  FTSENT *fts_ent;
Packit 709fb3
};
Packit 709fb3
Packit 709fb3
static bool
Packit 709fb3
AD_compare (void const *x, void const *y)
Packit 709fb3
{
Packit 709fb3
  struct Active_dir const *ax = x;
Packit 709fb3
  struct Active_dir const *ay = y;
Packit 709fb3
  return ax->ino == ay->ino
Packit 709fb3
      && ax->dev == ay->dev;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static size_t
Packit 709fb3
AD_hash (void const *x, size_t table_size)
Packit 709fb3
{
Packit 709fb3
  struct Active_dir const *ax = x;
Packit 709fb3
  return (uintmax_t) ax->ino % table_size;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Set up the cycle-detection machinery.  */
Packit 709fb3
Packit 709fb3
static bool
Packit 709fb3
setup_dir (FTS *fts)
Packit 709fb3
{
Packit 709fb3
  if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
Packit 709fb3
    {
Packit 709fb3
      enum { HT_INITIAL_SIZE = 31 };
Packit 709fb3
      fts->fts_cycle.ht = hash_initialize (HT_INITIAL_SIZE, NULL, AD_hash,
Packit 709fb3
                                           AD_compare, free);
Packit 709fb3
      if (! fts->fts_cycle.ht)
Packit 709fb3
        return false;
Packit 709fb3
    }
Packit 709fb3
  else
Packit 709fb3
    {
Packit 709fb3
      fts->fts_cycle.state = malloc (sizeof *fts->fts_cycle.state);
Packit 709fb3
      if (! fts->fts_cycle.state)
Packit 709fb3
        return false;
Packit 709fb3
      cycle_check_init (fts->fts_cycle.state);
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  return true;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Enter a directory during a file tree walk.  */
Packit 709fb3
Packit 709fb3
static bool
Packit 709fb3
enter_dir (FTS *fts, FTSENT *ent)
Packit 709fb3
{
Packit 709fb3
  if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
Packit 709fb3
    {
Packit 709fb3
      struct stat const *st = ent->fts_statp;
Packit 709fb3
      struct Active_dir *ad = malloc (sizeof *ad);
Packit 709fb3
      struct Active_dir *ad_from_table;
Packit 709fb3
Packit 709fb3
      if (!ad)
Packit 709fb3
        return false;
Packit 709fb3
Packit 709fb3
      ad->dev = st->st_dev;
Packit 709fb3
      ad->ino = st->st_ino;
Packit 709fb3
      ad->fts_ent = ent;
Packit 709fb3
Packit 709fb3
      /* See if we've already encountered this directory.
Packit 709fb3
         This can happen when following symlinks as well as
Packit 709fb3
         with a corrupted directory hierarchy. */
Packit 709fb3
      ad_from_table = hash_insert (fts->fts_cycle.ht, ad);
Packit 709fb3
Packit 709fb3
      if (ad_from_table != ad)
Packit 709fb3
        {
Packit 709fb3
          free (ad);
Packit 709fb3
          if (!ad_from_table)
Packit 709fb3
            return false;
Packit 709fb3
Packit 709fb3
          /* There was an entry with matching dev/inode already in the table.
Packit 709fb3
             Record the fact that we've found a cycle.  */
Packit 709fb3
          ent->fts_cycle = ad_from_table->fts_ent;
Packit 709fb3
          ent->fts_info = FTS_DC;
Packit 709fb3
        }
Packit 709fb3
    }
Packit 709fb3
  else
Packit 709fb3
    {
Packit 709fb3
      if (cycle_check (fts->fts_cycle.state, ent->fts_statp))
Packit 709fb3
        {
Packit 709fb3
          /* FIXME: setting fts_cycle like this isn't proper.
Packit 709fb3
             To do what the documentation requires, we'd have to
Packit 709fb3
             go around the cycle again and find the right entry.
Packit 709fb3
             But no callers in coreutils use the fts_cycle member. */
Packit 709fb3
          ent->fts_cycle = ent;
Packit 709fb3
          ent->fts_info = FTS_DC;
Packit 709fb3
        }
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  return true;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Leave a directory during a file tree walk.  */
Packit 709fb3
Packit 709fb3
static void
Packit 709fb3
leave_dir (FTS *fts, FTSENT *ent)
Packit 709fb3
{
Packit 709fb3
  struct stat const *st = ent->fts_statp;
Packit 709fb3
  if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
Packit 709fb3
    {
Packit 709fb3
      struct Active_dir obj;
Packit 709fb3
      void *found;
Packit 709fb3
      obj.dev = st->st_dev;
Packit 709fb3
      obj.ino = st->st_ino;
Packit 709fb3
      found = hash_delete (fts->fts_cycle.ht, &obj);
Packit 709fb3
      if (!found)
Packit 709fb3
        abort ();
Packit 709fb3
      free (found);
Packit 709fb3
    }
Packit 709fb3
  else
Packit 709fb3
    {
Packit 709fb3
      FTSENT *parent = ent->fts_parent;
Packit 709fb3
      if (parent != NULL && 0 <= parent->fts_level)
Packit 709fb3
        CYCLE_CHECK_REFLECT_CHDIR_UP (fts->fts_cycle.state,
Packit 709fb3
                                      *(parent->fts_statp), *st);
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Free any memory used for cycle detection.  */
Packit 709fb3
Packit 709fb3
static void
Packit 709fb3
free_dir (FTS *sp)
Packit 709fb3
{
Packit 709fb3
  if (sp->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
Packit 709fb3
    {
Packit 709fb3
      if (sp->fts_cycle.ht)
Packit 709fb3
        hash_free (sp->fts_cycle.ht);
Packit 709fb3
    }
Packit 709fb3
  else
Packit 709fb3
    free (sp->fts_cycle.state);
Packit 709fb3
}