/** * Iterate on each item of the map, applying callback. */ void map_foreach(const map_t *m, keyval_fn_t cb, void *u) { map_check(m); g_assert(cb); switch (m->type) { case MAP_HASH: htable_foreach(m->u.ht, (ckeyval_fn_t) cb, u); break; case MAP_ORDERED_HASH: ohash_table_foreach(m->u.ot, cb, u); break; case MAP_PATRICIA: { struct pat_foreach ctx; ctx.cb = cb; ctx.u = u; patricia_foreach(m->u.pt, pat_foreach_wrapper, &ctx); } break; case MAP_MAXTYPE: g_assert_not_reached(); } }
/** * ccache_free(cache): * Free the cache and all of its entries. */ void ccache_free(CCACHE * cache) { struct ccache_internal * C = cache; if (cache == NULL) return; /* Free all of the records in the patricia tree. */ patricia_foreach(C->tree, callback_free, NULL); /* Free the patricia tree itself. */ patricia_free(C->tree); /* Unmap memory. */ #ifdef HAVE_MMAP if (C->datalen > 0 && munmap(C->data, C->datalen)) warnp("munmap failed on cache data"); #else free(C->data); #endif /* Free the cache. */ free(C); }
/** * ccache_read(path): * Read the chunkification cache (if present) from the directory ${path}; * return a Patricia tree mapping absolute paths to cache entries. */ CCACHE * ccache_read(const char * path) { struct ccache_internal * C; struct ccache_read_internal R; struct ccache_record * ccr; #ifdef HAVE_MMAP struct stat sb; off_t fpos; long int pagesize; #endif size_t i; uint8_t N[4]; /* The caller must pass a file name to be read. */ assert(path != NULL); /* Allocate memory for the cache. */ if ((C = malloc(sizeof(struct ccache_internal))) == NULL) goto err0; memset(C, 0, sizeof(struct ccache_internal)); /* Create a Patricia tree to store cache entries. */ if ((C->tree = patricia_init()) == NULL) goto err1; /* Construct the name of cache file. */ if (asprintf(&R.s, "%s/cache", path) == -1) { warnp("asprintf"); goto err2; } /* Open the cache file. */ if ((R.f = fopen(R.s, "r")) == NULL) { /* ENOENT isn't an error. */ if (errno != ENOENT) { warnp("fopen(%s)", R.s); goto err3; } /* No cache exists on disk; return an empty cache. */ goto emptycache; } /** * We read the cache file in three steps: * 1. Read a little-endian uint32_t which indicates the number of * records in the cache file. * 2. Read N (record, path suffix) pairs and insert them into a * Patricia tree. * 3. Iterate through the tree and read chunk headers and compressed * entry trailers. */ /* Read the number of cache entries. */ if (fread(N, 4, 1, R.f) != 1) { if (ferror(R.f)) warnp("Error reading cache: %s", R.s); else warn0("Error reading cache: %s", R.s); goto err4; } R.N = le32dec(N); /* Read N (record, path suffix) pairs. */ R.sbuf = NULL; R.sbuflen = R.slen = R.datalen = 0; for (i = 0; i < R.N; i++) { if ((ccr = read_rec(&R)) == NULL) goto err5; if (patricia_insert(C->tree, R.sbuf, R.slen, ccr)) goto err5; C->chunksusage += ccr->nch * sizeof(struct chunkheader); C->trailerusage += ccr->tzlen; } #ifdef HAVE_MMAP /* Obtain page size, since mmapped regions must be page-aligned. */ if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) { warnp("sysconf(_SC_PAGESIZE)"); goto err5; } /* Map the remainder of the cache into memory. */ fpos = ftello(R.f); if (fpos == -1) { warnp("ftello(%s)", R.s); goto err5; } if (fstat(fileno(R.f), &sb)) { warnp("fstat(%s)", R.s); goto err5; } if (sb.st_size != (off_t)(fpos + R.datalen)) { warn0("Cache has incorrect size (%jd, expected %jd)\n", (intmax_t)(sb.st_size), (intmax_t)(fpos + R.datalen)); goto err5; } C->datalen = R.datalen + (fpos % pagesize); if ((C->data = mmap(NULL, C->datalen, PROT_READ, #ifdef MAP_NOCORE MAP_PRIVATE | MAP_NOCORE, #else MAP_PRIVATE, #endif fileno(R.f), fpos - (fpos % pagesize))) == MAP_FAILED) { warnp("mmap(%s)", R.s); goto err5; } R.data = (uint8_t *)C->data + (fpos % pagesize); #else /* Allocate space. */ C->datalen = R.datalen; if (((C->data = malloc(C->datalen)) == NULL) && (C->datalen > 0)) goto err5; if (fread(C->data, C->datalen, 1, R.f) != 1) { warnp("fread(%s)", R.s); goto err6; } R.data = (uint8_t *)C->data; #endif /* Iterate through the tree reading chunk headers and trailers. */ if (patricia_foreach(C->tree, callback_read_data, &R)) { warnp("Error reading cache: %s", R.s); goto err6; } /* Free buffer used for storing paths. */ free(R.sbuf); /* Close the cache file. */ fclose(R.f); /* Free string allocated by asprintf. */ free(R.s); /* Success! */ return (C); emptycache: /* Nothing went wrong, but there's nothing on disk. */ free(R.s); return (C); err6: #ifdef HAVE_MMAP if (C->datalen > 0) munmap(C->data, C->datalen); #else free(C->data); #endif err5: free(R.sbuf); patricia_foreach(C->tree, callback_free, NULL); err4: fclose(R.f); err3: free(R.s); err2: patricia_free(C->tree); err1: free(C); err0: /* Failure! */ return (NULL); }
/** * ccache_write(cache, path): * Write the given chunkification cache into the directory ${path}. */ int ccache_write(CCACHE * cache, const char * path) { struct ccache_internal * C = cache; struct ccache_write_internal W; uint8_t N[4]; char * s_old; /* Construct name of temporary cache file. */ if (asprintf(&W.s, "%s/cache.new", path) == -1) { warnp("asprintf"); goto err0; } /* Open the cache file for writing. */ if ((W.f = fopen(W.s, "w")) == NULL) { warnp("fopen(%s)", W.s); goto err1; } /** * We make three passes through the cache tree: * 1. Counting the number of records which will be written to disk. * This is necessary since records in the cache which are too old * will not be written, but the on-disk cache format starts with * the number of records. * 2. Writing the records and suffixes. * 3. Writing the cached chunk headers and compressed entry trailers. */ /* Count the number of records which need to be written. */ W.N = 0; if (patricia_foreach(C->tree, callback_count, &W)) { warnp("patricia_foreach"); goto err2; }; /* Write the number of records to the file. */ le32enc(N, W.N); if (fwrite(N, 4, 1, W.f) != 1) { warnp("fwrite(%s)", W.s); goto err2; } /* Write the records and suffixes. */ W.sbuf = NULL; W.sbuflen = 0; if (patricia_foreach(C->tree, callback_write_rec, &W)) { warnp("Error writing cache to %s", W.s); goto err2; } free(W.sbuf); /* Write the chunk headers and compressed entry trailers. */ if (patricia_foreach(C->tree, callback_write_data, &W)) { warnp("Error writing cache to %s", W.s); goto err2; } /* Close the file. */ fclose(W.f); /* Construct the name of the old cache file. */ if (asprintf(&s_old, "%s/cache", path) == -1) { warnp("asprintf"); goto err1; } /* Delete the old file, if it exists. */ if (unlink(s_old)) { if (errno != ENOENT) { warnp("unlink(%s)", s_old); free(s_old); goto err1; } } /* Move the new cache file into place. */ if (rename(W.s, s_old)) { warnp("rename(%s, %s)", W.s, s_old); free(s_old); goto err1; } /* Free strings allocated by asprintf. */ free(s_old); free(W.s); /* Success! */ return (0); err2: fclose(W.f); err1: free(W.s); err0: /* Failure! */ return (-1); }