varnish-cache/bin/varnishd/storage/stevedore_utils.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Utility functions for stevedores and storage modules
31
 */
32
33
#include "config.h"
34
35
#include <sys/stat.h>
36
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 1760
STV_GetFile(const char *fn, int *fdp, const char **fnp, const char *ctx)
72
{
73
        int fd;
74
        struct stat st;
75 1760
        int retval = 1;
76
        char buf[FILENAME_MAX];
77
78 1760
        AN(fn);
79 1760
        AN(fnp);
80 1760
        AN(fdp);
81 1760
        *fnp = NULL;
82 1760
        *fdp = -1;
83
84
        /* try to create a new file of this name */
85 1760
        VJ_master(JAIL_MASTER_STORAGE);
86 1760
        fd = open(fn, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE, 0600);
87 1760
        if (fd >= 0) {
88 880
                VJ_fix_fd(fd, JAIL_FIXFD_FILE);
89 880
                *fdp = fd;
90 880
                *fnp = fn;
91 880
                VJ_master(JAIL_MASTER_LOW);
92 880
                return (retval);
93
        }
94
95 880
        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 880
        if (S_ISDIR(st.st_mode)) {
101 40
                bprintf(buf, "%s/varnish.XXXXXX", fn);
102 40
                fd = mkstemp(buf);
103 40
                if (fd < 0)
104 0
                        ARGV_ERR("(%s) \"%s\" mkstemp(%s) failed (%s)\n",
105
                            ctx, fn, buf, VAS_errtxt(errno));
106 40
                AZ(unlink(buf));
107 40
                *fnp = strdup(buf);
108 40
                AN(*fnp);
109 40
                retval = 2;
110 880
        } else if (S_ISREG(st.st_mode)) {
111 840
                fd = open(fn, O_RDWR | O_LARGEFILE);
112 840
                if (fd < 0)
113 0
                        ARGV_ERR("(%s) \"%s\" could not open (%s)\n",
114
                            ctx, fn, VAS_errtxt(errno));
115 840
                *fnp = fn;
116 840
                retval = 0;
117 840
        } else
118 0
                ARGV_ERR(
119
                    "(%s) \"%s\" is neither file nor directory\n", ctx, fn);
120
121 880
        AZ(fstat(fd, &st));
122 880
        if (!S_ISREG(st.st_mode))
123 0
                ARGV_ERR("(%s) \"%s\" was not a file after opening\n",
124
                    ctx, fn);
125
126 880
        *fdp = fd;
127 880
        VJ_fix_fd(fd, JAIL_FIXFD_FILE);
128 880
        VJ_master(JAIL_MASTER_LOW);
129 880
        return (retval);
130 1760
}
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 1760
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 1760
        AN(granularity);
152 1760
        AN(ctx);
153
154 1760
        AZ(fstat(fd, &st));
155 1760
        xxxassert(S_ISREG(st.st_mode));
156
157 1760
        AZ(VFIL_fsinfo(fd, &bs, &fssize, NULL));
158
        /* Increase granularity if it is lower than the filesystem block size */
159 1760
        *granularity = vmax_t(unsigned, *granularity, bs);
160
161 1760
        if ((size == NULL || *size == '\0') && st.st_size != 0) {
162
                /*
163
                 * We have no size specification, but an existing file,
164
                 * use its existing size.
165
                 */
166 0
                l = st.st_size;
167 1760
        } else if (size == NULL || *size == '\0') {
168 0
                ARGV_ERR("(%s) no size specified\n",
169
                    ctx);
170 0
        } else {
171 1760
                AN(size);
172 1760
                q = VNUM_2bytes(size, &l, 0);
173
174 1760
                if (q != NULL)
175 40
                        ARGV_ERR("(%s) size \"%s\": %s\n", ctx, size, q);
176
177 1720
                if (l < 1024*1024)
178 0
                        ARGV_ERR("(%s) size \"%s\": too small, "
179
                            "did you forget to specify M or G?\n", ctx, size);
180
181 1720
                if (l > fssize)
182 0
                        ARGV_ERR("(%s) size \"%s\": larger than file system\n",
183
                            ctx, size);
184
        }
185
186
        /*
187
         * This trickery wouldn't be necessary if X/Open would
188
         * just add OFF_MAX to <limits.h>...
189
         */
190 1720
        i = 0;
191 1720
        while (1) {
192 1720
                o = l;
193 1720
                if (o == l && o > 0)
194 1720
                        break;
195 0
                l >>= 1;
196 0
                i++;
197
        }
198 1720
        if (i)
199 0
                fprintf(stderr, "WARNING: (%s) file size reduced"
200 0
                    " to %ju due to system \"off_t\" limitations\n", ctx, l);
201
202
        if (sizeof(void *) == 4 && l > INT32_MAX) { /*lint !e506 !e774 !e845 */
203
                fprintf(stderr,
204
                    "NB: Storage size limited to 2GB on 32 bit architecture,\n"
205
                    "NB: otherwise we could run out of address space.\n"
206
                );
207
                l = INT32_MAX;
208
        }
209
210
        /* Round down */
211 1720
        l -= (l % *granularity);
212
213 1720
        return (l);
214
}