]> err.no Git - varnish/commitdiff
Calculate the size of the backing store file.
authorphk <phk@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Tue, 13 Jun 2006 13:14:12 +0000 (13:14 +0000)
committerphk <phk@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Tue, 13 Jun 2006 13:14:12 +0000 (13:14 +0000)
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

index 3e5c96011de6e80f7533f11ad42c78a66e77ed02..eb29ff6ea3197cc44e6c0c5220a1f8281b110acb 100644 (file)
  */
 
 #include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/queue.h>
+#include <sys/stat.h>
 #include <pthread.h>
+#include <sys/param.h>
+#include <sys/mount.h>
 
 #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