varnish-cache/bin/varnishd/storage/stevedore_utils.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2011 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * Utility functions for stevedores and storage modules
30
 */
31
32
#include "config.h"
33
34
#include <sys/stat.h>
35
36
#include <errno.h>
37
#include <fcntl.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
#include "mgt/mgt.h"
44
#include "common/heritage.h"
45
46
#include "storage/storage.h"
47
#include "vnum.h"
48
#include "vfil.h"
49
50
#ifndef O_LARGEFILE
51
#define O_LARGEFILE     0
52
#endif
53
54
/*--------------------------------------------------------------------
55
 * Get a storage file.
56
 *
57
 * The fn argument can be an existing file, an existing directory or
58
 * a nonexistent filename in an existing directory.
59
 *
60
 * If a directory is specified, the file will be anonymous (unlinked)
61
 *
62
 * Return:
63
 *       0 if the file was preexisting.
64
 *       1 if the file was created.
65
 *       2 if the file is anonymous.
66
 *
67
 * Uses ARGV_ERR to exit in case of trouble.
68
 */
69
70
int
71 54
STV_GetFile(const char *fn, int *fdp, const char **fnp, const char *ctx)
72
{
73
        int fd;
74
        struct stat st;
75 54
        int retval = 1;
76
        char buf[FILENAME_MAX];
77
78 54
        AN(fn);
79 54
        AN(fnp);
80 54
        AN(fdp);
81 54
        *fnp = NULL;
82 54
        *fdp = -1;
83
84
        /* try to create a new file of this name */
85 54
        VJ_master(JAIL_MASTER_STORAGE);
86 54
        fd = open(fn, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE, 0600);
87 54
        if (fd >= 0) {
88 42
                VJ_fix_fd(fd, JAIL_FIXFD_FILE);
89 42
                *fdp = fd;
90 42
                *fnp = fn;
91 42
                VJ_master(JAIL_MASTER_LOW);
92 42
                return (retval);
93
        }
94
95 12
        if (stat(fn, &st))
96 0
                ARGV_ERR(
97
                    "(%s) \"%s\" does not exist and could not be created\n",
98
                    ctx, fn);
99
100 12
        if (S_ISDIR(st.st_mode)) {
101 0
                bprintf(buf, "%s/varnish.XXXXXX", fn);
102 0
                fd = mkstemp(buf);
103 0
                if (fd < 0)
104 0
                        ARGV_ERR("(%s) \"%s\" mkstemp(%s) failed (%s)\n",
105
                            ctx, fn, buf, strerror(errno));
106 0
                AZ(unlink(buf));
107 0
                *fnp = strdup(buf);
108 0
                AN(*fnp);
109 0
                retval = 2;
110 12
        } else if (S_ISREG(st.st_mode)) {
111 12
                fd = open(fn, O_RDWR | O_LARGEFILE);
112 12
                if (fd < 0)
113 0
                        ARGV_ERR("(%s) \"%s\" could not open (%s)\n",
114
                            ctx, fn, strerror(errno));
115 12
                *fnp = fn;
116 12
                retval = 0;
117
        } else
118 0
                ARGV_ERR(
119
                    "(%s) \"%s\" is neither file nor directory\n", ctx, fn);
120
121 12
        AZ(fstat(fd, &st));
122 12
        if (!S_ISREG(st.st_mode))
123 0
                ARGV_ERR("(%s) \"%s\" was not a file after opening\n",
124
                    ctx, fn);
125
126 12
        *fdp = fd;
127 12
        VJ_fix_fd(fd, JAIL_FIXFD_FILE);
128 12
        VJ_master(JAIL_MASTER_LOW);
129 12
        return (retval);
130
}
131
132
/*--------------------------------------------------------------------
133
 * Decide file size.
134
 *
135
 * If the size specification is empty and the file exists with non-zero
136
 * size, use that, otherwise, interpret the specification.
137
 *
138
 * Handle off_t sizes and pointer width limitations.
139
 */
140
141
uintmax_t
142 54
STV_FileSize(int fd, const char *size, unsigned *granularity, const char *ctx)
143
{
144
        uintmax_t l, fssize;
145
        unsigned bs;
146
        const char *q;
147
        int i;
148
        off_t o;
149
        struct stat st;
150
151 54
        AN(granularity);
152 54
        AN(ctx);
153
154 54
        AZ(fstat(fd, &st));
155 54
        xxxassert(S_ISREG(st.st_mode));
156
157 54
        AZ(VFIL_fsinfo(fd, &bs, &fssize, NULL));
158
        /* Increase granularity if it is lower than the filesystem block size */
159 54
        if (*granularity < bs)
160 0
                *granularity = bs;
161
162 54
        if ((size == NULL || *size == '\0') && st.st_size != 0) {
163
                /*
164
                 * We have no size specification, but an existing file,
165
                 * use its existing size.
166
                 */
167 0
                l = st.st_size;
168 54
        } else if (size == NULL || *size == '\0') {
169 0
                ARGV_ERR("(%s) no size specified\n",
170
                    ctx);
171
        } else {
172 54
                AN(size);
173 54
                q = VNUM_2bytes(size, &l, 0);
174
175 54
                if (q != NULL)
176 0
                        ARGV_ERR("(%s) size \"%s\": %s\n", ctx, size, q);
177
178 54
                if (l < 1024*1024)
179 0
                        ARGV_ERR("(%s) size \"%s\": too small, "
180
                            "did you forget to specify M or G?\n", ctx, size);
181
182 54
                if (l > fssize)
183 0
                        ARGV_ERR("(%s) size \"%s\": larger than file system\n",
184
                            ctx, size);
185
        }
186
187
        /*
188
         * This trickery wouldn't be necessary if X/Open would
189
         * just add OFF_MAX to <limits.h>...
190
         */
191 54
        i = 0;
192
        while (1) {
193 54
                o = l;
194 54
                if (o == l && o > 0)
195 54
                        break;
196 0
                l >>= 1;
197 0
                i++;
198 0
        }
199 54
        if (i)
200 0
                fprintf(stderr, "WARNING: (%s) file size reduced"
201
                    " to %ju due to system \"off_t\" limitations\n", ctx, l);
202
203
        if (sizeof(void *) == 4 && l > INT32_MAX) { /*lint !e506 !e774 !e845 */
204
                fprintf(stderr,
205
                    "NB: Storage size limited to 2GB on 32 bit architecture,\n"
206
                    "NB: otherwise we could run out of address space.\n"
207
                );
208
                l = INT32_MAX;
209
        }
210
211
        /* Round down */
212 54
        l -= (l % *granularity);
213
214 54
        return(l);
215
}