• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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