1 /*
2  * Copyright (c) 2019 Bolt Innovation Management, LLC
3  * Copyright (c) 2019 Peter Bigot Consulting, LLC
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdio.h>
9 #include <string.h>
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12 #include <zephyr/init.h>
13 #include <zephyr/fs/fs.h>
14 #include <zephyr/fs/fs_sys.h>
15 
16 #define LFS_LOG_REGISTER
17 #include <lfs_util.h>
18 
19 #include <lfs.h>
20 #include <zephyr/fs/littlefs.h>
21 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
22 #include <zephyr/drivers/flash.h>
23 #include <zephyr/storage/flash_map.h>
24 #endif
25 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
26 #include <zephyr/storage/disk_access.h>
27 #endif
28 
29 #include "fs_impl.h"
30 
31 /* Used on devices that have no explicit erase */
32 #define LITTLEFS_DEFAULT_BLOCK_SIZE     4096
33 
34 /* note: one of the next options have to be enabled, at least */
35 BUILD_ASSERT(IS_ENABLED(CONFIG_FS_LITTLEFS_BLK_DEV) ||
36 	     IS_ENABLED(CONFIG_FS_LITTLEFS_FMP_DEV));
37 
38 struct lfs_file_data {
39 	struct lfs_file file;
40 	struct lfs_file_config config;
41 	void *cache_block;
42 };
43 
44 #define LFS_FILEP(fp) (&((struct lfs_file_data *)(fp->filep))->file)
45 
46 /* Global memory pool for open files and dirs */
47 K_MEM_SLAB_DEFINE_STATIC(file_data_pool, sizeof(struct lfs_file_data),
48 			 CONFIG_FS_LITTLEFS_NUM_FILES, 4);
49 K_MEM_SLAB_DEFINE_STATIC(lfs_dir_pool, sizeof(struct lfs_dir),
50 			 CONFIG_FS_LITTLEFS_NUM_DIRS, 4);
51 
52 /* Inferred overhead, in bytes, for each k_heap_aligned allocation for
53  * the filecache heap.  This relates to the CHUNK_UNIT parameter in
54  * the heap implementation, but that value is not visible outside the
55  * kernel.
56  * FIXME: value for this macro should be rather taken from the Kernel
57  * internals than set by user, but we do not have a way to do so now.
58  */
59 #define FC_HEAP_PER_ALLOC_OVERHEAD CONFIG_FS_LITTLEFS_HEAP_PER_ALLOC_OVERHEAD_SIZE
60 
61 #if (CONFIG_FS_LITTLEFS_FC_HEAP_SIZE - 0) <= 0
62 BUILD_ASSERT((CONFIG_FS_LITTLEFS_HEAP_PER_ALLOC_OVERHEAD_SIZE % 8) == 0);
63 /* Auto-generate heap size from cache size and number of files */
64 #undef CONFIG_FS_LITTLEFS_FC_HEAP_SIZE
65 #define CONFIG_FS_LITTLEFS_FC_HEAP_SIZE						\
66 	((CONFIG_FS_LITTLEFS_CACHE_SIZE + FC_HEAP_PER_ALLOC_OVERHEAD) *		\
67 	CONFIG_FS_LITTLEFS_NUM_FILES)
68 #endif /* CONFIG_FS_LITTLEFS_FC_HEAP_SIZE */
69 
70 static K_HEAP_DEFINE(file_cache_heap, CONFIG_FS_LITTLEFS_FC_HEAP_SIZE);
71 
littlefs_on_blkdev(int flags)72 static inline bool littlefs_on_blkdev(int flags)
73 {
74 	return (flags & FS_MOUNT_FLAG_USE_DISK_ACCESS) ? true : false;
75 }
76 
fc_allocate(size_t size)77 static inline void *fc_allocate(size_t size)
78 {
79 	void *ret = NULL;
80 
81 	ret = k_heap_alloc(&file_cache_heap, size, K_NO_WAIT);
82 
83 	return ret;
84 }
85 
fc_release(void * buf)86 static inline void fc_release(void *buf)
87 {
88 	k_heap_free(&file_cache_heap, buf);
89 }
90 
fs_lock(struct fs_littlefs * fs)91 static inline void fs_lock(struct fs_littlefs *fs)
92 {
93 	k_mutex_lock(&fs->mutex, K_FOREVER);
94 }
95 
fs_unlock(struct fs_littlefs * fs)96 static inline void fs_unlock(struct fs_littlefs *fs)
97 {
98 	k_mutex_unlock(&fs->mutex);
99 }
100 
lfs_to_errno(int error)101 static int lfs_to_errno(int error)
102 {
103 	if (error >= 0) {
104 		return error;
105 	}
106 
107 	switch (error) {
108 	default:
109 	case LFS_ERR_IO:        /* Error during device operation */
110 		return -EIO;
111 	case LFS_ERR_CORRUPT:   /* Corrupted */
112 		return -EFAULT;
113 	case LFS_ERR_NOENT:     /* No directory entry */
114 		return -ENOENT;
115 	case LFS_ERR_EXIST:     /* Entry already exists */
116 		return -EEXIST;
117 	case LFS_ERR_NOTDIR:    /* Entry is not a dir */
118 		return -ENOTDIR;
119 	case LFS_ERR_ISDIR:     /* Entry is a dir */
120 		return -EISDIR;
121 	case LFS_ERR_NOTEMPTY:  /* Dir is not empty */
122 		return -ENOTEMPTY;
123 	case LFS_ERR_BADF:      /* Bad file number */
124 		return -EBADF;
125 	case LFS_ERR_FBIG:      /* File too large */
126 		return -EFBIG;
127 	case LFS_ERR_INVAL:     /* Invalid parameter */
128 		return -EINVAL;
129 	case LFS_ERR_NOSPC:     /* No space left on device */
130 		return -ENOSPC;
131 	case LFS_ERR_NOMEM:     /* No more memory available */
132 		return -ENOMEM;
133 	}
134 }
135 
errno_to_lfs(int error)136 static int errno_to_lfs(int error)
137 {
138 	if (error >= 0) {
139 		return LFS_ERR_OK;
140 	}
141 
142 	switch (error) {
143 	default:
144 	case -EIO:              /* Error during device operation */
145 		return LFS_ERR_IO;
146 	case -EFAULT:		/* Corrupted */
147 		return LFS_ERR_CORRUPT;
148 	case -ENOENT:           /* No directory entry */
149 		return LFS_ERR_NOENT;
150 	case -EEXIST:           /* Entry already exists */
151 		return LFS_ERR_EXIST;
152 	case -ENOTDIR:          /* Entry is not a dir */
153 		return LFS_ERR_NOTDIR;
154 	case -EISDIR:           /* Entry is a dir */
155 		return LFS_ERR_ISDIR;
156 	case -ENOTEMPTY:        /* Dir is not empty */
157 		return LFS_ERR_NOTEMPTY;
158 	case -EBADF:            /* Bad file number */
159 		return LFS_ERR_BADF;
160 	case -EFBIG:            /* File too large */
161 		return LFS_ERR_FBIG;
162 	case -EINVAL:           /* Invalid parameter */
163 		return LFS_ERR_INVAL;
164 	case -ENOSPC:           /* No space left on device */
165 		return LFS_ERR_NOSPC;
166 	case -ENOMEM:           /* No more memory available */
167 		return LFS_ERR_NOMEM;
168 	}
169 }
170 
171 
172 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
173 
lfs_api_read(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)174 static int lfs_api_read(const struct lfs_config *c, lfs_block_t block,
175 			lfs_off_t off, void *buffer, lfs_size_t size)
176 {
177 	const struct flash_area *fa = c->context;
178 	size_t offset = block * c->block_size + off;
179 
180 	int rc = flash_area_read(fa, offset, buffer, size);
181 
182 	return errno_to_lfs(rc);
183 }
184 
lfs_api_prog(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)185 static int lfs_api_prog(const struct lfs_config *c, lfs_block_t block,
186 			lfs_off_t off, const void *buffer, lfs_size_t size)
187 {
188 	const struct flash_area *fa = c->context;
189 	size_t offset = block * c->block_size + off;
190 
191 	int rc = flash_area_write(fa, offset, buffer, size);
192 
193 	return errno_to_lfs(rc);
194 }
195 
lfs_api_erase(const struct lfs_config * c,lfs_block_t block)196 static int lfs_api_erase(const struct lfs_config *c, lfs_block_t block)
197 {
198 	const struct flash_area *fa = c->context;
199 	size_t offset = block * c->block_size;
200 
201 	int rc = flash_area_flatten(fa, offset, c->block_size);
202 
203 	return errno_to_lfs(rc);
204 }
205 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
206 
207 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
lfs_api_read_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)208 static int lfs_api_read_blk(const struct lfs_config *c, lfs_block_t block,
209 			    lfs_off_t off, void *buffer, lfs_size_t size)
210 {
211 	const char *disk = c->context;
212 	int rc = disk_access_read(disk, buffer, block,
213 				  size / c->block_size);
214 
215 	return errno_to_lfs(rc);
216 }
217 
lfs_api_prog_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)218 static int lfs_api_prog_blk(const struct lfs_config *c, lfs_block_t block,
219 			    lfs_off_t off, const void *buffer, lfs_size_t size)
220 {
221 	const char *disk = c->context;
222 	int rc = disk_access_write(disk, buffer, block, size / c->block_size);
223 
224 	return errno_to_lfs(rc);
225 }
226 
lfs_api_sync_blk(const struct lfs_config * c)227 static int lfs_api_sync_blk(const struct lfs_config *c)
228 {
229 	const char *disk = c->context;
230 	int rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
231 
232 	return errno_to_lfs(rc);
233 }
234 #else
lfs_api_read_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)235 static int lfs_api_read_blk(const struct lfs_config *c, lfs_block_t block,
236 			    lfs_off_t off, void *buffer, lfs_size_t size)
237 {
238 	return 0;
239 }
240 
lfs_api_prog_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)241 static int lfs_api_prog_blk(const struct lfs_config *c, lfs_block_t block,
242 			    lfs_off_t off, const void *buffer, lfs_size_t size)
243 {
244 	return 0;
245 }
246 
lfs_api_sync_blk(const struct lfs_config * c)247 static int lfs_api_sync_blk(const struct lfs_config *c)
248 {
249 	return 0;
250 }
251 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
lfs_api_erase_blk(const struct lfs_config * c,lfs_block_t block)252 static int lfs_api_erase_blk(const struct lfs_config *c, lfs_block_t block)
253 {
254 	return 0;
255 }
256 
lfs_api_sync(const struct lfs_config * c)257 static int lfs_api_sync(const struct lfs_config *c)
258 {
259 	return LFS_ERR_OK;
260 }
261 
release_file_data(struct fs_file_t * fp)262 static void release_file_data(struct fs_file_t *fp)
263 {
264 	struct lfs_file_data *fdp = fp->filep;
265 
266 	if (fdp->config.buffer) {
267 		fc_release(fdp->cache_block);
268 	}
269 
270 	k_mem_slab_free(&file_data_pool, fp->filep);
271 	fp->filep = NULL;
272 }
273 
lfs_flags_from_zephyr(unsigned int zflags)274 static int lfs_flags_from_zephyr(unsigned int zflags)
275 {
276 	int flags = (zflags & FS_O_CREATE) ? LFS_O_CREAT : 0;
277 
278 	/* LFS_O_READONLY and LFS_O_WRONLY can be selected at the same time,
279 	 * this is not a mistake, together they create RDWR access.
280 	 */
281 	flags |= (zflags & FS_O_READ) ? LFS_O_RDONLY : 0;
282 	flags |= (zflags & FS_O_WRITE) ? LFS_O_WRONLY : 0;
283 
284 	flags |= (zflags & FS_O_APPEND) ? LFS_O_APPEND : 0;
285 
286 	return flags;
287 }
288 
littlefs_open(struct fs_file_t * fp,const char * path,fs_mode_t zflags)289 static int littlefs_open(struct fs_file_t *fp, const char *path,
290 			 fs_mode_t zflags)
291 {
292 	struct fs_littlefs *fs = fp->mp->fs_data;
293 	struct lfs *lfs = &fs->lfs;
294 	int flags = lfs_flags_from_zephyr(zflags);
295 	int ret = k_mem_slab_alloc(&file_data_pool, &fp->filep, K_NO_WAIT);
296 
297 	if (ret != 0) {
298 		return ret;
299 	}
300 
301 	struct lfs_file_data *fdp = fp->filep;
302 
303 	memset(fdp, 0, sizeof(*fdp));
304 
305 	fdp->cache_block = fc_allocate(lfs->cfg->cache_size);
306 	if (fdp->cache_block == NULL) {
307 		ret = -ENOMEM;
308 		goto out;
309 	}
310 
311 	fdp->config.buffer = fdp->cache_block;
312 	path = fs_impl_strip_prefix(path, fp->mp);
313 
314 	fs_lock(fs);
315 
316 	ret = lfs_file_opencfg(&fs->lfs, &fdp->file,
317 			       path, flags, &fdp->config);
318 
319 	fs_unlock(fs);
320 out:
321 	if (ret < 0) {
322 		release_file_data(fp);
323 	}
324 
325 	return lfs_to_errno(ret);
326 }
327 
littlefs_close(struct fs_file_t * fp)328 static int littlefs_close(struct fs_file_t *fp)
329 {
330 	struct fs_littlefs *fs = fp->mp->fs_data;
331 
332 	fs_lock(fs);
333 
334 	int ret = lfs_file_close(&fs->lfs, LFS_FILEP(fp));
335 
336 	fs_unlock(fs);
337 
338 	release_file_data(fp);
339 
340 	return lfs_to_errno(ret);
341 }
342 
littlefs_unlink(struct fs_mount_t * mountp,const char * path)343 static int littlefs_unlink(struct fs_mount_t *mountp, const char *path)
344 {
345 	struct fs_littlefs *fs = mountp->fs_data;
346 
347 	path = fs_impl_strip_prefix(path, mountp);
348 
349 	fs_lock(fs);
350 
351 	int ret = lfs_remove(&fs->lfs, path);
352 
353 	fs_unlock(fs);
354 	return lfs_to_errno(ret);
355 }
356 
littlefs_rename(struct fs_mount_t * mountp,const char * from,const char * to)357 static int littlefs_rename(struct fs_mount_t *mountp, const char *from,
358 			   const char *to)
359 {
360 	struct fs_littlefs *fs = mountp->fs_data;
361 
362 	from = fs_impl_strip_prefix(from, mountp);
363 	to = fs_impl_strip_prefix(to, mountp);
364 
365 	fs_lock(fs);
366 
367 	int ret = lfs_rename(&fs->lfs, from, to);
368 
369 	fs_unlock(fs);
370 	return lfs_to_errno(ret);
371 }
372 
littlefs_read(struct fs_file_t * fp,void * ptr,size_t len)373 static ssize_t littlefs_read(struct fs_file_t *fp, void *ptr, size_t len)
374 {
375 	struct fs_littlefs *fs = fp->mp->fs_data;
376 
377 	fs_lock(fs);
378 
379 	ssize_t ret = lfs_file_read(&fs->lfs, LFS_FILEP(fp), ptr, len);
380 
381 	fs_unlock(fs);
382 	return lfs_to_errno(ret);
383 }
384 
littlefs_write(struct fs_file_t * fp,const void * ptr,size_t len)385 static ssize_t littlefs_write(struct fs_file_t *fp, const void *ptr, size_t len)
386 {
387 	struct fs_littlefs *fs = fp->mp->fs_data;
388 
389 	fs_lock(fs);
390 
391 	ssize_t ret = lfs_file_write(&fs->lfs, LFS_FILEP(fp), ptr, len);
392 
393 	fs_unlock(fs);
394 	return lfs_to_errno(ret);
395 }
396 
397 BUILD_ASSERT((FS_SEEK_SET == LFS_SEEK_SET)
398 	     && (FS_SEEK_CUR == LFS_SEEK_CUR)
399 	     && (FS_SEEK_END == LFS_SEEK_END));
400 
littlefs_seek(struct fs_file_t * fp,off_t off,int whence)401 static int littlefs_seek(struct fs_file_t *fp, off_t off, int whence)
402 {
403 	struct fs_littlefs *fs = fp->mp->fs_data;
404 
405 	fs_lock(fs);
406 
407 	off_t ret = lfs_file_seek(&fs->lfs, LFS_FILEP(fp), off, whence);
408 
409 	fs_unlock(fs);
410 
411 	if (ret >= 0) {
412 		ret = 0;
413 	}
414 
415 	return lfs_to_errno(ret);
416 }
417 
littlefs_tell(struct fs_file_t * fp)418 static off_t littlefs_tell(struct fs_file_t *fp)
419 {
420 	struct fs_littlefs *fs = fp->mp->fs_data;
421 
422 	fs_lock(fs);
423 
424 	off_t ret = lfs_file_tell(&fs->lfs, LFS_FILEP(fp));
425 
426 	fs_unlock(fs);
427 	return ret;
428 }
429 
littlefs_truncate(struct fs_file_t * fp,off_t length)430 static int littlefs_truncate(struct fs_file_t *fp, off_t length)
431 {
432 	struct fs_littlefs *fs = fp->mp->fs_data;
433 
434 	fs_lock(fs);
435 
436 	int ret = lfs_file_truncate(&fs->lfs, LFS_FILEP(fp), length);
437 
438 	fs_unlock(fs);
439 	return lfs_to_errno(ret);
440 }
441 
littlefs_sync(struct fs_file_t * fp)442 static int littlefs_sync(struct fs_file_t *fp)
443 {
444 	struct fs_littlefs *fs = fp->mp->fs_data;
445 
446 	fs_lock(fs);
447 
448 	int ret = lfs_file_sync(&fs->lfs, LFS_FILEP(fp));
449 
450 	fs_unlock(fs);
451 	return lfs_to_errno(ret);
452 }
453 
littlefs_mkdir(struct fs_mount_t * mountp,const char * path)454 static int littlefs_mkdir(struct fs_mount_t *mountp, const char *path)
455 {
456 	struct fs_littlefs *fs = mountp->fs_data;
457 
458 	path = fs_impl_strip_prefix(path, mountp);
459 	fs_lock(fs);
460 
461 	int ret = lfs_mkdir(&fs->lfs, path);
462 
463 	fs_unlock(fs);
464 	return lfs_to_errno(ret);
465 }
466 
littlefs_opendir(struct fs_dir_t * dp,const char * path)467 static int littlefs_opendir(struct fs_dir_t *dp, const char *path)
468 {
469 	struct fs_littlefs *fs = dp->mp->fs_data;
470 
471 	if (k_mem_slab_alloc(&lfs_dir_pool, &dp->dirp, K_NO_WAIT) != 0) {
472 		return -ENOMEM;
473 	}
474 
475 	memset(dp->dirp, 0, sizeof(struct lfs_dir));
476 
477 	path = fs_impl_strip_prefix(path, dp->mp);
478 
479 	fs_lock(fs);
480 
481 	int ret = lfs_dir_open(&fs->lfs, dp->dirp, path);
482 
483 	fs_unlock(fs);
484 
485 	if (ret < 0) {
486 		k_mem_slab_free(&lfs_dir_pool, dp->dirp);
487 	}
488 
489 	return lfs_to_errno(ret);
490 }
491 
info_to_dirent(const struct lfs_info * info,struct fs_dirent * entry)492 static void info_to_dirent(const struct lfs_info *info, struct fs_dirent *entry)
493 {
494 	entry->type = ((info->type == LFS_TYPE_DIR) ?
495 		       FS_DIR_ENTRY_DIR : FS_DIR_ENTRY_FILE);
496 	entry->size = info->size;
497 	strncpy(entry->name, info->name, sizeof(entry->name));
498 	entry->name[sizeof(entry->name) - 1] = '\0';
499 }
500 
littlefs_readdir(struct fs_dir_t * dp,struct fs_dirent * entry)501 static int littlefs_readdir(struct fs_dir_t *dp, struct fs_dirent *entry)
502 {
503 	struct fs_littlefs *fs = dp->mp->fs_data;
504 
505 	fs_lock(fs);
506 
507 	struct lfs_info info;
508 	int ret = lfs_dir_read(&fs->lfs, dp->dirp, &info);
509 
510 	fs_unlock(fs);
511 
512 	if (ret > 0) {
513 		info_to_dirent(&info, entry);
514 		ret = 0;
515 	} else if (ret == 0) {
516 		entry->name[0] = 0;
517 	}
518 
519 	return lfs_to_errno(ret);
520 }
521 
littlefs_closedir(struct fs_dir_t * dp)522 static int littlefs_closedir(struct fs_dir_t *dp)
523 {
524 	struct fs_littlefs *fs = dp->mp->fs_data;
525 
526 	fs_lock(fs);
527 
528 	int ret = lfs_dir_close(&fs->lfs, dp->dirp);
529 
530 	fs_unlock(fs);
531 
532 	k_mem_slab_free(&lfs_dir_pool, dp->dirp);
533 
534 	return lfs_to_errno(ret);
535 }
536 
littlefs_stat(struct fs_mount_t * mountp,const char * path,struct fs_dirent * entry)537 static int littlefs_stat(struct fs_mount_t *mountp,
538 			 const char *path, struct fs_dirent *entry)
539 {
540 	struct fs_littlefs *fs = mountp->fs_data;
541 
542 	path = fs_impl_strip_prefix(path, mountp);
543 
544 	fs_lock(fs);
545 
546 	struct lfs_info info;
547 	int ret = lfs_stat(&fs->lfs, path, &info);
548 
549 	fs_unlock(fs);
550 
551 	if (ret >= 0) {
552 		info_to_dirent(&info, entry);
553 		ret = 0;
554 	}
555 
556 	return lfs_to_errno(ret);
557 }
558 
littlefs_statvfs(struct fs_mount_t * mountp,const char * path,struct fs_statvfs * stat)559 static int littlefs_statvfs(struct fs_mount_t *mountp,
560 			    const char *path, struct fs_statvfs *stat)
561 {
562 	struct fs_littlefs *fs = mountp->fs_data;
563 	struct lfs *lfs = &fs->lfs;
564 
565 	stat->f_bsize = lfs->cfg->prog_size;
566 	stat->f_frsize = lfs->cfg->block_size;
567 	stat->f_blocks = lfs->cfg->block_count;
568 
569 	path = fs_impl_strip_prefix(path, mountp);
570 
571 	fs_lock(fs);
572 
573 	ssize_t ret = lfs_fs_size(lfs);
574 
575 	fs_unlock(fs);
576 
577 	if (ret >= 0) {
578 		stat->f_bfree = stat->f_blocks - ret;
579 		ret = 0;
580 	}
581 
582 	return lfs_to_errno(ret);
583 }
584 
585 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
586 
587 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
588 /* Return maximum page size in a flash area.  There's no flash_area
589  * API to implement this, so we have to make one here.
590  */
591 struct get_page_ctx {
592 	const struct flash_area *area;
593 	lfs_size_t max_size;
594 };
595 
get_page_cb(const struct flash_pages_info * info,void * ctxp)596 static bool get_page_cb(const struct flash_pages_info *info, void *ctxp)
597 {
598 	struct get_page_ctx *ctx = ctxp;
599 
600 	size_t info_start = info->start_offset;
601 	size_t info_end = info_start + info->size - 1U;
602 	size_t area_start = ctx->area->fa_off;
603 	size_t area_end = area_start + ctx->area->fa_size - 1U;
604 
605 	/* Ignore pages outside the area */
606 	if (info_end < area_start) {
607 		return true;
608 	}
609 	if (info_start > area_end) {
610 		return false;
611 	}
612 
613 	if (info->size > ctx->max_size) {
614 		ctx->max_size = info->size;
615 	}
616 
617 	return true;
618 }
619 #endif
620 
621 /* Iterate over all page groups in the flash area and return the
622  * largest page size we see.  This works as long as the partition is
623  * aligned so that erasing with this size is supported throughout the
624  * partition.
625  */
get_block_size(const struct flash_area * fa)626 static lfs_size_t get_block_size(const struct flash_area *fa)
627 {
628 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
629 	struct get_page_ctx ctx = {
630 		.area = fa,
631 		.max_size = 0,
632 	};
633 	const struct device *dev = flash_area_get_device(fa);
634 #if defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE)
635 	const struct flash_parameters *fparams = flash_get_parameters(dev);
636 
637 	if (!(flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT)) {
638 		return LITTLEFS_DEFAULT_BLOCK_SIZE;
639 	}
640 #endif
641 
642 	flash_page_foreach(dev, get_page_cb, &ctx);
643 
644 	return ctx.max_size;
645 #else
646 	return LITTLEFS_DEFAULT_BLOCK_SIZE;
647 #endif
648 }
649 
littlefs_flash_init(struct fs_littlefs * fs,void * dev_id)650 static int littlefs_flash_init(struct fs_littlefs *fs, void *dev_id)
651 {
652 	unsigned int area_id = POINTER_TO_UINT(dev_id);
653 	const struct flash_area **fap = (const struct flash_area **)&fs->backend;
654 	const struct device *dev;
655 	int ret;
656 
657 	/* Open flash area */
658 	ret = flash_area_open(area_id, fap);
659 	if ((ret < 0) || (*fap == NULL)) {
660 		LOG_ERR("can't open flash area %d", area_id);
661 		return -ENODEV;
662 	}
663 
664 	LOG_DBG("FS area %u at 0x%x for %u bytes", area_id,
665 		(uint32_t)(*fap)->fa_off, (uint32_t)(*fap)->fa_size);
666 
667 	dev = flash_area_get_device(*fap);
668 	if (dev == NULL) {
669 		LOG_ERR("can't get flash device: %s",
670 			(*fap)->fa_dev->name);
671 		return -ENODEV;
672 	}
673 
674 	fs->backend = (void *) *fap;
675 	return 0;
676 }
677 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
678 
littlefs_init_backend(struct fs_littlefs * fs,void * dev_id,int flags)679 static int littlefs_init_backend(struct fs_littlefs *fs, void *dev_id, int flags)
680 {
681 	int ret = 0;
682 
683 	if (!(IS_ENABLED(CONFIG_FS_LITTLEFS_FMP_DEV) && !littlefs_on_blkdev(flags)) &&
684 	    !(IS_ENABLED(CONFIG_FS_LITTLEFS_BLK_DEV) && littlefs_on_blkdev(flags))) {
685 		LOG_ERR("Can't init littlefs backend, review configs and flags 0x%08x", flags);
686 		return -ENOTSUP;
687 	}
688 
689 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
690 	if (littlefs_on_blkdev(flags)) {
691 		fs->backend = dev_id;
692 		ret = disk_access_init((char *) fs->backend);
693 		if (ret < 0) {
694 			LOG_ERR("Storage init ERROR!");
695 			return ret;
696 		}
697 	}
698 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
699 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
700 	if (!littlefs_on_blkdev(flags)) {
701 		ret = littlefs_flash_init(fs, dev_id);
702 		if (ret < 0) {
703 			return ret;
704 		}
705 	}
706 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
707 	return 0;
708 }
709 
littlefs_init_cfg(struct fs_littlefs * fs,int flags)710 static int littlefs_init_cfg(struct fs_littlefs *fs, int flags)
711 {
712 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_READ_SIZE > 0);
713 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_PROG_SIZE > 0);
714 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_CACHE_SIZE > 0);
715 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE > 0);
716 	BUILD_ASSERT((CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE % 8) == 0);
717 	BUILD_ASSERT((CONFIG_FS_LITTLEFS_CACHE_SIZE
718 		      % CONFIG_FS_LITTLEFS_READ_SIZE) == 0);
719 	BUILD_ASSERT((CONFIG_FS_LITTLEFS_CACHE_SIZE
720 		      % CONFIG_FS_LITTLEFS_PROG_SIZE) == 0);
721 
722 	struct lfs_config *lcp = &fs->cfg;
723 
724 	lfs_size_t read_size = lcp->read_size;
725 
726 	if (read_size == 0) {
727 		read_size = CONFIG_FS_LITTLEFS_READ_SIZE;
728 	}
729 
730 	lfs_size_t prog_size = lcp->prog_size;
731 
732 	if (prog_size == 0) {
733 		prog_size = CONFIG_FS_LITTLEFS_PROG_SIZE;
734 	}
735 
736 	/* Yes, you can override block size. */
737 	lfs_size_t block_size = lcp->block_size;
738 
739 	if (!(IS_ENABLED(CONFIG_FS_LITTLEFS_FMP_DEV) && !littlefs_on_blkdev(flags)) &&
740 	    !(IS_ENABLED(CONFIG_FS_LITTLEFS_BLK_DEV) && littlefs_on_blkdev(flags))) {
741 		LOG_ERR("Can't init littlefs config, review configs and flags 0x%08x", flags);
742 		return -ENOTSUP;
743 	}
744 
745 	if (block_size == 0) {
746 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
747 		if (littlefs_on_blkdev(flags)) {
748 			int ret = disk_access_ioctl((char *) fs->backend,
749 						DISK_IOCTL_GET_SECTOR_SIZE,
750 						&block_size);
751 			if (ret < 0) {
752 				LOG_ERR("Unable to get sector size");
753 				return ret;
754 			}
755 		}
756 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
757 
758 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
759 		if (!littlefs_on_blkdev(flags)) {
760 			block_size = get_block_size((struct flash_area *)fs->backend);
761 		}
762 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
763 	}
764 
765 	if (block_size == 0) {
766 		__ASSERT_NO_MSG(block_size != 0);
767 		return -EINVAL;
768 	}
769 
770 	int32_t block_cycles = lcp->block_cycles;
771 
772 	if (block_cycles == 0) {
773 		block_cycles = CONFIG_FS_LITTLEFS_BLOCK_CYCLES;
774 	}
775 	if (block_cycles <= 0) {
776 		/* Disable leveling (littlefs v2.1+ semantics) */
777 		block_cycles = -1;
778 	}
779 
780 	lfs_size_t cache_size = lcp->cache_size;
781 
782 	if (cache_size == 0) {
783 		cache_size = CONFIG_FS_LITTLEFS_CACHE_SIZE;
784 	}
785 
786 	lfs_size_t lookahead_size = lcp->lookahead_size;
787 
788 	if (lookahead_size == 0) {
789 		lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE;
790 	}
791 
792 #ifdef CONFIG_FS_LITTLEFS_DISK_VERSION
793 	uint32_t disk_version = lcp->disk_version;
794 
795 	if (disk_version == 0) {
796 		disk_version = LFS_DISK_VERSION;
797 	}
798 #endif /* CONFIG_FS_LITTLEFS_DISK_VERSION */
799 
800 	/* No, you don't get to override this. */
801 	lfs_size_t block_count = 0;
802 
803 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
804 	if (littlefs_on_blkdev(flags)) {
805 		int ret = disk_access_ioctl((char *) fs->backend,
806 					DISK_IOCTL_GET_SECTOR_COUNT,
807 					&block_count);
808 		if (ret < 0) {
809 			LOG_ERR("Unable to get sector count!");
810 			return -EINVAL;
811 		}
812 		LOG_INF("FS at %s: is %u 0x%x-byte blocks with %u cycle",
813 			(char *) fs->backend, block_count, block_size,
814 			block_cycles);
815 	}
816 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
817 
818 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
819 	if (!littlefs_on_blkdev(flags)) {
820 		block_count = ((struct flash_area *)fs->backend)->fa_size
821 			/ block_size;
822 		const struct device *dev =
823 			flash_area_get_device((struct flash_area *)fs->backend);
824 		LOG_INF("FS at %s:0x%x is %u 0x%x-byte blocks with %u cycle",
825 			dev->name,
826 			(uint32_t)((struct flash_area *)fs->backend)->fa_off,
827 			block_count, block_size, block_cycles);
828 		LOG_INF("partition sizes: rd %u ; pr %u ; ca %u ; la %u",
829 			read_size, prog_size, cache_size, lookahead_size);
830 	}
831 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
832 
833 	__ASSERT_NO_MSG(prog_size != 0);
834 	__ASSERT_NO_MSG(read_size != 0);
835 	__ASSERT_NO_MSG(cache_size != 0);
836 	__ASSERT_NO_MSG(block_size != 0);
837 	__ASSERT_NO_MSG(block_count != 0);
838 
839 	__ASSERT((block_size % prog_size) == 0,
840 		 "erase size must be multiple of write size");
841 	__ASSERT((block_size % cache_size) == 0,
842 		 "cache size incompatible with block size");
843 
844 	lcp->context = fs->backend;
845 	/* Set the validated/defaulted values. */
846 	if (littlefs_on_blkdev(flags)) {
847 		lfs_size_t new_cache_size = block_size;
848 		lfs_size_t new_lookahead_size = block_size * 4;
849 
850 		lcp->read = lfs_api_read_blk;
851 		lcp->prog = lfs_api_prog_blk;
852 		lcp->erase = lfs_api_erase_blk;
853 
854 		lcp->read_size = block_size;
855 		lcp->prog_size = block_size;
856 
857 		if (cache_size < new_cache_size) {
858 			LOG_ERR("Configured cache size is too small: %d < %d", cache_size,
859 				new_cache_size);
860 			return -ENOMEM;
861 		}
862 		lcp->cache_size = new_cache_size;
863 
864 		if (lookahead_size < new_lookahead_size) {
865 			LOG_ERR("Configured lookahead size is too small: %d < %d",
866 				lookahead_size, new_lookahead_size);
867 			return -ENOMEM;
868 		}
869 		lcp->lookahead_size = new_lookahead_size;
870 
871 		lcp->sync = lfs_api_sync_blk;
872 
873 		LOG_INF("partition sizes: rd %u ; pr %u ; ca %u ; la %u",
874 			lcp->read_size, lcp->prog_size, lcp->cache_size,
875 			lcp->lookahead_size);
876 	} else {
877 		__ASSERT((((struct flash_area *)fs->backend)->fa_size %
878 			  block_size) == 0,
879 			 "partition size must be multiple of block size");
880 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
881 		lcp->read = lfs_api_read;
882 		lcp->prog = lfs_api_prog;
883 		lcp->erase = lfs_api_erase;
884 #endif
885 
886 		lcp->read_size = read_size;
887 		lcp->prog_size = prog_size;
888 		lcp->cache_size = cache_size;
889 		lcp->lookahead_size = lookahead_size;
890 		lcp->sync = lfs_api_sync;
891 	}
892 
893 #ifdef CONFIG_FS_LITTLEFS_DISK_VERSION
894 	lcp->disk_version = disk_version;
895 	LOG_INF("partition disk version: %u.%u",
896 		(uint32_t)FS_LITTLEFS_DISK_VERSION_MAJOR_GET(disk_version),
897 		(uint32_t)FS_LITTLEFS_DISK_VERSION_MINOR_GET(disk_version));
898 #endif /* CONFIG_FS_LITTLEFS_DISK_VERSION */
899 
900 	lcp->block_size = block_size;
901 	lcp->block_count = block_count;
902 	lcp->block_cycles = block_cycles;
903 	return 0;
904 }
905 
littlefs_init_fs(struct fs_littlefs * fs,void * dev_id,int flags)906 static int littlefs_init_fs(struct fs_littlefs *fs, void *dev_id, int flags)
907 {
908 	int ret = 0;
909 
910 	LOG_INF("LittleFS version %u.%u, disk version %u.%u",
911 		LFS_VERSION_MAJOR, LFS_VERSION_MINOR,
912 		LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
913 
914 	if (fs->backend) {
915 		return -EBUSY;
916 	}
917 
918 	ret = littlefs_init_backend(fs, dev_id, flags);
919 	if (ret < 0) {
920 		return ret;
921 	}
922 
923 	ret = littlefs_init_cfg(fs, flags);
924 	if (ret < 0) {
925 		return ret;
926 	}
927 	return 0;
928 }
929 
littlefs_mount(struct fs_mount_t * mountp)930 static int littlefs_mount(struct fs_mount_t *mountp)
931 {
932 	int ret = 0;
933 	struct fs_littlefs *fs = mountp->fs_data;
934 
935 	/* Create and take mutex. */
936 	k_mutex_init(&fs->mutex);
937 	fs_lock(fs);
938 
939 	ret = littlefs_init_fs(fs, mountp->storage_dev, mountp->flags);
940 	if (ret < 0) {
941 		goto out;
942 	}
943 
944 	/* Mount it, formatting if needed. */
945 	ret = lfs_mount(&fs->lfs, &fs->cfg);
946 	if (ret < 0 &&
947 	    (mountp->flags & FS_MOUNT_FLAG_NO_FORMAT) == 0) {
948 		if ((mountp->flags & FS_MOUNT_FLAG_READ_ONLY) == 0) {
949 			LOG_WRN("can't mount (LFS %d); formatting", ret);
950 			ret = lfs_format(&fs->lfs, &fs->cfg);
951 			if (ret < 0) {
952 				LOG_ERR("format failed (LFS %d)", ret);
953 				ret = lfs_to_errno(ret);
954 				goto out;
955 			}
956 		} else {
957 			LOG_ERR("can not format read-only system");
958 			ret = -EROFS;
959 			goto out;
960 		}
961 
962 		ret = lfs_mount(&fs->lfs, &fs->cfg);
963 		if (ret < 0) {
964 			LOG_ERR("remount after format failed (LFS %d)", ret);
965 			ret = lfs_to_errno(ret);
966 			goto out;
967 		}
968 	} else {
969 		ret = lfs_to_errno(ret);
970 		goto out;
971 	}
972 
973 	LOG_INF("%s mounted", mountp->mnt_point);
974 
975 out:
976 	if (ret < 0) {
977 		fs->backend = NULL;
978 	}
979 
980 	fs_unlock(fs);
981 
982 	return ret;
983 }
984 
985 #if defined(CONFIG_FILE_SYSTEM_MKFS)
986 
987 FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(fs_cfg);
988 
littlefs_mkfs(uintptr_t dev_id,void * cfg,int flags)989 static int littlefs_mkfs(uintptr_t dev_id, void *cfg, int flags)
990 {
991 	int ret = 0;
992 	struct fs_littlefs *fs = &fs_cfg;
993 
994 	if (cfg != NULL) {
995 		fs = (struct fs_littlefs *)cfg;
996 	}
997 
998 	fs->backend = NULL;
999 
1000 	/* Create and take mutex. */
1001 	k_mutex_init(&fs->mutex);
1002 	fs_lock(fs);
1003 
1004 	ret = littlefs_init_fs(fs, UINT_TO_POINTER(dev_id), flags);
1005 	if (ret < 0) {
1006 		goto out;
1007 	}
1008 
1009 	ret = lfs_format(&fs->lfs, &fs->cfg);
1010 	if (ret < 0) {
1011 		LOG_ERR("format failed (LFS %d)", ret);
1012 		ret = lfs_to_errno(ret);
1013 		goto out;
1014 	}
1015 out:
1016 	fs->backend = NULL;
1017 	fs_unlock(fs);
1018 	return ret;
1019 }
1020 
1021 #endif /* CONFIG_FILE_SYSTEM_MKFS */
1022 
littlefs_unmount(struct fs_mount_t * mountp)1023 static int littlefs_unmount(struct fs_mount_t *mountp)
1024 {
1025 	struct fs_littlefs *fs = mountp->fs_data;
1026 
1027 	fs_lock(fs);
1028 
1029 	lfs_unmount(&fs->lfs);
1030 
1031 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
1032 	if (!littlefs_on_blkdev(mountp->flags)) {
1033 		flash_area_close(fs->backend);
1034 	}
1035 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
1036 
1037 	fs->backend = NULL;
1038 	fs_unlock(fs);
1039 
1040 	LOG_INF("%s unmounted", mountp->mnt_point);
1041 
1042 	return 0;
1043 }
1044 
1045 /* File system interface */
1046 static const struct fs_file_system_t littlefs_fs = {
1047 	.open = littlefs_open,
1048 	.close = littlefs_close,
1049 	.read = littlefs_read,
1050 	.write = littlefs_write,
1051 	.lseek = littlefs_seek,
1052 	.tell = littlefs_tell,
1053 	.truncate = littlefs_truncate,
1054 	.sync = littlefs_sync,
1055 	.opendir = littlefs_opendir,
1056 	.readdir = littlefs_readdir,
1057 	.closedir = littlefs_closedir,
1058 	.mount = littlefs_mount,
1059 	.unmount = littlefs_unmount,
1060 	.unlink = littlefs_unlink,
1061 	.rename = littlefs_rename,
1062 	.mkdir = littlefs_mkdir,
1063 	.stat = littlefs_stat,
1064 	.statvfs = littlefs_statvfs,
1065 #if defined(CONFIG_FILE_SYSTEM_MKFS)
1066 	.mkfs = littlefs_mkfs,
1067 #endif
1068 };
1069 
1070 #define DT_DRV_COMPAT zephyr_fstab_littlefs
1071 #define FS_PARTITION(inst) DT_PHANDLE_BY_IDX(DT_DRV_INST(inst), partition, 0)
1072 #ifdef CONFIG_FS_LITTLEFS_DISK_VERSION
1073 #define FS_DISK_VERSION(inst) \
1074 	.disk_version = DT_INST_PROP_OR(inst, disk_version, LFS_DISK_VERSION),
1075 #else
1076 #define FS_DISK_VERSION(inst)
1077 #endif
1078 
1079 #define DEFINE_FS(inst) \
1080 static uint8_t __aligned(4) \
1081 	read_buffer_##inst[DT_INST_PROP(inst, cache_size)]; \
1082 static uint8_t __aligned(4) \
1083 	prog_buffer_##inst[DT_INST_PROP(inst, cache_size)]; \
1084 static uint32_t lookahead_buffer_##inst[DT_INST_PROP(inst, lookahead_size) \
1085 					/ sizeof(uint32_t)]; \
1086 BUILD_ASSERT(DT_INST_PROP(inst, read_size) > 0); \
1087 BUILD_ASSERT(DT_INST_PROP(inst, prog_size) > 0); \
1088 BUILD_ASSERT(DT_INST_PROP(inst, cache_size) > 0); \
1089 BUILD_ASSERT(DT_INST_PROP(inst, lookahead_size) > 0); \
1090 BUILD_ASSERT((DT_INST_PROP(inst, lookahead_size) % 8) == 0); \
1091 BUILD_ASSERT((DT_INST_PROP(inst, cache_size) \
1092 	      % DT_INST_PROP(inst, read_size)) == 0); \
1093 BUILD_ASSERT((DT_INST_PROP(inst, cache_size) \
1094 	      % DT_INST_PROP(inst, prog_size)) == 0); \
1095 static struct fs_littlefs fs_data_##inst = { \
1096 	.cfg = { \
1097 		.read_size = DT_INST_PROP(inst, read_size), \
1098 		.prog_size = DT_INST_PROP(inst, prog_size), \
1099 		.cache_size = DT_INST_PROP(inst, cache_size), \
1100 		.lookahead_size = DT_INST_PROP(inst, lookahead_size), \
1101 		.block_cycles = DT_INST_PROP(inst, block_cycles), \
1102 		.read_buffer = read_buffer_##inst, \
1103 		.prog_buffer = prog_buffer_##inst, \
1104 		.lookahead_buffer = lookahead_buffer_##inst, \
1105 		FS_DISK_VERSION(inst) \
1106 	}, \
1107 }; \
1108 struct fs_mount_t FS_FSTAB_ENTRY(DT_DRV_INST(inst)) = { \
1109 	.type = FS_LITTLEFS, \
1110 	.mnt_point = DT_INST_PROP(inst, mount_point), \
1111 	.fs_data = &fs_data_##inst, \
1112 	.storage_dev = (void *)DT_FIXED_PARTITION_ID(FS_PARTITION(inst)), \
1113 	.flags = FSTAB_ENTRY_DT_MOUNT_FLAGS(DT_DRV_INST(inst)), \
1114 };
1115 
DT_INST_FOREACH_STATUS_OKAY(DEFINE_FS)1116 DT_INST_FOREACH_STATUS_OKAY(DEFINE_FS)
1117 
1118 #define REFERENCE_MOUNT(inst) (&FS_FSTAB_ENTRY(DT_DRV_INST(inst))),
1119 
1120 static void mount_init(struct fs_mount_t *mp)
1121 {
1122 
1123 	LOG_INF("littlefs partition at %s", mp->mnt_point);
1124 	if ((mp->flags & FS_MOUNT_FLAG_AUTOMOUNT) != 0) {
1125 		int rc = fs_mount(mp);
1126 
1127 		if (rc < 0) {
1128 			LOG_ERR("Automount %s failed: %d",
1129 				mp->mnt_point, rc);
1130 		} else {
1131 			LOG_INF("Automount %s succeeded",
1132 				mp->mnt_point);
1133 		}
1134 	}
1135 }
1136 
littlefs_init(void)1137 static int littlefs_init(void)
1138 {
1139 	static struct fs_mount_t *partitions[] = {
1140 		DT_INST_FOREACH_STATUS_OKAY(REFERENCE_MOUNT)
1141 	};
1142 
1143 	int rc = fs_register(FS_LITTLEFS, &littlefs_fs);
1144 
1145 	if (rc == 0) {
1146 		struct fs_mount_t **mpi = partitions;
1147 
1148 		while (mpi < (partitions + ARRAY_SIZE(partitions))) {
1149 			mount_init(*mpi++);
1150 		}
1151 	}
1152 
1153 	return rc;
1154 }
1155 
1156 SYS_INIT(littlefs_init, POST_KERNEL, CONFIG_FILE_SYSTEM_INIT_PRIORITY);
1157