diff --git a/lfs.c b/lfs.c index d35d5d6d..db7aae5c 100644 --- a/lfs.c +++ b/lfs.c @@ -282,6 +282,21 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { /// Small type-level utilities /// + +// some operations on paths +static inline lfs_size_t lfs_path_namelen(const char *path) { + return strcspn(path, "/"); +} + +static inline bool lfs_path_islast(const char *path) { + lfs_size_t namelen = lfs_path_namelen(path); + return path[namelen + strspn(path + namelen, "/")] == '\0'; +} + +static inline bool lfs_path_isdir(const char *path) { + return path[lfs_path_namelen(path)] != '\0'; +} + // operations on block pairs static inline void lfs_pair_swap(lfs_block_t pair[2]) { lfs_block_t t = pair[0]; @@ -1461,13 +1476,16 @@ static int lfs_dir_find_match(void *data, return LFS_CMP_EQ; } +// lfs_dir_find tries to set path and id even if file is not found +// +// returns: +// - 0 if file is found +// - LFS_ERR_NOENT if file or parent is not found +// - LFS_ERR_NOTDIR if parent is not a dir static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, const char **path, uint16_t *id) { // we reduce path to a single name if we can find it const char *name = *path; - if (id) { - *id = 0x3ff; - } // default to root dir lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); @@ -1476,8 +1494,10 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, while (true) { nextname: - // skip slashes - name += strspn(name, "/"); + // skip slashes if we're a directory + if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { + name += strspn(name, "/"); + } lfs_size_t namelen = strcspn(name, "/"); // skip '.' and root '..' @@ -1519,7 +1539,7 @@ nextname: // update what we've found so far *path = name; - // only continue if we hit a directory + // only continue if we're a directory if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { return LFS_ERR_NOTDIR; } @@ -1539,8 +1559,7 @@ nextname: tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, LFS_MKTAG(0x780, 0, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), - // are we last name? - (strchr(name, '/') == NULL) ? id : NULL, + id, lfs_dir_find_match, &(struct lfs_dir_find_match){ lfs, name, namelen}); if (tag < 0) { @@ -2603,12 +2622,12 @@ static int lfs_mkdir_(lfs_t *lfs, const char *path) { cwd.next = lfs->mlist; uint16_t id; err = lfs_dir_find(lfs, &cwd.m, &path, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { + if (!(err == LFS_ERR_NOENT && lfs_path_islast(path))) { return (err < 0) ? err : LFS_ERR_EXIST; } // check that name fits - lfs_size_t nlen = strlen(path); + lfs_size_t nlen = lfs_path_namelen(path); if (nlen > lfs->name_max) { return LFS_ERR_NAMETOOLONG; } @@ -3057,7 +3076,7 @@ static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file, // allocate entry for file if it doesn't exist lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) { + if (tag < 0 && !(tag == LFS_ERR_NOENT && lfs_path_islast(path))) { err = tag; goto cleanup; } @@ -3077,8 +3096,14 @@ static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file, goto cleanup; } + // don't allow trailing slashes + if (lfs_path_isdir(path)) { + err = LFS_ERR_ISDIR; + goto cleanup; + } + // check that name fits - lfs_size_t nlen = strlen(path); + lfs_size_t nlen = lfs_path_namelen(path); if (nlen > lfs->name_max) { err = LFS_ERR_NAMETOOLONG; goto cleanup; @@ -3842,6 +3867,12 @@ static int lfs_stat_(lfs_t *lfs, const char *path, struct lfs_info *info) { return (int)tag; } + // only allow trailing slashes on dirs + if (strchr(path, '/') != NULL + && lfs_tag_type3(tag) != LFS_TYPE_DIR) { + return LFS_ERR_NOTDIR; + } + return lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info); } @@ -3944,7 +3975,7 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) { uint16_t newid; lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) && - !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) { + !(prevtag == LFS_ERR_NOENT && lfs_path_islast(newpath))) { return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL; } @@ -3955,8 +3986,14 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) { struct lfs_mlist prevdir; prevdir.next = lfs->mlist; if (prevtag == LFS_ERR_NOENT) { + // if we're a file, don't allow trailing slashes + if (lfs_path_isdir(newpath) + && lfs_tag_type3(oldtag) != LFS_TYPE_DIR) { + return LFS_ERR_NOTDIR; + } + // check that name fits - lfs_size_t nlen = strlen(newpath); + lfs_size_t nlen = lfs_path_namelen(newpath); if (nlen > lfs->name_max) { return LFS_ERR_NAMETOOLONG; } @@ -4016,7 +4053,8 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) { {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT, LFS_TYPE_DELETE, newid, 0), NULL}, {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL}, - {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath}, + {LFS_MKTAG(lfs_tag_type3(oldtag), + newid, lfs_path_namelen(newpath)), newpath}, {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd}, {LFS_MKTAG_IF(samepair, LFS_TYPE_DELETE, newoldid, 0), NULL})); diff --git a/tests/test_paths.toml b/tests/test_paths.toml index 8ffb2658..69f1d7fe 100644 --- a/tests/test_paths.toml +++ b/tests/test_paths.toml @@ -1064,6 +1064,14 @@ code = ''' ''' # test trailing dots, these get a bit weird +# +# POSIX deviations: +# +# - We accept modifications of directories with trailing dots: +# - littlefs: remove("a/.") => 0 +# - POSIX: remove("a/.") => EBUSY +# Reason: Not worth implementing. +# [cases.test_paths_trailing_dots] defines.DIR = [false, true] code = ''' @@ -1214,26 +1222,6 @@ code = ''' // rename paths lfs_mkdir(&lfs, "espresso") => 0; if (DIR) { - // bad source - lfs_rename(&lfs, - "coffee/drip/./././././.", - "espresso/espresso") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/coldbrew/././././.", - "espresso/americano") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/turkish/./././.", - "espresso/macchiato") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/tubruk/././.", - "espresso/latte") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/vietnamese/./.", - "espresso/cappuccino") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/thai/.", - "espresso/mocha") => LFS_ERR_INVAL; - // bad destination lfs_rename(&lfs, "coffee/drip", @@ -1274,6 +1262,69 @@ code = ''' "coffee/thai/.", "espresso/mocha/./././././.") => LFS_ERR_NOENT; + // this one works + lfs_rename(&lfs, + "coffee/drip/./././././.", + "espresso/espresso") => 0; + lfs_rename(&lfs, + "coffee/coldbrew/././././.", + "espresso/americano") => 0; + lfs_rename(&lfs, + "coffee/turkish/./././.", + "espresso/macchiato") => 0; + lfs_rename(&lfs, + "coffee/tubruk/././.", + "espresso/latte") => 0; + lfs_rename(&lfs, + "coffee/vietnamese/./.", + "espresso/cappuccino") => 0; + lfs_rename(&lfs, + "coffee/thai/.", + "espresso/mocha") => 0; + + // stat paths + lfs_stat(&lfs, "espresso/espresso/./././././.", &info) => 0; + assert(strcmp(info.name, "espresso") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "espresso/americano/././././.", &info) => 0; + assert(strcmp(info.name, "americano") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "espresso/macchiato/./././.", &info) => 0; + assert(strcmp(info.name, "macchiato") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "espresso/latte/././.", &info) => 0; + assert(strcmp(info.name, "latte") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "espresso/cappuccino/./.", &info) => 0; + assert(strcmp(info.name, "cappuccino") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "espresso/mocha/.", &info) => 0; + assert(strcmp(info.name, "mocha") == 0); + assert(info.type == LFS_TYPE_DIR); + + lfs_stat(&lfs, "coffee/drip/./././././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "coffee/coldbrew/././././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "coffee/turkish/./././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "coffee/tubruk/././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "coffee/vietnamese/./.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "coffee/thai/.", &info) => LFS_ERR_NOENT; + + // remove paths + lfs_remove(&lfs, "espresso/espresso/.") => 0; + lfs_remove(&lfs, "espresso/americano/./.") => 0; + lfs_remove(&lfs, "espresso/macchiato/././.") => 0; + lfs_remove(&lfs, "espresso/latte/./././.") => 0; + lfs_remove(&lfs, "espresso/cappuccino/././././.") => 0; + lfs_remove(&lfs, "espresso/mocha/./././././.") => 0; + + // stat paths + lfs_stat(&lfs, "espresso/espresso/./././././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/americano/././././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/macchiato/./././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/latte/././.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/cappuccino/./.", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/mocha/.", &info) => LFS_ERR_NOENT; + } else { // bad source lfs_rename(&lfs, @@ -1334,52 +1385,43 @@ code = ''' lfs_rename(&lfs, "coffee/thai/.", "espresso/mocha/./././././.") => LFS_ERR_NOTDIR; - } - // remove paths - if (DIR) { - lfs_remove(&lfs, "coffee/drip/.") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/coldbrew/./.") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/turkish/././.") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/tubruk/./././.") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/vietnamese/././././.") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/thai/./././././.") => LFS_ERR_INVAL; - } else { + // remove paths lfs_remove(&lfs, "coffee/drip/.") => LFS_ERR_NOTDIR; lfs_remove(&lfs, "coffee/coldbrew/./.") => LFS_ERR_NOTDIR; lfs_remove(&lfs, "coffee/turkish/././.") => LFS_ERR_NOTDIR; lfs_remove(&lfs, "coffee/tubruk/./././.") => LFS_ERR_NOTDIR; lfs_remove(&lfs, "coffee/vietnamese/././././.") => LFS_ERR_NOTDIR; lfs_remove(&lfs, "coffee/thai/./././././.") => LFS_ERR_NOTDIR; + + // stat paths + lfs_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/americano", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/macchiato", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/latte", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/cappuccino", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "espresso/mocha", &info) => LFS_ERR_NOENT; + + lfs_stat(&lfs, "coffee/drip", &info) => 0; + assert(strcmp(info.name, "drip") == 0); + assert(info.type == LFS_TYPE_REG); + lfs_stat(&lfs, "coffee/coldbrew", &info) => 0; + assert(strcmp(info.name, "coldbrew") == 0); + assert(info.type == LFS_TYPE_REG); + lfs_stat(&lfs, "coffee/turkish", &info) => 0; + assert(strcmp(info.name, "turkish") == 0); + assert(info.type == LFS_TYPE_REG); + lfs_stat(&lfs, "coffee/tubruk", &info) => 0; + assert(strcmp(info.name, "tubruk") == 0); + assert(info.type == LFS_TYPE_REG); + lfs_stat(&lfs, "coffee/vietnamese", &info) => 0; + assert(strcmp(info.name, "vietnamese") == 0); + assert(info.type == LFS_TYPE_REG); + lfs_stat(&lfs, "coffee/thai", &info) => 0; + assert(strcmp(info.name, "thai") == 0); + assert(info.type == LFS_TYPE_REG); } - // stat paths - lfs_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT; - lfs_stat(&lfs, "espresso/americano", &info) => LFS_ERR_NOENT; - lfs_stat(&lfs, "espresso/macchiato", &info) => LFS_ERR_NOENT; - lfs_stat(&lfs, "espresso/latte", &info) => LFS_ERR_NOENT; - lfs_stat(&lfs, "espresso/cappuccino", &info) => LFS_ERR_NOENT; - lfs_stat(&lfs, "espresso/mocha", &info) => LFS_ERR_NOENT; - - lfs_stat(&lfs, "coffee/drip", &info) => 0; - assert(strcmp(info.name, "drip") == 0); - assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG)); - lfs_stat(&lfs, "coffee/coldbrew", &info) => 0; - assert(strcmp(info.name, "coldbrew") == 0); - assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG)); - lfs_stat(&lfs, "coffee/turkish", &info) => 0; - assert(strcmp(info.name, "turkish") == 0); - assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG)); - lfs_stat(&lfs, "coffee/tubruk", &info) => 0; - assert(strcmp(info.name, "tubruk") == 0); - assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG)); - lfs_stat(&lfs, "coffee/vietnamese", &info) => 0; - assert(strcmp(info.name, "vietnamese") == 0); - assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG)); - lfs_stat(&lfs, "coffee/thai", &info) => 0; - assert(strcmp(info.name, "thai") == 0); - assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG)); - lfs_unmount(&lfs) => 0; ''' @@ -1576,6 +1618,19 @@ code = ''' ''' # test trailing dot dots, these get really weird +# +# POSIX deviations: +# +# - We do not check for existance of directories followed by dotdots: +# - littlefs: stat("a/missing/..") => 0 +# - POSIX: stat("a/missing/..") => ENOENT +# Reason: Difficult to implement non-recursively. +# +# - We accept modifications of directories with trailing dotdots: +# - littlefs: rename("a/b/..", "c") => 0 +# - POSIX: rename("a/b/..", "c") => EBUSY +# Reason: Not worth implementing. +# [cases.test_paths_trailing_dotdots] defines.DIR = [false, true] code = ''' @@ -1586,12 +1641,12 @@ code = ''' // create paths lfs_mkdir(&lfs, "coffee") => 0; if (DIR) { - lfs_mkdir(&lfs, "coffee/drip/..") => LFS_ERR_NOENT; - lfs_mkdir(&lfs, "coffee/coldbrew/../..") => LFS_ERR_NOENT; - lfs_mkdir(&lfs, "coffee/turkish/../../..") => LFS_ERR_NOENT; - lfs_mkdir(&lfs, "coffee/tubruk/../../../..") => LFS_ERR_NOENT; - lfs_mkdir(&lfs, "coffee/vietnamese/../../../../..") => LFS_ERR_NOENT; - lfs_mkdir(&lfs, "coffee/thai/../../../../../..") => LFS_ERR_NOENT; + lfs_mkdir(&lfs, "coffee/drip/..") => LFS_ERR_EXIST; + lfs_mkdir(&lfs, "coffee/coldbrew/../..") => LFS_ERR_EXIST; + lfs_mkdir(&lfs, "coffee/turkish/../../..") => LFS_ERR_EXIST; + lfs_mkdir(&lfs, "coffee/tubruk/../../../..") => LFS_ERR_EXIST; + lfs_mkdir(&lfs, "coffee/vietnamese/../../../../..") => LFS_ERR_EXIST; + lfs_mkdir(&lfs, "coffee/thai/../../../../../..") => LFS_ERR_EXIST; // still create so we have something to test lfs_mkdir(&lfs, "coffee/drip") => 0; @@ -1604,17 +1659,17 @@ code = ''' } else { lfs_file_t file; lfs_file_open(&lfs, &file, "coffee/drip/..", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_NOENT; + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "coffee/coldbrew/../..", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_NOENT; + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "coffee/turkish/../../..", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_NOENT; + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "coffee/tubruk/../../../..", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_NOENT; + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "coffee/vietnamese/../../../../..", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_NOENT; + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "coffee/thai/../../../../../..", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_NOENT; + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; // still create so we have something to test lfs_file_open(&lfs, &file, "coffee/drip", @@ -1639,231 +1694,138 @@ code = ''' // stat paths struct lfs_info info; - if (DIR) { - lfs_stat(&lfs, "coffee/drip/../../../../../..", &info) => 0; - assert(strcmp(info.name, "/") == 0); - assert(info.type == LFS_TYPE_DIR); - lfs_stat(&lfs, "coffee/coldbrew/../../../../..", &info) => 0; - assert(strcmp(info.name, "/") == 0); - assert(info.type == LFS_TYPE_DIR); - lfs_stat(&lfs, "coffee/turkish/../../../..", &info) => 0; - assert(strcmp(info.name, "/") == 0); - assert(info.type == LFS_TYPE_DIR); - lfs_stat(&lfs, "coffee/tubruk/../../..", &info) => 0; - assert(strcmp(info.name, "/") == 0); - assert(info.type == LFS_TYPE_DIR); - lfs_stat(&lfs, "coffee/vietnamese/../..", &info) => 0; - assert(strcmp(info.name, "/") == 0); - assert(info.type == LFS_TYPE_DIR); - lfs_stat(&lfs, "coffee/thai/..", &info) => 0; - assert(strcmp(info.name, "coffee") == 0); - assert(info.type == LFS_TYPE_DIR); - } else { - lfs_stat(&lfs, "coffee/drip/../../../../../..", &info) => LFS_ERR_NOTDIR; - lfs_stat(&lfs, "coffee/coldbrew/../../../../..", &info) => LFS_ERR_NOTDIR; - lfs_stat(&lfs, "coffee/turkish/../../../..", &info) => LFS_ERR_NOTDIR; - lfs_stat(&lfs, "coffee/tubruk/../../..", &info) => LFS_ERR_NOTDIR; - lfs_stat(&lfs, "coffee/vietnamese/../..", &info) => LFS_ERR_NOTDIR; - lfs_stat(&lfs, "coffee/thai/..", &info) => LFS_ERR_NOTDIR; - } + lfs_stat(&lfs, "coffee/drip/../../../../../..", &info) => 0; + assert(strcmp(info.name, "/") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "coffee/coldbrew/../../../../..", &info) => 0; + assert(strcmp(info.name, "/") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "coffee/turkish/../../../..", &info) => 0; + assert(strcmp(info.name, "/") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "coffee/tubruk/../../..", &info) => 0; + assert(strcmp(info.name, "/") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "coffee/vietnamese/../..", &info) => 0; + assert(strcmp(info.name, "/") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_stat(&lfs, "coffee/thai/..", &info) => 0; + assert(strcmp(info.name, "coffee") == 0); + assert(info.type == LFS_TYPE_DIR); // file open paths, only works on files! - if (DIR) { - lfs_file_t file; - lfs_file_open(&lfs, &file, "coffee/drip/..", - LFS_O_RDONLY) => LFS_ERR_ISDIR; - lfs_file_open(&lfs, &file, "coffee/coldbrew/../..", - LFS_O_RDONLY) => LFS_ERR_ISDIR; - lfs_file_open(&lfs, &file, "coffee/turkish/../../..", - LFS_O_RDONLY) => LFS_ERR_ISDIR; - lfs_file_open(&lfs, &file, "coffee/tubruk/../../../..", - LFS_O_RDONLY) => LFS_ERR_ISDIR; - lfs_file_open(&lfs, &file, "coffee/vietnamese/../../../../..", - LFS_O_RDONLY) => LFS_ERR_ISDIR; - lfs_file_open(&lfs, &file, "coffee/thai/../../../../../..", - LFS_O_RDONLY) => LFS_ERR_ISDIR; - } else { - lfs_file_t file; - lfs_file_open(&lfs, &file, "coffee/drip/..", - LFS_O_RDONLY) => LFS_ERR_NOTDIR; - lfs_file_open(&lfs, &file, "coffee/coldbrew/../..", - LFS_O_RDONLY) => LFS_ERR_NOTDIR; - lfs_file_open(&lfs, &file, "coffee/turkish/../../..", - LFS_O_RDONLY) => LFS_ERR_NOTDIR; - lfs_file_open(&lfs, &file, "coffee/tubruk/../../../..", - LFS_O_RDONLY) => LFS_ERR_NOTDIR; - lfs_file_open(&lfs, &file, "coffee/vietnamese/../../../../..", - LFS_O_RDONLY) => LFS_ERR_NOTDIR; - lfs_file_open(&lfs, &file, "coffee/thai/../../../../../..", - LFS_O_RDONLY) => LFS_ERR_NOTDIR; - } + lfs_file_t file; + lfs_file_open(&lfs, &file, "coffee/drip/..", + LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfs_file_open(&lfs, &file, "coffee/coldbrew/../..", + LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfs_file_open(&lfs, &file, "coffee/turkish/../../..", + LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfs_file_open(&lfs, &file, "coffee/tubruk/../../../..", + LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfs_file_open(&lfs, &file, "coffee/vietnamese/../../../../..", + LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfs_file_open(&lfs, &file, "coffee/thai/../../../../../..", + LFS_O_RDONLY) => LFS_ERR_ISDIR; // dir open paths, only works on dirs! - if (DIR) { - lfs_dir_t dir; - lfs_dir_open(&lfs, &dir, "coffee/drip/..") => 0; - lfs_dir_close(&lfs, &dir) => 0; - lfs_dir_open(&lfs, &dir, "coffee/coldbrew/../..") => 0; - lfs_dir_close(&lfs, &dir) => 0; - lfs_dir_open(&lfs, &dir, "coffee/turkish/../../..") => 0; - lfs_dir_close(&lfs, &dir) => 0; - lfs_dir_open(&lfs, &dir, "coffee/tubruk/../../../..") => 0; - lfs_dir_close(&lfs, &dir) => 0; - lfs_dir_open(&lfs, &dir, "coffee/vietnamese/../../../../..") => 0; - lfs_dir_close(&lfs, &dir) => 0; - lfs_dir_open(&lfs, &dir, "coffee/thai/../../../../../..") => 0; - lfs_dir_close(&lfs, &dir) => 0; - } else { - lfs_dir_t dir; - lfs_dir_open(&lfs, &dir, "coffee/drip/..") => LFS_ERR_NOTDIR; - lfs_dir_open(&lfs, &dir, "coffee/coldbrew/../..") => LFS_ERR_NOTDIR; - lfs_dir_open(&lfs, &dir, "coffee/turkish/../../..") => LFS_ERR_NOTDIR; - lfs_dir_open(&lfs, &dir, "coffee/tubruk/../../../..") => LFS_ERR_NOTDIR; - lfs_dir_open(&lfs, &dir, "coffee/vietnamese/../../../../..") => LFS_ERR_NOTDIR; - lfs_dir_open(&lfs, &dir, "coffee/thai/../../../../../..") => LFS_ERR_NOTDIR; - } + lfs_dir_t dir; + lfs_dir_open(&lfs, &dir, "coffee/drip/..") => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "coffee/coldbrew/../..") => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "coffee/turkish/../../..") => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "coffee/tubruk/../../../..") => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "coffee/vietnamese/../../../../..") => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "coffee/thai/../../../../../..") => 0; + lfs_dir_close(&lfs, &dir) => 0; // rename paths lfs_mkdir(&lfs, "espresso") => 0; + // bad source + lfs_rename(&lfs, + "coffee/drip/../../../../../..", + "espresso/espresso") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/coldbrew/../../../../..", + "espresso/americano") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/turkish/../../../..", + "espresso/macchiato") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/tubruk/../../..", + "espresso/latte") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/vietnamese/../..", + "espresso/cappuccino") => LFS_ERR_INVAL; + // this one works + lfs_rename(&lfs, + "coffee/thai/..", + "espresso/mocha") => 0; + lfs_rename(&lfs, + "espresso/mocha", + "coffee") => 0; + + // bad destination if (DIR) { - // bad source - lfs_rename(&lfs, - "coffee/drip/../../../../../..", - "espresso/espresso") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/coldbrew/../../../../..", - "espresso/americano") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/turkish/../../../..", - "espresso/macchiato") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/tubruk/../../..", - "espresso/latte") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/vietnamese/../..", - "espresso/cappuccino") => LFS_ERR_INVAL; - lfs_rename(&lfs, - "coffee/thai/..", - "espresso/mocha") => LFS_ERR_INVAL; - - // bad destination + // this one works lfs_rename(&lfs, "coffee/drip", - "espresso/espresso/..") => LFS_ERR_NOENT; + "espresso/espresso/..") => 0; lfs_rename(&lfs, - "coffee/coldbrew", - "espresso/americano/../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/turkish", - "espresso/macchiato/../../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/tubruk", - "espresso/latte/../../../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/vietnamese", - "espresso/cappuccino/../../../../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/thai", - "espresso/mocha/../../../../../..") => LFS_ERR_NOENT; - - // bad source and bad destination - lfs_rename(&lfs, - "coffee/drip/../../../../../..", - "espresso/espresso/..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/coldbrew/../../../../..", - "espresso/americano/../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/turkish/../../../..", - "espresso/macchiato/../../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/tubruk/../../..", - "espresso/latte/../../../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/vietnamese/../..", - "espresso/cappuccino/../../../../..") => LFS_ERR_NOENT; - lfs_rename(&lfs, - "coffee/thai/..", - "espresso/mocha/../../../../../..") => LFS_ERR_NOENT; - + "espresso", + "coffee/drip") => 0; } else { - // bad source - lfs_rename(&lfs, - "coffee/drip/../../../../../..", - "espresso/espresso") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/coldbrew/../../../../..", - "espresso/americano") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/turkish/../../../..", - "espresso/macchiato") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/tubruk/../../..", - "espresso/latte") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/vietnamese/../..", - "espresso/cappuccino") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/thai/..", - "espresso/mocha") => LFS_ERR_NOTDIR; - - // bad destination lfs_rename(&lfs, "coffee/drip", - "espresso/espresso/..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/coldbrew", - "espresso/americano/../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/turkish", - "espresso/macchiato/../../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/tubruk", - "espresso/latte/../../../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/vietnamese", - "espresso/cappuccino/../../../../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/thai", - "espresso/mocha/../../../../../..") => LFS_ERR_NOTDIR; - - // bad source and bad destination - lfs_rename(&lfs, - "coffee/drip/../../../../../..", - "espresso/espresso/..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/coldbrew/../../../../..", - "espresso/americano/../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/turkish/../../../..", - "espresso/macchiato/../../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/tubruk/../../..", - "espresso/latte/../../../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/vietnamese/../..", - "espresso/cappuccino/../../../../..") => LFS_ERR_NOTDIR; - lfs_rename(&lfs, - "coffee/thai/..", - "espresso/mocha/../../../../../..") => LFS_ERR_NOTDIR; + "espresso/espresso/..") => LFS_ERR_ISDIR; } + lfs_rename(&lfs, + "coffee/coldbrew", + "espresso/americano/../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/turkish", + "espresso/macchiato/../../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/tubruk", + "espresso/latte/../../../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/vietnamese", + "espresso/cappuccino/../../../../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/thai", + "espresso/mocha/../../../../../..") => LFS_ERR_INVAL; + + // bad source and bad destination + lfs_rename(&lfs, + "coffee/drip/../../../../../..", + "espresso/espresso/..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/coldbrew/../../../../..", + "espresso/americano/../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/turkish/../../../..", + "espresso/macchiato/../../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/tubruk/../../..", + "espresso/latte/../../../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/vietnamese/../..", + "espresso/cappuccino/../../../../..") => LFS_ERR_INVAL; + lfs_rename(&lfs, + "coffee/thai/..", + "espresso/mocha/../../../../../..") => LFS_ERR_INVAL; // remove paths - if (DIR) { - lfs_remove(&lfs, "coffee/drip/..") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/coldbrew/../..") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/turkish/../../..") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/tubruk/../../../..") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/vietnamese/../../../../..") => LFS_ERR_INVAL; - lfs_remove(&lfs, "coffee/thai/../../../../../..") => LFS_ERR_INVAL; - } else { - lfs_remove(&lfs, "coffee/drip/..") => LFS_ERR_NOTDIR; - lfs_remove(&lfs, "coffee/coldbrew/../..") => LFS_ERR_NOTDIR; - lfs_remove(&lfs, "coffee/turkish/../../..") => LFS_ERR_NOTDIR; - lfs_remove(&lfs, "coffee/tubruk/../../../..") => LFS_ERR_NOTDIR; - lfs_remove(&lfs, "coffee/vietnamese/../../../../..") => LFS_ERR_NOTDIR; - lfs_remove(&lfs, "coffee/thai/../../../../../..") => LFS_ERR_NOTDIR; - } + lfs_remove(&lfs, "coffee/drip/..") => LFS_ERR_NOTEMPTY; + lfs_remove(&lfs, "coffee/coldbrew/../..") => LFS_ERR_INVAL; + lfs_remove(&lfs, "coffee/turkish/../../..") => LFS_ERR_INVAL; + lfs_remove(&lfs, "coffee/tubruk/../../../..") => LFS_ERR_INVAL; + lfs_remove(&lfs, "coffee/vietnamese/../../../../..") => LFS_ERR_INVAL; + lfs_remove(&lfs, "coffee/thai/../../../../../..") => LFS_ERR_INVAL; // stat paths lfs_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT; @@ -3048,6 +3010,14 @@ code = ''' ''' # root operations +# +# POSIX deviations: +# +# - Root modifications return EINVAL instead of EBUSY: +# - littlefs: remove("/") => EINVAL +# - POSIX: remove("/") => EBUSY +# Reason: This would be the only use of EBUSY in the system. +# [cases.test_paths_root] defines.DIR = [false, true] code = '''