1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) 26 27#include "private-lib-core.h" 28#include "private-lib-misc-lwsac.h" 29 30/* 31 * Helper for caching a file in memory in a lac, but also to check at intervals 32 * no less than 5s if the file is still fresh. 33 * 34 * Set *cache to NULL the first time before calling. 35 * 36 * You should call this each time before using the cache... if it's 37 * 38 * - less than 5s since the last freshness check, and 39 * - the file is already in memory 40 * 41 * it just returns with *cache left alone; this costs very little. You should 42 * call `lwsac_use_cached_file_start()` and `lwsac_use_cached_file_end()` 43 * to lock the cache against deletion while you are using it. 44 * 45 * If it's 46 * 47 * - at least 5s since the last freshness check, and 48 * - the file timestamp has changed 49 * 50 * then 51 * 52 * - the file is reloaded into a new lac and *cache set to that 53 * 54 * - the old cache lac, if any, is detached (so it will be freed when its 55 * reference count reaches zero, or immediately if nobody has it) 56 * 57 * Note the call can fail due to OOM or filesystem issue at any time. 58 * 59 * 60 * After the LAC header there is stored a `struct cached_file_info` and then 61 * the raw file contents. * 62 * 63 * [LAC header] 64 * [struct cached_file_info] 65 * [file contents] <--- *cache is set to here 66 * 67 * The api returns a lwsac_cached_file_t type offset to point to the file 68 * contents. Helpers for reference counting and freeing are also provided 69 * that take that type and know how to correct it back to operate on the LAC. 70 */ 71 72#define cache_file_to_lac(c) ((struct lwsac *)((char *)c - \ 73 sizeof(struct cached_file_info) - \ 74 sizeof(struct lwsac_head) - \ 75 sizeof(struct lwsac))) 76 77void 78lwsac_use_cached_file_start(lwsac_cached_file_t cache) 79{ 80 struct lwsac *lac = cache_file_to_lac(cache); 81 struct lwsac_head *lachead = (struct lwsac_head *)&lac->head[1]; 82 83 lachead->refcount++; 84 // lwsl_debug("%s: html refcount: %d\n", __func__, lachead->refcount); 85} 86 87void 88lwsac_use_cached_file_end(lwsac_cached_file_t *cache) 89{ 90 struct lwsac *lac; 91 struct lwsac_head *lachead; 92 93 if (!cache || !*cache) 94 return; 95 96 lac = cache_file_to_lac(*cache); 97 lachead = (struct lwsac_head *)&lac->head[1]; 98 99 if (!lachead->refcount) 100 lwsl_err("%s: html refcount zero on entry\n", __func__); 101 102 if (lachead->refcount && !--lachead->refcount && lachead->detached) { 103 *cache = NULL; /* not usable any more */ 104 lwsac_free(&lac); 105 } 106} 107 108void 109lwsac_use_cached_file_detach(lwsac_cached_file_t *cache) 110{ 111 struct lwsac *lac = cache_file_to_lac(*cache); 112 struct lwsac_head *lachead = NULL; 113 114 if (lac) { 115 lachead = (struct lwsac_head *)&lac->head[1]; 116 117 lachead->detached = 1; 118 if (lachead->refcount) 119 return; 120 } 121 122 *cache = NULL; 123 lwsac_free(&lac); 124} 125 126int 127lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len) 128{ 129 struct cached_file_info *info = NULL; 130 lwsac_cached_file_t old = *cache; 131 struct lwsac *lac = NULL; 132 time_t t = time(NULL); 133 unsigned char *a; 134 struct stat s; 135 size_t all; 136 ssize_t rd; 137 int fd; 138 139 if (old) { /* we already have a cached copy of it */ 140 141 info = (struct cached_file_info *)((*cache) - sizeof(*info)); 142 143 if (t - info->last_confirm < 5) 144 /* we checked it as fresh less than 5s ago, use old */ 145 return 0; 146 } 147 148 /* 149 * ...it's been 5s, we should check again on the filesystem 150 * that the file hasn't changed 151 */ 152 153 fd = open(filepath, O_RDONLY); 154 if (fd < 0) { 155 lwsl_err("%s: cannot open %s\n", __func__, filepath); 156 157 return 1; 158 } 159 160 if (fstat(fd, &s)) { 161 lwsl_err("%s: cannot stat %s\n", __func__, filepath); 162 163 goto bail; 164 } 165 166 if (old && s.st_mtime == info->s.st_mtime) { 167 /* it still seems to be the same as our cached one */ 168 info->last_confirm = t; 169 170 close(fd); 171 172 return 0; 173 } 174 175 /* 176 * we either didn't cache it yet, or it has changed since we cached 177 * it... reload in a new lac and then detach the old lac. 178 */ 179 180 all = sizeof(*info) + (unsigned long)s.st_size + 2; 181 182 info = lwsac_use(&lac, all, all); 183 if (!info) 184 goto bail; 185 186 info->s = s; 187 info->last_confirm = t; 188 189 a = (unsigned char *)(info + 1); 190 191 *len = (unsigned long)s.st_size; 192 a[s.st_size] = '\0'; 193 194 rd = read(fd, a, (unsigned long)s.st_size); 195 if (rd != s.st_size) { 196 lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath, 197 (int)rd); 198 goto bail1; 199 } 200 201 close(fd); 202 203 *cache = (lwsac_cached_file_t)a; 204 if (old) 205 lwsac_use_cached_file_detach(&old); 206 207 return 0; 208 209bail1: 210 lwsac_free(&lac); 211 212bail: 213 close(fd); 214 215 return 1; 216} 217 218#endif 219