mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-11-01 08:48:31 +01:00 
			
		
		
		
	Compare commits
	
		
			28 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 7e110b44c0 | ||
|  | 7f7b7332e3 | ||
|  | d3a2cf48d4 | ||
|  | 22b0456623 | ||
|  | ec4d8b68ad | ||
|  | c7894a61e1 | ||
|  | 195075819e | ||
|  | 97d8d5e96a | ||
|  | 0bb1f7af17 | ||
|  | 447d89cbd8 | ||
|  | 28d2d96a83 | ||
|  | cb62bf2188 | ||
|  | 646b1b5a6c | ||
|  | 1b7a15599e | ||
|  | e5a6938faf | ||
|  | 6ad544f3f3 | ||
|  | 3419284689 | ||
|  | 510cd13df9 | ||
|  | f5e0539951 | ||
|  | 066448055c | ||
|  | d66723ccfd | ||
|  | 0234c77102 | ||
|  | 84adead98b | ||
|  | 0422c55b81 | ||
|  | 11ad3a2414 | ||
|  | 16318d003f | ||
|  | 961fab70c3 | ||
|  | 041e90a1ca | 
							
								
								
									
										124
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -35,7 +35,7 @@ script: | ||||
|     if [ "$TRAVIS_TEST_RESULT" -eq 0 ] | ||||
|     then | ||||
|         CURR=$(tail -n1 sizes | awk '{print $1}') | ||||
|         PREV=$(curl -u $GEKY_BOT_STATUSES https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ | ||||
|         PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ | ||||
|             | jq -re "select(.sha != \"$TRAVIS_COMMIT\") | ||||
|                 | .statuses[] | select(.context == \"$STAGE/$NAME\").description | ||||
|                 | capture(\"code size is (?<size>[0-9]+)\").size" \ | ||||
| @@ -100,6 +100,7 @@ jobs: | ||||
|       env: | ||||
|         - STAGE=test | ||||
|         - NAME=littlefs-fuse | ||||
|       if: branch !~ -prefix$ | ||||
|       install: | ||||
|         - sudo apt-get install libfuse-dev | ||||
|         - git clone --depth 1 https://github.com/geky/littlefs-fuse | ||||
| @@ -128,65 +129,88 @@ jobs: | ||||
|         - ls | ||||
|         - make -B test_dirs test_files QUIET=1 | ||||
|  | ||||
|       # Automatically update releases | ||||
|     # Automatically create releases | ||||
|     - stage: deploy | ||||
|       env: | ||||
|         - STAGE=deploy | ||||
|         - NAME=deploy | ||||
|       script: | ||||
|         # Update tag for version defined in lfs.h | ||||
|         - LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3) | ||||
|         - LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16))) | ||||
|         - LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >>  0))) | ||||
|         - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR" | ||||
|         - echo "littlefs version $LFS_VERSION" | ||||
|         - | | ||||
|           curl -u $GEKY_BOT_RELEASES -X POST \ | ||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \ | ||||
|             -d "{ | ||||
|               \"ref\": \"refs/tags/$LFS_VERSION\", | ||||
|               \"sha\": \"$TRAVIS_COMMIT\" | ||||
|             }" | ||||
|         - | | ||||
|           curl -f -u $GEKY_BOT_RELEASES -X PATCH \ | ||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/$LFS_VERSION \ | ||||
|             -d "{ | ||||
|               \"sha\": \"$TRAVIS_COMMIT\" | ||||
|             }" | ||||
|         # Create release notes from commits | ||||
|         - LFS_PREV_VERSION="v$LFS_VERSION_MAJOR.$(($LFS_VERSION_MINOR-1))" | ||||
|         - | | ||||
|           if [ $(git tag -l "$LFS_PREV_VERSION") ] | ||||
|           bash << 'SCRIPT' | ||||
|           set -ev | ||||
|           # Find version defined in lfs.h | ||||
|           LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3) | ||||
|           LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16))) | ||||
|           LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >>  0))) | ||||
|           # Grab latests patch from repo tags, default to 0, needs finagling | ||||
|           # to get past github's pagination api | ||||
|           PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR. | ||||
|           PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I \ | ||||
|               | sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1' \ | ||||
|               || echo $PREV_URL) | ||||
|           LFS_VERSION_PATCH=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" \ | ||||
|               | jq 'map(.ref | match("\\bv.*\\..*\\.(.*)$";"g") | ||||
|                   .captures[].string | tonumber) | max + 1' \ | ||||
|               || echo 0) | ||||
|           # We have our new version | ||||
|           LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH" | ||||
|           echo "VERSION $LFS_VERSION" | ||||
|           # Check that we're the most recent commit | ||||
|           CURRENT_COMMIT=$(curl -f -u "$GEKY_BOT_RELEASES" \ | ||||
|               https://api.github.com/repos/$TRAVIS_REPO_SLUG/commits/master \ | ||||
|               | jq -re '.sha') | ||||
|           [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] || exit 0 | ||||
|           # Create major branch | ||||
|           git branch v$LFS_VERSION_MAJOR HEAD | ||||
|           # Create major prefix branch | ||||
|           git config user.name "geky bot" | ||||
|           git config user.email "bot@geky.net" | ||||
|           git fetch https://github.com/$TRAVIS_REPO_SLUG.git \ | ||||
|               --depth=50 v$LFS_VERSION_MAJOR-prefix || true | ||||
|           ./scripts/prefix.py lfs$LFS_VERSION_MAJOR | ||||
|           git branch v$LFS_VERSION_MAJOR-prefix $( \ | ||||
|               git commit-tree $(git write-tree) \ | ||||
|                   $(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \ | ||||
|                   -p HEAD \ | ||||
|                   -m "Generated v$LFS_VERSION_MAJOR prefixes") | ||||
|           git reset --hard | ||||
|           # Update major version branches (vN and vN-prefix) | ||||
|           git push https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \ | ||||
|               v$LFS_VERSION_MAJOR \ | ||||
|               v$LFS_VERSION_MAJOR-prefix | ||||
|           # Create patch version tag (vN.N.N) | ||||
|           curl -f -u "$GEKY_BOT_RELEASES" -X POST \ | ||||
|               https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \ | ||||
|               -d "{ | ||||
|                   \"ref\": \"refs/tags/$LFS_VERSION\", | ||||
|                   \"sha\": \"$TRAVIS_COMMIT\" | ||||
|               }" | ||||
|           # Create minor release? | ||||
|           [[ "$LFS_VERSION" == *.0 ]] || exit 0 | ||||
|           # Build release notes | ||||
|           PREV=$(git tag --sort=-v:refname -l "v*.0" | head -1) | ||||
|           if [ ! -z "$PREV" ] | ||||
|           then | ||||
|             curl -u $GEKY_BOT_RELEASES -X POST \ | ||||
|                 https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \ | ||||
|                 -d "{ | ||||
|                     \"tag_name\": \"$LFS_VERSION\", | ||||
|                     \"name\": \"$LFS_VERSION\" | ||||
|                 }" | ||||
|             RELEASE=$( | ||||
|                 curl -f -u $GEKY_BOT_RELEASES \ | ||||
|                     https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/tags/$LFS_VERSION | ||||
|             ) | ||||
|             CHANGES=$( | ||||
|                 git log --oneline $LFS_PREV_VERSION.. --grep='^Merge' --invert-grep | ||||
|             ) | ||||
|             curl -f -u $GEKY_BOT_RELEASES -X PATCH \ | ||||
|                 https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/$( | ||||
|                     jq -r '.id' <<< "$RELEASE" | ||||
|                 ) \ | ||||
|                 -d "$( | ||||
|                     jq -s '{ | ||||
|                         "body": ((.[0] // "" | sub("(?<=\n)#+ Changes.*"; ""; "mi")) | ||||
|                             + "### Changes\n\n" + .[1]) | ||||
|                     }' <(jq '.body' <<< "$RELEASE") <(jq -sR '.' <<< "$CHANGES") | ||||
|                 )" | ||||
|               echo "PREV $PREV" | ||||
|               CHANGES=$'### Changes\n\n'$( \ | ||||
|                   git log --oneline $PREV.. --grep='^Merge' --invert-grep) | ||||
|               printf "CHANGES\n%s\n\n" "$CHANGES" | ||||
|           fi | ||||
|           # Create the release | ||||
|           curl -f -u "$GEKY_BOT_RELEASES" -X POST \ | ||||
|               https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \ | ||||
|               -d "{ | ||||
|                   \"tag_name\": \"$LFS_VERSION\", | ||||
|                   \"name\": \"${LFS_VERSION%.0}\", | ||||
|                   \"draft\": true, | ||||
|                   \"body\": $(jq -sR '.' <<< "$CHANGES") | ||||
|               }" #" | ||||
|           SCRIPT | ||||
|  | ||||
| # Manage statuses | ||||
| before_install: | ||||
|   - | | ||||
|     curl -u $GEKY_BOT_STATUSES -X POST \ | ||||
|     curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||
|         https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||
|         -d "{ | ||||
|             \"context\": \"$STAGE/$NAME\", | ||||
| @@ -197,7 +221,7 @@ before_install: | ||||
|  | ||||
| after_failure: | ||||
|   - | | ||||
|     curl -u $GEKY_BOT_STATUSES -X POST \ | ||||
|     curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||
|         https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||
|         -d "{ | ||||
|             \"context\": \"$STAGE/$NAME\", | ||||
| @@ -208,7 +232,7 @@ after_failure: | ||||
|  | ||||
| after_success: | ||||
|   - | | ||||
|     curl -u $GEKY_BOT_STATUSES -X POST \ | ||||
|     curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||
|         https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||
|         -d "{ | ||||
|             \"context\": \"$STAGE/$NAME\", | ||||
|   | ||||
							
								
								
									
										12
									
								
								LICENSE.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								LICENSE.md
									
									
									
									
									
								
							| @@ -22,15 +22,3 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Note*: | ||||
