lfs_emubd.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Block device emulated on standard files
  3. *
  4. * Copyright (c) 2017 ARM Limited
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include "emubd/lfs_emubd.h"
  19. #include <errno.h>
  20. #include <string.h>
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <limits.h>
  24. #include <dirent.h>
  25. #include <sys/stat.h>
  26. #include <unistd.h>
  27. #include <assert.h>
  28. #include <stdbool.h>
  29. // Block device emulated on existing filesystem
  30. int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
  31. lfs_emubd_t *emu = cfg->context;
  32. emu->cfg.read_size = cfg->read_size;
  33. emu->cfg.prog_size = cfg->prog_size;
  34. emu->cfg.block_size = cfg->block_size;
  35. emu->cfg.block_count = cfg->block_count;
  36. // Allocate buffer for creating children files
  37. size_t pathlen = strlen(path);
  38. emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1);
  39. if (!emu->path) {
  40. return -ENOMEM;
  41. }
  42. strcpy(emu->path, path);
  43. emu->path[pathlen] = '/';
  44. emu->child = &emu->path[pathlen+1];
  45. memset(emu->child, '\0', LFS_NAME_MAX+1);
  46. // Create directory if it doesn't exist
  47. int err = mkdir(path, 0777);
  48. if (err && errno != EEXIST) {
  49. return -errno;
  50. }
  51. // Load stats to continue incrementing
  52. snprintf(emu->child, LFS_NAME_MAX, "stats");
  53. FILE *f = fopen(emu->path, "r");
  54. if (!f) {
  55. return -errno;
  56. }
  57. size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
  58. if (res < 1) {
  59. return -errno;
  60. }
  61. err = fclose(f);
  62. if (err) {
  63. return -errno;
  64. }
  65. return 0;
  66. }
  67. void lfs_emubd_destroy(const struct lfs_config *cfg) {
  68. lfs_emubd_sync(cfg);
  69. lfs_emubd_t *emu = cfg->context;
  70. free(emu->path);
  71. }
  72. int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
  73. lfs_off_t off, void *buffer, lfs_size_t size) {
  74. lfs_emubd_t *emu = cfg->context;
  75. uint8_t *data = buffer;
  76. // Check if read is valid
  77. assert(off % cfg->read_size == 0);
  78. assert(size % cfg->read_size == 0);
  79. assert(block < cfg->block_count);
  80. // Zero out buffer for debugging
  81. memset(data, 0, size);
  82. // Read data
  83. snprintf(emu->child, LFS_NAME_MAX, "%x", block);
  84. FILE *f = fopen(emu->path, "rb");
  85. if (!f && errno != ENOENT) {
  86. return -errno;
  87. }
  88. if (f) {
  89. int err = fseek(f, off, SEEK_SET);
  90. if (err) {
  91. return -errno;
  92. }
  93. size_t res = fread(data, 1, size, f);
  94. if (res < size && !feof(f)) {
  95. return -errno;
  96. }
  97. err = fclose(f);
  98. if (err) {
  99. return -errno;
  100. }
  101. }
  102. emu->stats.read_count += 1;
  103. return 0;
  104. }
  105. int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
  106. lfs_off_t off, const void *buffer, lfs_size_t size) {
  107. lfs_emubd_t *emu = cfg->context;
  108. const uint8_t *data = buffer;
  109. // Check if write is valid
  110. assert(off % cfg->prog_size == 0);
  111. assert(size % cfg->prog_size == 0);
  112. assert(block < cfg->block_count);
  113. // Program data
  114. snprintf(emu->child, LFS_NAME_MAX, "%x", block);
  115. FILE *f = fopen(emu->path, "r+b");
  116. if (!f) {
  117. return (errno == EACCES) ? 0 : -errno;
  118. }
  119. // Check that file was erased
  120. assert(f);
  121. int err = fseek(f, off, SEEK_SET);
  122. if (err) {
  123. return -errno;
  124. }
  125. size_t res = fwrite(data, 1, size, f);
  126. if (res < size) {
  127. return -errno;
  128. }
  129. err = fseek(f, off, SEEK_SET);
  130. if (err) {
  131. return -errno;
  132. }
  133. uint8_t dat;
  134. res = fread(&dat, 1, 1, f);
  135. if (res < 1) {
  136. return -errno;
  137. }
  138. err = fclose(f);
  139. if (err) {
  140. return -errno;
  141. }
  142. emu->stats.prog_count += 1;
  143. return 0;
  144. }
  145. int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
  146. lfs_emubd_t *emu = cfg->context;
  147. // Check if erase is valid
  148. assert(block < cfg->block_count);
  149. // Erase the block
  150. snprintf(emu->child, LFS_NAME_MAX, "%x", block);
  151. struct stat st;
  152. int err = stat(emu->path, &st);
  153. if (err && errno != ENOENT) {
  154. return -errno;
  155. }
  156. if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) {
  157. err = unlink(emu->path);
  158. if (err) {
  159. return -errno;
  160. }
  161. }
  162. if (err || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) {
  163. FILE *f = fopen(emu->path, "w");
  164. if (!f) {
  165. return -errno;
  166. }
  167. err = fclose(f);
  168. if (err) {
  169. return -errno;
  170. }
  171. }
  172. emu->stats.erase_count += 1;
  173. return 0;
  174. }
  175. int lfs_emubd_sync(const struct lfs_config *cfg) {
  176. lfs_emubd_t *emu = cfg->context;
  177. // Just write out info/stats for later lookup
  178. snprintf(emu->child, LFS_NAME_MAX, "config");
  179. FILE *f = fopen(emu->path, "w");
  180. if (!f) {
  181. return -errno;
  182. }
  183. size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
  184. if (res < 1) {
  185. return -errno;
  186. }
  187. int err = fclose(f);
  188. if (err) {
  189. return -errno;
  190. }
  191. snprintf(emu->child, LFS_NAME_MAX, "stats");
  192. f = fopen(emu->path, "w");
  193. if (!f) {
  194. return -errno;
  195. }
  196. res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
  197. if (res < 1) {
  198. return -errno;
  199. }
  200. err = fclose(f);
  201. if (err) {
  202. return -errno;
  203. }
  204. return 0;
  205. }