bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainenstruct file_cache *file_cache_new_path(int fd, const char *path)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid file_cache_free(struct file_cache **_cache)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("munmap_anon(%s) failed: %m", cache->path);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
f7e0daedc6838c45df6b2a0724a57662b92e7df4Timo Sirainenint file_cache_set_size(struct file_cache *cache, uoff_t size)
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("file_cache_set_size(%s, %"PRIuUOFF_T"): size too large",
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* grow mmaping */
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("mmap_anon(%s, %"PRIuUOFF_T") failed: %m",
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("mremap_anon(%s, %"PRIuUOFF_T") failed: %m",
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen size_t poffset, psize, dest_offset, dest_size;
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen /* make sure our calculations won't overflow. most likely
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen we'll be reading less data, but allow it anyway so caller
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen doesn't have to deal with any extra checks. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen offset + size - cache->mmap_length > 1024*1024) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* growing more than a megabyte, make sure that the
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file is large enough so we don't allocate memory
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen more than needed */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (file_cache_set_size(cache, offset + size) < 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
77717f1e9e8442fd2cdbca87cbc45c03d0b5fcaeTimo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen while (psize > 0) {
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if ((bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) != 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* page is already in cache */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* this is the last partially cached block.
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen use the caching only if we don't want to
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen read past read_highwater */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* mark the block noncached again and
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* EOF. mark the last block as cached even if it
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen isn't completely. read_highwater tells us how far
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen we've actually made. */
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen /* read_highwater needs to be updated. if we didn't
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen just read that block, we can't trust anymore that
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen we have it cached */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* partial read - probably EOF but make sure. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (file_cache_set_size(cache, offset + size) < 0) {
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* couldn't grow mapping. just make sure the written memory
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen area is invalidated then. */
4498761e9ee18c96a19ce3029258a6390efeb092Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen unsigned int page = cache->read_highwater / page_size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask,
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen /* mark fully written pages cached */
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen for (; first_page < last_page; first_page++) {
74eff344f67ca582eec8c38dfdc6f5f6faa21fd8Timo Sirainenvoid file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned int i;
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen if (offset >= cache->read_highwater || size == 0)
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen /* ignore anything after read highwater */
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen /* we're invalidating everything up to read highwater.
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen drop the highwater position. */
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen cache->read_highwater = offset & ~(page_size-1);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = (offset + size + page_size-1) / page_size;
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen /* tell operating system that we don't need the memory anymore
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen and it may free it. don't bother to do it for single pages,
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen there's a good chance that they get re-read back
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen immediately. */
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen (void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size),
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the first byte */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the middle bytes */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the last byte */