cached-file.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * libwebsockets - small server side websockets and web server implementation
  3. *
  4. * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
  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. #if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
  25. #include "private-lib-core.h"
  26. #include "private-lib-misc-lwsac.h"
  27. /*
  28. * Helper for caching a file in memory in a lac, but also to check at intervals
  29. * no less than 5s if the file is still fresh.
  30. *
  31. * Set *cache to NULL the first time before calling.
  32. *
  33. * You should call this each time before using the cache... if it's
  34. *
  35. * - less than 5s since the last freshness check, and
  36. * - the file is already in memory
  37. *
  38. * it just returns with *cache left alone; this costs very little. You should
  39. * call `lwsac_use_cached_file_start()` and `lwsac_use_cached_file_end()`
  40. * to lock the cache against deletion while you are using it.
  41. *
  42. * If it's
  43. *
  44. * - at least 5s since the last freshness check, and
  45. * - the file timestamp has changed
  46. *
  47. * then
  48. *
  49. * - the file is reloaded into a new lac and *cache set to that
  50. *
  51. * - the old cache lac, if any, is detached (so it will be freed when its
  52. * reference count reaches zero, or immediately if nobody has it)
  53. *
  54. * Note the call can fail due to OOM or filesystem issue at any time.
  55. *
  56. *
  57. * After the LAC header there is stored a `struct cached_file_info` and then
  58. * the raw file contents. *
  59. *
  60. * [LAC header]
  61. * [struct cached_file_info]
  62. * [file contents] <--- *cache is set to here
  63. *
  64. * The api returns a lwsac_cached_file_t type offset to point to the file
  65. * contents. Helpers for reference counting and freeing are also provided
  66. * that take that type and know how to correct it back to operate on the LAC.
  67. */
  68. #define cache_file_to_lac(c) ((struct lwsac *)((char *)c - \
  69. sizeof(struct cached_file_info) - \
  70. sizeof(struct lwsac_head) - \
  71. sizeof(struct lwsac)))
  72. void
  73. lwsac_use_cached_file_start(lwsac_cached_file_t cache)
  74. {
  75. struct lwsac *lac = cache_file_to_lac(cache);
  76. struct lwsac_head *lachead = (struct lwsac_head *)&lac->head[1];
  77. lachead->refcount++;
  78. // lwsl_debug("%s: html refcount: %d\n", __func__, lachead->refcount);
  79. }
  80. void
  81. lwsac_use_cached_file_end(lwsac_cached_file_t *cache)
  82. {
  83. struct lwsac *lac;
  84. struct lwsac_head *lachead;
  85. if (!cache || !*cache)
  86. return;
  87. lac = cache_file_to_lac(*cache);
  88. lachead = (struct lwsac_head *)&lac->head[1];
  89. if (!lachead->refcount)
  90. lwsl_err("%s: html refcount zero on entry\n", __func__);
  91. if (lachead->refcount && !--lachead->refcount && lachead->detached) {
  92. *cache = NULL; /* not usable any more */
  93. lwsac_free(&lac);
  94. }
  95. }
  96. void
  97. lwsac_use_cached_file_detach(lwsac_cached_file_t *cache)
  98. {
  99. struct lwsac *lac = cache_file_to_lac(*cache);
  100. struct lwsac_head *lachead = NULL;
  101. if (lac) {
  102. lachead = (struct lwsac_head *)&lac->head[1];
  103. lachead->detached = 1;
  104. if (lachead->refcount)
  105. return;
  106. }
  107. *cache = NULL;
  108. lwsac_free(&lac);
  109. }
  110. int
  111. lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len)
  112. {
  113. struct cached_file_info *info = NULL;
  114. lwsac_cached_file_t old = *cache;
  115. struct lwsac *lac = NULL;
  116. time_t t = time(NULL);
  117. unsigned char *a;
  118. struct stat s;
  119. size_t all;
  120. ssize_t rd;
  121. int fd;
  122. if (old) { /* we already have a cached copy of it */
  123. info = (struct cached_file_info *)((*cache) - sizeof(*info));
  124. if (t - info->last_confirm < 5)
  125. /* we checked it as fresh less than 5s ago, use old */
  126. return 0;
  127. }
  128. /*
  129. * ...it's been 5s, we should check again on the filesystem
  130. * that the file hasn't changed
  131. */
  132. fd = open(filepath, O_RDONLY);
  133. if (fd < 0) {
  134. lwsl_err("%s: cannot open %s\n", __func__, filepath);
  135. return 1;
  136. }
  137. if (fstat(fd, &s)) {
  138. lwsl_err("%s: cannot stat %s\n", __func__, filepath);
  139. goto bail;
  140. }
  141. if (old && s.st_mtime == info->s.st_mtime) {
  142. /* it still seems to be the same as our cached one */
  143. info->last_confirm = t;
  144. close(fd);
  145. return 0;
  146. }
  147. /*
  148. * we either didn't cache it yet, or it has changed since we cached
  149. * it... reload in a new lac and then detach the old lac.
  150. */
  151. all = sizeof(*info) + s.st_size + 2;
  152. info = lwsac_use(&lac, all, all);
  153. if (!info)
  154. goto bail;
  155. info->s = s;
  156. info->last_confirm = t;
  157. a = (unsigned char *)(info + 1);
  158. *len = s.st_size;
  159. a[s.st_size] = '\0';
  160. rd = read(fd, a, s.st_size);
  161. if (rd != s.st_size) {
  162. lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath,
  163. (int)rd);
  164. goto bail1;
  165. }
  166. close(fd);
  167. *cache = (lwsac_cached_file_t)a;
  168. if (old)
  169. lwsac_use_cached_file_detach(&old);
  170. return 0;
  171. bail1:
  172. lwsac_free(&lac);
  173. bail:
  174. close(fd);
  175. return 1;
  176. }
  177. #endif