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