From 015b86bc51fddbd9d0e7c68e02015f8fd12c5839 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 22 Apr 2018 07:26:31 -0500 Subject: [PATCH 01/16] Fixed issue with trailing dots in file paths Paths such as the following were causing issues: /tea/hottea/. /tea/hottea/.. Unfortunately the existing structure for path lookup didn't make it very easy to introduce proper handling in this case without duplicating the entire skip logic for paths. So the lfs_dir_find function had to be restructured a bit. One odd side-effect of this is that now lfs_dir_find includes the initial fetch operation. This kinda breaks the fetch -> op pattern of the dir functions, but does come with a nice code size reduction. --- lfs.c | 100 +++++++++++++------------------------------- tests/test_paths.sh | 16 +++++++ 2 files changed, 46 insertions(+), 70 deletions(-) diff --git a/lfs.c b/lfs.c index 3aef9fcc4ed..490a508f302 100644 --- a/lfs.c +++ b/lfs.c @@ -783,26 +783,19 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, const char **path) { const char *pathname = *path; size_t pathlen; + entry->d.type = LFS_TYPE_DIR; + entry->d.elen = sizeof(entry->d) - 4; + entry->d.alen = 0; + entry->d.nlen = 0; + entry->d.u.dir[0] = lfs->root[0]; + entry->d.u.dir[1] = lfs->root[1]; while (true) { - nextname: +nextname: // skip slashes pathname += strspn(pathname, "/"); pathlen = strcspn(pathname, "/"); - // special case for root dir - if (pathname[0] == '\0') { - *entry = (lfs_entry_t){ - .d.type = LFS_TYPE_DIR, - .d.elen = sizeof(entry->d) - 4, - .d.alen = 0, - .d.nlen = 0, - .d.u.dir[0] = lfs->root[0], - .d.u.dir[1] = lfs->root[1], - }; - return 0; - } - // skip '.' and root '..' if ((pathlen == 1 && memcmp(pathname, ".", 1) == 0) || (pathlen == 2 && memcmp(pathname, "..", 2) == 0)) { @@ -834,10 +827,25 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, suffix += sufflen; } + // found path + if (pathname[0] == '\0') { + return 0; + } + // update what we've found *path = pathname; - // find path + // continue on if we hit a directory + if (entry->d.type != LFS_TYPE_DIR) { + return LFS_ERR_NOTDIR; + } + + int err = lfs_dir_fetch(lfs, dir, entry->d.u.dir); + if (err) { + return err; + } + + // find entry matching name while (true) { int err = lfs_dir_next(lfs, dir, entry); if (err) { @@ -873,21 +881,8 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, entry->d.type &= ~0x80; } + // to next name pathname += pathlen; - pathname += strspn(pathname, "/"); - if (pathname[0] == '\0') { - return 0; - } - - // continue on if we hit a directory - if (entry->d.type != LFS_TYPE_DIR) { - return LFS_ERR_NOTDIR; - } - - int err = lfs_dir_fetch(lfs, dir, entry->d.u.dir); - if (err) { - return err; - } } } @@ -904,13 +899,8 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { // fetch parent directory lfs_dir_t cwd; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - lfs_entry_t entry; - err = lfs_dir_find(lfs, &cwd, &entry, &path); + int err = lfs_dir_find(lfs, &cwd, &entry, &path); if (err != LFS_ERR_NOENT || strchr(path, '/') != NULL) { return err ? err : LFS_ERR_EXIST; } @@ -954,13 +944,8 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { dir->pair[0] = lfs->root[0]; dir->pair[1] = lfs->root[1]; - int err = lfs_dir_fetch(lfs, dir, dir->pair); - if (err) { - return err; - } - lfs_entry_t entry; - err = lfs_dir_find(lfs, dir, &entry, &path); + int err = lfs_dir_find(lfs, dir, &entry, &path); if (err) { return err; } else if (entry.d.type != LFS_TYPE_DIR) { @@ -1302,13 +1287,8 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, // allocate entry for file if it doesn't exist lfs_dir_t cwd; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - lfs_entry_t entry; - err = lfs_dir_find(lfs, &cwd, &entry, &path); + int err = lfs_dir_find(lfs, &cwd, &entry, &path); if (err && (err != LFS_ERR_NOENT || strchr(path, '/') != NULL)) { return err; } @@ -1814,13 +1794,8 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { /// General fs operations /// int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { lfs_dir_t cwd; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - lfs_entry_t entry; - err = lfs_dir_find(lfs, &cwd, &entry, &path); + int err = lfs_dir_find(lfs, &cwd, &entry, &path); if (err) { return err; } @@ -1855,13 +1830,8 @@ int lfs_remove(lfs_t *lfs, const char *path) { } lfs_dir_t cwd; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - lfs_entry_t entry; - err = lfs_dir_find(lfs, &cwd, &entry, &path); + int err = lfs_dir_find(lfs, &cwd, &entry, &path); if (err) { return err; } @@ -1916,24 +1886,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { // find old entry lfs_dir_t oldcwd; - int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root); - if (err) { - return err; - } - lfs_entry_t oldentry; - err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); + int err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); if (err) { return err; } // allocate new entry lfs_dir_t newcwd; - err = lfs_dir_fetch(lfs, &newcwd, lfs->root); - if (err) { - return err; - } - lfs_entry_t preventry; err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath); if (err && (err != LFS_ERR_NOENT || strchr(newpath, '/') != NULL)) { diff --git a/tests/test_paths.sh b/tests/test_paths.sh index f277e451f9c..79c4e66571d 100755 --- a/tests/test_paths.sh +++ b/tests/test_paths.sh @@ -90,6 +90,22 @@ tests/test.py << TEST lfs_unmount(&lfs) => 0; TEST +echo "--- Trailing dot path tests ---" +tests/test.py << TEST + lfs_mount(&lfs, &cfg) => 0; + lfs_stat(&lfs, "tea/hottea/", &info) => 0; + strcmp(info.name, "hottea") => 0; + lfs_stat(&lfs, "tea/hottea/.", &info) => 0; + strcmp(info.name, "hottea") => 0; + lfs_stat(&lfs, "tea/hottea/./.", &info) => 0; + strcmp(info.name, "hottea") => 0; + lfs_stat(&lfs, "tea/hottea/..", &info) => 0; + strcmp(info.name, "tea") => 0; + lfs_stat(&lfs, "tea/hottea/../.", &info) => 0; + strcmp(info.name, "tea") => 0; + lfs_unmount(&lfs) => 0; +TEST + echo "--- Root dot dot path tests ---" tests/test.py << TEST lfs_mount(&lfs, &cfg) => 0; From c5e2b335d618a78656a867c7874835db141fbad1 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 29 Apr 2018 16:45:14 +0300 Subject: [PATCH 02/16] Added error when opening multiple files with a statically allocated buffer Opening multiple files simultaneously is not supported without dynamic memory, but the previous behaviour would just let the files overwrite each other, which could lead to bad errors down the line found by husigeza --- lfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lfs.c b/lfs.c index 490a508f302..58fc7370744 100644 --- a/lfs.c +++ b/lfs.c @@ -1335,6 +1335,10 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, // allocate buffer if needed file->cache.block = 0xffffffff; if (lfs->cfg->file_buffer) { + if (lfs->files) { + // already in use + return LFS_ERR_NOMEM; + } file->cache.buffer = lfs->cfg->file_buffer; } else if ((file->flags & 3) == LFS_O_RDONLY) { file->cache.buffer = lfs_malloc(lfs->cfg->read_size); From 6beff502e90fc2d09ead72f4968236bbf13ed9f2 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 21 Jun 2018 11:35:57 -0500 Subject: [PATCH 03/16] Changed license to BSD-3-Clause For better compatibility with GPL v2 With permissions from: - aldot - Sim4n6 - jrast --- LICENSE.md | 183 +++++++--------------------------------------- emubd/lfs_emubd.c | 15 +--- emubd/lfs_emubd.h | 15 +--- lfs.c | 15 +--- lfs.h | 17 +---- lfs_util.c | 15 +--- lfs_util.h | 15 +--- 7 files changed, 40 insertions(+), 235 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 59cd3f8a320..f794972754b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,165 +1,36 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ +Copyright (c) 2017, Arm Limited. All rights reserved. -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -1. Definitions. +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name of ARM nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior + written permission. -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +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. -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. +--- -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. +*Note*: +Individual files contain the following tag instead of the full license text. -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. + SPDX-License-Identifier: BSD-3-Clause -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. +This enables machine processing of license information based on the SPDX +License Identifiers that are here available: http://spdx.org/licenses/ diff --git a/emubd/lfs_emubd.c b/emubd/lfs_emubd.c index b1595963dd9..ed3414abfef 100644 --- a/emubd/lfs_emubd.c +++ b/emubd/lfs_emubd.c @@ -1,19 +1,8 @@ /* * Block device emulated on standard files * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #include "emubd/lfs_emubd.h" diff --git a/emubd/lfs_emubd.h b/emubd/lfs_emubd.h index 4f87ccecf10..85718de11a7 100644 --- a/emubd/lfs_emubd.h +++ b/emubd/lfs_emubd.h @@ -1,19 +1,8 @@ /* * Block device emulated on standard files * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #ifndef LFS_EMUBD_H #define LFS_EMUBD_H diff --git a/lfs.c b/lfs.c index 58fc7370744..b1693896dba 100644 --- a/lfs.c +++ b/lfs.c @@ -1,19 +1,8 @@ /* * The little filesystem * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #include "lfs.h" #include "lfs_util.h" diff --git a/lfs.h b/lfs.h index 376776f6ce6..3683d32d444 100644 --- a/lfs.h +++ b/lfs.h @@ -1,19 +1,8 @@ /* * The little filesystem * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #ifndef LFS_H #define LFS_H @@ -27,7 +16,7 @@ // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS_VERSION 0x00010003 +#define LFS_VERSION 0x00010004 #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) diff --git a/lfs_util.c b/lfs_util.c index 3a2a351b568..9ca0756d96b 100644 --- a/lfs_util.c +++ b/lfs_util.c @@ -1,19 +1,8 @@ /* * lfs util functions * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #include "lfs_util.h" diff --git a/lfs_util.h b/lfs_util.h index 3527ce6c150..51005dc336a 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -1,19 +1,8 @@ /* * lfs utility functions * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #ifndef LFS_UTIL_H #define LFS_UTIL_H From 51346b8bf4146cc145c2cefce9928b03fa9f4c8c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Jun 2018 11:24:27 +1000 Subject: [PATCH 04/16] Fixed shadowed variable warnings - Fixed shadowed variable warnings in lfs_dir_find. - Fixed unused parameter warnings when LFS_NO_MALLOC is enabled. - Added extra warning flags to CFLAGS. - Updated tests so they don't shadow the "size" variable for -Wshadow --- Makefile | 2 +- lfs.c | 2 +- lfs_util.h | 3 +++ tests/test_files.sh | 4 ++-- tests/test_seek.sh | 8 ++++---- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index c0933139dc3..8457c5f3e50 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ ifdef WORD override CFLAGS += -m$(WORD) endif override CFLAGS += -I. -override CFLAGS += -std=c99 -Wall -pedantic +override CFLAGS += -std=c99 -Wall -pedantic -Wshadow -Wunused-parameter all: $(TARGET) diff --git a/lfs.c b/lfs.c index b1693896dba..55446b513d1 100644 --- a/lfs.c +++ b/lfs.c @@ -836,7 +836,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, // find entry matching name while (true) { - int err = lfs_dir_next(lfs, dir, entry); + err = lfs_dir_next(lfs, dir, entry); if (err) { return err; } diff --git a/lfs_util.h b/lfs_util.h index 51005dc336a..d61c5458a0b 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -158,6 +158,7 @@ static inline void *lfs_malloc(size_t size) { #ifndef LFS_NO_MALLOC return malloc(size); #else + (void)size; return NULL; #endif } @@ -166,6 +167,8 @@ static inline void *lfs_malloc(size_t size) { static inline void lfs_free(void *p) { #ifndef LFS_NO_MALLOC free(p); +#else + (void)p; #endif } diff --git a/tests/test_files.sh b/tests/test_files.sh index b2039a7b1e2..bbecea9285b 100755 --- a/tests/test_files.sh +++ b/tests/test_files.sh @@ -30,7 +30,7 @@ TEST w_test() { tests/test.py << TEST - lfs_size_t size = $1; + size = $1; lfs_size_t chunk = 31; srand(0); lfs_mount(&lfs, &cfg) => 0; @@ -50,7 +50,7 @@ TEST r_test() { tests/test.py << TEST - lfs_size_t size = $1; + size = $1; lfs_size_t chunk = 29; srand(0); lfs_mount(&lfs, &cfg) => 0; diff --git a/tests/test_seek.sh b/tests/test_seek.sh index 3b46892b6ea..0084d42f167 100755 --- a/tests/test_seek.sh +++ b/tests/test_seek.sh @@ -153,7 +153,7 @@ tests/test.py << TEST lfs_file_read(&lfs, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_size_t size = lfs_file_size(&lfs, &file[0]); + size = lfs_file_size(&lfs, &file[0]); lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; lfs_file_close(&lfs, &file[0]) => 0; @@ -202,7 +202,7 @@ tests/test.py << TEST lfs_file_read(&lfs, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_size_t size = lfs_file_size(&lfs, &file[0]); + size = lfs_file_size(&lfs, &file[0]); lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; lfs_file_close(&lfs, &file[0]) => 0; @@ -243,7 +243,7 @@ tests/test.py << TEST lfs_file_read(&lfs, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_size_t size = lfs_file_size(&lfs, &file[0]); + size = lfs_file_size(&lfs, &file[0]); lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; lfs_file_close(&lfs, &file[0]) => 0; @@ -286,7 +286,7 @@ tests/test.py << TEST lfs_file_read(&lfs, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_size_t size = lfs_file_size(&lfs, &file[0]); + size = lfs_file_size(&lfs, &file[0]); lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; lfs_file_close(&lfs, &file[0]) => 0; From 4a863703276dd12b2a910db4171ee97a806c73ea Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 6 Jul 2018 13:13:36 -0500 Subject: [PATCH 05/16] Added quality of life improvements for main.c/test.c issues 1. Added check for main.c and test.c to decide compilation target 2. Added step to remove test.c after successful test completion The test.c file, which contains the expanded test main, is useful when debugging why tests are failing. However, keeping the test.c file around causes problems when a later attempt is made to compile a larger project containing the littlefs directory. Under (hopefully) normal operation, tests always pass. So it should be ok to remove the test.c file after a successful test run. Hopefully this behaviour doesn't cause too much confusion for contributors using the tests. On the other side of things, compiling the library with no main ends (successfully) with the "main not found" error message. By defaulting to lfs.a if neither test.c/main.c is avoid this in the common cases found by armijnhemel and Sim4n6 --- Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8457c5f3e50..99a3c0cc9cc 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,7 @@ -TARGET = lfs +TARGET = lfs.a +ifneq ($(wildcard test.c main.c),) +override TARGET = lfs +endif CC ?= gcc AR ?= ar @@ -35,7 +38,9 @@ size: $(OBJ) .SUFFIXES: test: test_format test_dirs test_files test_seek test_truncate \ test_interspersed test_alloc test_paths test_orphan test_move test_corrupt + @rm test.c test_%: tests/test_%.sh + ifdef QUIET @./$< | sed -n '/^[-=]/p' else @@ -44,7 +49,7 @@ endif -include $(DEP) -$(TARGET): $(OBJ) +lfs: $(OBJ) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ %.a: $(OBJ) From eed1eec5fd9c4367f4eaa52ba7b89afe86dd2c09 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 6 Jul 2018 11:14:30 -0500 Subject: [PATCH 06/16] Fixed information leaks through reused caches As a shortcut, littlefs never bother to zero any of the buffers is used. It didn't need to because it would always write out the entirety of the data it needed. Unfortunately, this, combined with the extra padding used to align buffers to the nearest prog size, would lead to uninitialized data getting written out to disk. This means unrelated file data could be written to different parts of storage, or worse, information leaked from the malloc calls could be written out to disk unnecessarily. found by rojer --- lfs.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/lfs.c b/lfs.c index 55446b513d1..9b44975f476 100644 --- a/lfs.c +++ b/lfs.c @@ -107,6 +107,19 @@ static int lfs_cache_crc(lfs_t *lfs, lfs_cache_t *rcache, return 0; } +static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) { + // do not zero, cheaper if cache is readonly or only going to be + // written with identical data (during relocates) + (void)lfs; + rcache->block = 0xffffffff; +} + +static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { + // zero to avoid information leak + memset(pcache->buffer, 0xff, lfs->cfg->prog_size); + pcache->block = 0xffffffff; +} + static int lfs_cache_flush(lfs_t *lfs, lfs_cache_t *pcache, lfs_cache_t *rcache) { if (pcache->block != 0xffffffff) { @@ -128,7 +141,7 @@ static int lfs_cache_flush(lfs_t *lfs, } } - pcache->block = 0xffffffff; + lfs_cache_zero(lfs, pcache); } return 0; @@ -233,7 +246,7 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { } static int lfs_bd_sync(lfs_t *lfs) { - lfs->rcache.block = 0xffffffff; + lfs_cache_drop(lfs, &lfs->rcache); int err = lfs_cache_flush(lfs, &lfs->pcache, NULL); if (err) { @@ -592,7 +605,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, // drop caches and prepare to relocate block relocated = true; - lfs->pcache.block = 0xffffffff; + lfs_cache_drop(lfs, &lfs->pcache); // can't relocate superblock, filesystem is now frozen if (lfs_paircmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) { @@ -1217,7 +1230,7 @@ static int lfs_ctz_extend(lfs_t *lfs, LFS_DEBUG("Bad block at %d", nblock); // just clear cache and try a new block - pcache->block = 0xffffffff; + lfs_cache_drop(lfs, &lfs->pcache); } } @@ -1322,7 +1335,6 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, } // allocate buffer if needed - file->cache.block = 0xffffffff; if (lfs->cfg->file_buffer) { if (lfs->files) { // already in use @@ -1341,6 +1353,9 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, } } + // zero to avoid information leak + lfs_cache_zero(lfs, &file->cache); + // add to list of files file->next = lfs->files; lfs->files = file; @@ -1409,7 +1424,7 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size); file->cache.block = lfs->pcache.block; file->cache.off = lfs->pcache.off; - lfs->pcache.block = 0xffffffff; + lfs_cache_zero(lfs, &lfs->pcache); file->block = nblock; return 0; @@ -1418,7 +1433,7 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { if (file->flags & LFS_F_READING) { // just drop read cache - file->cache.block = 0xffffffff; + lfs_cache_drop(lfs, &file->cache); file->flags &= ~LFS_F_READING; } @@ -1433,7 +1448,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { .pos = file->pos, .cache = lfs->rcache, }; - lfs->rcache.block = 0xffffffff; + lfs_cache_drop(lfs, &lfs->rcache); while (file->pos < file->size) { // copy over a byte at a time, leave it up to caching @@ -1451,8 +1466,8 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { // keep our reference to the rcache in sync if (lfs->rcache.block != 0xffffffff) { - orig.cache.block = 0xffffffff; - lfs->rcache.block = 0xffffffff; + lfs_cache_drop(lfs, &orig.cache); + lfs_cache_drop(lfs, &lfs->rcache); } } @@ -1630,7 +1645,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, } // mark cache as dirty since we may have read data into it - file->cache.block = 0xffffffff; + lfs_cache_zero(lfs, &file->cache); } // extend file with new blocks @@ -1981,7 +1996,6 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->cfg = cfg; // setup read cache - lfs->rcache.block = 0xffffffff; if (lfs->cfg->read_buffer) { lfs->rcache.buffer = lfs->cfg->read_buffer; } else { @@ -1992,7 +2006,6 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { } // setup program cache - lfs->pcache.block = 0xffffffff; if (lfs->cfg->prog_buffer) { lfs->pcache.buffer = lfs->cfg->prog_buffer; } else { @@ -2002,6 +2015,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { } } + // zero to avoid information leaks + lfs_cache_zero(lfs, &lfs->rcache); + lfs_cache_zero(lfs, &lfs->pcache); + // setup lookahead, round down to nearest 32-bits LFS_ASSERT(lfs->cfg->lookahead % 32 == 0); LFS_ASSERT(lfs->cfg->lookahead > 0); From 5a17fa42e40b7f4943a36e117fcf01d280c30f63 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 10 Jul 2018 08:44:00 -0500 Subject: [PATCH 07/16] Fixed script issue with bash expansion inside makefile parameter This was causing code sizes to be reported with several of the logging functions still built in. A useful number, but not the minimum achievable code size. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5c0988b55e9..e359eccdb0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ script: # compile and find the code size with the smallest configuration - make clean size OBJ="$(ls lfs*.o | tr '\n' ' ')" - CFLAGS+="-DLFS_NO{ASSERT,DEBUG,WARN,ERROR}" + CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR" | tee sizes # update status if we succeeded, compare with master if possible From 7e67f9324e1fb0a910244baac60121e945566639 Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Wed, 11 Jul 2018 12:32:21 +0200 Subject: [PATCH 08/16] Use PRIu32 and PRIx32 format specifiers to fix warnings When using "%d" or "%x" with uint32_t types, arm-none-eabi-gcc reports warnings like below: -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- In file included from lfs.c:8: lfs_util.h:45:12: warning: format '%d' expects argument of type 'int', but argument 4 has type 'lfs_block_t' {aka 'long unsigned int'} [-Wformat=] printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) ^~~~~~~~~~~~~~~~ lfs.c:2512:21: note: in expansion of macro 'LFS_DEBUG' LFS_DEBUG("Found partial move %d %d", ^~~~~~~~~ lfs.c:2512:55: note: format string is defined here LFS_DEBUG("Found partial move %d %d", ~^ %ld -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- Fix this by replacing "%d" and "%x" with `"%" PRIu32` and `"%" PRIx32`. --- emubd/lfs_emubd.c | 8 ++++---- lfs.c | 31 ++++++++++++++++++------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/emubd/lfs_emubd.c b/emubd/lfs_emubd.c index ed3414abfef..682ad925e26 100644 --- a/emubd/lfs_emubd.c +++ b/emubd/lfs_emubd.c @@ -16,6 +16,7 @@ #include #include #include +#include // Block device emulated on existing filesystem @@ -85,7 +86,7 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, memset(data, 0, size); // Read data - snprintf(emu->child, LFS_NAME_MAX, "%x", block); + snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); FILE *f = fopen(emu->path, "rb"); if (!f && errno != ENOENT) { @@ -124,7 +125,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, assert(block < cfg->block_count); // Program data - snprintf(emu->child, LFS_NAME_MAX, "%x", block); + snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); FILE *f = fopen(emu->path, "r+b"); if (!f) { @@ -171,7 +172,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { assert(block < cfg->block_count); // Erase the block - snprintf(emu->child, LFS_NAME_MAX, "%x", block); + snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); struct stat st; int err = stat(emu->path, &st); if (err && errno != ENOENT) { @@ -239,4 +240,3 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { return 0; } - diff --git a/lfs.c b/lfs.c index 9b44975f476..7f721f7c3f8 100644 --- a/lfs.c +++ b/lfs.c @@ -7,6 +7,8 @@ #include "lfs.h" #include "lfs_util.h" +#include + /// Caching block device operations /// static int lfs_cache_read(lfs_t *lfs, lfs_cache_t *rcache, @@ -308,7 +310,8 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { // check if we have looked at all blocks since last ack if (lfs->free.ack == 0) { - LFS_WARN("No more free space %d", lfs->free.i + lfs->free.off); + LFS_WARN("No more free space %" PRIu32, + lfs->free.i + lfs->free.off); return LFS_ERR_NOSPC; } @@ -478,7 +481,8 @@ static int lfs_dir_fetch(lfs_t *lfs, } if (!valid) { - LFS_ERROR("Corrupted dir pair at %d %d", tpair[0], tpair[1]); + LFS_ERROR("Corrupted dir pair at %" PRIu32 " %" PRIu32 , + tpair[0], tpair[1]); return LFS_ERR_CORRUPT; } @@ -601,7 +605,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, break; relocate: //commit was corrupted - LFS_DEBUG("Bad block at %d", dir->pair[0]); + LFS_DEBUG("Bad block at %" PRIu32, dir->pair[0]); // drop caches and prepare to relocate block relocated = true; @@ -609,7 +613,8 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, // can't relocate superblock, filesystem is now frozen if (lfs_paircmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) { - LFS_WARN("Superblock %d has become unwritable", oldpair[0]); + LFS_WARN("Superblock %" PRIu32 " has become unwritable", + oldpair[0]); return LFS_ERR_CORRUPT; } @@ -622,7 +627,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, if (relocated) { // update references if we relocated - LFS_DEBUG("Relocating %d %d to %d %d", + LFS_DEBUG("Relocating %" PRIu32 " %" PRIu32 " to %" PRIu32 " %" PRIu32, oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); int err = lfs_relocate(lfs, oldpair, dir->pair); if (err) { @@ -1227,7 +1232,7 @@ static int lfs_ctz_extend(lfs_t *lfs, } relocate: - LFS_DEBUG("Bad block at %d", nblock); + LFS_DEBUG("Bad block at %" PRIu32, nblock); // just clear cache and try a new block lfs_cache_drop(lfs, &lfs->pcache); @@ -1384,7 +1389,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { relocate: - LFS_DEBUG("Bad block at %d", file->block); + LFS_DEBUG("Bad block at %" PRIu32, file->block); // just relocate what exists into new block lfs_block_t nblock; @@ -2395,7 +2400,8 @@ static int lfs_relocate(lfs_t *lfs, // update internal root if (lfs_paircmp(oldpair, lfs->root) == 0) { - LFS_DEBUG("Relocating root %d %d", newpair[0], newpair[1]); + LFS_DEBUG("Relocating root %" PRIu32 " %" PRIu32, + newpair[0], newpair[1]); lfs->root[0] = newpair[0]; lfs->root[1] = newpair[1]; } @@ -2451,7 +2457,7 @@ int lfs_deorphan(lfs_t *lfs) { if (!res) { // we are an orphan - LFS_DEBUG("Found orphan %d %d", + LFS_DEBUG("Found orphan %" PRIu32 " %" PRIu32, pdir.d.tail[0], pdir.d.tail[1]); pdir.d.tail[0] = cwd.d.tail[0]; @@ -2467,7 +2473,7 @@ int lfs_deorphan(lfs_t *lfs) { if (!lfs_pairsync(entry.d.u.dir, pdir.d.tail)) { // we have desynced - LFS_DEBUG("Found desync %d %d", + LFS_DEBUG("Found desync %" PRIu32 " %" PRIu32, entry.d.u.dir[0], entry.d.u.dir[1]); pdir.d.tail[0] = entry.d.u.dir[0]; @@ -2502,14 +2508,14 @@ int lfs_deorphan(lfs_t *lfs) { } if (moved) { - LFS_DEBUG("Found move %d %d", + LFS_DEBUG("Found move %" PRIu32 " %" PRIu32, entry.d.u.dir[0], entry.d.u.dir[1]); err = lfs_dir_remove(lfs, &cwd, &entry); if (err) { return err; } } else { - LFS_DEBUG("Found partial move %d %d", + LFS_DEBUG("Found partial move %" PRIu32 " %" PRIu32, entry.d.u.dir[0], entry.d.u.dir[1]); entry.d.type &= ~0x80; err = lfs_dir_update(lfs, &cwd, &entry, NULL); @@ -2525,4 +2531,3 @@ int lfs_deorphan(lfs_t *lfs) { return 0; } - From 577d777c20095b6030a3e0227d144081d5c4fe5c Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Fri, 13 Jul 2018 09:34:49 +0200 Subject: [PATCH 09/16] Add C++ guards to public headers Fixes #53 Fixes #32 --- emubd/lfs_emubd.h | 9 +++++++++ lfs.h | 9 +++++++++ lfs_util.h | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/emubd/lfs_emubd.h b/emubd/lfs_emubd.h index 85718de11a7..0fd43875fdf 100644 --- a/emubd/lfs_emubd.h +++ b/emubd/lfs_emubd.h @@ -10,6 +10,11 @@ #include "lfs.h" #include "lfs_util.h" +#ifdef __cplusplus +extern "C" +{ +#endif + // Config options #ifndef LFS_EMUBD_READ_SIZE @@ -75,4 +80,8 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block); int lfs_emubd_sync(const struct lfs_config *cfg); +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif diff --git a/lfs.h b/lfs.h index 3683d32d444..1b1be19ce91 100644 --- a/lfs.h +++ b/lfs.h @@ -10,6 +10,11 @@ #include #include +#ifdef __cplusplus +extern "C" +{ +#endif + /// Version info /// @@ -460,4 +465,8 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); int lfs_deorphan(lfs_t *lfs); +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif diff --git a/lfs_util.h b/lfs_util.h index d61c5458a0b..b2dc2371472 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -34,6 +34,11 @@ #include #endif +#ifdef __cplusplus +extern "C" +{ +#endif + // Macros, may be replaced by system specific wrappers. Arguments to these // macros must not have side-effects as the macros can be removed for a smaller @@ -173,5 +178,9 @@ static inline void lfs_free(void *p) { } +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif #endif From 041e90a1ca7153e408bd2e9cea1f2157d8b0cb72 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 16 Jul 2018 15:22:02 -0500 Subject: [PATCH 10/16] Added handling for corrupt as initial state of blocks Before this, littlefs incorrectly assumed corrupt blocks were only the result of our own modification. This would be fine for most cases of freshly erased storage, but for storage with block-level ECC this wasn't always true. Fortunately, it's quite easy for littlefs to handle this case correctly, as long as corrupt storage always reports that it is corrupt, which for most forms of ECC is the case unless we perform a write on the storage. found by rojer --- lfs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lfs.c b/lfs.c index 7f721f7c3f8..77f25f1d5c5 100644 --- a/lfs.c +++ b/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; } From 961fab70c37e28f57e1f731cbe19717e7eb6c651 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Jun 2018 11:27:27 +1000 Subject: [PATCH 11/16] Added file config structure and lfs_file_opencfg The optional config structure options up the possibility of adding file-level configuration in a backwards compatible manner. Also adds possibility to open multiple files with LFS_NO_MALLOC enabled thanks to dpgeorge Also bumped minor version to v1.5 --- lfs.c | 18 ++++++++++++++---- lfs.h | 34 ++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lfs.c b/lfs.c index 7f721f7c3f8..cb058793d4f 100644 --- a/lfs.c +++ b/lfs.c @@ -1282,8 +1282,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 +1324,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 +1342,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; @@ -1368,6 +1373,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 +1390,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); } diff --git a/lfs.h b/lfs.h index 1b1be19ce91..cb7222c75f0 100644 --- a/lfs.h +++ b/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 0x00010005 #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) @@ -167,6 +167,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 +220,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; @@ -281,7 +288,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 +298,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 +337,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 From 0422c55b81cb50c3153822c53923c07fcd4246d6 Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Wed, 18 Jul 2018 16:50:00 +0200 Subject: [PATCH 12/16] Fix memory leaks in lfs_mount and lfs_format Squashed: - Change lfs_deinit() return to void to simplify error handling - Move lfs_deinit() before lfs_init() - Fix memory leaks in lfs_init() - Fix memory leaks in lfs_format() - Fix memory leaks in lfs_mount() --- lfs.c | 76 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/lfs.c b/lfs.c index e7dbeff13d7..c6b5870395c 100644 --- a/lfs.c +++ b/lfs.c @@ -2016,6 +2016,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; @@ -2025,7 +2040,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; } } @@ -2035,7 +2050,7 @@ 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; } } @@ -2051,7 +2066,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; } } @@ -2071,23 +2086,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->deorphaned = 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) { @@ -2107,19 +2109,19 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { lfs_dir_t superdir; err = lfs_dir_alloc(lfs, &superdir); if (err) { - return err; + goto cleanup; } // write root directory lfs_dir_t root; err = lfs_dir_alloc(lfs, &root); if (err) { - return err; + goto cleanup; } err = lfs_dir_commit(lfs, &root, NULL, 0); if (err) { - return err; + goto cleanup; } lfs->root[0] = root.pair[0]; @@ -2150,24 +2152,28 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { &superblock.d, sizeof(superblock.d)} }, 1); if (err && err != LFS_ERR_CORRUPT) { - return err; + goto cleanup; } valid = valid || !err; } if (!valid) { - return LFS_ERR_CORRUPT; + 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) { - return err; + goto cleanup; } lfs_alloc_ack(lfs); - return lfs_deinit(lfs); + +cleanup: + lfs_deinit(lfs); + return err; } int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { @@ -2187,7 +2193,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { 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; + goto cleanup; } if (!err) { @@ -2195,7 +2201,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { &superblock.d, sizeof(superblock.d)); lfs_superblock_fromle32(&superblock.d); if (err) { - return err; + goto cleanup; } lfs->root[0] = superblock.d.root[0]; @@ -2204,7 +2210,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { LFS_ERROR("Invalid superblock at %d %d", 0, 1); - return LFS_ERR_CORRUPT; + err = LFS_ERR_CORRUPT; + goto cleanup; } uint16_t major_version = (0xffff & (superblock.d.version >> 16)); @@ -2212,14 +2219,21 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { 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; + err = LFS_ERR_INVAL; + goto cleanup; } return 0; + +cleanup: + + lfs_deinit(lfs); + return err; } int lfs_unmount(lfs_t *lfs) { - return lfs_deinit(lfs); + lfs_deinit(lfs); + return 0; } From 0234c77102b545f95a6f8612c63bb7f5fba80173 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 25 Jul 2018 14:04:42 -0500 Subject: [PATCH 13/16] Simplified release process based on feedback Previously, littlefs had mutable versions. That is, anytime a new commit landed on master, the bot would update the most recent version to contain the patch. The idea was that this would make sure users always had the most recent bug fixes. Immutable snapshots could be accessed through the git hashes. However, at this point multiple developers have pointed out that this is confusing, with mutable versions being non-standard and surprising. This new release process adopts SemVer in its entirety, with incrementing patch numbers and immutable versions. When a new commit lands on master: 1. The major/minor version is taken from lfs.h 2. The most recent patch version is looked up on GitHub and incremented 3. A changelog is built out of the commits to the previous version 4. A new release is created on GitHub Additionally, any commits that land while CI is still running are coalesced together. Which means multiple PRs can land in a single release. --- .travis.yml | 67 +++++++++++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index e359eccdb0f..fced1416df8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -134,53 +134,44 @@ jobs: - STAGE=deploy - NAME=deploy script: - # Update tag for version defined in lfs.h + # 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))) - - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR" - - echo "littlefs version $LFS_VERSION" + # Grab latests patch from repo tags, default to 0 + - LFS_VERSION_PATCH=$(curl -f -u "$GEKY_BOT_RELEASES" + https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs + | jq 'map(.ref | match( + "refs/tags/v'"$LFS_VERSION_MAJOR"'\\.'"$LFS_VERSION_MINOR"'\\.(.*)$") + .captures[].string | tonumber + 1) | max // 0') + # We have our new version + - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH" + - echo "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") ] + # 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') + if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] then - curl -u $GEKY_BOT_RELEASES -X POST \ + # Build release notes + PREV=$(git tag --sort=-v:refname -l "v*.*.*" | head -1) + if [ ! -z "$PREV" ] + then + 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\" + \"target_commitish\": \"$TRAVIS_COMMIT\", + \"name\": \"${LFS_VERSION%.0}\", + \"body\": $(jq -sR '.' <<< "$CHANGES") }" - 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") - )" fi # Manage statuses From 066448055c551ec57d638415b84ae63543b4332e Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 27 Jul 2018 12:24:12 -0500 Subject: [PATCH 14/16] Moved SPDX and license info into README This makes is a bit easier to find the description about the SPDX tags, and fixes the issue where GitHub doesn't detect the license text. --- LICENSE.md | 12 ------------ README.md | 13 +++++++++++++ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index f794972754b..ed69bea4743 100644 --- a/LICENSE.md +++ b/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/ diff --git a/README.md b/README.md index e778a6506ef..623ba0ae47e 100644 --- a/README.md +++ b/README.md @@ -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) - From f5e0539951dd40f574030663c730bb10c171ff92 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 27 Jul 2018 15:20:00 -0500 Subject: [PATCH 15/16] Fixed issue with release script non-standard version tags --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fced1416df8..3714f7c84e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -155,7 +155,7 @@ jobs: if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] then # Build release notes - PREV=$(git tag --sort=-v:refname -l "v*.*.*" | head -1) + PREV=$(git tag --sort=-v:refname -l "v*" | head -1) if [ ! -z "$PREV" ] then echo "PREV $PREV" From 510cd13df99843174899aa3ddabcbc889c7872e8 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 27 Jul 2018 15:59:18 -0500 Subject: [PATCH 16/16] Bumped minor version to v1.6 --- lfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfs.h b/lfs.h index cb7222c75f0..7dd36046ea1 100644 --- a/lfs.h +++ b/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 0x00010005 +#define LFS_VERSION 0x00010006 #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))