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