xref: /dflybsd-src/contrib/grep/lib/fts-cycle.c (revision 91b9ed38d3db6a8a8ac5b66da1d43e6e331e259a)
1cf28ed85SJohn Marino /* Detect cycles in file tree walks.
2cf28ed85SJohn Marino 
3*09d4459fSDaniel Fojt    Copyright (C) 2003-2006, 2009-2020 Free Software Foundation, Inc.
4cf28ed85SJohn Marino 
5cf28ed85SJohn Marino    Written by Jim Meyering.
6cf28ed85SJohn Marino 
7cf28ed85SJohn Marino    This program is free software: you can redistribute it and/or modify
8cf28ed85SJohn Marino    it under the terms of the GNU General Public License as published by
9cf28ed85SJohn Marino    the Free Software Foundation; either version 3 of the License, or
10cf28ed85SJohn Marino    (at your option) any later version.
11cf28ed85SJohn Marino 
12cf28ed85SJohn Marino    This program is distributed in the hope that it will be useful,
13cf28ed85SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
14cf28ed85SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15cf28ed85SJohn Marino    GNU General Public License for more details.
16cf28ed85SJohn Marino 
17cf28ed85SJohn Marino    You should have received a copy of the GNU General Public License
18*09d4459fSDaniel Fojt    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
19cf28ed85SJohn Marino 
20cf28ed85SJohn Marino #include "cycle-check.h"
21cf28ed85SJohn Marino #include "hash.h"
22cf28ed85SJohn Marino 
23cf28ed85SJohn Marino /* Use each of these to map a device/inode pair to an FTSENT.  */
24cf28ed85SJohn Marino struct Active_dir
25cf28ed85SJohn Marino {
26cf28ed85SJohn Marino   dev_t dev;
27cf28ed85SJohn Marino   ino_t ino;
28cf28ed85SJohn Marino   FTSENT *fts_ent;
29cf28ed85SJohn Marino };
30cf28ed85SJohn Marino 
31cf28ed85SJohn Marino static bool
AD_compare(void const * x,void const * y)32cf28ed85SJohn Marino AD_compare (void const *x, void const *y)
33cf28ed85SJohn Marino {
34cf28ed85SJohn Marino   struct Active_dir const *ax = x;
35cf28ed85SJohn Marino   struct Active_dir const *ay = y;
36cf28ed85SJohn Marino   return ax->ino == ay->ino
37cf28ed85SJohn Marino       && ax->dev == ay->dev;
38cf28ed85SJohn Marino }
39cf28ed85SJohn Marino 
40cf28ed85SJohn Marino static size_t
AD_hash(void const * x,size_t table_size)41cf28ed85SJohn Marino AD_hash (void const *x, size_t table_size)
42cf28ed85SJohn Marino {
43cf28ed85SJohn Marino   struct Active_dir const *ax = x;
44cf28ed85SJohn Marino   return (uintmax_t) ax->ino % table_size;
45cf28ed85SJohn Marino }
46cf28ed85SJohn Marino 
47cf28ed85SJohn Marino /* Set up the cycle-detection machinery.  */
48cf28ed85SJohn Marino 
49cf28ed85SJohn Marino static bool
setup_dir(FTS * fts)50cf28ed85SJohn Marino setup_dir (FTS *fts)
51cf28ed85SJohn Marino {
52cf28ed85SJohn Marino   if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
53cf28ed85SJohn Marino     {
54cf28ed85SJohn Marino       enum { HT_INITIAL_SIZE = 31 };
55cf28ed85SJohn Marino       fts->fts_cycle.ht = hash_initialize (HT_INITIAL_SIZE, NULL, AD_hash,
56cf28ed85SJohn Marino                                            AD_compare, free);
57cf28ed85SJohn Marino       if (! fts->fts_cycle.ht)
58cf28ed85SJohn Marino         return false;
59cf28ed85SJohn Marino     }
60cf28ed85SJohn Marino   else
61cf28ed85SJohn Marino     {
62cf28ed85SJohn Marino       fts->fts_cycle.state = malloc (sizeof *fts->fts_cycle.state);
63cf28ed85SJohn Marino       if (! fts->fts_cycle.state)
64cf28ed85SJohn Marino         return false;
65cf28ed85SJohn Marino       cycle_check_init (fts->fts_cycle.state);
66cf28ed85SJohn Marino     }
67cf28ed85SJohn Marino 
68cf28ed85SJohn Marino   return true;
69cf28ed85SJohn Marino }
70cf28ed85SJohn Marino 
71cf28ed85SJohn Marino /* Enter a directory during a file tree walk.  */
72cf28ed85SJohn Marino 
73cf28ed85SJohn Marino static bool
enter_dir(FTS * fts,FTSENT * ent)74cf28ed85SJohn Marino enter_dir (FTS *fts, FTSENT *ent)
75cf28ed85SJohn Marino {
76cf28ed85SJohn Marino   if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
77cf28ed85SJohn Marino     {
78cf28ed85SJohn Marino       struct stat const *st = ent->fts_statp;
79cf28ed85SJohn Marino       struct Active_dir *ad = malloc (sizeof *ad);
80cf28ed85SJohn Marino       struct Active_dir *ad_from_table;
81cf28ed85SJohn Marino 
82cf28ed85SJohn Marino       if (!ad)
83cf28ed85SJohn Marino         return false;
84cf28ed85SJohn Marino 
85cf28ed85SJohn Marino       ad->dev = st->st_dev;
86cf28ed85SJohn Marino       ad->ino = st->st_ino;
87cf28ed85SJohn Marino       ad->fts_ent = ent;
88cf28ed85SJohn Marino 
89cf28ed85SJohn Marino       /* See if we've already encountered this directory.
90cf28ed85SJohn Marino          This can happen when following symlinks as well as
91cf28ed85SJohn Marino          with a corrupted directory hierarchy. */
92cf28ed85SJohn Marino       ad_from_table = hash_insert (fts->fts_cycle.ht, ad);
93cf28ed85SJohn Marino 
94cf28ed85SJohn Marino       if (ad_from_table != ad)
95cf28ed85SJohn Marino         {
96cf28ed85SJohn Marino           free (ad);
97cf28ed85SJohn Marino           if (!ad_from_table)
98cf28ed85SJohn Marino             return false;
99cf28ed85SJohn Marino 
100cf28ed85SJohn Marino           /* There was an entry with matching dev/inode already in the table.
101cf28ed85SJohn Marino              Record the fact that we've found a cycle.  */
102cf28ed85SJohn Marino           ent->fts_cycle = ad_from_table->fts_ent;
103cf28ed85SJohn Marino           ent->fts_info = FTS_DC;
104cf28ed85SJohn Marino         }
105cf28ed85SJohn Marino     }
106cf28ed85SJohn Marino   else
107cf28ed85SJohn Marino     {
108cf28ed85SJohn Marino       if (cycle_check (fts->fts_cycle.state, ent->fts_statp))
109cf28ed85SJohn Marino         {
110cf28ed85SJohn Marino           /* FIXME: setting fts_cycle like this isn't proper.
111cf28ed85SJohn Marino              To do what the documentation requires, we'd have to
112cf28ed85SJohn Marino              go around the cycle again and find the right entry.
113cf28ed85SJohn Marino              But no callers in coreutils use the fts_cycle member. */
114cf28ed85SJohn Marino           ent->fts_cycle = ent;
115cf28ed85SJohn Marino           ent->fts_info = FTS_DC;
116cf28ed85SJohn Marino         }
117cf28ed85SJohn Marino     }
118cf28ed85SJohn Marino 
119cf28ed85SJohn Marino   return true;
120cf28ed85SJohn Marino }
121cf28ed85SJohn Marino 
122cf28ed85SJohn Marino /* Leave a directory during a file tree walk.  */
123cf28ed85SJohn Marino 
124cf28ed85SJohn Marino static void
leave_dir(FTS * fts,FTSENT * ent)125cf28ed85SJohn Marino leave_dir (FTS *fts, FTSENT *ent)
126cf28ed85SJohn Marino {
127cf28ed85SJohn Marino   struct stat const *st = ent->fts_statp;
128cf28ed85SJohn Marino   if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
129cf28ed85SJohn Marino     {
130cf28ed85SJohn Marino       struct Active_dir obj;
131cf28ed85SJohn Marino       void *found;
132cf28ed85SJohn Marino       obj.dev = st->st_dev;
133cf28ed85SJohn Marino       obj.ino = st->st_ino;
134cf28ed85SJohn Marino       found = hash_delete (fts->fts_cycle.ht, &obj);
135cf28ed85SJohn Marino       if (!found)
136cf28ed85SJohn Marino         abort ();
137cf28ed85SJohn Marino       free (found);
138cf28ed85SJohn Marino     }
139cf28ed85SJohn Marino   else
140cf28ed85SJohn Marino     {
141cf28ed85SJohn Marino       FTSENT *parent = ent->fts_parent;
142cf28ed85SJohn Marino       if (parent != NULL && 0 <= parent->fts_level)
143cf28ed85SJohn Marino         CYCLE_CHECK_REFLECT_CHDIR_UP (fts->fts_cycle.state,
144cf28ed85SJohn Marino                                       *(parent->fts_statp), *st);
145cf28ed85SJohn Marino     }
146cf28ed85SJohn Marino }
147cf28ed85SJohn Marino 
148cf28ed85SJohn Marino /* Free any memory used for cycle detection.  */
149cf28ed85SJohn Marino 
150cf28ed85SJohn Marino static void
free_dir(FTS * sp)151cf28ed85SJohn Marino free_dir (FTS *sp)
152cf28ed85SJohn Marino {
153cf28ed85SJohn Marino   if (sp->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
154cf28ed85SJohn Marino     {
155cf28ed85SJohn Marino       if (sp->fts_cycle.ht)
156cf28ed85SJohn Marino         hash_free (sp->fts_cycle.ht);
157cf28ed85SJohn Marino     }
158cf28ed85SJohn Marino   else
159cf28ed85SJohn Marino     free (sp->fts_cycle.state);
160cf28ed85SJohn Marino }
161