Blame lib/cycle-check.c

Packit 709fb3
/* help detect directory cycles efficiently
Packit 709fb3
Packit 709fb3
   Copyright (C) 2003-2006, 2009-2017 Free Software Foundation, Inc.
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
/* Written by Jim Meyering */
Packit 709fb3
Packit 709fb3
#include <config.h>
Packit 709fb3
Packit 709fb3
#include "cycle-check.h"
Packit 709fb3
Packit 709fb3
#include <sys/types.h>
Packit 709fb3
#include <sys/stat.h>
Packit 709fb3
#include <stdio.h>
Packit 709fb3
#include <stdlib.h>
Packit 709fb3
#include <stdbool.h>
Packit 709fb3
Packit 709fb3
#include "assure.h"
Packit 709fb3
Packit 709fb3
#define CC_MAGIC 9827862
Packit 709fb3
Packit 709fb3
/* Return true if I is a power of 2, or is zero.  */
Packit 709fb3
Packit 709fb3
static bool
Packit 709fb3
is_zero_or_power_of_two (uintmax_t i)
Packit 709fb3
{
Packit 709fb3
  return (i & (i - 1)) == 0;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
void
Packit 709fb3
cycle_check_init (struct cycle_check_state *state)
Packit 709fb3
{
Packit 709fb3
  state->chdir_counter = 0;
Packit 709fb3
  state->magic = CC_MAGIC;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* In traversing a directory hierarchy, call this function once for each
Packit 709fb3
   descending chdir call, with SB corresponding to the chdir operand.
Packit 709fb3
   If SB corresponds to a directory that has already been seen,
Packit 709fb3
   return true to indicate that there is a directory cycle.
Packit 709fb3
   Note that this is done "lazily", which means that some of
Packit 709fb3
   the directories in the cycle may be processed twice before
Packit 709fb3
   the cycle is detected.  */
Packit 709fb3
Packit 709fb3
bool
Packit 709fb3
cycle_check (struct cycle_check_state *state, struct stat const *sb)
Packit 709fb3
{
Packit 709fb3
  assure (state->magic == CC_MAGIC);
Packit 709fb3
Packit 709fb3
  /* If the current directory ever happens to be the same
Packit 709fb3
     as the one we last recorded for the cycle detection,
Packit 709fb3
     then it's obviously part of a cycle.  */
Packit 709fb3
  if (state->chdir_counter && SAME_INODE (*sb, state->dev_ino))
Packit 709fb3
    return true;
Packit 709fb3
Packit 709fb3
  /* If the number of "descending" chdir calls is a power of two,
Packit 709fb3
     record the dev/ino of the current directory.  */
Packit 709fb3
  if (is_zero_or_power_of_two (++(state->chdir_counter)))
Packit 709fb3
    {
Packit 709fb3
      /* On all architectures that we know about, if the counter
Packit 709fb3
         overflows then there is a directory cycle here somewhere,
Packit 709fb3
         even if we haven't detected it yet.  Typically this happens
Packit 709fb3
         only after the counter is incremented 2**64 times, so it's a
Packit 709fb3
         fairly theoretical point.  */
Packit 709fb3
      if (state->chdir_counter == 0)
Packit 709fb3
        return true;
Packit 709fb3
Packit 709fb3
      state->dev_ino.st_dev = sb->st_dev;
Packit 709fb3
      state->dev_ino.st_ino = sb->st_ino;
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  return false;
Packit 709fb3
}