Bug Summary

File:content/fs_backing_store.c
Warning:line 599, column 17
Result of 'malloc' is converted to a pointer of type 'struct store_entry *', which is incompatible with sizeof operand type 'struct state_entry *'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name fs_backing_store.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -I . -I include -I build/Linux-gtk2 -I frontends -I content/handlers -D WITH_JPEG -U WITH_PDF_EXPORT -D LIBICONV_PLUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /usr/include/x86_64-linux-gnu -D WITH_CURL -D WITH_OPENSSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D UTF8PROC_EXPORTS -D WITH_UTF8PROC -D WITH_WEBP -I /usr/include/libpng16 -D WITH_PNG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include/ -D WITH_BMP -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_GIF -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NS_SVG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSSPRITE -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSPSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSLOG -D NETSURF_UA_FORMAT_STRING="Mozilla/5.0 (%s) NetSurf/%d.%d" -D NETSURF_HOMEPAGE="about:welcome" -D NETSURF_LOG_LEVEL=VERBOSE -D NETSURF_BUILTIN_LOG_FILTER="(level:WARNING || cat:jserrors)" -D NETSURF_BUILTIN_VERBOSE_FILTER="(level:VERBOSE || cat:jserrors)" -D STMTEXPR=1 -I /usr/include/librsvg-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/libpng16 -I /usr/include/x86_64-linux-gnu -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/freetype2 -D WITH_RSVG -I /usr/include/gtk-2.0 -I /usr/lib/x86_64-linux-gnu/gtk-2.0/include -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/atk-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -D gtk -D nsgtk -D G_DISABLE_SINGLE_INCLUDES -D G_DISABLE_DEPRECATED -D GTK_DISABLE_SINGLE_INCLUDES -D GTK_MULTIHEAD_SAFE -D PANGO_DISABLE_DEPRECATED -D GTK_DISABLE_DEPRECATED -D _XOPEN_SOURCE=700 -D _POSIX_C_SOURCE=200809L -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _NETBSD_SOURCE -D GTK_RESPATH="/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf/:./frontends/gtk/res/" -D WITH_GRESOURCE -D DUK_OPT_HAVE_CUSTOM_H -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wwrite-strings -Wno-unused-parameter -Wno-unused-but-set-variable -std=c99 -fconst-strings -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-netsurf/clangScanBuildReports/2024-12-18-105127-2905466-1 -x c content/fs_backing_store.c
1/*
2 * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * Low-level resource cache persistent storage implementation.
22 *
23 * file based backing store.
24 *
25 * \todo Consider improving eviction sorting to include objects size
26 * and remaining lifetime and other cost metrics.
27 *
28 * \todo Implement mmap retrieval where supported.
29 *
30 * \todo Implement static retrieval for metadata objects as their heap
31 * lifetime is typically very short, though this may be obsoleted
32 * by a small object storage strategy.
33 *
34 */
35
36#include <unistd.h>
37#include <string.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <fcntl.h>
41#include <errno(*__errno_location ()).h>
42#include <time.h>
43#include <stdlib.h>
44#include <nsutils/unistd.h>
45
46#include "netsurf/inttypes.h"
47#include "utils/filepath.h"
48#include "utils/file.h"
49#include "utils/nsurl.h"
50#include "utils/log.h"
51#include "utils/messages.h"
52#include "utils/hashmap.h"
53#include "desktop/gui_internal.h"
54#include "netsurf/misc.h"
55
56#include "content/backing_store.h"
57
58/** Backing store file format version */
59#define CONTROL_VERSION202 202
60
61/**
62 * Number of milliseconds after a update before control data
63 * maintenance is performed
64 */
65#define CONTROL_MAINT_TIME10000 10000
66
67/** Filename of serialised entries */
68#define ENTRIES_FNAME"entries" "entries"
69
70/** Filename of block file index */
71#define BLOCKS_FNAME"blocks" "blocks"
72
73/** log2 block data address length (64k) */
74#define BLOCK_ADDR_LEN16 16
75
76/** log2 number of entries per block file(1024) */
77#define BLOCK_ENTRY_COUNT10 10
78
79/** log2 number of data block files */
80#define BLOCK_FILE_COUNT(16 - 10) (BLOCK_ADDR_LEN16 - BLOCK_ENTRY_COUNT10)
81
82/** log2 size of data blocks (8k) */
83#define BLOCK_DATA_SIZE13 13
84
85/** log2 size of metadata blocks (8k) */
86#define BLOCK_META_SIZE13 13
87
88/** length in bytes of a block files use map */
89#define BLOCK_USE_MAP_SIZE(1 << (10 - 3)) (1 << (BLOCK_ENTRY_COUNT10 - 3))
90
91/**
92 * The type used as a binary identifier for each entry derived from
93 * the URL. A larger identifier will have fewer collisions but
94 * requires proportionately more storage.
95 */
96typedef uint32_t entry_ident_t;
97
98/**
99 * The type used to store block file index values. If this is changed
100 * it will affect the entry storage/alignment and BLOCK_ADDR_LEN must
101 * also be updated.
102 */
103typedef uint16_t block_index_t;
104
105
106/**
107 * Entry element index values.
108 */
109enum store_entry_elem_idx {
110 ENTRY_ELEM_DATA = 0, /**< entry element is data */
111 ENTRY_ELEM_META = 1, /**< entry element is metadata */
112 ENTRY_ELEM_COUNT = 2, /**< count of elements on an entry */
113};
114
115/**
116 * flags that indicate what additional information is contained within
117 * an entry element.
118 */
119enum store_entry_elem_flags {
120 /** store not managing any allocation on entry */
121 ENTRY_ELEM_FLAG_NONE = 0,
122 /** entry data allocation is on heap */
123 ENTRY_ELEM_FLAG_HEAP = 0x1,
124 /** entry data allocation is mmaped */
125 ENTRY_ELEM_FLAG_MMAP = 0x2,
126 /** entry data allocation is in small object pool */
127 ENTRY_ELEM_FLAG_SMALL = 0x4,
128};
129
130
131enum store_entry_flags {
132 /** entry is normal */
133 ENTRY_FLAGS_NONE = 0,
134 /** entry has been invalidated but something still holding a reference */
135 ENTRY_FLAGS_INVALID = 1,
136};
137
138/**
139 * Backing store entry element.
140 *
141 * An element keeps data about:
142 * - the current memory allocation
143 * - the number of outstanding references to the memory
144 * - the size of the element data
145 * - flags controlling how the memory and element are handled
146 *
147 * @note Order is important to avoid excessive structure packing overhead.
148 */
149struct store_entry_element {
150 uint8_t* data; /**< data allocated */
151 uint32_t size; /**< size of entry element on disc */
152 block_index_t block; /**< small object data block */
153 uint8_t ref; /**< element data reference count */
154 uint8_t flags; /**< entry flags */
155};
156
157/**
158 * Backing store object index entry.
159 *
160 * An entry in the backing store contains two elements for the actual
161 * data and the metadata. The two elements are treated identically for
162 * storage lifetime but as a collective whole for expiration and
163 * indexing.
164 *
165 * @note Order is important to avoid excessive structure packing overhead.
166 */
167struct store_entry {
168 nsurl *url; /**< The URL for this entry */
169 int64_t last_used; /**< UNIX time the entry was last used */
170 uint16_t use_count; /**< number of times this entry has been accessed */
171 uint8_t flags; /**< entry flags */
172 /** Entry element (data or meta) specific information */
173 struct store_entry_element elem[ENTRY_ELEM_COUNT];
174};
175
176/**
177 * Small block file.
178 */
179struct block_file {
180 /** file descriptor of the block file */
181 int fd;
182 /** map of used and unused entries within the block file */
183 uint8_t use_map[BLOCK_USE_MAP_SIZE(1 << (10 - 3))];
184};
185
186/**
187 * log2 of block size.
188 */
189static const unsigned int log2_block_size[ENTRY_ELEM_COUNT] = {
190 BLOCK_DATA_SIZE13, /**< Data block size */
191 BLOCK_META_SIZE13 /**< Metadata block size */
192};
193
194/**
195 * Parameters controlling the backing store.
196 */
197struct store_state {
198 /* store config */
199 char *path; /**< The path to the backing store */
200 size_t limit; /**< The backing store upper bound target size */
201 size_t hysteresis; /**< The hysteresis around the target size */
202
203 /**
204 * The cache object hash
205 */
206 hashmap_t *entries;
207
208 /** flag indicating if the entries have been made persistent
209 * since they were last changed.
210 */
211 bool_Bool entries_dirty;
212
213 /** small block indexes */
214 struct block_file blocks[ENTRY_ELEM_COUNT][BLOCK_FILE_COUNT(16 - 10)];
215
216 /** flag indicating if the block file use maps have been made
217 * persistent since they were last changed.
218 */
219 bool_Bool blocks_dirty;
220
221 /** flag indicating if a block file has been opened for update
222 * since maintenance was previously done.
223 */
224 bool_Bool blocks_opened;
225
226
227 /* stats */
228 uint64_t total_alloc; /**< total size of all allocated storage. */
229
230 size_t hit_count; /**< number of cache hits */
231 uint64_t hit_size; /**< size of storage served */
232 size_t miss_count; /**< number of cache misses */
233
234};
235
236/**
237 * Global storage state.
238 *
239 * @todo Investigate if there is a way to have a context rather than
240 * use a global.
241 */
242struct store_state *storestate;
243
244/* Entries hashmap parameters
245 *
246 * Our hashmap has nsurl keys and store_entry values
247 */
248
249static bool_Bool
250entries_hashmap_key_eq(void *key1, void *key2)
251{
252 return nsurl_compare((nsurl *)key1, (nsurl *)key2, NSURL_COMPLETE);
253}
254
255static void *
256entries_hashmap_value_alloc(void *key)
257{
258 struct store_entry *ent = calloc(1, sizeof(struct store_entry));
259 if (ent != NULL((void*)0)) {
260 ent->url = nsurl_ref(key);
261 }
262 return ent;
263}
264
265static void
266entries_hashmap_value_destroy(void *value)
267{
268 struct store_entry *ent = value;
269 /** \todo Do we need to do any disk cleanup here? if so, meep! */
270 nsurl_unref(ent->url);
271 free(ent);
272}
273
274static hashmap_parameters_t entries_hashmap_parameters = {
275 .key_clone = (hashmap_key_clone_t)nsurl_ref,
276 .key_destroy = (hashmap_key_destroy_t)nsurl_unref,
277 .key_hash = (hashmap_key_hash_t)nsurl_hash,
278 .key_eq = entries_hashmap_key_eq,
279 .value_alloc = entries_hashmap_value_alloc,
280 .value_destroy = entries_hashmap_value_destroy,
281};
282
283/**
284 * Generate a filename for an object.
285 *
286 * this generates the filename for an object on disc. It is necessary
287 * for this to generate a filename which conforms to the limitations
288 * of all the filesystems the cache can be placed upon.
289 *
290 * From http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
291 * the relevant subset is:
292 * - path elements no longer than 8 characters
293 * - acceptable characters are A-Z, 0-9
294 * - short total path lengths (255 or less)
295 * - no more than 77 entries per directory (6bits worth)
296 *
297 * The short total path lengths mean the encoding must represent as
298 * much data as possible in the least number of characters.
299 *
300 * To achieve all these goals we use RFC4648 base32 encoding which
301 * packs 5bits into each character of the filename. To represent a 32
302 * bit ident this requires a total path length of between 17 and 22
303 * bytes (including directory separators) BA/BB/BC/BD/BE/ABCDEFG
304 *
305 * @note Version 1.00 of the cache implementation used base64 to
306 * encode this, however that did not meet the requirement for only
307 * using uppercase characters.
308 *
309 * @note Versions prior to 1.30 only packed 5 bits per directory level
310 * A/B/C/D/E/F/ABCDEFG which only required 19 characters to represent
311 * but resulted in requiring an extra level of directory which is less
312 * desirable than the three extra characters using six bits.
313 *
314 * @param state The store state to use.
315 * @param ident The identifier to use.
316 * @param elem_idx The element index.
317 * @return The filename string or NULL on allocation error.
318 */
319static char *
320store_fname(struct store_state *state,
321 entry_ident_t ident,
322 int elem_idx)
323{
324 char *fname = NULL((void*)0);
325 uint8_t b32u_i[8]; /* base32 encoded ident */
326 const uint8_t *b32u_d[6]; /* base32 ident as separate components */
327
328 /* directories used to separate elements */
329 const char *base_dir_table[] = {
330 "d", "m", "dblk", "mblk"
331 };
332
333 /* RFC4648 base32 encoding table (six bits) */
334 const uint8_t encoding_table[64][3] = {
335 { 'A', 0, 0 }, { 'B', 0, 0 }, /* 0 */
336 { 'C', 0, 0 }, { 'D', 0, 0 }, /* 2 */
337 { 'E', 0, 0 }, { 'F', 0, 0 }, /* 4 */
338 { 'G', 0, 0 }, { 'H', 0, 0 }, /* 6 */
339 { 'I', 0, 0 }, { 'J', 0, 0 }, /* 8 */
340 { 'K', 0, 0 }, { 'L', 0, 0 }, /* 10 */
341 { 'M', 0, 0 }, { 'N', 0, 0 }, /* 12 */
342 { 'O', 0, 0 }, { 'P', 0, 0 }, /* 14 */
343 { 'Q', 0, 0 }, { 'R', 0, 0 }, /* 16 */
344 { 'S', 0, 0 }, { 'T', 0, 0 }, /* 18 */
345 { 'U', 0, 0 }, { 'V', 0, 0 }, /* 20 */
346 { 'W', 0, 0 }, { 'X', 0, 0 }, /* 22 */
347 { 'Y', 0, 0 }, { 'Z', 0, 0 }, /* 24 */
348 { '2', 0, 0 }, { '3', 0, 0 }, /* 26 */
349 { '4', 0, 0 }, { '5', 0, 0 }, /* 28 */
350 { '6', 0, 0 }, { '7', 0, 0 }, /* 30 */
351 { 'B', 'A', 0 }, { 'B', 'B', 0 }, /* 32 */
352 { 'B', 'C', 0 }, { 'B', 'D', 0 }, /* 34 */
353 { 'B', 'E', 0 }, { 'B', 'F', 0 }, /* 36 */
354 { 'B', 'G', 0 }, { 'B', 'H', 0 }, /* 38 */
355 { 'B', 'I', 0 }, { 'B', 'J', 0 }, /* 40 */
356 { 'B', 'K', 0 }, { 'B', 'L', 0 }, /* 42 */
357 { 'B', 'M', 0 }, { 'B', 'N', 0 }, /* 44 */
358 { 'B', 'O', 0 }, { 'B', 'P', 0 }, /* 46 */
359 { 'B', 'Q', 0 }, { 'B', 'R', 0 }, /* 48 */
360 { 'B', 'S', 0 }, { 'B', 'T', 0 }, /* 50 */
361 { 'B', 'U', 0 }, { 'B', 'V', 0 }, /* 52 */
362 { 'B', 'W', 0 }, { 'B', 'X', 0 }, /* 54 */
363 { 'B', 'Y', 0 }, { 'B', 'Z', 0 }, /* 56 */
364 { 'B', '2', 0 }, { 'B', '3', 0 }, /* 58 */
365 { 'B', '4', 0 }, { 'B', '5', 0 }, /* 60 */
366 { 'B', '6', 0 }, { 'B', '7', 0 } /* 62 */
367 };
368
369 /* base32 encode ident */
370 b32u_i[0] = encoding_table[(ident ) & 0x1f][0];
371 b32u_i[1] = encoding_table[(ident >> 5) & 0x1f][0];
372 b32u_i[2] = encoding_table[(ident >> 10) & 0x1f][0];
373 b32u_i[3] = encoding_table[(ident >> 15) & 0x1f][0];
374 b32u_i[4] = encoding_table[(ident >> 20) & 0x1f][0];
375 b32u_i[5] = encoding_table[(ident >> 25) & 0x1f][0];
376 b32u_i[6] = encoding_table[(ident >> 30) & 0x1f][0];
377 b32u_i[7] = 0; /* null terminate ident string */
378
379 /* base32 encode directory separators */
380 b32u_d[0] = (uint8_t*)base_dir_table[elem_idx];
381 b32u_d[1] = &encoding_table[(ident ) & 0x3f][0];
382 b32u_d[2] = &encoding_table[(ident >> 6) & 0x3f][0];
383 b32u_d[3] = &encoding_table[(ident >> 12) & 0x3f][0];
384 b32u_d[4] = &encoding_table[(ident >> 18) & 0x3f][0];
385 b32u_d[5] = &encoding_table[(ident >> 24) & 0x3f][0];
386
387 switch (elem_idx) {
388 case ENTRY_ELEM_DATA:
389 case ENTRY_ELEM_META:
390 netsurf_mkpath(&fname, NULL((void*)0), 8,
391 state->path, b32u_d[0], b32u_d[1], b32u_d[2],
392 b32u_d[3], b32u_d[4], b32u_d[5], b32u_i);
393 break;
394
395 case (ENTRY_ELEM_COUNT + ENTRY_ELEM_META):
396 case (ENTRY_ELEM_COUNT + ENTRY_ELEM_DATA):
397 netsurf_mkpath(&fname, NULL((void*)0), 3,
398 state->path, b32u_d[0], b32u_d[1]);
399 break;
400
401 default:
402 assert("bad element index" == NULL)(("bad element index" == ((void*)0)) ? (void) (0) : __assert_fail
("\"bad element index\" == NULL", "content/fs_backing_store.c"
, 402, __extension__ __PRETTY_FUNCTION__))
;
403 break;
404 }
405
406 return fname;
407}
408
409/**
410 * invalidate an element of an entry
411 *
412 * @param state The store state to use.
413 * @param bse The entry to invalidate.
414 * @param elem_idx The element index to invalidate.
415 * @return NSERROR_OK on success or error code on failure.
416 */
417static nserror
418invalidate_element(struct store_state *state,
419 struct store_entry *bse,
420 int elem_idx)
421{
422 if (bse->elem[elem_idx].block != 0) {
423 block_index_t bf;
424 block_index_t bi;
425
426 /* block file block resides in */
427 bf = (bse->elem[elem_idx].block >> BLOCK_ENTRY_COUNT10) &
428 ((1 << BLOCK_FILE_COUNT(16 - 10)) - 1);
429
430 /* block index in file */
431 bi = bse->elem[elem_idx].block & ((1U << BLOCK_ENTRY_COUNT10) -1);
432
433 /* clear bit in use map */
434 state->blocks[elem_idx][bf].use_map[bi >> 3] &= ~(1U << (bi & 7));
435 } else {
436 char *fname;
437
438 /* unlink the file from disc */
439 fname = store_fname(state, nsurl_hash(bse->url), elem_idx);
440 if (fname == NULL((void*)0)) {
441 return NSERROR_NOMEM;
442 }
443 unlink(fname);
444 free(fname);
445 }
446
447 state->total_alloc -= bse->elem[elem_idx].size;
448
449 return NSERROR_OK;
450}
451
452/**
453 * Remove the entry and files associated with an identifier.
454 *
455 * @param state The store state to use.
456 * @param bse The entry to invalidate.
457 * @return NSERROR_OK on success or error code on failure.
458 */
459static nserror
460invalidate_entry(struct store_state *state, struct store_entry *bse)
461{
462 nserror ret;
463
464 /* mark entry as invalid */
465 bse->flags |= ENTRY_FLAGS_INVALID;
466
467 /* check if the entry has storage already allocated */
468 if (((bse->elem[ENTRY_ELEM_DATA].flags &
469 (ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP)) != 0) ||
470 ((bse->elem[ENTRY_ELEM_META].flags &
471 (ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP)) != 0)) {
472 /*
473 * This entry cannot be immediately removed as it has
474 * associated allocation so wait for allocation release.
475 */
476 NSLOG(netsurf, DEBUG,do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 477
, }; nslog__log(&_nslog_ctx, "invalidating entry with referenced allocation"
); } } while(0)
477 "invalidating entry with referenced allocation")do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 477
, }; nslog__log(&_nslog_ctx, "invalidating entry with referenced allocation"
); } } while(0)
;
478 return NSERROR_OK;
479 }
480
481 NSLOG(netsurf, VERBOSE, "Removing entry for %s", nsurl_access(bse->url))do { if (NSLOG_LEVEL_VERBOSE >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_VERBOSE, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 481
, }; nslog__log(&_nslog_ctx, "Removing entry for %s", nsurl_access
(bse->url)); } } while(0)
;
482
483 ret = invalidate_element(state, bse, ENTRY_ELEM_META);
484 if (ret != NSERROR_OK) {
485 NSLOG(netsurf, ERROR, "Error invalidating metadata element")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 485
, }; nslog__log(&_nslog_ctx, "Error invalidating metadata element"
); } } while(0)
;
486 }
487
488 ret = invalidate_element(state, bse, ENTRY_ELEM_DATA);
489 if (ret != NSERROR_OK) {
490 NSLOG(netsurf, ERROR, "Error invalidating data element")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 490
, }; nslog__log(&_nslog_ctx, "Error invalidating data element"
); } } while(0)
;
491 }
492
493 /* As our final act we remove bse from the cache */
494 hashmap_remove(state->entries, bse->url);
495 /* From now, bse is invalid memory */
496
497 return NSERROR_OK;
498}
499
500
501/**
502 * Quick sort comparison.
503 */
504static int compar(const void *va, const void *vb)
505{
506 const struct store_entry *a = *(const struct store_entry **)va;
507 const struct store_entry *b = *(const struct store_entry **)vb;
508
509 /* consider the allocation flags - if an entry has an
510 * allocation it is considered more valuable as it cannot be
511 * freed.
512 */
513 if ((a->elem[ENTRY_ELEM_DATA].flags == ENTRY_ELEM_FLAG_NONE) &&
514 (b->elem[ENTRY_ELEM_DATA].flags != ENTRY_ELEM_FLAG_NONE)) {
515 return -1;
516 } else if ((a->elem[ENTRY_ELEM_DATA].flags != ENTRY_ELEM_FLAG_NONE) &&
517 (b->elem[ENTRY_ELEM_DATA].flags == ENTRY_ELEM_FLAG_NONE)) {
518 return 1;
519 }
520
521 if ((a->elem[ENTRY_ELEM_META].flags == ENTRY_ELEM_FLAG_NONE) &&
522 (b->elem[ENTRY_ELEM_META].flags != ENTRY_ELEM_FLAG_NONE)) {
523 return -1;
524 } else if ((a->elem[ENTRY_ELEM_META].flags != ENTRY_ELEM_FLAG_NONE) &&
525 (b->elem[ENTRY_ELEM_META].flags == ENTRY_ELEM_FLAG_NONE)) {
526 return 1;
527 }
528
529 if (a->use_count < b->use_count) {
530 return -1;
531 } else if (a->use_count > b->use_count) {
532 return 1;
533 }
534 /* use count is the same - now consider last use time */
535
536 if (a->last_used < b->last_used) {
537 return -1;
538 } else if (a->last_used > b->last_used) {
539 return 1;
540 }
541
542 /* they are the same */
543 return 0;
544}
545
546typedef struct {
547 struct store_entry **elist;
548 size_t ent_count;
549} eviction_state_t;
550
551/**
552 * Iterator for gathering entries to compute eviction order
553 */
554static bool_Bool
555entry_eviction_iterator_cb(void *key, void *value, void *ctx)
556{
557 eviction_state_t *estate = ctx;
558 struct store_entry *ent = value;
559 estate->elist[estate->ent_count++] = ent;
560 return false0;
561}
562
563/**
564 * Evict entries from backing store as per configuration.
565 *
566 * Entries are evicted to ensure the cache remains within the
567 * configured limits on size and number of entries.
568 *
569 * The approach is to check if the cache limits have been exceeded and
570 * if so build and sort list of entries to evict. The list is sorted
571 * by use count and then by age, so oldest object with least number of uses
572 * get evicted first.
573 *
574 * @param state The store state to use.
575 * @return NSERROR_OK on success or error code on failure.
576 */
577static nserror store_evict(struct store_state *state)
578{
579 size_t ent = 0;
580 size_t removed = 0; /* size of removed entries */
581 nserror ret = NSERROR_OK;
582 size_t old_count;
583 eviction_state_t estate;
584
585 /* check if the cache has exceeded configured limit */
586 if (state->total_alloc < state->limit) {
587 /* cache within limits */
588 return NSERROR_OK;
589 }
590
591 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 594
, }; nslog__log(&_nslog_ctx, "Evicting entries to reduce %"
"l" "u"" by %""zu", state->total_alloc, state->hysteresis
); } } while(0)
592 "Evicting entries to reduce %"PRIu64" by %"PRIsizet,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 594
, }; nslog__log(&_nslog_ctx, "Evicting entries to reduce %"
"l" "u"" by %""zu", state->total_alloc, state->hysteresis
); } } while(0)
593 state->total_alloc,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 594
, }; nslog__log(&_nslog_ctx, "Evicting entries to reduce %"
"l" "u"" by %""zu", state->total_alloc, state->hysteresis
); } } while(0)
594 state->hysteresis)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 594
, }; nslog__log(&_nslog_ctx, "Evicting entries to reduce %"
"l" "u"" by %""zu", state->total_alloc, state->hysteresis
); } } while(0)
;
595
596 /* allocate storage for the list */
597 old_count = hashmap_count(state->entries);
598 estate.ent_count = 0;
599 estate.elist = malloc(sizeof(struct state_entry*) * old_count);
Result of 'malloc' is converted to a pointer of type 'struct store_entry *', which is incompatible with sizeof operand type 'struct state_entry *'
600 if (estate.elist == NULL((void*)0)) {
601 return NSERROR_NOMEM;
602 }
603
604 if (hashmap_iterate(state->entries, entry_eviction_iterator_cb, &estate)) {
605 NSLOG(netsurf, WARNING, "Unexpected termination of eviction iterator")do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 605
, }; nslog__log(&_nslog_ctx, "Unexpected termination of eviction iterator"
); } } while(0)
;
606 free(estate.elist);
607 return NSERROR_UNKNOWN;
608 }
609
610 if (old_count != estate.ent_count) {
611 NSLOG(netsurf, WARNING, "Incorrect entry count after eviction iterator")do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 611
, }; nslog__log(&_nslog_ctx, "Incorrect entry count after eviction iterator"
); } } while(0)
;
612 free(estate.elist);
613 return NSERROR_UNKNOWN;
614 }
615
616 qsort(estate.elist, estate.ent_count, sizeof(struct state_entry*), compar);
617
618 /* evict entries in listed order */
619 removed = 0;
620 for (ent = 0; ent < estate.ent_count; ent++) {
621 struct store_entry *bse = estate.elist[ent];
622
623 removed += bse->elem[ENTRY_ELEM_DATA].size;
624 removed += bse->elem[ENTRY_ELEM_META].size;
625
626 ret = invalidate_entry(state, bse);
627 if (ret != NSERROR_OK) {
628 break;
629 }
630
631 if (removed > state->hysteresis) {
632 break;
633 }
634 }
635
636 free(estate.elist);
637
638 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 640
, }; nslog__log(&_nslog_ctx, "removed %""zu"" in %""zu"" entries, %"
"l" "u"" remaining in %""zu"" entries", removed, ent, state->
total_alloc, old_count - ent); } } while(0)
639 "removed %"PRIsizet" in %"PRIsizet" entries, %"PRIu64" remaining in %"PRIsizet" entries",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 640
, }; nslog__log(&_nslog_ctx, "removed %""zu"" in %""zu"" entries, %"
"l" "u"" remaining in %""zu"" entries", removed, ent, state->
total_alloc, old_count - ent); } } while(0)
640 removed, ent, state->total_alloc, old_count - ent)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 640
, }; nslog__log(&_nslog_ctx, "removed %""zu"" in %""zu"" entries, %"
"l" "u"" remaining in %""zu"" entries", removed, ent, state->
total_alloc, old_count - ent); } } while(0)
;
641
642 return ret;
643}
644
645/**
646 * Write a single store entry to disk
647 *
648 * To serialise a single store entry for now we write out a 32bit int
649 * which is the length of the url, then that many bytes of the url.
650 * Then we write out the full store entry struct as-is, which includes
651 * a useless nsurl pointer.
652 */
653static nserror
654write_entry(struct store_entry *ent, int fd)
655{
656 uint32_t len = strlen(nsurl_access(ent->url));
657 if (write(fd, &len, sizeof(len)) != sizeof(len))
658 return NSERROR_SAVE_FAILED;
659 if (write(fd, nsurl_access(ent->url), len) != (ssize_t)len)
660 return NSERROR_SAVE_FAILED;
661 if (write(fd, ent, sizeof(*ent)) != sizeof(*ent))
662 return NSERROR_SAVE_FAILED;
663
664 return NSERROR_OK;
665}
666
667typedef struct {
668 int fd;
669 size_t written;
670} write_entry_iteration_state;
671
672/**
673 * Callback for iterating the entries hashmap
674 */
675static bool_Bool
676write_entry_iterator(void *key, void *value, void *ctx)
677{
678 /* We ignore the key */
679 struct store_entry *ent = value;
680 write_entry_iteration_state *state = ctx;
681 state->written++;
682 /* We stop early if we fail to write this entry */
683 return write_entry(ent, state->fd) != NSERROR_OK;
684}
685
686/**
687 * Write filesystem entries to file.
688 *
689 * Serialise entry index out to storage.
690 *
691 * @param state The backing store state to serialise.
692 * @return NSERROR_OK on success or error code on failure.
693 */
694static nserror write_entries(struct store_state *state)
695{
696 char *tname = NULL((void*)0); /* temporary file name for atomic replace */
697 char *fname = NULL((void*)0); /* target filename */
698 write_entry_iteration_state weistate;
699 nserror ret;
700
701 memset(&weistate, 0, sizeof(weistate));
702
703 if (state->entries_dirty == false0) {
704 /* entries have not been updated since last write */
705 return NSERROR_OK;
706 }
707
708 ret = netsurf_mkpath(&tname, NULL((void*)0), 2, state->path, "t"ENTRIES_FNAME"entries");
709 if (ret != NSERROR_OK) {
710 return ret;
711 }
712
713 weistate.fd = open(tname, O_RDWR02 | O_CREAT0100 | O_TRUNC01000, S_IRUSR0400 | S_IWUSR0200);
714 if (weistate.fd == -1) {
715 free(tname);
716 return NSERROR_SAVE_FAILED;
717 }
718
719 if (hashmap_iterate(state->entries, write_entry_iterator, &weistate)) {
720 /* The iteration ended early, so we failed */
721 close(weistate.fd);
722 unlink(tname);
723 free(tname);
724 return NSERROR_SAVE_FAILED;
725 }
726
727 close(weistate.fd);
728
729 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, ENTRIES_FNAME"entries");
730 if (ret != NSERROR_OK) {
731 unlink(tname);
732 free(tname);
733 return ret;
734 }
735
736 /* remove() call is to handle non-POSIX rename() implementations */
737 (void)remove(fname);
738 if (rename(tname, fname) != 0) {
739 unlink(tname);
740 free(tname);
741 free(fname);
742 return NSERROR_SAVE_FAILED;
743 }
744
745 NSLOG(netsurf, INFO, "Wrote out %"PRIsizet" entries", weistate.written)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 745
, }; nslog__log(&_nslog_ctx, "Wrote out %""zu"" entries",
weistate.written); } } while(0)
;
746
747 return NSERROR_OK;
748}
749
750/**
751 * Write block file use map to file.
752 *
753 * Serialise block file use map out to storage.
754 *
755 * \param state The backing store state to serialise.
756 * \return NSERROR_OK on success or error code on failure.
757 */
758static nserror write_blocks(struct store_state *state)
759{
760 int fd;
761 char *tname = NULL((void*)0); /* temporary file name for atomic replace */
762 char *fname = NULL((void*)0); /* target filename */
763 size_t blocks_size;
764 size_t written = 0;
765 size_t wr;
766 nserror ret;
767 int bfidx; /* block file index */
768 int elem_idx;
769
770 if (state->blocks_dirty == false0) {
771 /* blocks use maps have not been updated since last write */
772 return NSERROR_OK;
773 }
774
775 ret = netsurf_mkpath(&tname, NULL((void*)0), 2, state->path, "t"BLOCKS_FNAME"blocks");
776 if (ret != NSERROR_OK) {
777 return ret;
778 }
779
780 fd = open(tname, O_RDWR02 | O_CREAT0100 | O_TRUNC01000, S_IRUSR0400 | S_IWUSR0200);
781 if (fd == -1) {
782 free(tname);
783 return NSERROR_SAVE_FAILED;
784 }
785
786 blocks_size = (BLOCK_FILE_COUNT(16 - 10) * ENTRY_ELEM_COUNT) * BLOCK_USE_MAP_SIZE(1 << (10 - 3));
787
788 for (elem_idx = 0; elem_idx < ENTRY_ELEM_COUNT; elem_idx++) {
789 for (bfidx = 0; bfidx < BLOCK_FILE_COUNT(16 - 10); bfidx++) {
790 wr = write(fd,
791 &state->blocks[elem_idx][bfidx].use_map[0],
792 BLOCK_USE_MAP_SIZE(1 << (10 - 3)));
793 if (wr != BLOCK_USE_MAP_SIZE(1 << (10 - 3))) {
794 NSLOG(netsurf, DEBUG,do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 797
, }; nslog__log(&_nslog_ctx, "writing block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
795 "writing block file %d use index on file number %d failed",do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 797
, }; nslog__log(&_nslog_ctx, "writing block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
796 elem_idx,do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 797
, }; nslog__log(&_nslog_ctx, "writing block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
797 bfidx)do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 797
, }; nslog__log(&_nslog_ctx, "writing block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
;
798 goto wr_err;
799 }
800 written += wr;
801 }
802 }
803wr_err:
804 close(fd);
805
806 /* check all data was written */
807 if (written != blocks_size) {
808 unlink(tname);
809 free(tname);
810 return NSERROR_SAVE_FAILED;
811 }
812
813 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, BLOCKS_FNAME"blocks");
814 if (ret != NSERROR_OK) {
815 unlink(tname);
816 free(tname);
817 return ret;
818 }
819
820 /* remove() call is to handle non-POSIX rename() implementations */
821 (void)remove(fname);
822 if (rename(tname, fname) != 0) {
823 unlink(tname);
824 free(tname);
825 free(fname);
826 return NSERROR_SAVE_FAILED;
827 }
828
829 return NSERROR_OK;
830}
831
832/**
833 * Ensures block files are of the correct extent
834 *
835 * block files have their extent set to their maximum size to ensure
836 * subsequent reads and writes do not need to extend the file and are
837 * therefore faster.
838 *
839 * \param state The backing store state to set block extent for.
840 * \return NSERROR_OK on success or error code on failure.
841 */
842static nserror set_block_extents(struct store_state *state)
843{
844 int bfidx; /* block file index */
845 int elem_idx;
846 int ftr;
847
848 if (state->blocks_opened == false0) {
849 /* no blocks have been opened since last write */
850 return NSERROR_OK;
851 }
852
853 NSLOG(netsurf, DEBUG, "Starting")do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 853
, }; nslog__log(&_nslog_ctx, "Starting"); } } while(0)
;
854 for (elem_idx = 0; elem_idx < ENTRY_ELEM_COUNT; elem_idx++) {
855 for (bfidx = 0; bfidx < BLOCK_FILE_COUNT(16 - 10); bfidx++) {
856 if (state->blocks[elem_idx][bfidx].fd != -1) {
857 /* ensure block file is correct extent */
858 ftr = ftruncate(state->blocks[elem_idx][bfidx].fd, 1U << (log2_block_size[elem_idx] + BLOCK_ENTRY_COUNT10));
859 if (ftr == -1) {
860 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 862
, }; nslog__log(&_nslog_ctx, "Truncate failed errno:%d", (
*__errno_location ())); } } while(0)
861 "Truncate failed errno:%d",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 862
, }; nslog__log(&_nslog_ctx, "Truncate failed errno:%d", (
*__errno_location ())); } } while(0)
862 errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 862
, }; nslog__log(&_nslog_ctx, "Truncate failed errno:%d", (
*__errno_location ())); } } while(0)
;
863 }
864 }
865 }
866 }
867 NSLOG(netsurf, DEBUG, "Complete")do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 867
, }; nslog__log(&_nslog_ctx, "Complete"); } } while(0)
;
868
869 state->blocks_opened = false0;
870
871 return NSERROR_OK;
872}
873
874/**
875 * maintenance of control structures.
876 *
877 * callback scheduled when control data has been update. Currently
878 * this is for when the entries table is dirty and requires
879 * serialising.
880 *
881 * \param s store state to maintain.
882 */
883static void control_maintenance(void *s)
884{
885 struct store_state *state = s;
886
887 write_entries(state);
888 write_blocks(state);
889 set_block_extents(state);
890}
891
892
893/**
894 * Lookup a backing store entry in the entry table from a url.
895 *
896 * This finds the store entry associated with the given
897 * key. Additionally if an entry is found it updates the usage data
898 * about the entry.
899 *
900 * @param state The store state to use.
901 * @param url The value used as the unique key to search entries for.
902 * @param bse Pointer used to return value.
903 * @return NSERROR_OK and bse updated on success or NSERROR_NOT_FOUND
904 * if no entry corresponds to the url.
905 */
906static nserror
907get_store_entry(struct store_state *state, nsurl *url, struct store_entry **bse)
908{
909 struct store_entry *ent;
910
911 ent = hashmap_lookup(state->entries, url);
912
913 if (ent == NULL((void*)0)) {
914 return NSERROR_NOT_FOUND;
915 }
916
917 *bse = ent;
918
919 ent->last_used = time(NULL((void*)0));
920 ent->use_count++;
921
922 state->entries_dirty = true1;
923
924 guit->misc->schedule(CONTROL_MAINT_TIME10000, control_maintenance, state);
925
926 return NSERROR_OK;
927}
928
929/**
930 * Find next available small block.
931 */
932static block_index_t alloc_block(struct store_state *state, int elem_idx)
933{
934 int bf;
935 int idx;
936 int bit;
937 uint8_t *map;
938
939 for (bf = 0; bf < BLOCK_FILE_COUNT(16 - 10); bf++) {
940 map = &state->blocks[elem_idx][bf].use_map[0];
941
942 for (idx = 0; idx < BLOCK_USE_MAP_SIZE(1 << (10 - 3)); idx++) {
943 if (*(map + idx) != 0xff) {
944 /* located an unused block */
945 for (bit = 0; bit < 8;bit++) {
946 if (((*(map + idx)) & (1U << bit)) == 0) {
947 /* mark block as used */
948 *(map + idx) |= 1U << bit;
949 state->blocks_dirty = true1;
950 return (((bf * BLOCK_USE_MAP_SIZE(1 << (10 - 3))) + idx) * 8) + bit;
951 }
952 }
953 }
954 }
955 }
956
957 return 0;
958}
959
960/**
961 * Set a backing store entry in the entry table from a url.
962 *
963 * This creates a backing store entry in the entry table for a url.
964 *
965 * @param state The store state to use.
966 * @param url The value used as the unique key to search entries for.
967 * @param elem_idx The index of the entry element to use.
968 * @param data The data to store
969 * @param datalen The length of data in \a data
970 * @param bse Pointer used to return value.
971 * @return NSERROR_OK and \a bse updated on success or NSERROR_NOT_FOUND
972 * if no entry corresponds to the url.
973 */
974static nserror
975set_store_entry(struct store_state *state,
976 nsurl *url,
977 int elem_idx,
978 uint8_t *data,
979 const size_t datalen,
980 struct store_entry **bse)
981{
982 struct store_entry *se;
983 nserror ret;
984 struct store_entry_element *elem;
985
986 NSLOG(netsurf, DEBUG, "url:%s", nsurl_access(url))do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 986
, }; nslog__log(&_nslog_ctx, "url:%s", nsurl_access(url))
; } } while(0)
;
987
988 /* evict entries as required and ensure there is at least one
989 * new entry available.
990 */
991 ret = store_evict(state);
992 if (ret != NSERROR_OK) {
993 return ret;
994 }
995
996 se = hashmap_lookup(state->entries, url);
997 if (se == NULL((void*)0)) {
998 se = hashmap_insert(state->entries, url);
999 }
1000 if (se == NULL((void*)0)) {
1001 return NSERROR_NOMEM;
1002 }
1003
1004 /* the entry element */
1005 elem = &se->elem[elem_idx];
1006
1007 /* check if the element has storage already allocated */
1008 if ((elem->flags & (ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP)) != 0) {
1009 /* this entry cannot be removed as it has associated
1010 * allocation.
1011 */
1012 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1013
, }; nslog__log(&_nslog_ctx, "attempt to overwrite entry with in use data"
); } } while(0)
1013 "attempt to overwrite entry with in use data")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1013
, }; nslog__log(&_nslog_ctx, "attempt to overwrite entry with in use data"
); } } while(0)
;
1014 return NSERROR_PERMISSION;
1015 }
1016
1017 /* set the common entry data */
1018 se->use_count = 1;
1019 se->last_used = time(NULL((void*)0));
1020
1021 /* store the data in the element */
1022 elem->flags |= ENTRY_ELEM_FLAG_HEAP;
1023 elem->data = data;
1024 elem->ref = 1;
1025
1026 /* account for size of entry element */
1027 state->total_alloc -= elem->size;
1028 elem->size = datalen;
1029 state->total_alloc += elem->size;
1030
1031 /* if the element will fit in a small block attempt to allocate one */
1032 if (elem->size <= (1U << log2_block_size[elem_idx])) {
1033 elem->block = alloc_block(state, elem_idx);
1034 }
1035
1036 /* ensure control maintenance scheduled. */
1037 state->entries_dirty = true1;
1038 guit->misc->schedule(CONTROL_MAINT_TIME10000, control_maintenance, state);
1039
1040 *bse = se;
1041
1042 return NSERROR_OK;
1043}
1044
1045
1046/**
1047 * Open a file using a store ident.
1048 *
1049 * @param state The store state to use.
1050 * @param ident The identifier to open file for.
1051 * @param elem_idx The element within the store entry to open. The
1052 * value should be be one of the values in the
1053 * store_entry_elem_idx enum. Additionally it may have
1054 * ENTRY_ELEM_COUNT added to it to indicate block file
1055 * names.
1056 * @param openflags The flags used with the open call.
1057 * @return An fd from the open call or -1 on error.
1058 */
1059static int
1060store_open(struct store_state *state,
1061 entry_ident_t ident,
1062 int elem_idx,
1063 int openflags)
1064{
1065 char *fname;
1066 nserror ret;
1067 int fd;
1068
1069 fname = store_fname(state, ident, elem_idx);
1070 if (fname == NULL((void*)0)) {
1071 NSLOG(netsurf, ERROR, "filename error")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1071
, }; nslog__log(&_nslog_ctx, "filename error"); } } while
(0)
;
1072 return -1;
1073 }
1074
1075 /* ensure all path elements to file exist if creating file */
1076 if (openflags & O_CREAT0100) {
1077 ret = netsurf_mkdir_all(fname);
1078 if (ret != NSERROR_OK) {
1079 NSLOG(netsurf, WARNING,do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1080
, }; nslog__log(&_nslog_ctx, "file path \"%s\" could not be created"
, fname); } } while(0)
1080 "file path \"%s\" could not be created", fname)do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1080
, }; nslog__log(&_nslog_ctx, "file path \"%s\" could not be created"
, fname); } } while(0)
;
1081 free(fname);
1082 return -1;
1083 }
1084 }
1085
1086 NSLOG(netsurf, DEBUG, "opening %s", fname)do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1086
, }; nslog__log(&_nslog_ctx, "opening %s", fname); } } while
(0)
;
1087 fd = open(fname, openflags, S_IRUSR0400 | S_IWUSR0200);
1088
1089 free(fname);
1090
1091 return fd;
1092}
1093
1094
1095/**
1096 * Unlink entries file
1097 *
1098 * @param state The backing store state.
1099 * @return NSERROR_OK on success or error code on failure.
1100 */
1101static nserror
1102unlink_entries(struct store_state *state)
1103{
1104 char *fname = NULL((void*)0);
1105 nserror ret;
1106
1107 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, ENTRIES_FNAME"entries");
1108 if (ret != NSERROR_OK) {
1109 return ret;
1110 }
1111
1112 unlink(fname);
1113
1114 free(fname);
1115 return NSERROR_OK;
1116}
1117
1118/**
1119 * Read description entries into memory.
1120 *
1121 * @param state The backing store state to put the loaded entries in.
1122 * @return NSERROR_OK on success or error code on faliure.
1123 */
1124static nserror
1125read_entries(struct store_state *state)
1126{
1127 char *fname = NULL((void*)0);
1128 char *url;
1129 nsurl *nsurl;
1130 nserror ret;
1131 size_t read_entries = 0;
1132 struct store_entry *ent;
1133 int fd;
1134
1135 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, ENTRIES_FNAME"entries");
1136 if (ret != NSERROR_OK) {
1137 return ret;
1138 }
1139
1140 state->entries = hashmap_create(&entries_hashmap_parameters);
1141 if (state->entries == NULL((void*)0)) {
1142 free(fname);
1143 return NSERROR_NOMEM;
1144 }
1145
1146 fd = open(fname, O_RDWR02);
1147 if (fd != -1) {
1148 uint32_t urllen;
1149 while (read(fd, &urllen, sizeof(urllen)) == sizeof(urllen)) {
1150 url = calloc(1, urllen+1);
1151 if (url == NULL((void*)0)) {
1152 close(fd);
1153 free(fname);
1154 return NSERROR_NOMEM;
1155 }
1156 if (read(fd, url, urllen) != (ssize_t)urllen) {
1157 free(url);
1158 close(fd);
1159 free(fname);
1160 return NSERROR_INIT_FAILED;
1161 }
1162 ret = nsurl_create(url, &nsurl);
1163 if (ret != NSERROR_OK) {
1164 free(url);
1165 close(fd);
1166 free(fname);
1167 return ret;
1168 }
1169 free(url);
1170 /* We have to be careful here about nsurl refs */
1171 ent = hashmap_insert(state->entries, nsurl);
1172 if (ent == NULL((void*)0)) {
1173 nsurl_unref(nsurl);
1174 close(fd);
1175 free(fname);
1176 return NSERROR_NOMEM;
1177 }
1178 /* At this point, ent actually owns a ref of nsurl */
1179 if (read(fd, ent, sizeof(*ent)) != sizeof(*ent)) {
1180 /* The read failed, so reset the ptr */
1181 ent->url = nsurl; /* It already had a ref */
1182 nsurl_unref(nsurl);
1183 close(fd);
1184 free(fname);
1185 return NSERROR_INIT_FAILED;
1186 }
1187 ent->url = nsurl; /* It already owns a ref */
1188 nsurl_unref(nsurl);
1189 NSLOG(netsurf, DEBUG, "Successfully read entry for %s", nsurl_access(ent->url))do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1189
, }; nslog__log(&_nslog_ctx, "Successfully read entry for %s"
, nsurl_access(ent->url)); } } while(0)
;
1190 read_entries++;
1191 /* Note the size allocation */
1192 state->total_alloc += ent->elem[ENTRY_ELEM_DATA].size;
1193 state->total_alloc += ent->elem[ENTRY_ELEM_META].size;
1194 /* And ensure we don't pretend to have this in memory yet */
1195 ent->elem[ENTRY_ELEM_DATA].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
1196 ent->elem[ENTRY_ELEM_META].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
1197
1198 }
1199 close(fd);
1200 }
1201
1202 NSLOG(netsurf, INFO, "Read %"PRIsizet" entries from cache", read_entries)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1202
, }; nslog__log(&_nslog_ctx, "Read %""zu"" entries from cache"
, read_entries); } } while(0)
;
1203
1204 free(fname);
1205 return NSERROR_OK;
1206}
1207
1208
1209/**
1210 * Read block file usage bitmaps.
1211 *
1212 * @param state The backing store state to put the loaded entries in.
1213 * @return NSERROR_OK on success or error code on failure.
1214 */
1215static nserror
1216read_blocks(struct store_state *state)
1217{
1218 int bfidx; /* block file index */
1219 int elem_idx;
1220 int fd;
1221 ssize_t rd;
1222 char *fname = NULL((void*)0);
1223 nserror ret;
1224
1225 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, BLOCKS_FNAME"blocks");
1226 if (ret != NSERROR_OK) {
1227 return ret;
1228 }
1229
1230 NSLOG(netsurf, INFO, "Initialising block use map from %s", fname)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1230
, }; nslog__log(&_nslog_ctx, "Initialising block use map from %s"
, fname); } } while(0)
;
1231
1232 fd = open(fname, O_RDWR02);
1233 free(fname);
1234 if (fd != -1) {
1235 /* initialise block file use array */
1236 for (elem_idx = 0; elem_idx < ENTRY_ELEM_COUNT; elem_idx++) {
1237 for (bfidx = 0; bfidx < BLOCK_FILE_COUNT(16 - 10); bfidx++) {
1238 rd = read(fd,
1239 &state->blocks[elem_idx][bfidx].use_map[0],
1240 BLOCK_USE_MAP_SIZE(1 << (10 - 3)));
1241 if (rd <= 0) {
1242 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1245
, }; nslog__log(&_nslog_ctx, "reading block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
1243 "reading block file %d use index on file number %d failed",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1245
, }; nslog__log(&_nslog_ctx, "reading block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
1244 elem_idx,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1245
, }; nslog__log(&_nslog_ctx, "reading block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
1245 bfidx)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1245
, }; nslog__log(&_nslog_ctx, "reading block file %d use index on file number %d failed"
, elem_idx, bfidx); } } while(0)
;
1246 goto rd_err;
1247 }
1248 }
1249 }
1250 rd_err:
1251 close(fd);
1252
1253 } else {
1254 NSLOG(netsurf, INFO, "Initialising block use map to defaults")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1254
, }; nslog__log(&_nslog_ctx, "Initialising block use map to defaults"
); } } while(0)
;
1255 /* ensure block 0 (invalid sentinel) is skipped */
1256 state->blocks[ENTRY_ELEM_DATA][0].use_map[0] = 1;
1257 state->blocks[ENTRY_ELEM_META][0].use_map[0] = 1;
1258 }
1259
1260 /* initialise block file file descriptors */
1261 for (bfidx = 0; bfidx < BLOCK_FILE_COUNT(16 - 10); bfidx++) {
1262 state->blocks[ENTRY_ELEM_DATA][bfidx].fd = -1;
1263 state->blocks[ENTRY_ELEM_META][bfidx].fd = -1;
1264 }
1265
1266 return NSERROR_OK;
1267}
1268
1269/**
1270 * Write the cache tag file.
1271 *
1272 * @param state The cache state.
1273 * @return NSERROR_OK on success or error code on failure.
1274 */
1275static nserror
1276write_cache_tag(struct store_state *state)
1277{
1278 FILE *fcachetag;
1279 nserror ret;
1280 char *fname = NULL((void*)0);
1281
1282 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, "CACHEDIR.TAG");
1283 if (ret != NSERROR_OK) {
1284 return ret;
1285 }
1286
1287 fcachetag = fopen(fname, "wb");
1288
1289 free(fname);
1290
1291 if (fcachetag == NULL((void*)0)) {
1292 return NSERROR_NOT_FOUND;
1293 }
1294
1295 fprintf(fcachetag,
1296 "Signature: 8a477f597d28d172789f06886806bc55\n"
1297 "# This file is a cache directory tag created by NetSurf.\n"
1298 "# For information about cache directory tags, see:\n"
1299 "# http://www.brynosaurus.com/cachedir/\n");
1300
1301 fclose(fcachetag);
1302
1303 return NSERROR_OK;
1304}
1305
1306/**
1307 * Write the control file for the current state.
1308 *
1309 * @param state The state to write to the control file.
1310 * @return NSERROR_OK on success or error code on failure.
1311 */
1312static nserror
1313write_control(struct store_state *state)
1314{
1315 FILE *fcontrol;
1316 nserror ret;
1317 char *fname = NULL((void*)0);
1318
1319 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, "control");
1320 if (ret != NSERROR_OK) {
1321 return ret;
1322 }
1323
1324 NSLOG(netsurf, INFO, "writing control file \"%s\"", fname)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1324
, }; nslog__log(&_nslog_ctx, "writing control file \"%s\""
, fname); } } while(0)
;
1325
1326 ret = netsurf_mkdir_all(fname);
1327 if (ret != NSERROR_OK) {
1328 free(fname);
1329 return ret;
1330 }
1331
1332 fcontrol = fopen(fname, "wb");
1333
1334 free(fname);
1335
1336 if (fcontrol == NULL((void*)0)) {
1337 return NSERROR_NOT_FOUND;
1338 }
1339
1340 fprintf(fcontrol, "%u%c", CONTROL_VERSION202, 0);
1341
1342 fclose(fcontrol);
1343
1344 return NSERROR_OK;
1345}
1346
1347
1348/**
1349 * Read and parse the control file.
1350 *
1351 * @param state The state to read from the control file.
1352 * @return NSERROR_OK on success or error code on failure.
1353 */
1354static nserror
1355read_control(struct store_state *state)
1356{
1357 nserror ret;
1358 FILE *fcontrol;
1359 unsigned int ctrlversion;
1360 char *fname = NULL((void*)0);
1361
1362 ret = netsurf_mkpath(&fname, NULL((void*)0), 2, state->path, "control");
1363 if (ret != NSERROR_OK) {
1364 return ret;
1365 }
1366
1367 NSLOG(netsurf, INFO, "opening control file \"%s\"", fname)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1367
, }; nslog__log(&_nslog_ctx, "opening control file \"%s\""
, fname); } } while(0)
;
1368
1369 fcontrol = fopen(fname, "rb");
1370
1371 free(fname);
1372
1373 if (fcontrol == NULL((void*)0)) {
1374 /* unable to open control file */
1375 if (errno(*__errno_location ()) == ENOENT2) {
1376 return NSERROR_NOT_FOUND;
1377 } else {
1378 return NSERROR_INIT_FAILED;
1379 }
1380 }
1381
1382 /* read control and setup new state */
1383
1384 /* first line is version */
1385 if (fscanf(fcontrol, "%u", &ctrlversion) != 1) {
1386 goto control_error;
1387 }
1388
1389 if (ctrlversion != CONTROL_VERSION202) {
1390 goto control_error;
1391 }
1392
1393 if (fgetc(fcontrol) != 0) {
1394 goto control_error;
1395 }
1396
1397 fclose(fcontrol);
1398
1399 return NSERROR_OK;
1400
1401control_error: /* problem with the control file */
1402
1403 fclose(fcontrol);
1404
1405 return NSERROR_INIT_FAILED;
1406}
1407
1408
1409
1410
1411/* Functions exported in the backing store table */
1412
1413/**
1414 * Initialise the backing store.
1415 *
1416 * @param parameters to configure backing store.
1417 * @return NSERROR_OK on success or error code on failure.
1418 */
1419static nserror
1420initialise(const struct llcache_store_parameters *parameters)
1421{
1422 struct store_state *newstate;
1423 nserror ret;
1424
1425 /* check backing store is not already initialised */
1426 if (storestate != NULL((void*)0)) {
1427 return NSERROR_INIT_FAILED;
1428 }
1429
1430 /* if we are not allowed any space simply give up on init */
1431 if (parameters->limit == 0) {
1432 return NSERROR_OK;
1433 }
1434
1435 /* if the path to the cache directory is not set do not init */
1436 if (parameters->path == NULL((void*)0)) {
1437 return NSERROR_OK;
1438 }
1439
1440 /* allocate new store state and set defaults */
1441 newstate = calloc(1, sizeof(struct store_state));
1442 if (newstate == NULL((void*)0)) {
1443 return NSERROR_NOMEM;
1444 }
1445
1446 newstate->path = strdup(parameters->path);
1447 newstate->limit = parameters->limit;
1448 newstate->hysteresis = parameters->hysteresis;
1449
1450 /* read store control and create new if required */
1451 ret = read_control(newstate);
1452 if (ret != NSERROR_OK) {
1453 if (ret == NSERROR_NOT_FOUND) {
1454 NSLOG(netsurf, INFO, "cache control file not found, making fresh")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1454
, }; nslog__log(&_nslog_ctx, "cache control file not found, making fresh"
); } } while(0)
;
1455 } else {
1456 NSLOG(netsurf, ERROR, "read control failed %s",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1457
, }; nslog__log(&_nslog_ctx, "read control failed %s", messages_get_errorcode
(ret)); } } while(0)
1457 messages_get_errorcode(ret))do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1457
, }; nslog__log(&_nslog_ctx, "read control failed %s", messages_get_errorcode
(ret)); } } while(0)
;
1458 ret = netsurf_recursive_rm(newstate->path);
1459 if (ret != NSERROR_OK) {
1460 NSLOG(netsurf, WARNING, "Error `%s` while removing `%s`",do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1461
, }; nslog__log(&_nslog_ctx, "Error `%s` while removing `%s`"
, messages_get_errorcode(ret), newstate->path); } } while(
0)
1461 messages_get_errorcode(ret), newstate->path)do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1461
, }; nslog__log(&_nslog_ctx, "Error `%s` while removing `%s`"
, messages_get_errorcode(ret), newstate->path); } } while(
0)
;
1462 NSLOG(netsurf, WARNING, "Unable to clean up partial cache state.")do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1462
, }; nslog__log(&_nslog_ctx, "Unable to clean up partial cache state."
); } } while(0)
;
1463 NSLOG(netsurf, WARNING, "Funky behaviour may ensue.")do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1463
, }; nslog__log(&_nslog_ctx, "Funky behaviour may ensue."
); } } while(0)
;
1464 } else {
1465 NSLOG(netsurf, INFO, "Successfully removed old cache from `%s`",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1466
, }; nslog__log(&_nslog_ctx, "Successfully removed old cache from `%s`"
, newstate->path); } } while(0)
1466 newstate->path)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1466
, }; nslog__log(&_nslog_ctx, "Successfully removed old cache from `%s`"
, newstate->path); } } while(0)
;
1467 }
1468 }
1469 ret = write_control(newstate);
1470 if (ret == NSERROR_OK) {
1471 unlink_entries(newstate);
1472 write_cache_tag(newstate);
1473 }
1474 }
1475 if (ret != NSERROR_OK) {
1476 /* that went well obviously */
1477 free(newstate->path);
1478 free(newstate);
1479 return ret;
1480 }
1481
1482 /* read filesystem entries */
1483 ret = read_entries(newstate);
1484 if (ret != NSERROR_OK) {
1485 /* that went well obviously */
1486 free(newstate->path);
1487 free(newstate);
1488 return ret;
1489 }
1490
1491 /* read blocks */
1492 ret = read_blocks(newstate);
1493 if (ret != NSERROR_OK) {
1494 /* oh dear */
1495 hashmap_destroy(newstate->entries);
1496 free(newstate->path);
1497 free(newstate);
1498 return ret;
1499 }
1500
1501 storestate = newstate;
1502
1503 NSLOG(netsurf, INFO, "FS backing store init successful")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1503
, }; nslog__log(&_nslog_ctx, "FS backing store init successful"
); } } while(0)
;
1504
1505 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1509
, }; nslog__log(&_nslog_ctx, "path:%s limit:%""zu"" hyst:%"
"zu", newstate->path, newstate->limit, newstate->hysteresis
); } } while(0)
1506 "path:%s limit:%"PRIsizet" hyst:%"PRIsizet,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1509
, }; nslog__log(&_nslog_ctx, "path:%s limit:%""zu"" hyst:%"
"zu", newstate->path, newstate->limit, newstate->hysteresis
); } } while(0)
1507 newstate->path,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1509
, }; nslog__log(&_nslog_ctx, "path:%s limit:%""zu"" hyst:%"
"zu", newstate->path, newstate->limit, newstate->hysteresis
); } } while(0)
1508 newstate->limit,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1509
, }; nslog__log(&_nslog_ctx, "path:%s limit:%""zu"" hyst:%"
"zu", newstate->path, newstate->limit, newstate->hysteresis
); } } while(0)
1509 newstate->hysteresis)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1509
, }; nslog__log(&_nslog_ctx, "path:%s limit:%""zu"" hyst:%"
"zu", newstate->path, newstate->limit, newstate->hysteresis
); } } while(0)
;
1510 NSLOG(netsurf, INFO, "Using %"PRIu64"/%"PRIsizet,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1511
, }; nslog__log(&_nslog_ctx, "Using %""l" "u""/%""zu", newstate
->total_alloc, newstate->limit); } } while(0)
1511 newstate->total_alloc, newstate->limit)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1511
, }; nslog__log(&_nslog_ctx, "Using %""l" "u""/%""zu", newstate
->total_alloc, newstate->limit); } } while(0)
;
1512
1513 return NSERROR_OK;
1514}
1515
1516
1517/**
1518 * Finalise the backing store.
1519 *
1520 * \todo This will cause the backing store to leak any outstanding memory
1521 * allocations. This will probably best be done by a global use count.
1522 *
1523 * @return NSERROR_OK on success.
1524 */
1525static nserror
1526finalise(void)
1527{
1528 int bf; /* block file index */
1529 unsigned int op_count;
1530
1531 if (storestate != NULL((void*)0)) {
1532 guit->misc->schedule(-1, control_maintenance, storestate);
1533 write_entries(storestate);
1534 write_blocks(storestate);
1535
1536 /* ensure all block files are closed */
1537 for (bf = 0; bf < BLOCK_FILE_COUNT(16 - 10); bf++) {
1538 if (storestate->blocks[ENTRY_ELEM_DATA][bf].fd != -1) {
1539 close(storestate->blocks[ENTRY_ELEM_DATA][bf].fd);
1540 }
1541 if (storestate->blocks[ENTRY_ELEM_META][bf].fd != -1) {
1542 close(storestate->blocks[ENTRY_ELEM_META][bf].fd);
1543 }
1544 }
1545
1546 op_count = storestate->hit_count + storestate->miss_count;
1547
1548 /* avoid division by zero */
1549 if (op_count > 0) {
1550 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1551 "Cache total/hit/miss/fail (counts) %d/%"PRIsizet"/%"PRIsizet"/%d (100%%/%"PRIsizet"%%/%"PRIsizet"%%/%d%%)",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1552 op_count,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1553 storestate->hit_count,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1554 storestate->miss_count,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1555 0,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1556 (storestate->hit_count * 100) / op_count,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1557 (storestate->miss_count * 100) / op_count,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
1558 0)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1558
, }; nslog__log(&_nslog_ctx, "Cache total/hit/miss/fail (counts) %d/%"
"zu""/%""zu""/%d (100%%/%""zu""%%/%""zu""%%/%d%%)", op_count,
storestate->hit_count, storestate->miss_count, 0, (storestate
->hit_count * 100) / op_count, (storestate->miss_count *
100) / op_count, 0); } } while(0)
;
1559 }
1560
1561 hashmap_destroy(storestate->entries);
1562 free(storestate->path);
1563 free(storestate);
1564 storestate = NULL((void*)0);
1565 }
1566 return NSERROR_OK;
1567}
1568
1569
1570/**
1571 * Write an element of an entry to backing storage in a small block file.
1572 *
1573 * \param state The backing store state to use.
1574 * \param bse The entry to store
1575 * \param elem_idx The element index within the entry.
1576 * \return NSERROR_OK on success or error code.
1577 */
1578static nserror store_write_block(struct store_state *state,
1579 struct store_entry *bse,
1580 int elem_idx)
1581{
1582 block_index_t bf = (bse->elem[elem_idx].block >> BLOCK_ENTRY_COUNT10) &
1583 ((1 << BLOCK_FILE_COUNT(16 - 10)) - 1); /* block file block resides in */
1584 block_index_t bi = bse->elem[elem_idx].block & ((1U << BLOCK_ENTRY_COUNT10) -1); /* block index in file */
1585 ssize_t wr;
1586 off_t offst;
1587
1588 /* ensure the block file fd is good */
1589 if (state->blocks[elem_idx][bf].fd == -1) {
1590 state->blocks[elem_idx][bf].fd = store_open(state, bf,
1591 elem_idx + ENTRY_ELEM_COUNT, O_CREAT0100 | O_RDWR02);
1592 if (state->blocks[elem_idx][bf].fd == -1) {
1593 NSLOG(netsurf, ERROR, "Open failed errno %d", errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1593
, }; nslog__log(&_nslog_ctx, "Open failed errno %d", (*__errno_location
())); } } while(0)
;
1594 return NSERROR_SAVE_FAILED;
1595 }
1596
1597 /* flag that a block file has been opened */
1598 state->blocks_opened = true1;
1599 }
1600
1601 offst = (unsigned int)bi << log2_block_size[elem_idx];
1602
1603 wr = nsu_pwrite(state->blocks[elem_idx][bf].fd,
1604 bse->elem[elem_idx].data,
1605 bse->elem[elem_idx].size,
1606 offst);
1607 if (wr != (ssize_t)bse->elem[elem_idx].size) {
1608 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1609 "Write failed %"PRIssizet" of %"PRId32" bytes from %p at %"PRIsizet" block %"PRIu16" errno %d",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1610 wr,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1611 bse->elem[elem_idx].size,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1612 bse->elem[elem_idx].data,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1613 (size_t)offst,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1614 bse->elem[elem_idx].block,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
1615 errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1615
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p at %""zu"" block %""u"" errno %d", wr, bse->
elem[elem_idx].size, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block, (*__errno_location ())); } } while
(0)
;
1616 return NSERROR_SAVE_FAILED;
1617 }
1618
1619 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1622
, }; nslog__log(&_nslog_ctx, "Wrote %""zd"" bytes from %p at %"
"zu"" block %d", wr, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block); } } while(0)
1620 "Wrote %"PRIssizet" bytes from %p at %"PRIsizet" block %d", wr,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1622
, }; nslog__log(&_nslog_ctx, "Wrote %""zd"" bytes from %p at %"
"zu"" block %d", wr, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block); } } while(0)
1621 bse->elem[elem_idx].data, (size_t)offst,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1622
, }; nslog__log(&_nslog_ctx, "Wrote %""zd"" bytes from %p at %"
"zu"" block %d", wr, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block); } } while(0)
1622 bse->elem[elem_idx].block)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1622
, }; nslog__log(&_nslog_ctx, "Wrote %""zd"" bytes from %p at %"
"zu"" block %d", wr, bse->elem[elem_idx].data, (size_t)offst
, bse->elem[elem_idx].block); } } while(0)
;
1623
1624 return NSERROR_OK;
1625}
1626
1627/**
1628 * Write an element of an entry to backing storage as an individual file.
1629 *
1630 * \param state The backing store state to use.
1631 * \param bse The entry to store
1632 * \param elem_idx The element index within the entry.
1633 * \return NSERROR_OK on success or error code.
1634 */
1635static nserror store_write_file(struct store_state *state,
1636 struct store_entry *bse,
1637 int elem_idx)
1638{
1639 ssize_t wr;
1640 int fd;
1641 int err;
1642
1643 fd = store_open(state, nsurl_hash(bse->url), elem_idx, O_CREAT0100 | O_WRONLY01);
1644 if (fd < 0) {
1645 perror("");
1646 NSLOG(netsurf, ERROR, "Open failed %d errno %d", fd, errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1646
, }; nslog__log(&_nslog_ctx, "Open failed %d errno %d", fd
, (*__errno_location ())); } } while(0)
;
1647 return NSERROR_SAVE_FAILED;
1648 }
1649
1650 wr = write(fd, bse->elem[elem_idx].data, bse->elem[elem_idx].size);
1651 err = errno(*__errno_location ()); /* close can change errno */
1652
1653 close(fd);
1654 if (wr != (ssize_t)bse->elem[elem_idx].size) {
1655 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1660
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p errno %d", wr, bse->elem[elem_idx].size, bse
->elem[elem_idx].data, err); } } while(0)
1656 "Write failed %"PRIssizet" of %"PRId32" bytes from %p errno %d",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1660
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p errno %d", wr, bse->elem[elem_idx].size, bse
->elem[elem_idx].data, err); } } while(0)
1657 wr,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1660
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p errno %d", wr, bse->elem[elem_idx].size, bse
->elem[elem_idx].data, err); } } while(0)
1658 bse->elem[elem_idx].size,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1660
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p errno %d", wr, bse->elem[elem_idx].size, bse
->elem[elem_idx].data, err); } } while(0)
1659 bse->elem[elem_idx].data,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1660
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p errno %d", wr, bse->elem[elem_idx].size, bse
->elem[elem_idx].data, err); } } while(0)
1660 err)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1660
, }; nslog__log(&_nslog_ctx, "Write failed %""zd"" of %""d"
" bytes from %p errno %d", wr, bse->elem[elem_idx].size, bse
->elem[elem_idx].data, err); } } while(0)
;
1661
1662 /** @todo Delete the file? */
1663 return NSERROR_SAVE_FAILED;
1664 }
1665
1666 NSLOG(netsurf, VERBOSE, "Wrote %"PRIssizet" bytes from %p", wr,do { if (NSLOG_LEVEL_VERBOSE >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_VERBOSE, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1667
, }; nslog__log(&_nslog_ctx, "Wrote %""zd"" bytes from %p"
, wr, bse->elem[elem_idx].data); } } while(0)
1667 bse->elem[elem_idx].data)do { if (NSLOG_LEVEL_VERBOSE >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_VERBOSE, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1667
, }; nslog__log(&_nslog_ctx, "Wrote %""zd"" bytes from %p"
, wr, bse->elem[elem_idx].data); } } while(0)
;
1668
1669 return NSERROR_OK;
1670}
1671
1672/**
1673 * Place an object in the backing store.
1674 *
1675 * takes ownership of the heap block passed in.
1676 *
1677 * @param url The url is used as the unique primary key for the data.
1678 * @param bsflags The flags to control how the object is stored.
1679 * @param data The objects source data.
1680 * @param datalen The length of the \a data.
1681 * @return NSERROR_OK on success or error code on failure.
1682 */
1683static nserror
1684store(nsurl *url,
1685 enum backing_store_flags bsflags,
1686 uint8_t *data,
1687 const size_t datalen)
1688{
1689 nserror ret;
1690 struct store_entry *bse;
1691 int elem_idx;
1692
1693 /* check backing store is initialised */
1694 if (storestate == NULL((void*)0)) {
1695 return NSERROR_INIT_FAILED;
1696 }
1697
1698 /* calculate the entry element index */
1699 if ((bsflags & BACKING_STORE_META) != 0) {
1700 elem_idx = ENTRY_ELEM_META;
1701 } else {
1702 elem_idx = ENTRY_ELEM_DATA;
1703 }
1704
1705 /* set the store entry up */
1706 ret = set_store_entry(storestate, url, elem_idx, data, datalen, &bse);
1707 if (ret != NSERROR_OK) {
1708 NSLOG(netsurf, ERROR, "store entry setting failed")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1708
, }; nslog__log(&_nslog_ctx, "store entry setting failed"
); } } while(0)
;
1709 return ret;
1710 }
1711
1712 if (bse->elem[elem_idx].block != 0) {
1713 /* small block storage */
1714 ret = store_write_block(storestate, bse, elem_idx);
1715 } else {
1716 /* separate file in backing store */
1717 ret = store_write_file(storestate, bse, elem_idx);
1718 }
1719
1720 return ret;
1721}
1722
1723/**
1724 * release any allocation for an entry
1725 */
1726static nserror entry_release_alloc(struct store_entry_element *elem)
1727{
1728 if ((elem->flags & ENTRY_ELEM_FLAG_HEAP) != 0) {
1729 elem->ref--;
1730 if (elem->ref == 0) {
1731 NSLOG(netsurf, DEEPDEBUG, "freeing %p", elem->data)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1731, }; nslog__log(&_nslog_ctx
, "freeing %p", elem->data); } } while(0)
;
1732 free(elem->data);
1733 elem->flags &= ~ENTRY_ELEM_FLAG_HEAP;
1734 }
1735 }
1736 return NSERROR_OK;
1737}
1738
1739
1740/**
1741 * Read an element of an entry from a small block file in the backing storage.
1742 *
1743 * \param state The backing store state to use.
1744 * \param bse The entry to read.
1745 * \param elem_idx The element index within the entry.
1746 * \return NSERROR_OK on success or error code.
1747 */
1748static nserror store_read_block(struct store_state *state,
1749 struct store_entry *bse,
1750 int elem_idx)
1751{
1752 block_index_t bf = (bse->elem[elem_idx].block >> BLOCK_ENTRY_COUNT10) &
1753 ((1 << BLOCK_FILE_COUNT(16 - 10)) - 1); /* block file block resides in */
1754 block_index_t bi = bse->elem[elem_idx].block & ((1 << BLOCK_ENTRY_COUNT10) -1); /* block index in file */
1755 ssize_t rd;
1756 off_t offst;
1757
1758 /* ensure the block file fd is good */
1759 if (state->blocks[elem_idx][bf].fd == -1) {
1760 state->blocks[elem_idx][bf].fd = store_open(state, bf,
1761 elem_idx + ENTRY_ELEM_COUNT, O_CREAT0100 | O_RDWR02);
1762 if (state->blocks[elem_idx][bf].fd == -1) {
1763 NSLOG(netsurf, ERROR, "Open failed errno %d", errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1763
, }; nslog__log(&_nslog_ctx, "Open failed errno %d", (*__errno_location
())); } } while(0)
;
1764 return NSERROR_SAVE_FAILED;
1765 }
1766
1767 /* flag that a block file has been opened */
1768 state->blocks_opened = true1;
1769 }
1770
1771 offst = (unsigned int)bi << log2_block_size[elem_idx];
1772
1773 rd = nsu_pread(state->blocks[elem_idx][bf].fd,
1774 bse->elem[elem_idx].data,
1775 bse->elem[elem_idx].size,
1776 offst);
1777 if (rd != (ssize_t)bse->elem[elem_idx].size) {
1778 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1779 "Failed reading %"PRIssizet" of %"PRId32" bytes into %p from %"PRIsizet" block %"PRIu16" errno %d",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1780 rd,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1781 bse->elem[elem_idx].size,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1782 bse->elem[elem_idx].data,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1783 (size_t)offst,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1784 bse->elem[elem_idx].block,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
1785 errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1785
, }; nslog__log(&_nslog_ctx, "Failed reading %""zd"" of %"
"d"" bytes into %p from %""zu"" block %""u"" errno %d", rd, bse
->elem[elem_idx].size, bse->elem[elem_idx].data, (size_t
)offst, bse->elem[elem_idx].block, (*__errno_location ()))
; } } while(0)
;
1786 return NSERROR_SAVE_FAILED;
1787 }
1788
1789 NSLOG(netsurf, DEEPDEBUG,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1792, }; nslog__log(&_nslog_ctx
, "Read %""zd"" bytes into %p from %""zu"" block %d", rd, bse
->elem[elem_idx].data, (size_t)offst, bse->elem[elem_idx
].block); } } while(0)
1790 "Read %"PRIssizet" bytes into %p from %"PRIsizet" block %d", rd,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1792, }; nslog__log(&_nslog_ctx
, "Read %""zd"" bytes into %p from %""zu"" block %d", rd, bse
->elem[elem_idx].data, (size_t)offst, bse->elem[elem_idx
].block); } } while(0)
1791 bse->elem[elem_idx].data, (size_t)offst,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1792, }; nslog__log(&_nslog_ctx
, "Read %""zd"" bytes into %p from %""zu"" block %d", rd, bse
->elem[elem_idx].data, (size_t)offst, bse->elem[elem_idx
].block); } } while(0)
1792 bse->elem[elem_idx].block)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1792, }; nslog__log(&_nslog_ctx
, "Read %""zd"" bytes into %p from %""zu"" block %d", rd, bse
->elem[elem_idx].data, (size_t)offst, bse->elem[elem_idx
].block); } } while(0)
;
1793
1794 return NSERROR_OK;
1795}
1796
1797/**
1798 * Read an element of an entry from an individual file in the backing storage.
1799 *
1800 * \param state The backing store state to use.
1801 * \param bse The entry to read.
1802 * \param elem_idx The element index within the entry.
1803 * \return NSERROR_OK on success or error code.
1804 */
1805static nserror store_read_file(struct store_state *state,
1806 struct store_entry *bse,
1807 int elem_idx)
1808{
1809 int fd;
1810 ssize_t rd; /* return from read */
1811 int ret = NSERROR_OK;
1812 size_t tot = 0; /* total size */
1813
1814 /* separate file in backing store */
1815 fd = store_open(storestate, nsurl_hash(bse->url), elem_idx, O_RDONLY00);
1816 if (fd < 0) {
1817 NSLOG(netsurf, ERROR, "Open failed %d errno %d", fd, errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1817
, }; nslog__log(&_nslog_ctx, "Open failed %d errno %d", fd
, (*__errno_location ())); } } while(0)
;
1818 /** @todo should this invalidate the entry? */
1819 return NSERROR_NOT_FOUND;
1820 }
1821
1822 while (tot < bse->elem[elem_idx].size) {
1823 rd = read(fd,
1824 bse->elem[elem_idx].data + tot,
1825 bse->elem[elem_idx].size - tot);
1826 if (rd <= 0) {
1827 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1830
, }; nslog__log(&_nslog_ctx, "read error returned %""zd"" errno %d"
, rd, (*__errno_location ())); } } while(0)
1828 "read error returned %"PRIssizet" errno %d",do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1830
, }; nslog__log(&_nslog_ctx, "read error returned %""zd"" errno %d"
, rd, (*__errno_location ())); } } while(0)
1829 rd,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1830
, }; nslog__log(&_nslog_ctx, "read error returned %""zd"" errno %d"
, rd, (*__errno_location ())); } } while(0)
1830 errno)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1830
, }; nslog__log(&_nslog_ctx, "read error returned %""zd"" errno %d"
, rd, (*__errno_location ())); } } while(0)
;
1831 ret = NSERROR_NOT_FOUND;
1832 break;
1833 }
1834 tot += rd;
1835 }
1836
1837 close(fd);
1838
1839 NSLOG(netsurf, DEEPDEBUG, "Read %"PRIsizet" bytes into %p", tot,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1840, }; nslog__log(&_nslog_ctx
, "Read %""zu"" bytes into %p", tot, bse->elem[elem_idx].data
); } } while(0)
1840 bse->elem[elem_idx].data)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1840, }; nslog__log(&_nslog_ctx
, "Read %""zu"" bytes into %p", tot, bse->elem[elem_idx].data
); } } while(0)
;
1841
1842 return ret;
1843}
1844
1845/**
1846 * Retrieve an object from the backing store.
1847 *
1848 * @param[in] url The url is used as the unique primary key for the data.
1849 * @param[in] bsflags The flags to control how the object is retrieved.
1850 * @param[out] data_out The objects data.
1851 * @param[out] datalen_out The length of the \a data retrieved.
1852 * @return NSERROR_OK on success or error code on failure.
1853 */
1854static nserror
1855fetch(nsurl *url,
1856 enum backing_store_flags bsflags,
1857 uint8_t **data_out,
1858 size_t *datalen_out)
1859{
1860 nserror ret;
1861 struct store_entry *bse;
1862 struct store_entry_element *elem;
1863 int elem_idx;
1864
1865 /* check backing store is initialised */
1866 if (storestate == NULL((void*)0)) {
1867 return NSERROR_INIT_FAILED;
1868 }
1869
1870 /* fetch store entry */
1871 ret = get_store_entry(storestate, url, &bse);
1872 if (ret != NSERROR_OK) {
1873 NSLOG(netsurf, DEBUG, "Entry for %s not found", nsurl_access(url))do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1873
, }; nslog__log(&_nslog_ctx, "Entry for %s not found", nsurl_access
(url)); } } while(0)
;
1874 storestate->miss_count++;
1875 return ret;
1876 }
1877 storestate->hit_count++;
1878
1879 NSLOG(netsurf, DEBUG, "retrieving cache data for url:%s",do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1880
, }; nslog__log(&_nslog_ctx, "retrieving cache data for url:%s"
, nsurl_access(url)); } } while(0)
1880 nsurl_access(url))do { if (NSLOG_LEVEL_DEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEBUG, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1880
, }; nslog__log(&_nslog_ctx, "retrieving cache data for url:%s"
, nsurl_access(url)); } } while(0)
;
1881
1882 /* calculate the entry element index */
1883 if ((bsflags & BACKING_STORE_META) != 0) {
1884 elem_idx = ENTRY_ELEM_META;
1885 } else {
1886 elem_idx = ENTRY_ELEM_DATA;
1887 }
1888 elem = &bse->elem[elem_idx];
1889
1890 /* if an allocation already exists return it */
1891 if ((elem->flags & ENTRY_ELEM_FLAG_HEAP) != 0) {
1892 /* use the existing allocation and bump the ref count. */
1893 elem->ref++;
1894
1895 NSLOG(netsurf, DEEPDEBUG,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1897, }; nslog__log(&_nslog_ctx
, "Using existing entry (%p) allocation %p refs:%d", bse, elem
->data, elem->ref); } } while(0)
1896 "Using existing entry (%p) allocation %p refs:%d", bse,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1897, }; nslog__log(&_nslog_ctx
, "Using existing entry (%p) allocation %p refs:%d", bse, elem
->data, elem->ref); } } while(0)
1897 elem->data, elem->ref)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1897, }; nslog__log(&_nslog_ctx
, "Using existing entry (%p) allocation %p refs:%d", bse, elem
->data, elem->ref); } } while(0)
;
1898
1899 } else {
1900 /* allocate from the heap */
1901 elem->data = malloc(elem->size);
1902 if (elem->data == NULL((void*)0)) {
1903 NSLOG(netsurf, ERROR,do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1904
, }; nslog__log(&_nslog_ctx, "Failed to create new heap allocation"
); } } while(0)
1904 "Failed to create new heap allocation")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_ERROR, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1904
, }; nslog__log(&_nslog_ctx, "Failed to create new heap allocation"
); } } while(0)
;
1905 return NSERROR_NOMEM;
1906 }
1907 NSLOG(netsurf, DEEPDEBUG, "Created new heap allocation %p",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1908, }; nslog__log(&_nslog_ctx
, "Created new heap allocation %p", elem->data); } } while
(0)
1908 elem->data)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_DEEPDEBUG, "content/fs_backing_store.c", sizeof
("content/fs_backing_store.c") - 1, __PRETTY_FUNCTION__, sizeof
(__PRETTY_FUNCTION__) - 1, 1908, }; nslog__log(&_nslog_ctx
, "Created new heap allocation %p", elem->data); } } while
(0)
;
1909
1910 /* mark the entry as having a valid heap allocation */
1911 elem->flags |= ENTRY_ELEM_FLAG_HEAP;
1912 elem->ref = 1;
1913
1914 /* fill the new block */
1915 if (elem->block != 0) {
1916 ret = store_read_block(storestate, bse, elem_idx);
1917 } else {
1918 ret = store_read_file(storestate, bse, elem_idx);
1919 }
1920 }
1921
1922 /* free the allocation if there is a read error */
1923 if (ret != NSERROR_OK) {
1924 entry_release_alloc(elem);
1925 } else {
1926 /* update stats and setup return pointers */
1927 storestate->hit_size += elem->size;
1928
1929 *data_out = elem->data;
1930 *datalen_out = elem->size;
1931 }
1932
1933 return ret;
1934}
1935
1936
1937/**
1938 * release a previously fetched or stored memory object.
1939 *
1940 * @param[in] url The url is used as the unique primary key to invalidate.
1941 * @param[in] bsflags The flags to control how the object data is released.
1942 * @return NSERROR_OK on success or error code on failure.
1943 */
1944static nserror release(nsurl *url, enum backing_store_flags bsflags)
1945{
1946 nserror ret;
1947 struct store_entry *bse;
1948 struct store_entry_element *elem;
1949
1950 /* check backing store is initialised */
1951 if (storestate == NULL((void*)0)) {
1952 return NSERROR_INIT_FAILED;
1953 }
1954
1955 ret = get_store_entry(storestate, url, &bse);
1956 if (ret != NSERROR_OK) {
1957 NSLOG(netsurf, WARNING, "entry not found")do { if (NSLOG_LEVEL_WARNING >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_WARNING, "content/fs_backing_store.c", sizeof("content/fs_backing_store.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1957
, }; nslog__log(&_nslog_ctx, "entry not found"); } } while
(0)
;
1958 return ret;
1959 }
1960
1961 /* the entry element */
1962 if ((bsflags & BACKING_STORE_META) != 0) {
1963 elem = &bse->elem[ENTRY_ELEM_META];
1964 } else {
1965 elem = &bse->elem[ENTRY_ELEM_DATA];
1966 }
1967
1968 ret = entry_release_alloc(elem);
1969
1970 /* if the entry has previously been invalidated but had
1971 * allocation it must be invalidated fully now the allocation
1972 * has been released.
1973 */
1974 if ((ret == NSERROR_OK) &&
1975 ((bse->flags & ENTRY_FLAGS_INVALID) != 0)) {
1976 ret = invalidate_entry(storestate, bse);
1977 }
1978
1979 return ret;
1980}
1981
1982
1983/**
1984 * Invalidate a source object from the backing store.
1985 *
1986 * The entry (if present in the backing store) must no longer
1987 * be returned as a result to the fetch or meta operations.
1988 *
1989 * @param url The url is used as the unique primary key to invalidate.
1990 * @return NSERROR_OK on success or error code on failure.
1991 */
1992static nserror
1993invalidate(nsurl *url)
1994{
1995 nserror ret;
1996 struct store_entry *bse;
1997
1998 /* check backing store is initialised */
1999 if (storestate == NULL((void*)0)) {
2000 return NSERROR_INIT_FAILED;
2001 }
2002
2003 ret = get_store_entry(storestate, url, &bse);
2004 if (ret != NSERROR_OK) {
2005 return ret;
2006 }
2007
2008 return invalidate_entry(storestate, bse);
2009}
2010
2011
2012static struct gui_llcache_table llcache_table = {
2013 .initialise = initialise,
2014 .finalise = finalise,
2015 .store = store,
2016 .fetch = fetch,
2017 .invalidate = invalidate,
2018 .release = release,
2019};
2020
2021struct gui_llcache_table *filesystem_llcache_table = &llcache_table;