| Individual files contain the following tag instead of the full license text. | ||||
|  | ||||
|  | ||||
|  | ||||
|     SPDX-License-Identifier:    BSD-3-Clause | ||||
|  | ||||
| This enables machine processing of license information based on the SPDX | ||||
| License Identifiers that are here available: http://spdx.org/licenses/ | ||||
|   | ||||
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @@ -25,7 +25,8 @@ ifdef WORD | ||||
| override CFLAGS += -m$(WORD) | ||||
| endif | ||||
| override CFLAGS += -I. | ||||
| override CFLAGS += -std=c99 -Wall -pedantic -Wshadow -Wunused-parameter | ||||
| override CFLAGS += -std=c99 -Wall -pedantic | ||||
| override CFLAGS += -Wshadow -Wunused-parameter -Wjump-misses-init -Wsign-compare | ||||
|  | ||||
|  | ||||
| all: $(TARGET) | ||||
|   | ||||
							
								
								
									
										34
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								README.md
									
									
									
									
									
								
							| @@ -111,9 +111,9 @@ filesystem until sync or close is called on the file. | ||||
|  | ||||
| ## Other notes | ||||
|  | ||||
| All littlefs have the potential to return a negative error code. The errors | ||||
| can be either one of those found in the `enum lfs_error` in [lfs.h](lfs.h), | ||||
| or an error returned by the user's block device operations. | ||||
| All littlefs calls have the potential to return a negative error code. The  | ||||
| errors can be either one of those found in the `enum lfs_error` in  | ||||
| [lfs.h](lfs.h), or an error returned by the user's block device operations. | ||||
|  | ||||
| In the configuration struct, the `prog` and `erase` function provided by the | ||||
| user may return a `LFS_ERR_CORRUPT` error if the implementation already can | ||||
| @@ -146,6 +146,19 @@ The tests assume a Linux environment and can be started with make: | ||||
| make test | ||||
| ``` | ||||
|  | ||||
| ## License | ||||
|  | ||||
| The littlefs is provided under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html) | ||||
| license. See [LICENSE.md](LICENSE.md) for more information. Contributions to | ||||
| this project are accepted under the same license. | ||||
|  | ||||
| Individual files contain the following tag instead of the full license text. | ||||
|  | ||||
|     SPDX-License-Identifier:    BSD-3-Clause | ||||
|  | ||||
| This enables machine processing of license information based on the SPDX | ||||
| License Identifiers that are here available: http://spdx.org/licenses/ | ||||
|  | ||||
| ## Related projects | ||||
|  | ||||
| [Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) - | ||||
| @@ -162,3 +175,18 @@ handy. | ||||
| [littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for | ||||
| littlefs. I'm not sure why you would want this, but it is handy for demos. | ||||
| You can see it in action [here](http://littlefs.geky.net/demo.html). | ||||
|  | ||||
| [mklfs](https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src) - | ||||
| A command line tool built by the [Lua RTOS](https://github.com/whitecatboard/Lua-RTOS-ESP32) | ||||
| guys for making littlefs images from a host PC. Supports Windows, Mac OS, | ||||
| and Linux. | ||||
|  | ||||
| [SPIFFS](https://github.com/pellepl/spiffs) - Another excellent embedded | ||||
| filesystem for NOR flash. As a more traditional logging filesystem with full | ||||
| static wear-leveling, SPIFFS will likely outperform littlefs on small | ||||
| memories such as the internal flash on microcontrollers. | ||||
|  | ||||
| [Dhara](https://github.com/dlbeer/dhara) - An interesting NAND flash | ||||
| translation layer designed for small MCUs. It offers static wear-leveling and | ||||
| power-resilience with only a fixed O(|address|) pointer structure stored on | ||||
| each block and in RAM. | ||||
|   | ||||
| @@ -47,19 +47,24 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|  | ||||
|     // Load stats to continue incrementing | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); | ||||
|  | ||||
|     FILE *f = fopen(emu->path, "r"); | ||||
|     if (!f) { | ||||
|     if (!f && errno != ENOENT) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
|     if (errno == ENOENT) { | ||||
|         memset(&emu->stats, 0x0, sizeof(emu->stats)); | ||||
|     } else { | ||||
|         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|         if (res < 1) { | ||||
|             return -errno; | ||||
|         } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|   | ||||
							
								
								
									
										414
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										414
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -417,11 +417,14 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { | ||||
|     // rather than clobbering one of the blocks we just pretend | ||||
|     // the revision may be valid | ||||
|     int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4); | ||||
|     dir->d.rev = lfs_fromle32(dir->d.rev); | ||||
|     if (err) { | ||||
|     if (err && err != LFS_ERR_CORRUPT) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     if (err != LFS_ERR_CORRUPT) { | ||||
|         dir->d.rev = lfs_fromle32(dir->d.rev); | ||||
|     } | ||||
|  | ||||
|     // set defaults | ||||
|     dir->d.rev += 1; | ||||
|     dir->d.size = sizeof(dir->d)+4; | ||||
| @@ -445,6 +448,9 @@ static int lfs_dir_fetch(lfs_t *lfs, | ||||
|         int err = lfs_bd_read(lfs, tpair[i], 0, &test, sizeof(test)); | ||||
|         lfs_dir_fromle32(&test); | ||||
|         if (err) { | ||||
|             if (err == LFS_ERR_CORRUPT) { | ||||
|                 continue; | ||||
|             } | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
| @@ -464,6 +470,9 @@ static int lfs_dir_fetch(lfs_t *lfs, | ||||
|         err = lfs_bd_crc(lfs, tpair[i], sizeof(test), | ||||
|                 (0x7fffffff & test.size) - sizeof(test), &crc); | ||||
|         if (err) { | ||||
|             if (err == LFS_ERR_CORRUPT) { | ||||
|                 continue; | ||||
|             } | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
| @@ -879,7 +888,7 @@ nextname: | ||||
|         } | ||||
|  | ||||
|         // check that entry has not been moved | ||||
|         if (entry->d.type & 0x80) { | ||||
|         if (!lfs->moving && entry->d.type & 0x80) { | ||||
|             int moved = lfs_moved(lfs, &entry->d.u); | ||||
|             if (moved < 0 || moved) { | ||||
|                 return (moved < 0) ? moved : LFS_ERR_NOENT; | ||||
| @@ -1282,8 +1291,9 @@ static int lfs_ctz_traverse(lfs_t *lfs, | ||||
|  | ||||
|  | ||||
| /// Top level file operations /// | ||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags) { | ||||
| int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags, | ||||
|         const struct lfs_file_config *cfg) { | ||||
|     // deorphan if we haven't yet, needed at most once after poweron | ||||
|     if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) { | ||||
|         int err = lfs_deorphan(lfs); | ||||
| @@ -1323,6 +1333,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|     } | ||||
|  | ||||
|     // setup file struct | ||||
|     file->cfg = cfg; | ||||
|     file->pair[0] = cwd.pair[0]; | ||||
|     file->pair[1] = cwd.pair[1]; | ||||
|     file->poff = entry.off; | ||||
| @@ -1340,7 +1351,10 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|     } | ||||
|  | ||||
|     // allocate buffer if needed | ||||
|     if (lfs->cfg->file_buffer) { | ||||
|     file->cache.block = 0xffffffff; | ||||
|     if (file->cfg && file->cfg->buffer) { | ||||
|         file->cache.buffer = file->cfg->buffer; | ||||
|     } else if (lfs->cfg->file_buffer) { | ||||
|         if (lfs->files) { | ||||
|             // already in use | ||||
|             return LFS_ERR_NOMEM; | ||||
| @@ -1359,7 +1373,10 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|     } | ||||
|  | ||||
|     // zero to avoid information leak | ||||
|     lfs_cache_zero(lfs, &file->cache); | ||||
|     lfs_cache_drop(lfs, &file->cache); | ||||
|     if ((file->flags & 3) != LFS_O_RDONLY) { | ||||
|         lfs_cache_zero(lfs, &file->cache); | ||||
|     } | ||||
|  | ||||
|     // add to list of files | ||||
|     file->next = lfs->files; | ||||
| @@ -1368,6 +1385,11 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags) { | ||||
|     return lfs_file_opencfg(lfs, file, path, flags, NULL); | ||||
| } | ||||
|  | ||||
| int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { | ||||
|     int err = lfs_file_sync(lfs, file); | ||||
|  | ||||
| @@ -1380,7 +1402,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { | ||||
|     } | ||||
|  | ||||
|     // clean up memory | ||||
|     if (!lfs->cfg->file_buffer) { | ||||
|     if (!(file->cfg && file->cfg->buffer) && !lfs->cfg->file_buffer) { | ||||
|         lfs_free(file->cache.buffer); | ||||
|     } | ||||
|  | ||||
| @@ -1622,6 +1644,11 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
|         file->pos = file->size; | ||||
|     } | ||||
|  | ||||
|     if (file->pos + size > LFS_FILE_MAX) { | ||||
|         // larger than file limit? | ||||
|         return LFS_ERR_FBIG; | ||||
|     } | ||||
|  | ||||
|     if (!(file->flags & LFS_F_WRITING) && file->pos > file->size) { | ||||
|         // fill with zeros | ||||
|         lfs_off_t pos = file->pos; | ||||
| @@ -1708,24 +1735,24 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // update pos | ||||
|     // find new pos | ||||
|     lfs_soff_t npos = file->pos; | ||||
|     if (whence == LFS_SEEK_SET) { | ||||
|         file->pos = off; | ||||
|         npos = off; | ||||
|     } else if (whence == LFS_SEEK_CUR) { | ||||
|         if (off < 0 && (lfs_off_t)-off > file->pos) { | ||||
|             return LFS_ERR_INVAL; | ||||
|         } | ||||
|  | ||||
|         file->pos = file->pos + off; | ||||
|         npos = file->pos + off; | ||||
|     } else if (whence == LFS_SEEK_END) { | ||||
|         if (off < 0 && (lfs_off_t)-off > file->size) { | ||||
|             return LFS_ERR_INVAL; | ||||
|         } | ||||
|  | ||||
|         file->pos = file->size + off; | ||||
|         npos = file->size + off; | ||||
|     } | ||||
|  | ||||
|     return file->pos; | ||||
|     if (npos < 0 || npos > LFS_FILE_MAX) { | ||||
|         // file position out of range | ||||
|         return LFS_ERR_INVAL; | ||||
|     } | ||||
|  | ||||
|     // update pos | ||||
|     file->pos = npos; | ||||
|     return npos; | ||||
| } | ||||
|  | ||||
| int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { | ||||
| @@ -1900,7 +1927,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|     // find old entry | ||||
|     lfs_dir_t oldcwd; | ||||
|     lfs_entry_t oldentry; | ||||
|     int err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); | ||||
|     int err = lfs_dir_find(lfs, &oldcwd, &oldentry, &(const char *){oldpath}); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // mark as moving | ||||
|     oldentry.d.type |= 0x80; | ||||
|     err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -1913,11 +1947,9 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     bool prevexists = (err != LFS_ERR_NOENT); | ||||
|     bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0); | ||||
|  | ||||
|     // must have same type | ||||
|     if (prevexists && preventry.d.type != oldentry.d.type) { | ||||
|     bool prevexists = (err != LFS_ERR_NOENT); | ||||
|     if (prevexists && preventry.d.type != (0x7f & oldentry.d.type)) { | ||||
|         return LFS_ERR_ISDIR; | ||||
|     } | ||||
|  | ||||
| @@ -1934,18 +1966,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // mark as moving | ||||
|     oldentry.d.type |= 0x80; | ||||
|     err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // update pair if newcwd == oldcwd | ||||
|     if (samepair) { | ||||
|         newcwd = oldcwd; | ||||
|     } | ||||
|  | ||||
|     // move to new location | ||||
|     lfs_entry_t newentry = preventry; | ||||
|     newentry.d = oldentry.d; | ||||
| @@ -1964,10 +1984,13 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // update pair if newcwd == oldcwd | ||||
|     if (samepair) { | ||||
|         oldcwd = newcwd; | ||||
|     // fetch old pair again in case dir block changed | ||||
|     lfs->moving = true; | ||||
|     err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|     lfs->moving = false; | ||||
|  | ||||
|     // remove old entry | ||||
|     err = lfs_dir_remove(lfs, &oldcwd, &oldentry); | ||||
| @@ -1997,6 +2020,21 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|  | ||||
|  | ||||
| /// Filesystem operations /// | ||||
| static void lfs_deinit(lfs_t *lfs) { | ||||
|     // free allocated memory | ||||
|     if (!lfs->cfg->read_buffer) { | ||||
|         lfs_free(lfs->rcache.buffer); | ||||
|     } | ||||
|  | ||||
|     if (!lfs->cfg->prog_buffer) { | ||||
|         lfs_free(lfs->pcache.buffer); | ||||
|     } | ||||
|  | ||||
|     if (!lfs->cfg->lookahead_buffer) { | ||||
|         lfs_free(lfs->free.buffer); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->cfg = cfg; | ||||
|  | ||||
| @@ -2006,7 +2044,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     } else { | ||||
|         lfs->rcache.buffer = lfs_malloc(lfs->cfg->read_size); | ||||
|         if (!lfs->rcache.buffer) { | ||||
|             return LFS_ERR_NOMEM; | ||||
|             goto cleanup; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -2016,13 +2054,13 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     } else { | ||||
|         lfs->pcache.buffer = lfs_malloc(lfs->cfg->prog_size); | ||||
|         if (!lfs->pcache.buffer) { | ||||
|             return LFS_ERR_NOMEM; | ||||
|             goto cleanup; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // zero to avoid information leaks | ||||
|     lfs_cache_zero(lfs, &lfs->rcache); | ||||
|     lfs_cache_zero(lfs, &lfs->pcache); | ||||
|     lfs_cache_drop(lfs, &lfs->rcache); | ||||
|  | ||||
|     // setup lookahead, round down to nearest 32-bits | ||||
|     LFS_ASSERT(lfs->cfg->lookahead % 32 == 0); | ||||
| @@ -2032,7 +2070,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     } else { | ||||
|         lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead/8); | ||||
|         if (!lfs->free.buffer) { | ||||
|             return LFS_ERR_NOMEM; | ||||
|             goto cleanup; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -2050,157 +2088,163 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->files = NULL; | ||||
|     lfs->dirs = NULL; | ||||
|     lfs->deorphaned = false; | ||||
|     lfs->moving = false; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_deinit(lfs_t *lfs) { | ||||
|     // free allocated memory | ||||
|     if (!lfs->cfg->read_buffer) { | ||||
|         lfs_free(lfs->rcache.buffer); | ||||
|     } | ||||
|  | ||||
|     if (!lfs->cfg->prog_buffer) { | ||||
|         lfs_free(lfs->pcache.buffer); | ||||
|     } | ||||
|  | ||||
|     if (!lfs->cfg->lookahead_buffer) { | ||||
|         lfs_free(lfs->free.buffer); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| cleanup: | ||||
|     lfs_deinit(lfs); | ||||
|     return LFS_ERR_NOMEM; | ||||
| } | ||||
|  | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = lfs_init(lfs, cfg); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // create free lookahead | ||||
|     memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); | ||||
|     lfs->free.off = 0; | ||||
|     lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count); | ||||
|     lfs->free.i = 0; | ||||
|     lfs_alloc_ack(lfs); | ||||
|  | ||||
|     // create superblock dir | ||||
|     lfs_dir_t superdir; | ||||
|     err = lfs_dir_alloc(lfs, &superdir); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // write root directory | ||||
|     lfs_dir_t root; | ||||
|     err = lfs_dir_alloc(lfs, &root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = lfs_dir_commit(lfs, &root, NULL, 0); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs->root[0] = root.pair[0]; | ||||
|     lfs->root[1] = root.pair[1]; | ||||
|  | ||||
|     // write superblocks | ||||
|     lfs_superblock_t superblock = { | ||||
|         .off = sizeof(superdir.d), | ||||
|         .d.type = LFS_TYPE_SUPERBLOCK, | ||||
|         .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, | ||||
|         .d.nlen = sizeof(superblock.d.magic), | ||||
|         .d.version = LFS_DISK_VERSION, | ||||
|         .d.magic = {"littlefs"}, | ||||
|         .d.block_size  = lfs->cfg->block_size, | ||||
|         .d.block_count = lfs->cfg->block_count, | ||||
|         .d.root = {lfs->root[0], lfs->root[1]}, | ||||
|     }; | ||||
|     superdir.d.tail[0] = root.pair[0]; | ||||
|     superdir.d.tail[1] = root.pair[1]; | ||||
|     superdir.d.size = sizeof(superdir.d) + sizeof(superblock.d) + 4; | ||||
|  | ||||
|     // write both pairs to be safe | ||||
|     lfs_superblock_tole32(&superblock.d); | ||||
|     bool valid = false; | ||||
|     for (int i = 0; i < 2; i++) { | ||||
|         err = lfs_dir_commit(lfs, &superdir, (struct lfs_region[]){ | ||||
|                 {sizeof(superdir.d), sizeof(superblock.d), | ||||
|                  &superblock.d, sizeof(superblock.d)} | ||||
|             }, 1); | ||||
|         if (err && err != LFS_ERR_CORRUPT) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         valid = valid || !err; | ||||
|     } | ||||
|  | ||||
|     if (!valid) { | ||||
|         return LFS_ERR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     // sanity check that fetch works | ||||
|     err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs_alloc_ack(lfs); | ||||
|     return lfs_deinit(lfs); | ||||
| } | ||||
|  | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = lfs_init(lfs, cfg); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // setup free lookahead | ||||
|     lfs->free.off = 0; | ||||
|     lfs->free.size = 0; | ||||
|     lfs->free.i = 0; | ||||
|     lfs_alloc_ack(lfs); | ||||
|  | ||||
|     // load superblock | ||||
|     lfs_dir_t dir; | ||||
|     lfs_superblock_t superblock; | ||||
|     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||
|     if (err && err != LFS_ERR_CORRUPT) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     if (!err) { | ||||
|         err = lfs_bd_read(lfs, dir.pair[0], sizeof(dir.d), | ||||
|                 &superblock.d, sizeof(superblock.d)); | ||||
|         lfs_superblock_fromle32(&superblock.d); | ||||
|     int err = 0; | ||||
|     if (true) { | ||||
|         err = lfs_init(lfs, cfg); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         lfs->root[0] = superblock.d.root[0]; | ||||
|         lfs->root[1] = superblock.d.root[1]; | ||||
|         // create free lookahead | ||||
|         memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); | ||||
|         lfs->free.off = 0; | ||||
|         lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count); | ||||
|         lfs->free.i = 0; | ||||
|         lfs_alloc_ack(lfs); | ||||
|  | ||||
|         // create superblock dir | ||||
|         lfs_dir_t superdir; | ||||
|         err = lfs_dir_alloc(lfs, &superdir); | ||||
|         if (err) { | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         // write root directory | ||||
|         lfs_dir_t root; | ||||
|         err = lfs_dir_alloc(lfs, &root); | ||||
|         if (err) { | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         err = lfs_dir_commit(lfs, &root, NULL, 0); | ||||
|         if (err) { | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         lfs->root[0] = root.pair[0]; | ||||
|         lfs->root[1] = root.pair[1]; | ||||
|  | ||||
|         // write superblocks | ||||
|         lfs_superblock_t superblock = { | ||||
|             .off = sizeof(superdir.d), | ||||
|             .d.type = LFS_TYPE_SUPERBLOCK, | ||||
|             .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, | ||||
|             .d.nlen = sizeof(superblock.d.magic), | ||||
|             .d.version = LFS_DISK_VERSION, | ||||
|             .d.magic = {"littlefs"}, | ||||
|             .d.block_size  = lfs->cfg->block_size, | ||||
|             .d.block_count = lfs->cfg->block_count, | ||||
|             .d.root = {lfs->root[0], lfs->root[1]}, | ||||
|         }; | ||||
|         superdir.d.tail[0] = root.pair[0]; | ||||
|         superdir.d.tail[1] = root.pair[1]; | ||||
|         superdir.d.size = sizeof(superdir.d) + sizeof(superblock.d) + 4; | ||||
|  | ||||
|         // write both pairs to be safe | ||||
|         lfs_superblock_tole32(&superblock.d); | ||||
|         bool valid = false; | ||||
|         for (int i = 0; i < 2; i++) { | ||||
|             err = lfs_dir_commit(lfs, &superdir, (struct lfs_region[]){ | ||||
|                     {sizeof(superdir.d), sizeof(superblock.d), | ||||
|                      &superblock.d, sizeof(superblock.d)} | ||||
|                 }, 1); | ||||
|             if (err && err != LFS_ERR_CORRUPT) { | ||||
|                 goto cleanup; | ||||
|             } | ||||
|  | ||||
|             valid = valid || !err; | ||||
|         } | ||||
|  | ||||
|         if (!valid) { | ||||
|             err = LFS_ERR_CORRUPT; | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         // sanity check that fetch works | ||||
|         err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); | ||||
|         if (err) { | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         lfs_alloc_ack(lfs); | ||||
|     } | ||||
|  | ||||
|     if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { | ||||
|         LFS_ERROR("Invalid superblock at %d %d", 0, 1); | ||||
|         return LFS_ERR_CORRUPT; | ||||
| cleanup: | ||||
|     lfs_deinit(lfs); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = 0; | ||||
|     if (true) { | ||||
|         err = lfs_init(lfs, cfg); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // setup free lookahead | ||||
|         lfs->free.off = 0; | ||||
|         lfs->free.size = 0; | ||||
|         lfs->free.i = 0; | ||||
|         lfs_alloc_ack(lfs); | ||||
|  | ||||
|         // load superblock | ||||
|         lfs_dir_t dir; | ||||
|         lfs_superblock_t superblock; | ||||
|         err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||
|         if (err && err != LFS_ERR_CORRUPT) { | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         if (!err) { | ||||
|             err = lfs_bd_read(lfs, dir.pair[0], sizeof(dir.d), | ||||
|                     &superblock.d, sizeof(superblock.d)); | ||||
|             lfs_superblock_fromle32(&superblock.d); | ||||
|             if (err) { | ||||
|                 goto cleanup; | ||||
|             } | ||||
|  | ||||
|             lfs->root[0] = superblock.d.root[0]; | ||||
|             lfs->root[1] = superblock.d.root[1]; | ||||
|         } | ||||
|  | ||||
|         if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { | ||||
|             LFS_ERROR("Invalid superblock at %d %d", 0, 1); | ||||
|             err = LFS_ERR_CORRUPT; | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         uint16_t major_version = (0xffff & (superblock.d.version >> 16)); | ||||
|         uint16_t minor_version = (0xffff & (superblock.d.version >>  0)); | ||||
|         if ((major_version != LFS_DISK_VERSION_MAJOR || | ||||
|              minor_version > LFS_DISK_VERSION_MINOR)) { | ||||
|             LFS_ERROR("Invalid version %d.%d", major_version, minor_version); | ||||
|             err = LFS_ERR_INVAL; | ||||
|             goto cleanup; | ||||
|         } | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     uint16_t major_version = (0xffff & (superblock.d.version >> 16)); | ||||
|     uint16_t minor_version = (0xffff & (superblock.d.version >>  0)); | ||||
|     if ((major_version != LFS_DISK_VERSION_MAJOR || | ||||
|          minor_version > LFS_DISK_VERSION_MINOR)) { | ||||
|         LFS_ERROR("Invalid version %d.%d", major_version, minor_version); | ||||
|         return LFS_ERR_INVAL; | ||||
|     } | ||||
| cleanup: | ||||
|  | ||||
|     return 0; | ||||
|     lfs_deinit(lfs); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_unmount(lfs_t *lfs) { | ||||
|     return lfs_deinit(lfs); | ||||
|     lfs_deinit(lfs); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -2439,7 +2483,11 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|     lfs_dir_t cwd = {.d.tail[0] = 0, .d.tail[1] = 1}; | ||||
|  | ||||
|     // iterate over all directory directory entries | ||||
|     while (!lfs_pairisnull(cwd.d.tail)) { | ||||
|     for (lfs_size_t i = 0; i < lfs->cfg->block_count; i++) { | ||||
|         if (lfs_pairisnull(cwd.d.tail)) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         int err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail); | ||||
|         if (err) { | ||||
|             return err; | ||||
| @@ -2468,7 +2516,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             if (!lfs_pairsync(entry.d.u.dir, pdir.d.tail)) { | ||||
| @@ -2484,7 +2532,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -2529,5 +2577,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|         memcpy(&pdir, &cwd, sizeof(pdir)); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|     // If we reached here, we have more directory pairs than blocks in the | ||||
|     // filesystem... So something must be horribly wrong | ||||
|     return LFS_ERR_CORRUPT; | ||||
| } | ||||
|   | ||||
							
								
								
									
										41
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -21,7 +21,7 @@ extern "C" | ||||
| // Software library version | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // Minor (bottom-nibble), incremented on feature additions | ||||
| #define LFS_VERSION 0x00010004 | ||||
| #define LFS_VERSION 0x00010007 | ||||
| #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | ||||
| #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | ||||
|  | ||||
| @@ -49,6 +49,11 @@ typedef uint32_t lfs_block_t; | ||||
| #define LFS_NAME_MAX 255 | ||||
| #endif | ||||
|  | ||||
| // Max file size in bytes | ||||
| #ifndef LFS_FILE_MAX | ||||
| #define LFS_FILE_MAX 2147483647 | ||||
| #endif | ||||
|  | ||||
| // Possible error codes, these are negative to allow | ||||
| // valid positive return values | ||||
| enum lfs_error { | ||||
| @@ -61,6 +66,7 @@ enum lfs_error { | ||||
|     LFS_ERR_ISDIR    = -21,  // Entry is a dir | ||||
|     LFS_ERR_NOTEMPTY = -39,  // Dir is not empty | ||||
|     LFS_ERR_BADF     = -9,   // Bad file number | ||||
|     LFS_ERR_FBIG     = -27,  // File too large | ||||
|     LFS_ERR_INVAL    = -22,  // Invalid parameter | ||||
|     LFS_ERR_NOSPC    = -28,  // No space left on device | ||||
|     LFS_ERR_NOMEM    = -12,  // No more memory available | ||||
| @@ -167,6 +173,12 @@ struct lfs_config { | ||||
|     void *file_buffer; | ||||
| }; | ||||
|  | ||||
| // Optional configuration provided during lfs_file_opencfg | ||||
| struct lfs_file_config { | ||||
|     // Optional, statically allocated buffer for files. Must be program sized. | ||||
|     // If NULL, malloc will be used by default. | ||||
|     void *buffer; | ||||
| }; | ||||
|  | ||||
| // File info structure | ||||
| struct lfs_info { | ||||
| @@ -214,6 +226,7 @@ typedef struct lfs_file { | ||||
|     lfs_block_t head; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     const struct lfs_file_config *cfg; | ||||
|     uint32_t flags; | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t block; | ||||
| @@ -273,6 +286,7 @@ typedef struct lfs { | ||||
|  | ||||
|     lfs_free_t free; | ||||
|     bool deorphaned; | ||||
|     bool moving; | ||||
| } lfs_t; | ||||
|  | ||||
|  | ||||
| @@ -281,7 +295,8 @@ typedef struct lfs { | ||||
| // Format a block device with the littlefs | ||||
| // | ||||
| // Requires a littlefs object and config struct. This clobbers the littlefs | ||||
| // object, and does not leave the filesystem mounted. | ||||
| // object, and does not leave the filesystem mounted. The config struct must | ||||
| // be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| @@ -290,7 +305,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| // | ||||
| // Requires a littlefs object and config struct. Multiple filesystems | ||||
| // may be mounted simultaneously with multiple littlefs objects. Both | ||||
| // lfs and config must be allocated while mounted. | ||||
| // lfs and config must be allocated while mounted. The config struct must | ||||
| // be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *config); | ||||
| @@ -328,14 +344,27 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||
|  | ||||
| // Open a file | ||||
| // | ||||
| // The mode that the file is opened in is determined | ||||
| // by the flags, which are values from the enum lfs_open_flags | ||||
| // that are bitwise-ored together. | ||||
| // The mode that the file is opened in is determined by the flags, which | ||||
| // are values from the enum lfs_open_flags that are bitwise-ored together. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags); | ||||
|  | ||||
| // Open a file with extra configuration | ||||
| // | ||||
| // The mode that the file is opened in is determined by the flags, which | ||||
| // are values from the enum lfs_open_flags that are bitwise-ored together. | ||||
| // | ||||
| // The config struct provides additional config options per file as described | ||||
| // above. The config struct must be allocated while the file is open, and the | ||||
| // config struct must be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags, | ||||
|         const struct lfs_file_config *config); | ||||
|  | ||||
| // Close a file | ||||
| // | ||||
| // Any pending writes are written out to storage as though | ||||
|   | ||||
							
								
								
									
										61
									
								
								scripts/prefix.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										61
									
								
								scripts/prefix.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #!/usr/bin/env python | ||||
|  | ||||
| # This script replaces prefixes of files, and symbols in that file. | ||||
| # Useful for creating different versions of the codebase that don't | ||||
| # conflict at compile time. | ||||
| # | ||||
| # example: | ||||
| # $ ./scripts/prefix.py lfs2 | ||||
|  | ||||
| import os | ||||
| import os.path | ||||
| import re | ||||
| import glob | ||||
| import itertools | ||||
| import tempfile | ||||
| import shutil | ||||
| import subprocess | ||||
|  | ||||
| DEFAULT_PREFIX = "lfs" | ||||
|  | ||||
| def subn(from_prefix, to_prefix, name): | ||||
|     name, count1 = re.subn('\\b'+from_prefix, to_prefix, name) | ||||
|     name, count2 = re.subn('\\b'+from_prefix.upper(), to_prefix.upper(), name) | ||||
|     name, count3 = re.subn('\\B-D'+from_prefix.upper(), | ||||
|             '-D'+to_prefix.upper(), name) | ||||
|     return name, count1+count2+count3 | ||||
|  | ||||
| def main(from_prefix, to_prefix=None, files=None): | ||||
|     if not to_prefix: | ||||
|         from_prefix, to_prefix = DEFAULT_PREFIX, from_prefix | ||||
|  | ||||
|     if not files: | ||||
|         files = subprocess.check_output([ | ||||
|                 'git', 'ls-tree', '-r', '--name-only', 'HEAD']).split() | ||||
|  | ||||
|     for oldname in files: | ||||
|         # Rename any matching file names | ||||
|         newname, namecount = subn(from_prefix, to_prefix, oldname) | ||||
|         if namecount: | ||||
|             subprocess.check_call(['git', 'mv', oldname, newname]) | ||||
|  | ||||
|         # Rename any prefixes in file | ||||
|         count = 0 | ||||
|         with open(newname+'~', 'w') as tempf: | ||||
|             with open(newname) as newf: | ||||
|                 for line in newf: | ||||
|                     line, n = subn(from_prefix, to_prefix, line) | ||||
|                     count += n | ||||
|                     tempf.write(line) | ||||
|         shutil.copystat(newname, newname+'~') | ||||
|         os.rename(newname+'~', newname) | ||||
|         subprocess.check_call(['git', 'add', newname]) | ||||
|  | ||||
|         # Summary | ||||
|         print '%s: %d replacements' % ( | ||||
|                 '%s -> %s' % (oldname, newname) if namecount else oldname, | ||||
|                 count) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     sys.exit(main(*sys.argv[1:])) | ||||
| @@ -32,18 +32,18 @@ lfs_alloc_singleproc() { | ||||
| tests/test.py << TEST | ||||
|     const char *names[] = {"bacon", "eggs", "pancakes"}; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         sprintf((char*)buffer, "$1/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &file[n], (char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     } | ||||
|     for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         size = strlen(names[n]); | ||||
|         for (int i = 0; i < $SIZE; i++) { | ||||
|             lfs_file_write(&lfs, &file[n], names[n], size) => size; | ||||
|         } | ||||
|     } | ||||
|     for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         lfs_file_close(&lfs, &file[n]) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|   | ||||
| @@ -326,13 +326,42 @@ tests/test.py << TEST | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block rename ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "cactus/test%d", i); | ||||
|         sprintf((char*)wbuffer, "cactus/tedd%d", i); | ||||
|         lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "cactus") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "tedd%d", i); | ||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|         strcmp(info.name, (char*)buffer) => 0; | ||||
|         info.type => LFS_TYPE_DIR; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block remove ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; | ||||
|  | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "cactus/test%d", i); | ||||
|         sprintf((char*)buffer, "cactus/tedd%d", i); | ||||
|         lfs_remove(&lfs, (char*)buffer) => 0; | ||||
|     } | ||||
|  | ||||
| @@ -391,13 +420,43 @@ tests/test.py << TEST | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block rename with files ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "prickly-pear/test%d", i); | ||||
|         sprintf((char*)wbuffer, "prickly-pear/tedd%d", i); | ||||
|         lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "tedd%d", i); | ||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|         strcmp(info.name, (char*)buffer) => 0; | ||||
|         info.type => LFS_TYPE_REG; | ||||
|         info.size => 6; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block remove with files ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; | ||||
|  | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "prickly-pear/test%d", i); | ||||
|         sprintf((char*)buffer, "prickly-pear/tedd%d", i); | ||||
|         lfs_remove(&lfs, (char*)buffer) => 0; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -301,7 +301,7 @@ tests/test.py << TEST | ||||
|     size = strlen("hedgehoghog"); | ||||
|     const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { | ||||
|     for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { | ||||
|         lfs_soff_t off = offsets[i]; | ||||
|         memcpy(buffer, "hedgehoghog", size); | ||||
|         lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off; | ||||
|   | ||||
| @@ -23,14 +23,14 @@ tests/test.py << TEST | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|  | ||||
|         strcpy((char*)buffer, "hair"); | ||||
|         size = strlen((char*)buffer); | ||||
|         for (int j = 0; j < startsizes[i]; j += size) { | ||||
|         for (lfs_off_t j = 0; j < startsizes[i]; j += size) { | ||||
|             lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         } | ||||
|         lfs_file_size(&lfs, &file[0]) => startsizes[i]; | ||||
| @@ -55,13 +55,13 @@ tests/test.py << TEST | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => hotsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         int j = 0; | ||||
|         lfs_off_t j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
| @@ -87,13 +87,13 @@ tests/test.py << TEST | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => coldsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         int j = 0; | ||||
|         lfs_off_t j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; | ||||
|                 j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user