vfs_fat_mh.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #include <unistd.h>
  9. #include <dirent.h>
  10. #include <sys/errno.h>
  11. #include <sys/fcntl.h>
  12. #include <sys/lock.h>
  13. #include "esp_vfs.h"
  14. #include "esp_log.h"
  15. #include "ff_mh.h"
  16. #include "diskio_impl_mh.h"
  17. typedef struct {
  18. char fat_drive[8]; /* FAT drive name */
  19. char base_path[ESP_VFS_PATH_MAX]; /* base path in VFS where partition is registered */
  20. size_t max_files; /* max number of simultaneously open files; size of files[] array */
  21. _lock_t lock; /* guard for access to this structure */
  22. FATFS fs; /* fatfs library FS structure */
  23. char tmp_path_buf[FILENAME_MAX+3]; /* temporary buffer used to prepend drive name to the path */
  24. char tmp_path_buf2[FILENAME_MAX+3]; /* as above; used in functions which take two path arguments */
  25. bool *o_append; /* O_APPEND is stored here for each max_files entries (because O_APPEND is not compatible with FA_OPEN_APPEND) */
  26. FIL files[0]; /* array with max_files entries; must be the final member of the structure */
  27. } vfs_fat_ctx_t;
  28. typedef struct {
  29. DIR dir;
  30. long offset;
  31. FF_DIR ffdir;
  32. FILINFO filinfo;
  33. struct dirent cur_dirent;
  34. } vfs_fat_dir_t;
  35. /* Date and time storage formats in FAT */
  36. typedef union {
  37. struct {
  38. uint16_t mday : 5; /* Day of month, 1 - 31 */
  39. uint16_t mon : 4; /* Month, 1 - 12 */
  40. uint16_t year : 7; /* Year, counting from 1980. E.g. 37 for 2017 */
  41. };
  42. uint16_t as_int;
  43. } fat_date_t;
  44. typedef union {
  45. struct {
  46. uint16_t sec : 5; /* Seconds divided by 2. E.g. 21 for 42 seconds */
  47. uint16_t min : 6; /* Minutes, 0 - 59 */
  48. uint16_t hour : 5; /* Hour, 0 - 23 */
  49. };
  50. uint16_t as_int;
  51. } fat_time_t;
  52. static const char* TAG = "vfs_fat";
  53. static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size);
  54. static off_t vfs_fat_lseek(void* p, int fd, off_t size, int mode);
  55. static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size);
  56. static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t offset);
  57. static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset);
  58. static int vfs_fat_open(void* ctx, const char * path, int flags, int mode);
  59. static int vfs_fat_close(void* ctx, int fd);
  60. static int vfs_fat_fstat(void* ctx, int fd, struct stat * st);
  61. static int vfs_fat_fsync(void* ctx, int fd);
  62. #ifdef CONFIG_VFS_SUPPORT_DIR
  63. static int vfs_fat_stat(void* ctx, const char * path, struct stat * st);
  64. static int vfs_fat_link(void* ctx, const char* n1, const char* n2);
  65. static int vfs_fat_unlink(void* ctx, const char *path);
  66. static int vfs_fat_rename(void* ctx, const char *src, const char *dst);
  67. static DIR* vfs_fat_opendir(void* ctx, const char* name);
  68. static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir);
  69. static int vfs_fat_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
  70. static long vfs_fat_telldir(void* ctx, DIR* pdir);
  71. static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset);
  72. static int vfs_fat_closedir(void* ctx, DIR* pdir);
  73. static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode);
  74. static int vfs_fat_rmdir(void* ctx, const char* name);
  75. static int vfs_fat_access(void* ctx, const char *path, int amode);
  76. static int vfs_fat_truncate(void* ctx, const char *path, off_t length);
  77. static int vfs_fat_ftruncate(void* ctx, int fd, off_t length);
  78. static int vfs_fat_utime(void* ctx, const char *path, const struct utimbuf *times);
  79. #endif // CONFIG_VFS_SUPPORT_DIR
  80. static int fresult_to_errno(FRESULT fr);
  81. static vfs_fat_ctx_t* s_fat_ctxs[FF_VOLUMES] = { NULL };
  82. //backwards-compatibility with esp_vfs_fat_unregister()
  83. static vfs_fat_ctx_t* s_fat_ctx = NULL;
  84. static size_t find_context_index_by_path(const char* base_path)
  85. {
  86. for(size_t i=0; i<FF_VOLUMES; i++) {
  87. if (s_fat_ctxs[i] && !strcmp(s_fat_ctxs[i]->base_path, base_path)) {
  88. return i;
  89. }
  90. }
  91. return FF_VOLUMES;
  92. }
  93. static size_t find_unused_context_index(void)
  94. {
  95. for(size_t i=0; i<FF_VOLUMES; i++) {
  96. if (!s_fat_ctxs[i]) {
  97. return i;
  98. }
  99. }
  100. return FF_VOLUMES;
  101. }
  102. esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, size_t max_files, FATFS** out_fs)
  103. {
  104. size_t ctx = find_context_index_by_path(base_path);
  105. if (ctx < FF_VOLUMES) {
  106. return ESP_ERR_INVALID_STATE;
  107. }
  108. ctx = find_unused_context_index();
  109. if (ctx == FF_VOLUMES) {
  110. return ESP_ERR_NO_MEM;
  111. }
  112. const esp_vfs_t vfs = {
  113. .flags = ESP_VFS_FLAG_CONTEXT_PTR,
  114. .write_p = &vfs_fat_write,
  115. .lseek_p = &vfs_fat_lseek,
  116. .read_p = &vfs_fat_read,
  117. .pread_p = &vfs_fat_pread,
  118. .pwrite_p = &vfs_fat_pwrite,
  119. .open_p = &vfs_fat_open,
  120. .close_p = &vfs_fat_close,
  121. .fstat_p = &vfs_fat_fstat,
  122. .fsync_p = &vfs_fat_fsync,
  123. #ifdef CONFIG_VFS_SUPPORT_DIR
  124. .stat_p = &vfs_fat_stat,
  125. .link_p = &vfs_fat_link,
  126. .unlink_p = &vfs_fat_unlink,
  127. .rename_p = &vfs_fat_rename,
  128. .opendir_p = &vfs_fat_opendir,
  129. .closedir_p = &vfs_fat_closedir,
  130. .readdir_p = &vfs_fat_readdir,
  131. .readdir_r_p = &vfs_fat_readdir_r,
  132. .seekdir_p = &vfs_fat_seekdir,
  133. .telldir_p = &vfs_fat_telldir,
  134. .mkdir_p = &vfs_fat_mkdir,
  135. .rmdir_p = &vfs_fat_rmdir,
  136. .access_p = &vfs_fat_access,
  137. .truncate_p = &vfs_fat_truncate,
  138. .ftruncate_p = &vfs_fat_ftruncate,
  139. .utime_p = &vfs_fat_utime,
  140. #endif // CONFIG_VFS_SUPPORT_DIR
  141. };
  142. size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL);
  143. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ff_memalloc(ctx_size);
  144. if (fat_ctx == NULL) {
  145. return ESP_ERR_NO_MEM;
  146. }
  147. memset(fat_ctx, 0, ctx_size);
  148. fat_ctx->o_append = ff_memalloc(max_files * sizeof(bool));
  149. if (fat_ctx->o_append == NULL) {
  150. free(fat_ctx);
  151. return ESP_ERR_NO_MEM;
  152. }
  153. memset(fat_ctx->o_append, 0, max_files * sizeof(bool));
  154. fat_ctx->max_files = max_files;
  155. strlcpy(fat_ctx->fat_drive, fat_drive, sizeof(fat_ctx->fat_drive) - 1);
  156. strlcpy(fat_ctx->base_path, base_path, sizeof(fat_ctx->base_path) - 1);
  157. esp_err_t err = esp_vfs_register(base_path, &vfs, fat_ctx);
  158. if (err != ESP_OK) {
  159. free(fat_ctx->o_append);
  160. free(fat_ctx);
  161. return err;
  162. }
  163. _lock_init(&fat_ctx->lock);
  164. s_fat_ctxs[ctx] = fat_ctx;
  165. //compatibility
  166. s_fat_ctx = fat_ctx;
  167. *out_fs = &fat_ctx->fs;
  168. return ESP_OK;
  169. }
  170. esp_err_t esp_vfs_fat_unregister_path(const char* base_path)
  171. {
  172. size_t ctx = find_context_index_by_path(base_path);
  173. if (ctx == FF_VOLUMES) {
  174. return ESP_ERR_INVALID_STATE;
  175. }
  176. vfs_fat_ctx_t* fat_ctx = s_fat_ctxs[ctx];
  177. esp_err_t err = esp_vfs_unregister(fat_ctx->base_path);
  178. if (err != ESP_OK) {
  179. return err;
  180. }
  181. _lock_close(&fat_ctx->lock);
  182. free(fat_ctx->o_append);
  183. free(fat_ctx);
  184. s_fat_ctxs[ctx] = NULL;
  185. return ESP_OK;
  186. }
  187. esp_err_t esp_vfs_fat_info(const char* base_path,
  188. uint64_t* out_total_bytes,
  189. uint64_t* out_free_bytes)
  190. {
  191. size_t ctx = find_context_index_by_path(base_path);
  192. if (ctx == FF_VOLUMES) {
  193. return ESP_ERR_INVALID_STATE;
  194. }
  195. char* path = s_fat_ctxs[ctx]->fat_drive;
  196. FATFS* fs;
  197. DWORD free_clusters;
  198. int res = f_getfree(path, &free_clusters, &fs);
  199. if (res != FR_OK) {
  200. ESP_LOGE(TAG, "Failed to get number of free clusters (%d)", res);
  201. errno = fresult_to_errno(res);
  202. return ESP_FAIL;
  203. }
  204. uint64_t total_sectors = ((uint64_t)(fs->n_fatent - 2)) * fs->csize;
  205. uint64_t free_sectors = ((uint64_t)free_clusters) * fs->csize;
  206. WORD sector_size = FF_MIN_SS; // 512
  207. #if FF_MAX_SS != FF_MIN_SS
  208. sector_size = fs->ssize;
  209. #endif
  210. // Assuming the total size is < 4GiB, should be true for SPI Flash
  211. if (out_total_bytes != NULL) {
  212. *out_total_bytes = total_sectors * sector_size;
  213. }
  214. if (out_free_bytes != NULL) {
  215. *out_free_bytes = free_sectors * sector_size;
  216. }
  217. return ESP_OK;
  218. }
  219. static int get_next_fd(vfs_fat_ctx_t* fat_ctx)
  220. {
  221. for (size_t i = 0; i < fat_ctx->max_files; ++i) {
  222. if (fat_ctx->files[i].obj.fs == NULL) {
  223. return (int) i;
  224. }
  225. }
  226. return -1;
  227. }
  228. static int fat_mode_conv(int m)
  229. {
  230. int res = 0;
  231. int acc_mode = m & O_ACCMODE;
  232. if (acc_mode == O_RDONLY) {
  233. res |= FA_READ;
  234. } else if (acc_mode == O_WRONLY) {
  235. res |= FA_WRITE;
  236. } else if (acc_mode == O_RDWR) {
  237. res |= FA_READ | FA_WRITE;
  238. }
  239. if ((m & O_CREAT) && (m & O_EXCL)) {
  240. res |= FA_CREATE_NEW;
  241. } else if ((m & O_CREAT) && (m & O_TRUNC)) {
  242. res |= FA_CREATE_ALWAYS;
  243. } else if (m & O_APPEND) {
  244. res |= FA_OPEN_ALWAYS;
  245. } else {
  246. res |= FA_OPEN_EXISTING;
  247. }
  248. return res;
  249. }
  250. static int fresult_to_errno(FRESULT fr)
  251. {
  252. switch(fr) {
  253. case FR_DISK_ERR: return EIO;
  254. case FR_INT_ERR: return EIO;
  255. case FR_NOT_READY: return ENODEV;
  256. case FR_NO_FILE: return ENOENT;
  257. case FR_NO_PATH: return ENOENT;
  258. case FR_INVALID_NAME: return EINVAL;
  259. case FR_DENIED: return EACCES;
  260. case FR_EXIST: return EEXIST;
  261. case FR_INVALID_OBJECT: return EBADF;
  262. case FR_WRITE_PROTECTED: return EACCES;
  263. case FR_INVALID_DRIVE: return ENXIO;
  264. case FR_NOT_ENABLED: return ENODEV;
  265. case FR_NO_FILESYSTEM: return ENODEV;
  266. case FR_MKFS_ABORTED: return EINTR;
  267. case FR_TIMEOUT: return ETIMEDOUT;
  268. case FR_LOCKED: return EACCES;
  269. case FR_NOT_ENOUGH_CORE: return ENOMEM;
  270. case FR_TOO_MANY_OPEN_FILES: return ENFILE;
  271. case FR_INVALID_PARAMETER: return EINVAL;
  272. case FR_OK: return 0;
  273. }
  274. assert(0 && "unhandled FRESULT");
  275. return ENOTSUP;
  276. }
  277. static void file_cleanup(vfs_fat_ctx_t* ctx, int fd)
  278. {
  279. memset(&ctx->files[fd], 0, sizeof(FIL));
  280. }
  281. /**
  282. * @brief Prepend drive letters to path names
  283. * This function returns new path path pointers, pointing to a temporary buffer
  284. * inside ctx.
  285. * @note Call this function with ctx->lock acquired. Paths are valid while the
  286. * lock is held.
  287. * @param ctx vfs_fat_ctx_t context
  288. * @param[inout] path as input, pointer to the path; as output, pointer to the new path
  289. * @param[inout] path2 as input, pointer to the path; as output, pointer to the new path
  290. */
  291. static void prepend_drive_to_path(vfs_fat_ctx_t * ctx, const char ** path, const char ** path2){
  292. snprintf(ctx->tmp_path_buf, sizeof(ctx->tmp_path_buf), "%s%s", ctx->fat_drive, *path);
  293. *path = ctx->tmp_path_buf;
  294. if(path2){
  295. snprintf(ctx->tmp_path_buf2, sizeof(ctx->tmp_path_buf2), "%s%s", ((vfs_fat_ctx_t*)ctx)->fat_drive, *path2);
  296. *path2 = ctx->tmp_path_buf2;
  297. }
  298. }
  299. static int vfs_fat_open(void* ctx, const char * path, int flags, int mode)
  300. {
  301. ESP_LOGV(TAG, "%s: path=\"%s\", flags=%x, mode=%x", __func__, path, flags, mode);
  302. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  303. _lock_acquire(&fat_ctx->lock);
  304. prepend_drive_to_path(fat_ctx, &path, NULL);
  305. int fd = get_next_fd(fat_ctx);
  306. if (fd < 0) {
  307. _lock_release(&fat_ctx->lock);
  308. ESP_LOGE(TAG, "open: no free file descriptors");
  309. errno = ENFILE;
  310. return -1;
  311. }
  312. FRESULT res = f_open(&fat_ctx->files[fd], path, fat_mode_conv(flags));
  313. if (res != FR_OK) {
  314. file_cleanup(fat_ctx, fd);
  315. _lock_release(&fat_ctx->lock);
  316. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  317. errno = fresult_to_errno(res);
  318. return -1;
  319. }
  320. #ifdef CONFIG_FATFS_USE_FASTSEEK
  321. FIL* file = &fat_ctx->files[fd];
  322. //fast-seek is only allowed in read mode, since file cannot be expanded
  323. //to use it.
  324. if(!(fat_mode_conv(flags) & (FA_WRITE))) {
  325. DWORD *clmt_mem = ff_memalloc(sizeof(DWORD) * CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE);
  326. if (clmt_mem == NULL) {
  327. f_close(file);
  328. file_cleanup(fat_ctx, fd);
  329. _lock_release(&fat_ctx->lock);
  330. ESP_LOGE(TAG, "open: Failed to pre-allocate CLMT buffer for fast-seek");
  331. errno = ENOMEM;
  332. return -1;
  333. }
  334. file->cltbl = clmt_mem;
  335. file->cltbl[0] = CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE;
  336. res = f_lseek(file, CREATE_LINKMAP);
  337. ESP_LOGD(TAG, "%s: fast-seek has: %s",
  338. __func__,
  339. (res == FR_OK) ? "activated" : "failed");
  340. if(res != FR_OK) {
  341. ESP_LOGW(TAG, "%s: fast-seek not activated reason code: %d",
  342. __func__, res);
  343. //If linkmap creation fails, fallback to the non fast seek.
  344. ff_memfree(file->cltbl);
  345. file->cltbl = NULL;
  346. }
  347. } else {
  348. file->cltbl = NULL;
  349. }
  350. #endif
  351. // O_APPEND need to be stored because it is not compatible with FA_OPEN_APPEND:
  352. // - FA_OPEN_APPEND means to jump to the end of file only after open()
  353. // - O_APPEND means to jump to the end only before each write()
  354. // Other VFS drivers handles O_APPEND well (to the best of my knowledge),
  355. // therefore this flag is stored here (at this VFS level) in order to save
  356. // memory.
  357. fat_ctx->o_append[fd] = (flags & O_APPEND) == O_APPEND;
  358. _lock_release(&fat_ctx->lock);
  359. return fd;
  360. }
  361. static ssize_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size)
  362. {
  363. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  364. FIL* file = &fat_ctx->files[fd];
  365. FRESULT res;
  366. if (fat_ctx->o_append[fd]) {
  367. if ((res = f_lseek(file, f_size(file))) != FR_OK) {
  368. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  369. errno = fresult_to_errno(res);
  370. return -1;
  371. }
  372. }
  373. unsigned written = 0;
  374. res = f_write(file, data, size, &written);
  375. if (((written == 0) && (size != 0)) && (res == 0)) {
  376. errno = ENOSPC;
  377. return -1;
  378. }
  379. if (res != FR_OK) {
  380. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  381. errno = fresult_to_errno(res);
  382. if (written == 0) {
  383. return -1;
  384. }
  385. }
  386. return written;
  387. }
  388. static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size)
  389. {
  390. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  391. FIL* file = &fat_ctx->files[fd];
  392. unsigned read = 0;
  393. FRESULT res = f_read(file, dst, size, &read);
  394. if (res != FR_OK) {
  395. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  396. errno = fresult_to_errno(res);
  397. if (read == 0) {
  398. return -1;
  399. }
  400. }
  401. return read;
  402. }
  403. static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t offset)
  404. {
  405. ssize_t ret = -1;
  406. vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
  407. _lock_acquire(&fat_ctx->lock);
  408. FIL *file = &fat_ctx->files[fd];
  409. const off_t prev_pos = f_tell(file);
  410. FRESULT f_res = f_lseek(file, offset);
  411. if (f_res != FR_OK) {
  412. ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
  413. errno = fresult_to_errno(f_res);
  414. goto pread_release;
  415. }
  416. unsigned read = 0;
  417. f_res = f_read(file, dst, size, &read);
  418. if (f_res == FR_OK) {
  419. ret = read;
  420. } else {
  421. ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
  422. errno = fresult_to_errno(f_res);
  423. // No return yet - need to restore previous position
  424. }
  425. f_res = f_lseek(file, prev_pos);
  426. if (f_res != FR_OK) {
  427. ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
  428. if (ret >= 0) {
  429. errno = fresult_to_errno(f_res);
  430. } // else f_read failed so errno shouldn't be overwritten
  431. ret = -1; // in case the read was successful but the seek wasn't
  432. }
  433. pread_release:
  434. _lock_release(&fat_ctx->lock);
  435. return ret;
  436. }
  437. static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset)
  438. {
  439. ssize_t ret = -1;
  440. vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
  441. _lock_acquire(&fat_ctx->lock);
  442. FIL *file = &fat_ctx->files[fd];
  443. const off_t prev_pos = f_tell(file);
  444. FRESULT f_res = f_lseek(file, offset);
  445. if (f_res != FR_OK) {
  446. ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
  447. errno = fresult_to_errno(f_res);
  448. goto pwrite_release;
  449. }
  450. unsigned wr = 0;
  451. f_res = f_write(file, src, size, &wr);
  452. if (((wr == 0) && (size != 0)) && (f_res == 0)) {
  453. errno = ENOSPC;
  454. return -1;
  455. }
  456. if (f_res == FR_OK) {
  457. ret = wr;
  458. } else {
  459. ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
  460. errno = fresult_to_errno(f_res);
  461. // No return yet - need to restore previous position
  462. }
  463. f_res = f_lseek(file, prev_pos);
  464. if (f_res != FR_OK) {
  465. ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
  466. if (ret >= 0) {
  467. errno = fresult_to_errno(f_res);
  468. } // else f_write failed so errno shouldn't be overwritten
  469. ret = -1; // in case the write was successful but the seek wasn't
  470. }
  471. pwrite_release:
  472. _lock_release(&fat_ctx->lock);
  473. return ret;
  474. }
  475. static int vfs_fat_fsync(void* ctx, int fd)
  476. {
  477. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  478. _lock_acquire(&fat_ctx->lock);
  479. FIL* file = &fat_ctx->files[fd];
  480. FRESULT res = f_sync(file);
  481. _lock_release(&fat_ctx->lock);
  482. int rc = 0;
  483. if (res != FR_OK) {
  484. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  485. errno = fresult_to_errno(res);
  486. rc = -1;
  487. }
  488. return rc;
  489. }
  490. static int vfs_fat_close(void* ctx, int fd)
  491. {
  492. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  493. _lock_acquire(&fat_ctx->lock);
  494. FIL* file = &fat_ctx->files[fd];
  495. #ifdef CONFIG_FATFS_USE_FASTSEEK
  496. ff_memfree(file->cltbl);
  497. file->cltbl = NULL;
  498. #endif
  499. FRESULT res = f_close(file);
  500. file_cleanup(fat_ctx, fd);
  501. _lock_release(&fat_ctx->lock);
  502. int rc = 0;
  503. if (res != FR_OK) {
  504. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  505. errno = fresult_to_errno(res);
  506. rc = -1;
  507. }
  508. return rc;
  509. }
  510. static off_t vfs_fat_lseek(void* ctx, int fd, off_t offset, int mode)
  511. {
  512. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  513. FIL* file = &fat_ctx->files[fd];
  514. off_t new_pos;
  515. if (mode == SEEK_SET) {
  516. new_pos = offset;
  517. } else if (mode == SEEK_CUR) {
  518. off_t cur_pos = f_tell(file);
  519. new_pos = cur_pos + offset;
  520. } else if (mode == SEEK_END) {
  521. off_t size = f_size(file);
  522. new_pos = size + offset;
  523. } else {
  524. errno = EINVAL;
  525. return -1;
  526. }
  527. #if FF_FS_EXFAT
  528. ESP_LOGD(TAG, "%s: offset=%ld, filesize:=%lld", __func__, new_pos, f_size(file));
  529. #else
  530. ESP_LOGD(TAG, "%s: offset=%ld, filesize:=%d", __func__, new_pos, f_size(file));
  531. #endif
  532. FRESULT res = f_lseek(file, new_pos);
  533. if (res != FR_OK) {
  534. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  535. errno = fresult_to_errno(res);
  536. return -1;
  537. }
  538. return new_pos;
  539. }
  540. static int vfs_fat_fstat(void* ctx, int fd, struct stat * st)
  541. {
  542. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  543. FIL* file = &fat_ctx->files[fd];
  544. memset(st, 0, sizeof(*st));
  545. st->st_size = f_size(file);
  546. st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
  547. st->st_mtime = 0;
  548. st->st_atime = 0;
  549. st->st_ctime = 0;
  550. return 0;
  551. }
  552. #ifdef CONFIG_VFS_SUPPORT_DIR
  553. static inline mode_t get_stat_mode(bool is_dir)
  554. {
  555. return S_IRWXU | S_IRWXG | S_IRWXO |
  556. ((is_dir) ? S_IFDIR : S_IFREG);
  557. }
  558. static int vfs_fat_stat(void* ctx, const char * path, struct stat * st)
  559. {
  560. if (strcmp(path, "/") == 0) {
  561. /* FatFS f_stat function does not work for the drive root.
  562. * Just pretend that this is a directory.
  563. */
  564. memset(st, 0, sizeof(*st));
  565. st->st_mode = get_stat_mode(true);
  566. return 0;
  567. }
  568. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  569. _lock_acquire(&fat_ctx->lock);
  570. prepend_drive_to_path(fat_ctx, &path, NULL);
  571. FILINFO info;
  572. FRESULT res = f_stat(path, &info);
  573. _lock_release(&fat_ctx->lock);
  574. if (res != FR_OK) {
  575. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  576. errno = fresult_to_errno(res);
  577. return -1;
  578. }
  579. memset(st, 0, sizeof(*st));
  580. st->st_size = info.fsize;
  581. st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0);
  582. fat_date_t fdate = { .as_int = info.fdate };
  583. fat_time_t ftime = { .as_int = info.ftime };
  584. struct tm tm = {
  585. .tm_mday = fdate.mday,
  586. .tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */
  587. .tm_year = fdate.year + 80,
  588. .tm_sec = ftime.sec * 2,
  589. .tm_min = ftime.min,
  590. .tm_hour = ftime.hour,
  591. /* FAT doesn't keep track if the time was DST or not, ask the C library
  592. * to try to figure this out. Note that this may yield incorrect result
  593. * in the hour before the DST comes in effect, when the local time can't
  594. * be converted to UTC uniquely.
  595. */
  596. .tm_isdst = -1
  597. };
  598. st->st_mtime = mktime(&tm);
  599. st->st_atime = 0;
  600. st->st_ctime = 0;
  601. return 0;
  602. }
  603. static int vfs_fat_unlink(void* ctx, const char *path)
  604. {
  605. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  606. _lock_acquire(&fat_ctx->lock);
  607. prepend_drive_to_path(fat_ctx, &path, NULL);
  608. FRESULT res = f_unlink(path);
  609. _lock_release(&fat_ctx->lock);
  610. if (res != FR_OK) {
  611. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  612. errno = fresult_to_errno(res);
  613. return -1;
  614. }
  615. return 0;
  616. }
  617. static int vfs_fat_link(void* ctx, const char* n1, const char* n2)
  618. {
  619. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  620. _lock_acquire(&fat_ctx->lock);
  621. prepend_drive_to_path(fat_ctx, &n1, &n2);
  622. const size_t copy_buf_size = fat_ctx->fs.csize;
  623. FRESULT res;
  624. FIL* pf1 = (FIL*) ff_memalloc(sizeof(FIL));
  625. FIL* pf2 = (FIL*) ff_memalloc(sizeof(FIL));
  626. void* buf = ff_memalloc(copy_buf_size);
  627. if (buf == NULL || pf1 == NULL || pf2 == NULL) {
  628. _lock_release(&fat_ctx->lock);
  629. ESP_LOGD(TAG, "alloc failed, pf1=%p, pf2=%p, buf=%p", pf1, pf2, buf);
  630. free(pf1);
  631. free(pf2);
  632. free(buf);
  633. errno = ENOMEM;
  634. return -1;
  635. }
  636. memset(pf1, 0, sizeof(*pf1));
  637. memset(pf2, 0, sizeof(*pf2));
  638. res = f_open(pf1, n1, FA_READ | FA_OPEN_EXISTING);
  639. if (res != FR_OK) {
  640. _lock_release(&fat_ctx->lock);
  641. goto fail1;
  642. }
  643. res = f_open(pf2, n2, FA_WRITE | FA_CREATE_NEW);
  644. _lock_release(&fat_ctx->lock);
  645. if (res != FR_OK) {
  646. goto fail2;
  647. }
  648. size_t size_left = f_size(pf1);
  649. while (size_left > 0) {
  650. size_t will_copy = (size_left < copy_buf_size) ? size_left : copy_buf_size;
  651. size_t read;
  652. res = f_read(pf1, buf, will_copy, &read);
  653. if (res != FR_OK) {
  654. goto fail3;
  655. } else if (read != will_copy) {
  656. res = FR_DISK_ERR;
  657. goto fail3;
  658. }
  659. size_t written;
  660. res = f_write(pf2, buf, will_copy, &written);
  661. if (res != FR_OK) {
  662. goto fail3;
  663. } else if (written != will_copy) {
  664. res = FR_DISK_ERR;
  665. goto fail3;
  666. }
  667. size_left -= will_copy;
  668. }
  669. fail3:
  670. f_close(pf2);
  671. fail2:
  672. f_close(pf1);
  673. fail1:
  674. free(buf);
  675. free(pf2);
  676. free(pf1);
  677. if (res != FR_OK) {
  678. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  679. errno = fresult_to_errno(res);
  680. return -1;
  681. }
  682. return 0;
  683. }
  684. static int vfs_fat_rename(void* ctx, const char *src, const char *dst)
  685. {
  686. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  687. _lock_acquire(&fat_ctx->lock);
  688. prepend_drive_to_path(fat_ctx, &src, &dst);
  689. FRESULT res = f_rename(src, dst);
  690. _lock_release(&fat_ctx->lock);
  691. if (res != FR_OK) {
  692. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  693. errno = fresult_to_errno(res);
  694. return -1;
  695. }
  696. return 0;
  697. }
  698. static DIR* vfs_fat_opendir(void* ctx, const char* name)
  699. {
  700. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  701. _lock_acquire(&fat_ctx->lock);
  702. prepend_drive_to_path(fat_ctx, &name, NULL);
  703. vfs_fat_dir_t* fat_dir = ff_memalloc(sizeof(vfs_fat_dir_t));
  704. if (!fat_dir) {
  705. _lock_release(&fat_ctx->lock);
  706. errno = ENOMEM;
  707. return NULL;
  708. }
  709. memset(fat_dir, 0, sizeof(*fat_dir));
  710. FRESULT res = f_opendir(&fat_dir->ffdir, name);
  711. _lock_release(&fat_ctx->lock);
  712. if (res != FR_OK) {
  713. free(fat_dir);
  714. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  715. errno = fresult_to_errno(res);
  716. return NULL;
  717. }
  718. return (DIR*) fat_dir;
  719. }
  720. static int vfs_fat_closedir(void* ctx, DIR* pdir)
  721. {
  722. assert(pdir);
  723. vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
  724. FRESULT res = f_closedir(&fat_dir->ffdir);
  725. free(pdir);
  726. if (res != FR_OK) {
  727. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  728. errno = fresult_to_errno(res);
  729. return -1;
  730. }
  731. return 0;
  732. }
  733. static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir)
  734. {
  735. vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
  736. struct dirent* out_dirent;
  737. int err = vfs_fat_readdir_r(ctx, pdir, &fat_dir->cur_dirent, &out_dirent);
  738. if (err != 0) {
  739. errno = err;
  740. return NULL;
  741. }
  742. return out_dirent;
  743. }
  744. static int vfs_fat_readdir_r(void* ctx, DIR* pdir,
  745. struct dirent* entry, struct dirent** out_dirent)
  746. {
  747. assert(pdir);
  748. vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
  749. FRESULT res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo);
  750. if (res != FR_OK) {
  751. *out_dirent = NULL;
  752. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  753. return fresult_to_errno(res);
  754. }
  755. if (fat_dir->filinfo.fname[0] == 0) {
  756. // end of directory
  757. *out_dirent = NULL;
  758. return 0;
  759. }
  760. entry->d_ino = 0;
  761. if (fat_dir->filinfo.fattrib & AM_DIR) {
  762. entry->d_type = DT_DIR;
  763. } else {
  764. entry->d_type = DT_REG;
  765. }
  766. strlcpy(entry->d_name, fat_dir->filinfo.fname,
  767. sizeof(entry->d_name));
  768. fat_dir->offset++;
  769. *out_dirent = entry;
  770. return 0;
  771. }
  772. static long vfs_fat_telldir(void* ctx, DIR* pdir)
  773. {
  774. assert(pdir);
  775. vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
  776. return fat_dir->offset;
  777. }
  778. static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset)
  779. {
  780. assert(pdir);
  781. vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
  782. FRESULT res;
  783. if (offset < fat_dir->offset) {
  784. res = f_rewinddir(&fat_dir->ffdir);
  785. if (res != FR_OK) {
  786. ESP_LOGD(TAG, "%s: rewinddir fresult=%d", __func__, res);
  787. errno = fresult_to_errno(res);
  788. return;
  789. }
  790. fat_dir->offset = 0;
  791. }
  792. while (fat_dir->offset < offset) {
  793. res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo);
  794. if (res != FR_OK) {
  795. ESP_LOGD(TAG, "%s: f_readdir fresult=%d", __func__, res);
  796. errno = fresult_to_errno(res);
  797. return;
  798. }
  799. fat_dir->offset++;
  800. }
  801. }
  802. static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode)
  803. {
  804. (void) mode;
  805. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  806. _lock_acquire(&fat_ctx->lock);
  807. prepend_drive_to_path(fat_ctx, &name, NULL);
  808. FRESULT res = f_mkdir(name);
  809. _lock_release(&fat_ctx->lock);
  810. if (res != FR_OK) {
  811. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  812. errno = fresult_to_errno(res);
  813. return -1;
  814. }
  815. return 0;
  816. }
  817. static int vfs_fat_rmdir(void* ctx, const char* name)
  818. {
  819. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  820. _lock_acquire(&fat_ctx->lock);
  821. prepend_drive_to_path(fat_ctx, &name, NULL);
  822. FRESULT res = f_unlink(name);
  823. _lock_release(&fat_ctx->lock);
  824. if (res != FR_OK) {
  825. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  826. errno = fresult_to_errno(res);
  827. return -1;
  828. }
  829. return 0;
  830. }
  831. static int vfs_fat_access(void* ctx, const char *path, int amode)
  832. {
  833. FILINFO info;
  834. int ret = 0;
  835. FRESULT res;
  836. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  837. _lock_acquire(&fat_ctx->lock);
  838. prepend_drive_to_path(fat_ctx, &path, NULL);
  839. res = f_stat(path, &info);
  840. _lock_release(&fat_ctx->lock);
  841. if (res == FR_OK) {
  842. if (((amode & W_OK) == W_OK) && ((info.fattrib & AM_RDO) == AM_RDO)) {
  843. ret = -1;
  844. errno = EACCES;
  845. }
  846. // There is no flag to test readable or executable: we assume that if
  847. // it exists then it is readable and executable
  848. } else {
  849. ret = -1;
  850. errno = fresult_to_errno(res);
  851. }
  852. return ret;
  853. }
  854. static int vfs_fat_truncate(void* ctx, const char *path, off_t length)
  855. {
  856. FRESULT res;
  857. FIL* file = NULL;
  858. int ret = 0;
  859. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  860. if (length < 0) {
  861. errno = EINVAL;
  862. ret = -1;
  863. goto out;
  864. }
  865. _lock_acquire(&fat_ctx->lock);
  866. prepend_drive_to_path(fat_ctx, &path, NULL);
  867. file = (FIL*) ff_memalloc(sizeof(FIL));
  868. if (file == NULL) {
  869. _lock_release(&fat_ctx->lock);
  870. ESP_LOGD(TAG, "truncate alloc failed");
  871. errno = ENOMEM;
  872. ret = -1;
  873. goto out;
  874. }
  875. memset(file, 0, sizeof(*file));
  876. res = f_open(file, path, FA_WRITE);
  877. if (res != FR_OK) {
  878. _lock_release(&fat_ctx->lock);
  879. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  880. errno = fresult_to_errno(res);
  881. ret = -1;
  882. goto out;
  883. }
  884. long sz = f_size(file);
  885. if (sz < length) {
  886. _lock_release(&fat_ctx->lock);
  887. ESP_LOGD(TAG, "truncate does not support extending size");
  888. errno = EPERM;
  889. ret = -1;
  890. goto close;
  891. }
  892. res = f_lseek(file, length);
  893. if (res != FR_OK) {
  894. _lock_release(&fat_ctx->lock);
  895. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  896. errno = fresult_to_errno(res);
  897. ret = -1;
  898. goto close;
  899. }
  900. res = f_truncate(file);
  901. _lock_release(&fat_ctx->lock);
  902. if (res != FR_OK) {
  903. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  904. errno = fresult_to_errno(res);
  905. ret = -1;
  906. }
  907. close:
  908. res = f_close(file);
  909. if (res != FR_OK) {
  910. ESP_LOGE(TAG, "closing file opened for truncate failed");
  911. // Overwrite previous errors, since not being able to close
  912. // an opened file is a more critical issue.
  913. errno = fresult_to_errno(res);
  914. ret = -1;
  915. }
  916. out:
  917. free(file);
  918. return ret;
  919. }
  920. static int vfs_fat_ftruncate(void* ctx, int fd, off_t length)
  921. {
  922. FRESULT res;
  923. FIL* file = NULL;
  924. int ret = 0;
  925. vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
  926. if (length < 0) {
  927. errno = EINVAL;
  928. ret = -1;
  929. return ret;
  930. }
  931. _lock_acquire(&fat_ctx->lock);
  932. file = &fat_ctx->files[fd];
  933. if (file == NULL) {
  934. ESP_LOGD(TAG, "ftruncate NULL file pointer");
  935. errno = EINVAL;
  936. ret = -1;
  937. goto out;
  938. }
  939. long sz = f_size(file);
  940. if (sz < length) {
  941. ESP_LOGD(TAG, "ftruncate does not support extending size");
  942. errno = EPERM;
  943. ret = -1;
  944. goto out;
  945. }
  946. res = f_lseek(file, length);
  947. if (res != FR_OK) {
  948. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  949. errno = fresult_to_errno(res);
  950. ret = -1;
  951. goto out;
  952. }
  953. res = f_truncate(file);
  954. if (res != FR_OK) {
  955. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  956. errno = fresult_to_errno(res);
  957. ret = -1;
  958. }
  959. out:
  960. _lock_release(&fat_ctx->lock);
  961. return ret;
  962. }
  963. static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *times)
  964. {
  965. FILINFO filinfo_time;
  966. {
  967. struct tm tm_time;
  968. if (times) {
  969. localtime_r(&times->modtime, &tm_time);
  970. } else {
  971. // use current time
  972. struct timeval tv;
  973. gettimeofday(&tv, NULL);
  974. localtime_r(&tv.tv_sec, &tm_time);
  975. }
  976. if (tm_time.tm_year < 80) {
  977. // FATFS cannot handle years before 1980
  978. errno = EINVAL;
  979. return -1;
  980. }
  981. fat_date_t fdate;
  982. fat_time_t ftime;
  983. // this time transformation is esentially the reverse of the one in vfs_fat_stat()
  984. fdate.mday = tm_time.tm_mday;
  985. fdate.mon = tm_time.tm_mon + 1; // January in fdate.mon is 1, and 0 in tm_time.tm_mon
  986. fdate.year = tm_time.tm_year - 80; // tm_time.tm_year=0 is 1900, tm_time.tm_year=0 is 1980
  987. ftime.sec = tm_time.tm_sec / 2, // ftime.sec counts seconds by 2
  988. ftime.min = tm_time.tm_min;
  989. ftime.hour = tm_time.tm_hour;
  990. filinfo_time.fdate = fdate.as_int;
  991. filinfo_time.ftime = ftime.as_int;
  992. }
  993. vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
  994. _lock_acquire(&fat_ctx->lock);
  995. prepend_drive_to_path(fat_ctx, &path, NULL);
  996. FRESULT res = f_utime(path, &filinfo_time);
  997. _lock_release(&fat_ctx->lock);
  998. if (res != FR_OK) {
  999. ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
  1000. errno = fresult_to_errno(res);
  1001. return -1;
  1002. }
  1003. return 0;
  1004. }
  1005. #endif // CONFIG_VFS_SUPPORT_DIR