kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

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 }