276 lines
6.6 KiB
C
276 lines
6.6 KiB
C
/*
|
|
* cache.c - allocation/initialization/free routines for cache
|
|
*
|
|
* Copyright (C) 2001 Andreas Dilger
|
|
* Copyright (C) 2003 Theodore Ts'o
|
|
*
|
|
* %Begin-Header%
|
|
* This file may be redistributed under the terms of the
|
|
* GNU Lesser General Public License.
|
|
* %End-Header%
|
|
*/
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#include "blkidP.h"
|
|
#include "env.h"
|
|
|
|
int blkid_debug_mask = 0;
|
|
|
|
/**
|
|
* SECTION:cache
|
|
* @title: Cache
|
|
* @short_description: basic routines to work with libblkid cache
|
|
*
|
|
* Block device information is normally kept in a cache file blkid.tab and is
|
|
* verified to still be valid before being returned to the user (if the user has
|
|
* read permission on the raw block device, otherwise not). The cache file also
|
|
* allows unprivileged users (normally anyone other than root, or those not in the
|
|
* "disk" group) to locate devices by label/id. The standard location of the
|
|
* cache file can be overridden by the environment variable BLKID_FILE.
|
|
*
|
|
* In situations where one is getting information about a single known device, it
|
|
* does not impact performance whether the cache is used or not (unless you are
|
|
* not able to read the block device directly). If you are dealing with multiple
|
|
* devices, use of the cache is highly recommended (even if empty) as devices will
|
|
* be scanned at most one time and the on-disk cache will be updated if possible.
|
|
* There is rarely a reason not to use the cache.
|
|
*
|
|
* In some cases (modular kernels), block devices are not even visible until after
|
|
* they are accessed the first time, so it is critical that there is some way to
|
|
* locate these devices without enumerating only visible devices, so the use of
|
|
* the cache file is required in this situation.
|
|
*/
|
|
|
|
#if 0 /* ifdef CONFIG_BLKID_DEBUG */
|
|
static blkid_debug_dump_cache(int mask, blkid_cache cache)
|
|
{
|
|
struct list_head *p;
|
|
|
|
if (!cache) {
|
|
printf("cache: NULL\n");
|
|
return;
|
|
}
|
|
|
|
printf("cache: time = %lu\n", cache->bic_time);
|
|
printf("cache: flags = 0x%08X\n", cache->bic_flags);
|
|
|
|
list_for_each(p, &cache->bic_devs) {
|
|
blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
|
|
blkid_debug_dump_dev(dev);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_BLKID_DEBUG
|
|
void blkid_init_debug(int mask)
|
|
{
|
|
if (blkid_debug_mask & DEBUG_INIT)
|
|
return;
|
|
|
|
if (!mask)
|
|
{
|
|
char *dstr = getenv("LIBBLKID_DEBUG");
|
|
|
|
if (!dstr)
|
|
dstr = getenv("BLKID_DEBUG"); /* for backward compatibility */
|
|
if (dstr)
|
|
blkid_debug_mask = strtoul(dstr, 0, 0);
|
|
} else
|
|
blkid_debug_mask = mask;
|
|
|
|
if (blkid_debug_mask)
|
|
printf("libblkid: debug mask set to 0x%04x.\n", blkid_debug_mask);
|
|
|
|
blkid_debug_mask |= DEBUG_INIT;
|
|
}
|
|
#endif
|
|
|
|
static const char *get_default_cache_filename(void)
|
|
{
|
|
struct stat st;
|
|
|
|
if (stat(BLKID_RUNTIME_TOPDIR, &st) == 0 && S_ISDIR(st.st_mode))
|
|
return BLKID_CACHE_FILE; /* cache in /run */
|
|
|
|
return BLKID_CACHE_FILE_OLD; /* cache in /etc */
|
|
}
|
|
|
|
/* returns allocated path to cache */
|
|
char *blkid_get_cache_filename(struct blkid_config *conf)
|
|
{
|
|
char *filename;
|
|
|
|
filename = safe_getenv("BLKID_FILE");
|
|
if (filename)
|
|
filename = strdup(filename);
|
|
else if (conf)
|
|
filename = conf->cachefile ? strdup(conf->cachefile) : NULL;
|
|
else {
|
|
struct blkid_config *c = blkid_read_config(NULL);
|
|
if (!c)
|
|
filename = strdup(get_default_cache_filename());
|
|
else {
|
|
filename = c->cachefile; /* already allocated */
|
|
c->cachefile = NULL;
|
|
blkid_free_config(c);
|
|
}
|
|
}
|
|
return filename;
|
|
}
|
|
|
|
/**
|
|
* blkid_get_cache:
|
|
* @cache: pointer to return cache handler
|
|
* @filename: path to the cache file or NULL for the default path
|
|
*
|
|
* Allocates and initialize librray cache handler.
|
|
*
|
|
* Returns: 0 on success or number less than zero in case of error.
|
|
*/
|
|
int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
|
|
{
|
|
blkid_cache cache;
|
|
|
|
if (!ret_cache)
|
|
return -BLKID_ERR_PARAM;
|
|
|
|
blkid_init_debug(0);
|
|
|
|
DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
|
|
filename ? filename : "default cache"));
|
|
|
|
if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
|
|
return -BLKID_ERR_MEM;
|
|
|
|
INIT_LIST_HEAD(&cache->bic_devs);
|
|
INIT_LIST_HEAD(&cache->bic_tags);
|
|
|
|
if (filename && !*filename)
|
|
filename = NULL;
|
|
if (filename)
|
|
cache->bic_filename = strdup(filename);
|
|
else
|
|
cache->bic_filename = blkid_get_cache_filename(NULL);
|
|
|
|
blkid_read_cache(cache);
|
|
*ret_cache = cache;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* blkid_put_cache:
|
|
* @cache: cache handler
|
|
*
|
|
* Saves changes to cache file.
|
|
*/
|
|
void blkid_put_cache(blkid_cache cache)
|
|
{
|
|
if (!cache)
|
|
return;
|
|
|
|
(void) blkid_flush_cache(cache);
|
|
|
|
DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
|
|
|
|
/* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
|
|
|
|
while (!list_empty(&cache->bic_devs)) {
|
|
blkid_dev dev = list_entry(cache->bic_devs.next,
|
|
struct blkid_struct_dev,
|
|
bid_devs);
|
|
blkid_free_dev(dev);
|
|
}
|
|
|
|
while (!list_empty(&cache->bic_tags)) {
|
|
blkid_tag tag = list_entry(cache->bic_tags.next,
|
|
struct blkid_struct_tag,
|
|
bit_tags);
|
|
|
|
while (!list_empty(&tag->bit_names)) {
|
|
blkid_tag bad = list_entry(tag->bit_names.next,
|
|
struct blkid_struct_tag,
|
|
bit_names);
|
|
|
|
DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
|
|
bad->bit_name, bad->bit_val));
|
|
blkid_free_tag(bad);
|
|
}
|
|
blkid_free_tag(tag);
|
|
}
|
|
|
|
blkid_free_probe(cache->probe);
|
|
|
|
free(cache->bic_filename);
|
|
free(cache);
|
|
}
|
|
|
|
/**
|
|
* blkid_gc_cache:
|
|
* @cache: cache handler
|
|
*
|
|
* Removes garbage (non-existing devices) from the cache.
|
|
*/
|
|
void blkid_gc_cache(blkid_cache cache)
|
|
{
|
|
struct list_head *p, *pnext;
|
|
struct stat st;
|
|
|
|
if (!cache)
|
|
return;
|
|
|
|
list_for_each_safe(p, pnext, &cache->bic_devs) {
|
|
blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
|
|
if (stat(dev->bid_name, &st) < 0) {
|
|
DBG(DEBUG_CACHE,
|
|
printf("freeing %s\n", dev->bid_name));
|
|
blkid_free_dev(dev);
|
|
cache->bic_flags |= BLKID_BIC_FL_CHANGED;
|
|
} else {
|
|
DBG(DEBUG_CACHE,
|
|
printf("Device %s exists\n", dev->bid_name));
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TEST_PROGRAM
|
|
int main(int argc, char** argv)
|
|
{
|
|
blkid_cache cache = NULL;
|
|
int ret;
|
|
|
|
blkid_init_debug(DEBUG_ALL);
|
|
|
|
if ((argc > 2)) {
|
|
fprintf(stderr, "Usage: %s [filename] \n", argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
|
|
fprintf(stderr, "error %d parsing cache file %s\n", ret,
|
|
argv[1] ? argv[1] : blkid_get_cache_filename(NULL));
|
|
exit(1);
|
|
}
|
|
if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
|
|
fprintf(stderr, "%s: error creating cache (%d)\n",
|
|
argv[0], ret);
|
|
exit(1);
|
|
}
|
|
if ((ret = blkid_probe_all(cache)) < 0)
|
|
fprintf(stderr, "error probing devices\n");
|
|
|
|
blkid_put_cache(cache);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|