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