Coverage Report

Created: 2026-04-28 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/cache.c
Line
Count
Source
1
/*
2
 * cache.c - allocation/initialization/free routines for cache
3
 *
4
 * Copyright (C) 2001 Andreas Dilger
5
 * Copyright (C) 2003 Theodore Ts'o
6
 *
7
 * %Begin-Header%
8
 * This file may be redistributed under the terms of the
9
 * GNU Lesser General Public License.
10
 * %End-Header%
11
 */
12
13
#ifdef HAVE_UNISTD_H
14
#include <unistd.h>
15
#endif
16
#ifdef HAVE_ERRNO_H
17
#include <errno.h>
18
#endif
19
#include <stdlib.h>
20
#include <string.h>
21
#ifdef HAVE_SYS_STAT_H
22
#include <sys/stat.h>
23
#endif
24
#include "blkidP.h"
25
#include "env.h"
26
#include "loopdev.h"
27
28
/**
29
 * SECTION:cache
30
 * @title: Cache
31
 * @short_description: basic routines to work with libblkid cache
32
 *
33
 * Block device information is normally kept in a cache file blkid.tab and is
34
 * verified to still be valid before being returned to the user (if the user has
35
 * read permission on the raw block device, otherwise not).  The cache file also
36
 * allows unprivileged users (normally anyone other than root, or those not in the
37
 * "disk" group) to locate devices by label/id.  The standard location of the
38
 * cache file can be overridden by the environment variable BLKID_FILE.
39
 *
40
 * In situations where one is getting information about a single known device, it
41
 * does not impact performance whether the cache is used or not (unless you are
42
 * not able to read the block device directly).  If you are dealing with multiple
43
 * devices, use of the cache is highly recommended (even if empty) as devices will
44
 * be scanned at most one time and the on-disk cache will be updated if possible.
45
 * There is rarely a reason not to use the cache.
46
 *
47
 * In some cases (modular kernels), block devices are not even visible until after
48
 * they are accessed the first time, so it is critical that there is some way to
49
 * locate these devices without enumerating only visible devices, so the use of
50
 * the cache file is required in this situation.
51
 */
52
static const char *get_default_cache_filename(void)
53
0
{
54
0
  struct stat st;
55
56
0
  if (stat(BLKID_RUNTIME_TOPDIR, &st) == 0 && S_ISDIR(st.st_mode))
57
0
    return BLKID_CACHE_FILE; /* cache in /run */
58
59
0
  return BLKID_CACHE_FILE_OLD; /* cache in /etc */
60
0
}
61
62
/* returns allocated path to cache */
63
char *blkid_get_cache_filename(struct blkid_config *conf)
64
0
{
65
0
  char *filename;
66
67
0
  filename = safe_getenv("BLKID_FILE");
68
0
  if (filename)
69
0
    filename = strdup(filename);
70
0
  else if (conf)
71
0
    filename = conf->cachefile ? strdup(conf->cachefile) : NULL;
72
0
  else {
73
0
    struct blkid_config *c = blkid_read_config(NULL);
74
0
    if (!c)
75
0
      filename = strdup(get_default_cache_filename());
76
0
    else {
77
0
      filename = c->cachefile;  /* already allocated */
78
0
      c->cachefile = NULL;
79
0
      blkid_free_config(c);
80
0
    }
81
0
  }
82
0
  return filename;
83
0
}
84
85
/**
86
 * blkid_get_cache:
87
 * @cache: pointer to return cache handler
88
 * @filename: path to the cache file or NULL for the default path
89
 *
90
 * Allocates and initializes library cache handler.
91
 *
92
 * Returns: 0 on success or number less than zero in case of error.
93
 */
94
int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
95
0
{
96
0
  blkid_cache cache;
97
98
0
  if (!ret_cache)
99
0
    return -BLKID_ERR_PARAM;
100
101
0
  if (!(cache = calloc(1, sizeof(struct blkid_struct_cache))))
102
0
    return -BLKID_ERR_MEM;
103
104
0
  DBG_OBJ(CACHE, cache, ul_debug("alloc (from %s)", filename ? filename : "default cache"));
105
0
  INIT_LIST_HEAD(&cache->bic_devs);
106
0
  INIT_LIST_HEAD(&cache->bic_tags);
107
108
0
  if (filename && !*filename)
109
0
    filename = NULL;
110
0
  if (filename)
111
0
    cache->bic_filename = strdup(filename);
112
0
  else
113
0
    cache->bic_filename = blkid_get_cache_filename(NULL);
114
115
0
  blkid_read_cache(cache);
116
0
  *ret_cache = cache;
117
0
  return 0;
118
0
}
119
120
/**
121
 * blkid_put_cache:
122
 * @cache: cache handler
123
 *
124
 * Saves changes to cache file.
125
 */
