165 lines
4.4 KiB
C
165 lines
4.4 KiB
C
/* Map an ino_t inode number to a small integer.
|
|
|
|
Copyright 2009-2022 Free Software Foundation, Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
/* written by Paul Eggert and Jim Meyering */
|
|
|
|
#include <config.h>
|
|
#include "ino-map.h"
|
|
|
|
#include "hash.h"
|
|
#include "verify.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
|
|
/* A pair that maps an inode number to a mapped inode number; the
|
|
latter is a small unique ID for the former. */
|
|
struct ino_map_ent
|
|
{
|
|
ino_t ino;
|
|
size_t mapped_ino;
|
|
};
|
|
|
|
/* A table that manages and indexes these pairs. */
|
|
struct ino_map
|
|
{
|
|
/* A table of KEY,VAL pairs, where KEY is the raw ino_t value and
|
|
VAL is the small number that it maps to. */
|
|
struct hash_table *map;
|
|
|
|
/* The next mapped inode number to hand out. */
|
|
size_t next_mapped_ino;
|
|
|
|
/* Cache of the most recently allocated and otherwise-unused storage
|
|
for probing the table. */
|
|
struct ino_map_ent *probe;
|
|
};
|
|
|
|
/* Hash an inode map entry. */
|
|
static size_t
|
|
ino_hash (void const *x, size_t table_size)
|
|
{
|
|
struct ino_map_ent const *p = x;
|
|
ino_t ino = p->ino;
|
|
|
|
/* When INO is wider than size_t, exclusive-OR the words of INO into H.
|
|
This avoids loss of info, without applying % to the wider type,
|
|
which could be quite slow on some systems. */
|
|
size_t h = ino;
|
|
unsigned int i;
|
|
unsigned int n_words = sizeof ino / sizeof h + (sizeof ino % sizeof h != 0);
|
|
for (i = 1; i < n_words; i++)
|
|
h ^= ino >> CHAR_BIT * sizeof h * i;
|
|
|
|
return h % table_size;
|
|
}
|
|
|
|
/* Return true if two inode map entries are the same. */
|
|
static bool
|
|
ino_compare (void const *x, void const *y)
|
|
{
|
|
struct ino_map_ent const *a = x;
|
|
struct ino_map_ent const *b = y;
|
|
return a->ino == b->ino;
|
|
}
|
|
|
|
/* Allocate an inode map that will hand out integers starting with
|
|
NEXT_MAPPED_INO. Return NULL if memory is exhausted. */
|
|
struct ino_map *
|
|
ino_map_alloc (size_t next_mapped_ino)
|
|
{
|
|
struct ino_map *im = malloc (sizeof *im);
|
|
|
|
if (im)
|
|
{
|
|
enum { INITIAL_INO_MAP_TABLE_SIZE = 1021 };
|
|
im->map = hash_initialize (INITIAL_INO_MAP_TABLE_SIZE, NULL,
|
|
ino_hash, ino_compare, free);
|
|
if (! im->map)
|
|
{
|
|
free (im);
|
|
return NULL;
|
|
}
|
|
im->next_mapped_ino = next_mapped_ino;
|
|
im->probe = NULL;
|
|
}
|
|
|
|
return im;
|
|
}
|
|
|
|
/* Free an inode map. */
|
|
void
|
|
ino_map_free (struct ino_map *map)
|
|
{
|
|
hash_free (map->map);
|
|
free (map->probe);
|
|
free (map);
|
|
}
|
|
|
|
|
|
/* Insert into MAP the inode number INO if it's not there already,
|
|
and return its nonnegative mapped inode number.
|
|
If INO is already in MAP, return the existing mapped inode number.
|
|
Return INO_MAP_INSERT_FAILURE on memory or counter exhaustion. */
|
|
size_t
|
|
ino_map_insert (struct ino_map *im, ino_t ino)
|
|
{
|
|
struct ino_map_ent *ent;
|
|
|
|
/* Find space for the probe, reusing the cache if available. */
|
|
struct ino_map_ent *probe = im->probe;
|
|
if (probe)
|
|
{
|
|
/* If repeating a recent query, return the cached result. */
|
|
if (probe->ino == ino)
|
|
return probe->mapped_ino;
|
|
}
|
|
else
|
|
{
|
|
im->probe = probe = malloc (sizeof *probe);
|
|
if (! probe)
|
|
return INO_MAP_INSERT_FAILURE;
|
|
}
|
|
|
|
probe->ino = ino;
|
|
ent = hash_insert (im->map, probe);
|
|
if (! ent)
|
|
return INO_MAP_INSERT_FAILURE;
|
|
|
|
if (ent != probe)
|
|
{
|
|
/* Use the existing entry. */
|
|
probe->mapped_ino = ent->mapped_ino;
|
|
}
|
|
else
|
|
{
|
|
/* If adding 1 to map->next_mapped_ino would cause it to
|
|
overflow to zero, then it must equal INO_MAP_INSERT_FAILURE,
|
|
which is the value that should be returned in that case.
|
|
Verify that this works. */
|
|
verify (INO_MAP_INSERT_FAILURE + 1 == 0);
|
|
|
|
/* Prepare to allocate a new probe next time; this one is in use. */
|
|
im->probe = NULL;
|
|
|
|
/* INO is new; allocate a mapped inode number for it. */
|
|
probe->mapped_ino = im->next_mapped_ino++;
|
|
}
|
|
|
|
return probe->mapped_ino;
|
|
}
|