| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010 - 2019 Andy Green <[email protected]> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to |
| * deal in the Software without restriction, including without limitation the |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| * sell copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) |
| |
| #include "private-lib-core.h" |
| #include "private-lib-misc-lwsac.h" |
| |
| /* |
| * Helper for caching a file in memory in a lac, but also to check at intervals |
| * no less than 5s if the file is still fresh. |
| * |
| * Set *cache to NULL the first time before calling. |
| * |
| * You should call this each time before using the cache... if it's |
| * |
| * - less than 5s since the last freshness check, and |
| * - the file is already in memory |
| * |
| * it just returns with *cache left alone; this costs very little. You should |
| * call `lwsac_use_cached_file_start()` and `lwsac_use_cached_file_end()` |
| * to lock the cache against deletion while you are using it. |
| * |
| * If it's |
| * |
| * - at least 5s since the last freshness check, and |
| * - the file timestamp has changed |
| * |
| * then |
| * |
| * - the file is reloaded into a new lac and *cache set to that |
| * |
| * - the old cache lac, if any, is detached (so it will be freed when its |
| * reference count reaches zero, or immediately if nobody has it) |
| * |
| * Note the call can fail due to OOM or filesystem issue at any time. |
| * |
| * |
| * After the LAC header there is stored a `struct cached_file_info` and then |
| * the raw file contents. * |
| * |
| * [LAC header] |
| * [struct cached_file_info] |
| * [file contents] <--- *cache is set to here |
| * |
| * The api returns a lwsac_cached_file_t type offset to point to the file |
| * contents. Helpers for reference counting and freeing are also provided |
| * that take that type and know how to correct it back to operate on the LAC. |
| */ |
| |
| #define cache_file_to_lac(c) ((struct lwsac *)((char *)c - \ |
| sizeof(struct cached_file_info) - \ |
| sizeof(struct lwsac_head) - \ |
| sizeof(struct lwsac))) |
| |
| void |
| lwsac_use_cached_file_start(lwsac_cached_file_t cache) |
| { |
| struct lwsac *lac = cache_file_to_lac(cache); |
| struct lwsac_head *lachead = (struct lwsac_head *)&lac->head[1]; |
| |
| lachead->refcount++; |
| // lwsl_debug("%s: html refcount: %d\n", __func__, lachead->refcount); |
| } |
| |
| void |
| lwsac_use_cached_file_end(lwsac_cached_file_t *cache) |
| { |
| struct lwsac *lac; |
| struct lwsac_head *lachead; |
| |
| if (!cache || !*cache) |
| return; |
| |
| lac = cache_file_to_lac(*cache); |
| lachead = (struct lwsac_head *)&lac->head[1]; |
| |
| if (!lachead->refcount) |
| lwsl_err("%s: html refcount zero on entry\n", __func__); |
| |
| if (lachead->refcount && !--lachead->refcount && lachead->detached) { |
| *cache = NULL; /* not usable any more */ |
| lwsac_free(&lac); |
| } |
| } |
| |
| void |
| lwsac_use_cached_file_detach(lwsac_cached_file_t *cache) |
| { |
| struct lwsac *lac = cache_file_to_lac(*cache); |
| struct lwsac_head *lachead = NULL; |
| |
| if (lac) { |
| lachead = (struct lwsac_head *)&lac->head[1]; |
| |
| lachead->detached = 1; |
| if (lachead->refcount) |
| return; |
| } |
| |
| *cache = NULL; |
| lwsac_free(&lac); |
| } |
| |
| int |
| lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len) |
| { |
| struct cached_file_info *info = NULL; |
| lwsac_cached_file_t old = *cache; |
| struct lwsac *lac = NULL; |
| time_t t = time(NULL); |
| unsigned char *a; |
| struct stat s; |
| size_t all; |
| ssize_t rd; |
| int fd; |
| |
| if (old) { /* we already have a cached copy of it */ |
| |
| info = (struct cached_file_info *)((*cache) - sizeof(*info)); |
| |
| if (t - info->last_confirm < 5) |
| /* we checked it as fresh less than 5s ago, use old */ |
| return 0; |
| } |
| |
| /* |
| * ...it's been 5s, we should check again on the filesystem |
| * that the file hasn't changed |
| */ |
| |
| fd = open(filepath, O_RDONLY); |
| if (fd < 0) { |
| lwsl_err("%s: cannot open %s\n", __func__, filepath); |
| |
| return 1; |
| } |
| |
| if (fstat(fd, &s)) { |
| lwsl_err("%s: cannot stat %s\n", __func__, filepath); |
| |
| goto bail; |
| } |
| |
| if (old && s.st_mtime == info->s.st_mtime) { |
| /* it still seems to be the same as our cached one */ |
| info->last_confirm = t; |
| |
| close(fd); |
| |
| return 0; |
| } |
| |
| /* |
| * we either didn't cache it yet, or it has changed since we cached |
| * it... reload in a new lac and then detach the old lac. |
| */ |
| |
| all = sizeof(*info) + (unsigned long)s.st_size + 2; |
| |
| info = lwsac_use(&lac, all, all); |
| if (!info) |
| goto bail; |
| |
| info->s = s; |
| info->last_confirm = t; |
| |
| a = (unsigned char *)(info + 1); |
| |
| *len = (unsigned long)s.st_size; |
| a[s.st_size] = '\0'; |
| |
| rd = read(fd, a, (unsigned long)s.st_size); |
| if (rd != s.st_size) { |
| lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath, |
| (int)rd); |
| goto bail1; |
| } |
| |
| close(fd); |
| |
| *cache = (lwsac_cached_file_t)a; |
| if (old) |
| lwsac_use_cached_file_detach(&old); |
| |
| return 0; |
| |
| bail1: |
| lwsac_free(&lac); |
| |
| bail: |
| close(fd); |
| |
| return 1; |
| } |
| |
| #endif |