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
77 void
lwsac_use_cached_file_start(lwsac_cached_file_t cache)78 lwsac_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
87 void
lwsac_use_cached_file_end(lwsac_cached_file_t * cache)88 lwsac_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
108 void
lwsac_use_cached_file_detach(lwsac_cached_file_t * cache)109 lwsac_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
126 int
lwsac_cached_file(const char * filepath,lwsac_cached_file_t * cache,size_t * len)127 lwsac_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
209 bail1:
210 lwsac_free(&lac);
211
212 bail:
213 close(fd);
214
215 return 1;
216 }
217
218 #endif
219