1 /*
2 * virfilecache.c: file caching for data
3 *
4 * Copyright (C) 2017 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 *
20 */
21
22
23 #include <config.h>
24
25 #include "internal.h"
26
27 #include "viralloc.h"
28 #include "virbuffer.h"
29 #include "vircrypto.h"
30 #include "virerror.h"
31 #include "virfile.h"
32 #include "virfilecache.h"
33 #include "virhash.h"
34 #include "virlog.h"
35 #include "virobject.h"
36 #include "virstring.h"
37
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41
42 #define VIR_FROM_THIS VIR_FROM_NONE
43
44 VIR_LOG_INIT("util.filecache");
45
46
47 struct _virFileCache {
48 virObjectLockable parent;
49
50 GHashTable *table;
51
52 char *dir;
53 char *suffix;
54
55 void *priv;
56
57 virFileCacheHandlers handlers;
58 };
59
60
61 static virClass *virFileCacheClass;
62
63
64 static void
virFileCachePrivFree(virFileCache * cache)65 virFileCachePrivFree(virFileCache *cache)
66 {
67 if (cache->priv && cache->handlers.privFree)
68 cache->handlers.privFree(cache->priv);
69 }
70
71
72 static void
virFileCacheDispose(void * obj)73 virFileCacheDispose(void *obj)
74 {
75 virFileCache *cache = obj;
76
77 g_free(cache->dir);
78 g_free(cache->suffix);
79
80 virHashFree(cache->table);
81
82 virFileCachePrivFree(cache);
83 }
84
85
86 static int
virFileCacheOnceInit(void)87 virFileCacheOnceInit(void)
88 {
89 if (!VIR_CLASS_NEW(virFileCache, virClassForObjectLockable()))
90 return -1;
91
92 return 0;
93 }
94
95
96 VIR_ONCE_GLOBAL_INIT(virFileCache);
97
98
99 static char *
virFileCacheGetFileName(virFileCache * cache,const char * name)100 virFileCacheGetFileName(virFileCache *cache,
101 const char *name)
102 {
103 g_autofree char *namehash = NULL;
104 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
105
106 if (virCryptoHashString(VIR_CRYPTO_HASH_SHA256, name, &namehash) < 0)
107 return NULL;
108
109 if (g_mkdir_with_parents(cache->dir, 0777) < 0) {
110 virReportSystemError(errno,
111 _("Unable to create directory '%s'"),
112 cache->dir);
113 return NULL;
114 }
115
116 virBufferAsprintf(&buf, "%s/%s", cache->dir, namehash);
117
118 if (cache->suffix)
119 virBufferAsprintf(&buf, ".%s", cache->suffix);
120
121 return virBufferContentAndReset(&buf);
122 }
123
124
125 static int
virFileCacheLoad(virFileCache * cache,const char * name,void ** data)126 virFileCacheLoad(virFileCache *cache,
127 const char *name,
128 void **data)
129 {
130 g_autofree char *file = NULL;
131 int ret = -1;
132 void *loadData = NULL;
133 bool outdated = false;
134
135 *data = NULL;
136
137 if (!(file = virFileCacheGetFileName(cache, name)))
138 return ret;
139
140 if (!virFileExists(file)) {
141 if (errno == ENOENT) {
142 VIR_DEBUG("No cached data '%s' for '%s'", file, name);
143 ret = 0;
144 goto cleanup;
145 }
146 virReportSystemError(errno,
147 _("Unable to access cache '%s' for '%s'"),
148 file, name);
149 goto cleanup;
150 }
151
152 if (!(loadData = cache->handlers.loadFile(file, name, cache->priv, &outdated))) {
153 if (!outdated) {
154 VIR_WARN("Failed to load cached data from '%s' for '%s': %s",
155 file, name, virGetLastErrorMessage());
156 virResetLastError();
157 }
158 ret = 0;
159 goto cleanup;
160 }
161
162 if (!cache->handlers.isValid(loadData, cache->priv)) {
163 VIR_DEBUG("Outdated cached capabilities '%s' for '%s'", file, name);
164 unlink(file);
165 ret = 0;
166 goto cleanup;
167 }
168
169 VIR_DEBUG("Loaded cached data '%s' for '%s'", file, name);
170
171 ret = 1;
172 *data = g_steal_pointer(&loadData);
173
174 cleanup:
175 virObjectUnref(loadData);
176 return ret;
177 }
178
179
180 static int
virFileCacheSave(virFileCache * cache,const char * name,void * data)181 virFileCacheSave(virFileCache *cache,
182 const char *name,
183 void *data)
184 {
185 g_autofree char *file = NULL;
186
187 if (!(file = virFileCacheGetFileName(cache, name)))
188 return -1;
189
190 if (cache->handlers.saveFile(data, file, cache->priv) < 0)
191 return -1;
192
193 return 0;
194 }
195
196
197 static void *
virFileCacheNewData(virFileCache * cache,const char * name)198 virFileCacheNewData(virFileCache *cache,
199 const char *name)
200 {
201 void *data = NULL;
202 int rv;
203
204 if ((rv = virFileCacheLoad(cache, name, &data)) < 0)
205 return NULL;
206
207 if (rv == 0) {
208 if (!(data = cache->handlers.newData(name, cache->priv)))
209 return NULL;
210
211 if (virFileCacheSave(cache, name, data) < 0) {
212 virObjectUnref(data);
213 data = NULL;
214 }
215 }
216
217 return data;
218 }
219
220
221 /**
222 * virFileCacheNew:
223 * @dir: the cache directory where all the cache files will be stored
224 * @suffix: the cache file suffix or NULL if no suffix is required
225 * @handlers: filled structure with all required handlers
226 *
227 * Creates a new cache object which handles caching any data to files
228 * stored on a filesystem.
229 *
230 * Returns new cache object or NULL on error.
231 */
232 virFileCache *
virFileCacheNew(const char * dir,const char * suffix,virFileCacheHandlers * handlers)233 virFileCacheNew(const char *dir,
234 const char *suffix,
235 virFileCacheHandlers *handlers)
236 {
237 virFileCache *cache;
238
239 if (virFileCacheInitialize() < 0)
240 return NULL;
241
242 if (!(cache = virObjectNew(virFileCacheClass)))
243 return NULL;
244
245 cache->table = virHashNew(virObjectFreeHashData);
246
247 cache->dir = g_strdup(dir);
248
249 cache->suffix = g_strdup(suffix);
250
251 cache->handlers = *handlers;
252
253 return cache;
254 }
255
256
257 static void
virFileCacheValidate(virFileCache * cache,const char * name,void ** data)258 virFileCacheValidate(virFileCache *cache,
259 const char *name,
260 void **data)
261 {
262 if (*data && !cache->handlers.isValid(*data, cache->priv)) {
263 VIR_DEBUG("Cached data '%p' no longer valid for '%s'",
264 *data, NULLSTR(name));
265 if (name)
266 virHashRemoveEntry(cache->table, name);
267 *data = NULL;
268 }
269
270 if (!*data && name) {
271 VIR_DEBUG("Creating data for '%s'", name);
272 *data = virFileCacheNewData(cache, name);
273 if (*data) {
274 VIR_DEBUG("Caching data '%p' for '%s'", *data, name);
275 if (virHashAddEntry(cache->table, name, *data) < 0) {
276 virObjectUnref(*data);
277 *data = NULL;
278 }
279 }
280 }
281 }
282
283
284 /**
285 * virFileCacheLookup:
286 * @cache: existing cache object
287 * @name: name of the data stored in a cache
288 *
289 * Lookup a data specified by name. This tries to find a file with
290 * cached data, if it doesn't exist or is no longer valid new data
291 * is created.
292 *
293 * Returns data object or NULL on error. The caller is responsible for
294 * unrefing the data.
295 */
296 void *
virFileCacheLookup(virFileCache * cache,const char * name)297 virFileCacheLookup(virFileCache *cache,
298 const char *name)
299 {
300 void *data = NULL;
301
302 virObjectLock(cache);
303
304 data = virHashLookup(cache->table, name);
305 virFileCacheValidate(cache, name, &data);
306
307 virObjectRef(data);
308 virObjectUnlock(cache);
309
310 return data;
311 }
312
313
314 /**
315 * virFileCacheLookupByFunc:
316 * @cache: existing cache object
317 * @iter: an iterator to identify the desired data
318 * @iterData: extra opaque information passed to the @iter
319 *
320 * Similar to virFileCacheLookup() except it search by @iter.
321 *
322 * Returns data object or NULL on error. The caller is responsible for
323 * unrefing the data.
324 */
325 void *
virFileCacheLookupByFunc(virFileCache * cache,virHashSearcher iter,const void * iterData)326 virFileCacheLookupByFunc(virFileCache *cache,
327 virHashSearcher iter,
328 const void *iterData)
329 {
330 void *data = NULL;
331 g_autofree char *name = NULL;
332
333 virObjectLock(cache);
334
335 data = virHashSearch(cache->table, iter, iterData, &name);
336 virFileCacheValidate(cache, name, &data);
337
338 virObjectRef(data);
339 virObjectUnlock(cache);
340
341 return data;
342 }
343
344
345 /**
346 * virFileCacheGetPriv:
347 * @cache: existing cache object
348 *
349 * Returns private data used by @handlers.
350 */
351 void *
virFileCacheGetPriv(virFileCache * cache)352 virFileCacheGetPriv(virFileCache *cache)
353 {
354 void *priv;
355
356 virObjectLock(cache);
357
358 priv = cache->priv;
359
360 virObjectUnlock(cache);
361
362 return priv;
363 }
364
365
366 /**
367 * virFileCacheSetPriv:
368 * @cache: existing cache object
369 * @priv: private data to set
370 *
371 * Sets private data used by @handlers. If there is already some @priv
372 * set, privFree() will be called on the old @priv before setting a new one.
373 */
374 void
virFileCacheSetPriv(virFileCache * cache,void * priv)375 virFileCacheSetPriv(virFileCache *cache,
376 void *priv)
377 {
378 virObjectLock(cache);
379
380 virFileCachePrivFree(cache);
381
382 cache->priv = priv;
383
384 virObjectUnlock(cache);
385 }
386
387
388 /**
389 * virFileCacheInsertData:
390 * @cache: existing cache object
391 * @name: name of the new data
392 * @data: the actual data object to store in cache
393 *
394 * Adds a new data into a cache but doesn't store the data into
395 * a file. This function should be used only by testing code.
396 *
397 * Returns 0 on success, -1 on error.
398 */
399 int
virFileCacheInsertData(virFileCache * cache,const char * name,void * data)400 virFileCacheInsertData(virFileCache *cache,
401 const char *name,
402 void *data)
403 {
404 int ret;
405
406 virObjectLock(cache);
407
408 ret = virHashUpdateEntry(cache->table, name, data);
409
410 virObjectUnlock(cache);
411
412 return ret;
413 }
414