mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00
206 lines
4.8 KiB
C
206 lines
4.8 KiB
C
/*
|
|
* libwebsockets - lws alloc chunk live file caching
|
|
*
|
|
* Copyright (C) 2018 Andy Green <andy@warmcat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation:
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
|
|
#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
|
|
|
|
#include "core/private.h"
|
|
#include "misc/lwsac/private.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)))
|
|
|
|
void
|
|
lwsac_use_cached_file_start(lwsac_cached_file_t cache)
|
|
{
|
|
struct lwsac *lac = cache_file_to_lac(cache);
|
|
|
|
lac->refcount++;
|
|
// lwsl_debug("%s: html refcount: %d\n", __func__, lac->refcount);
|
|
}
|
|
|
|
void
|
|
lwsac_use_cached_file_end(lwsac_cached_file_t *cache)
|
|
{
|
|
struct lwsac *lac;
|
|
|
|
if (!cache || !*cache)
|
|
return;
|
|
|
|
lac = cache_file_to_lac(*cache);
|
|
|
|
if (!lac->refcount)
|
|
lwsl_err("%s: html refcount zero on entry\n", __func__);
|
|
|
|
if (lac->refcount && !--lac->refcount && lac->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);
|
|
|
|
lac->detached = 1;
|
|
if (lac->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) + s.st_size + 1;
|
|
|
|
info = lwsac_use(&lac, all, all);
|
|
if (!info)
|
|
goto bail;
|
|
|
|
info->s = s;
|
|
info->last_confirm = t;
|
|
|
|
a = (unsigned char *)(info + 1);
|
|
|
|
*len = s.st_size;
|
|
a[s.st_size] = '\0';
|
|
|
|
rd = read(fd, a, 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
|