fileref.c (5806B)
1 /*
2 * Copyright (c) 2019-2022 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/param.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <sys/mman.h>
21
22 #include <stdint.h>
23
24 #include "kore.h"
25
26 /* cached filerefs expire after 30 seconds of inactivity. */
27 #define FILEREF_EXPIRATION (1000 * 30)
28
29 static void fileref_timer_prime(void);
30 static void fileref_drop(struct kore_fileref *);
31 static void fileref_soft_remove(struct kore_fileref *);
32 static void fileref_expiration_check(void *, u_int64_t);
33
34 static TAILQ_HEAD(, kore_fileref) refs;
35 static struct kore_pool ref_pool;
36 static struct kore_timer *ref_timer = NULL;
37
38 void
39 kore_fileref_init(void)
40 {
41 TAILQ_INIT(&refs);
42 kore_pool_init(&ref_pool, "ref_pool", sizeof(struct kore_fileref), 100);
43 }
44
45 struct kore_fileref *
46 kore_fileref_create(struct kore_server *srv, const char *path, int fd,
47 off_t size, struct timespec *ts)
48 {
49 struct kore_fileref *ref;
50
51 fileref_timer_prime();
52
53 if ((ref = kore_fileref_get(path, srv->tls)) != NULL)
54 return (ref);
55
56 ref = kore_pool_get(&ref_pool);
57
58 ref->cnt = 1;
59 ref->flags = 0;
60 ref->size = size;
61 ref->ontls = srv->tls;
62 ref->path = kore_strdup(path);
63 ref->mtime_sec = ts->tv_sec;
64 ref->mtime = ((u_int64_t)(ts->tv_sec * 1000 + (ts->tv_nsec / 1000000)));
65
66 #if !defined(KORE_USE_PLATFORM_SENDFILE)
67 if ((uintmax_t)size> SIZE_MAX) {
68 kore_pool_put(&ref_pool, ref);
69 return (NULL);
70 }
71
72 ref->base = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE, fd, 0);
73 if (ref->base == MAP_FAILED)
74 fatal("net_send_file: mmap failed: %s", errno_s);
75 if (madvise(ref->base, (size_t)size, MADV_SEQUENTIAL) == -1)
76 fatal("net_send_file: madvise: %s", errno_s);
77 close(fd);
78 #else
79 if (srv->tls == 0) {
80 ref->fd = fd;
81 } else {
82 if ((uintmax_t)size > SIZE_MAX) {
83 kore_pool_put(&ref_pool, ref);
84 return (NULL);
85 }
86
87 ref->base = mmap(NULL,
88 (size_t)size, PROT_READ, MAP_PRIVATE, fd, 0);
89 if (ref->base == MAP_FAILED)
90 fatal("net_send_file: mmap failed: %s", errno_s);
91 if (madvise(ref->base, (size_t)size, MADV_SEQUENTIAL) == -1)
92 fatal("net_send_file: madvise: %s", errno_s);
93 close(fd);
94 }
95 #endif
96
97 #if defined(FILEREF_DEBUG)
98 kore_log(LOG_DEBUG, "ref:%p created", (void *)ref);
99 #endif
100
101 TAILQ_INSERT_TAIL(&refs, ref, list);
102
103 return (ref);
104 }
105
106 /*
107 * Caller must call kore_fileref_release() after kore_fileref_get() even
108 * if they don't end up using the ref.
109 */
110 struct kore_fileref *
111 kore_fileref_get(const char *path, int ontls)
112 {
113 struct stat st;
114 struct kore_fileref *ref;
115 u_int64_t mtime;
116
117 TAILQ_FOREACH(ref, &refs, list) {
118 if (!strcmp(ref->path, path) && ref->ontls == ontls) {
119 if (stat(ref->path, &st) == -1) {
120 if (errno != ENOENT) {
121 kore_log(LOG_ERR, "stat(%s): %s",
122 ref->path, errno_s);
123 }
124 fileref_soft_remove(ref);
125 return (NULL);
126 }
127
128 mtime = ((u_int64_t)(st.st_mtim.tv_sec * 1000 +
129 (st.st_mtim.tv_nsec / 1000000)));
130
131 if (ref->mtime != mtime) {
132 fileref_soft_remove(ref);
133 return (NULL);
134 }
135
136 ref->cnt++;
137 #if defined(FILEREF_DEBUG)
138 kore_log(LOG_DEBUG, "ref:%p cnt:%d",
139 (void *)ref, ref->cnt);
140 #endif
141 TAILQ_REMOVE(&refs, ref, list);
142 TAILQ_INSERT_HEAD(&refs, ref, list);
143 return (ref);
144 }
145 }
146
147 return (NULL);
148 }
149
150 void
151 kore_fileref_release(struct kore_fileref *ref)
152 {
153 ref->cnt--;
154
155 #if defined(FILEREF_DEBUG)
156 kore_log(LOG_DEBUG, "ref:%p released cnt:%d", (void *)ref, ref->cnt);
157 #endif
158
159 if (ref->cnt < 0) {
160 fatal("kore_fileref_release: cnt < 0 (%p:%d)",
161 (void *)ref, ref->cnt);
162 }
163
164 if (ref->cnt == 0) {
165 if (ref->flags & KORE_FILEREF_SOFT_REMOVED)
166 fileref_drop(ref);
167 else
168 ref->expiration = kore_time_ms() + FILEREF_EXPIRATION;
169 }
170 }
171
172 static void
173 fileref_timer_prime(void)
174 {
175 if (ref_timer != NULL)
176 return;
177
178 ref_timer = kore_timer_add(fileref_expiration_check, 10000, NULL, 0);
179 }
180
181 static void
182 fileref_soft_remove(struct kore_fileref *ref)
183 {
184 if (ref->flags & KORE_FILEREF_SOFT_REMOVED)
185 fatal("fileref_soft_remove: %p already removed", (void *)ref);
186
187 #if defined(FILEREF_DEBUG)
188 kore_log(LOG_DEBUG, "ref:%p softremoved", (void *)ref);
189 #endif
190
191 TAILQ_REMOVE(&refs, ref, list);
192 ref->flags |= KORE_FILEREF_SOFT_REMOVED;
193
194 if (ref->cnt == 0)
195 fileref_drop(ref);
196 }
197
198 static void
199 fileref_expiration_check(void *arg, u_int64_t now)
200 {
201 struct kore_fileref *ref, *next;
202
203 for (ref = TAILQ_FIRST(&refs); ref != NULL; ref = next) {
204 next = TAILQ_NEXT(ref, list);
205
206 if (ref->cnt != 0)
207 continue;
208
209 if (ref->expiration > now)
210 continue;
211
212 #if defined(FILEREF_DEBUG)
213 kore_log(LOG_DEBUG, "ref:%p expired, removing", (void *)ref);
214 #endif
215
216 fileref_drop(ref);
217 }
218
219 if (TAILQ_EMPTY(&refs)) {
220 /* remove the timer. */
221 ref_timer->flags |= KORE_TIMER_ONESHOT;
222 ref_timer = NULL;
223 }
224 }
225
226 static void
227 fileref_drop(struct kore_fileref *ref)
228 {
229 #if defined(FILEREF_DEBUG)
230 kore_log(LOG_DEBUG, "ref:%p dropped", (void *)ref);
231 #endif
232
233 if (!(ref->flags & KORE_FILEREF_SOFT_REMOVED))
234 TAILQ_REMOVE(&refs, ref, list);
235
236 kore_free(ref->path);
237
238 #if !defined(KORE_USE_PLATFORM_SENDFILE)
239 (void)munmap(ref->base, ref->size);
240 #else
241 close(ref->fd);
242 #endif
243 kore_pool_put(&ref_pool, ref);
244 }