+/* Map and return a read-only version of a byte range from a cached file. The
+ * CacheFile object must be locked. */
+BlueSkyRCStr *bluesky_cachefile_map_raw(BlueSkyCacheFile *cachefile,
+ off_t offset, size_t size)
+{
+ cachefile->atime = bluesky_get_current_time();
+
+ /* Easy case: the needed data is already in memory */
+ if (cachefile->addr != NULL && offset + size <= cachefile->len)
+ return bluesky_string_new_from_mmap(cachefile, offset, size);
+
+ int fd = openat(cachefile->log->dirfd, cachefile->filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening logfile %s: %m\n",
+ cachefile->filename);
+ return NULL;
+ }
+
+ off_t length = lseek(fd, 0, SEEK_END);
+ if (offset + size > length) {
+ close(fd);
+ return NULL;
+ }
+
+ /* File is not mapped in memory. Map the entire file in, then return a
+ * pointer to just the required data. */
+ if (cachefile->addr == NULL) {
+ cachefile->addr = (const char *)mmap(NULL, length, PROT_READ,
+ MAP_SHARED, fd, 0);
+ cachefile->len = length;
+ g_atomic_int_inc(&cachefile->refcount);
+
+ close(fd);
+ return bluesky_string_new_from_mmap(cachefile, offset, size);
+ }
+
+ /* Otherwise, the file was mapped in but doesn't cover the data we need.
+ * This shouldn't happen much, if at all, but if it does just read the data
+ * we need directly from the file. We lose memory-management benefits of
+ * using mmapped data, but otherwise this works. */
+ char *buf = g_malloc(size);
+ size_t actual_size = readbuf(fd, buf, size);
+ close(fd);
+ if (actual_size != size) {
+ g_free(buf);
+ return NULL;
+ } else {
+ return bluesky_string_new(buf, size);
+ }
+}
+