126
void blkid_put_cache(blkid_cache cache)
127
0
{
128
0
  if (!cache)
129
0
    return;
130
131
0
  (void) blkid_flush_cache(cache);
132
133
0
  DBG_OBJ(CACHE, cache, ul_debug("freeing cache struct"));
134
135
  /* DBG(CACHE, ul_debug_dump_cache(cache)); */
136
137
0
  while (!list_empty(&cache->bic_devs)) {
138
0
    blkid_dev dev = list_entry(cache->bic_devs.next,
139
0
             struct blkid_struct_dev,
140
0
              bid_devs);
141
0
    blkid_free_dev(dev);
142
0
  }
143
144
0
  DBG_OBJ(CACHE, cache, ul_debug("freeing cache tag heads"));
145
0
  while (!list_empty(&cache->bic_tags)) {
146
0
    blkid_tag tag = list_entry(cache->bic_tags.next,
147
0
             struct blkid_struct_tag,
148
0
             bit_tags);
149
150
0
    while (!list_empty(&tag->bit_names)) {
151
0
      blkid_tag bad = list_entry(tag->bit_names.next,
152
0
               struct blkid_struct_tag,
153
0
               bit_names);
154
155
0
      DBG_OBJ(CACHE, cache, ul_debug("warning: unfreed tag %s=%s",
156
0
            bad->bit_name, bad->bit_val));
157
0
      blkid_free_tag(bad);
158
0
    }
159
0
    blkid_free_tag(tag);
160
0
  }
161
162
0
  blkid_free_probe(cache->probe);
163
164
0
  free(cache->bic_filename);
165
0
  free(cache);
166
0
}
167
168
/**
169
 * blkid_gc_cache:
170
 * @cache: cache handler
171
 *
172
 * Removes garbage (non-existing devices) from the cache.
173
 */
174
void blkid_gc_cache(blkid_cache cache)
175
0
{
176
0
  struct list_head *p, *pnext;
177
0
  struct stat st;
178
0
  int ret;
179
180
0
  if (!cache)
181
0
    return;
182
183
0
  list_for_each_safe(p, pnext, &cache->bic_devs) {
184
0
    blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
185
186
0
    ret = stat(dev->bid_name, &st);
187
0
    if (ret < 0) {
188
0
      DBG_OBJ(CACHE, cache, ul_debug("freeing non-existing %s", dev->bid_name));
189
0
      blkid_free_dev(dev);
190
0
      cache->bic_flags |= BLKID_BIC_FL_CHANGED;
191
192
0
#ifdef __linux__
193
0
    } else if (is_loopdev(dev->bid_name)
194
0
          && !loopdev_has_backing_file(dev->bid_name)) {
195
      /* remove empty loop device from cache */
196
0
      DBG_OBJ(CACHE, cache, ul_debug("freeing empty loop device %s", dev->bid_name));
197
0
      blkid_free_dev(dev);
198
0
      cache->bic_flags |= BLKID_BIC_FL_CHANGED;
199
0
#endif
200
0
    } else {
201
0
      DBG(CACHE, ul_debug("Device %s exists", dev->bid_name));
202
0
    }
203
0
  }
204
0
}
205
206
#ifdef TEST_PROGRAM
207
int main(int argc, char** argv)
208
{
209
  blkid_cache cache = NULL;
210
  int ret;
211
212
  blkid_init_debug(UL_DEBUG_ALL);
213
214
  if ((argc > 2)) {
215
    fprintf(stderr, "Usage: %s [filename] \n", argv[0]);
216
    exit(1);
217
  }
218
219
  if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
220
    fprintf(stderr, "error %d parsing cache file %s\n", ret,
221
      argv[1] ? argv[1] : blkid_get_cache_filename(NULL));
222
    exit(1);
223
  }
224
  if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
225
    fprintf(stderr, "%s: error creating cache (%d)\n",
226
      argv[0], ret);
227
    exit(1);
228
  }
229
  if ((ret = blkid_probe_all(cache)) < 0)
230
    fprintf(stderr, "error probing devices\n");
231
232
  blkid_put_cache(cache);
233
234
  return ret;
235
}
236
#endif