From 2506db95cbd52d2523025050f9fe8d5cda02778e Mon Sep 17 00:00:00 2001 From: phk Date: Tue, 13 Jun 2006 13:14:12 +0000 Subject: [PATCH] Calculate the size of the backing store file. A size can be specified in absolute terms (suffix: k, m, g, t supported), but also as a percentage of the filesystems free space (suffix '%'). If the specified size is larger than an off_t can cope, we bisect repeatedly until it can. If the size exceeds the available space of the filesystem, we truncate to 80% of the free space. Then round down to an integral number of blocks, sized by the larger of the filesystem blocksize and the pagesize. This was tricker than I'd expected... git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@175 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- varnish-cache/bin/varnishd/storage_file.c | 207 +++++++++++++++++++++- 1 file changed, 206 insertions(+), 1 deletion(-) diff --git a/varnish-cache/bin/varnishd/storage_file.c b/varnish-cache/bin/varnishd/storage_file.c index 3e5c9601..eb29ff6e 100644 --- a/varnish-cache/bin/varnishd/storage_file.c +++ b/varnish-cache/bin/varnishd/storage_file.c @@ -5,17 +5,222 @@ */ #include +#include +#include +#include +#include +#include +#include #include +#include #include +#include #include +#include +#include #include "vcl_lang.h" +#include "libvarnish.h" #include "cache.h" +struct smf_sc { + char *filename; + int fd; + uintmax_t filesize; +}; + struct smf { struct storage s; }; +/*--------------------------------------------------------------------*/ + +static void +smf_calcsize(struct smf_sc *sc, const char *size, int newfile) +{ + uintmax_t l; + unsigned bs; + char suff[2]; + int i; + off_t o; + struct statfs fsst; + struct stat st; + + AZ(fstat(sc->fd, &st)); + AZ(fstatfs(sc->fd, &fsst)); + + /* We use units of the larger of filesystem blocksize and pagesize */ + bs = getpagesize(); + if (bs < fsst.f_bsize) + bs = fsst.f_bsize; + + assert(S_ISREG(st.st_mode)); + + i = sscanf(size, "%ju%1s", &l, suff); /* can return -1, 0, 1 or 2 */ + + if (i == 0) { + fprintf(stderr, + "Error: (-sfile) size \"%s\" not understood\n", size); + exit (2); + } + + if (i >= 1 && l == 0) { + fprintf(stderr, + "Error: (-sfile) zero size not permitted\n"); + exit (2); + } + + if (i == -1 && !newfile) /* Use the existing size of the file */ + l = st.st_size; + + /* We must have at least one block */ + if (l < bs) { + if (i == -1) { + fprintf(stderr, + "Info: (-sfile) default to 80%% size\n"); + l = 80; + suff[0] = '%'; + i = 2; + } + + if (i == 2) { + if (suff[0] == 'k' || suff[0] == 'K') + l *= 1024UL; + else if (suff[0] == 'm' || suff[0] == 'M') + l *= 1024UL * 1024UL; + else if (suff[0] == 'g' || suff[0] == 'G') + l *= 1024UL * 1024UL * 1024UL; + else if (suff[0] == 't' || suff[0] == 'T') + l *= (uintmax_t)(1024UL * 1024UL) * + (uintmax_t)(1024UL * 1024UL); + else if (suff[0] == '%') { + l *= fsst.f_bsize * fsst.f_bavail; + l /= 100; + } + } + + o = l; + if (o != l || o < 0) { + fprintf(stderr, + "Warning: size reduced to system limit (off_t)\n"); + do { + l >>= 1; + o = l; + } while (o != l || o < 0); + } + + if (l < st.st_size) { + AZ(ftruncate(sc->fd, l)); + } else if (l - st.st_size > fsst.f_bsize * fsst.f_bavail) { + fprintf(stderr, + "Warning: size larger than filesystem free space," + " reduced to 80%% of free space.\n"); + l = (fsst.f_bsize * fsst.f_bavail * 80) / 100; + } + } + + /* round down to of filesystem blocksize or pagesize */ + l -= (l % bs); + + assert(l >= bs); + + printf("file %s size %ju bytes (%ju fs-blocks, %ju pages)\n", + sc->filename, l, l / fsst.f_bsize, l / getpagesize()); + + sc->filesize = l; +} + +static void +smf_initfile(struct smf_sc *sc, const char *size, int newfile) +{ + smf_calcsize(sc, size, newfile); +} + +static void +smf_init(struct stevedore *parent, const char *spec) +{ + char *size; + char *p, *q; + struct stat st; + struct smf_sc *sc; + + sc = calloc(sizeof *sc, 1); + assert(sc != NULL); + + /* If no size specified, use 50% of filesystem free space */ + if (spec == NULL || *spec == '\0') + spec = "/tmp,50%"; + + if (strchr(spec, ',') == NULL) + asprintf(&p, "%s,", spec); + else + p = strdup(spec); + assert(p != NULL); + size = strchr(p, ','); + assert(size != NULL); + + *size++ = '\0'; + + /* try to create a new file of this name */ + sc->fd = open(p, O_RDWR | O_CREAT | O_EXCL, 0600); + if (sc->fd >= 0) { + sc->filename = p; + smf_initfile(sc, size, 1); + return; + } + + /* it must exist then */ + if (stat(p, &st)) { + fprintf(stderr, + "Error: (-sfile) \"%s\" " + "does not exist and could not be created\n", p); + exit (2); + } + + /* and it should be a file or directory */ + if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) { + fprintf(stderr, + "Error: (-sfile) \"%s\" " + "is neither file nor directory\n", p); + exit (2); + } + + if (S_ISREG(st.st_mode)) { + sc->fd = open(p, O_RDWR); + if (sc->fd < 0) { + fprintf(stderr, + "Error: (-sfile) \"%s\" " + "could not open (%s)\n", p, strerror(errno)); + exit (2); + } + AZ(fstat(sc->fd, &st)); + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, + "Error: (-sfile) \"%s\" " + "was not a file after opening\n", p); + exit (2); + } + sc->filename = p; + smf_initfile(sc, size, 0); + return; + } + + asprintf(&q, "%s/varnish.XXXXXX", p); + assert(q != NULL); + sc->fd = mkstemp(q); + if (sc->fd < 0) { + fprintf(stderr, + "Error: (-sfile) \"%s\" " + "mkstemp(%s) failed (%s)\n", p, q, strerror(errno)); + exit (2); + } + AZ(unlink(q)); + asprintf(&sc->filename, "%s (unlinked)", q); + assert(sc->filename != NULL); + free(q); + smf_initfile(sc, size, 1); +} + static struct storage * smf_alloc(struct stevedore *st __unused, unsigned size) { @@ -42,7 +247,7 @@ smf_free(struct storage *s) struct stevedore smf_stevedore = { "file", - NULL, /* init */ + smf_init, /* init */ NULL, /* open */ smf_alloc, smf_free -- 2.39.5