Merge pull request #1046 from littlefs-project/fix-trailing-slashes

paths: Revisit path parsing, fix trailing slash behavior
This commit is contained in:
Christopher Haster
2024-12-06 13:48:26 -06:00
committed by GitHub
2 changed files with 7325 additions and 214 deletions
+68 -19
View File
@@ -282,6 +282,21 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
/// Small type-level utilities /// /// 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 // operations on block pairs
static inline void lfs_pair_swap(lfs_block_t pair[2]) { static inline void lfs_pair_swap(lfs_block_t pair[2]) {
lfs_block_t t = pair[0]; lfs_block_t t = pair[0];
@@ -1461,32 +1476,46 @@ static int lfs_dir_find_match(void *data,
return LFS_CMP_EQ; 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, static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
const char **path, uint16_t *id) { const char **path, uint16_t *id) {
// we reduce path to a single name if we can find it // we reduce path to a single name if we can find it
const char *name = *path; const char *name = *path;
if (id) {
*id = 0x3ff;
}
// default to root dir // default to root dir
lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0);
dir->tail[0] = lfs->root[0]; dir->tail[0] = lfs->root[0];
dir->tail[1] = lfs->root[1]; dir->tail[1] = lfs->root[1];
// empty paths are not allowed
if (*name == '\0') {
return LFS_ERR_INVAL;
}
while (true) { while (true) {
nextname: nextname:
// skip slashes // skip slashes if we're a directory
if (lfs_tag_type3(tag) == LFS_TYPE_DIR) {
name += strspn(name, "/"); name += strspn(name, "/");
}
lfs_size_t namelen = strcspn(name, "/"); lfs_size_t namelen = strcspn(name, "/");
// skip '.' and root '..' // skip '.'
if ((namelen == 1 && memcmp(name, ".", 1) == 0) || if (namelen == 1 && memcmp(name, ".", 1) == 0) {
(namelen == 2 && memcmp(name, "..", 2) == 0)) {
name += namelen; name += namelen;
goto nextname; goto nextname;
} }
// error on unmatched '..', trying to go above root?
if (namelen == 2 && memcmp(name, "..", 2) == 0) {
return LFS_ERR_INVAL;
}
// skip if matched by '..' in name // skip if matched by '..' in name
const char *suffix = name + namelen; const char *suffix = name + namelen;
lfs_size_t sufflen; lfs_size_t sufflen;
@@ -1498,7 +1527,9 @@ nextname:
break; break;
} }
if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { if (sufflen == 1 && memcmp(suffix, ".", 1) == 0) {
// noop
} else if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
depth -= 1; depth -= 1;
if (depth == 0) { if (depth == 0) {
name = suffix + sufflen; name = suffix + sufflen;
@@ -1512,14 +1543,14 @@ nextname:
} }
// found path // found path
if (name[0] == '\0') { if (*name == '\0') {
return tag; return tag;
} }
// update what we've found so far // update what we've found so far
*path = name; *path = name;
// only continue if we hit a directory // only continue if we're a directory
if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { if (lfs_tag_type3(tag) != LFS_TYPE_DIR) {
return LFS_ERR_NOTDIR; return LFS_ERR_NOTDIR;
} }
@@ -1539,8 +1570,7 @@ nextname:
tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
LFS_MKTAG(0x780, 0, 0), LFS_MKTAG(0x780, 0, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), LFS_MKTAG(LFS_TYPE_NAME, 0, namelen),
// are we last name? id,
(strchr(name, '/') == NULL) ? id : NULL,
lfs_dir_find_match, &(struct lfs_dir_find_match){ lfs_dir_find_match, &(struct lfs_dir_find_match){
lfs, name, namelen}); lfs, name, namelen});
if (tag < 0) { if (tag < 0) {
@@ -2604,12 +2634,12 @@ static int lfs_mkdir_(lfs_t *lfs, const char *path) {
cwd.next = lfs->mlist; cwd.next = lfs->mlist;
uint16_t id; uint16_t id;
err = lfs_dir_find(lfs, &cwd.m, &path, &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; return (err < 0) ? err : LFS_ERR_EXIST;
} }
// check that name fits // check that name fits
lfs_size_t nlen = strlen(path); lfs_size_t nlen = lfs_path_namelen(path);
if (nlen > lfs->name_max) { if (nlen > lfs->name_max) {
return LFS_ERR_NAMETOOLONG; return LFS_ERR_NAMETOOLONG;
} }
@@ -3058,7 +3088,7 @@ static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file,
// allocate entry for file if it doesn't exist // allocate entry for file if it doesn't exist
lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); 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; err = tag;
goto cleanup; goto cleanup;
} }
@@ -3078,8 +3108,14 @@ static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file,
goto cleanup; goto cleanup;
} }
// don't allow trailing slashes
if (lfs_path_isdir(path)) {
err = LFS_ERR_NOTDIR;
goto cleanup;
}
// check that name fits // check that name fits
lfs_size_t nlen = strlen(path); lfs_size_t nlen = lfs_path_namelen(path);
if (nlen > lfs->name_max) { if (nlen > lfs->name_max) {
err = LFS_ERR_NAMETOOLONG; err = LFS_ERR_NAMETOOLONG;
goto cleanup; goto cleanup;
@@ -3837,6 +3873,12 @@ static int lfs_stat_(lfs_t *lfs, const char *path, struct lfs_info *info) {
return (int)tag; 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); return lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info);
} }
@@ -3939,7 +3981,7 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
uint16_t newid; uint16_t newid;
lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) && 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; return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL;
} }
@@ -3950,8 +3992,14 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
struct lfs_mlist prevdir; struct lfs_mlist prevdir;
prevdir.next = lfs->mlist; prevdir.next = lfs->mlist;
if (prevtag == LFS_ERR_NOENT) { 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 // check that name fits
lfs_size_t nlen = strlen(newpath); lfs_size_t nlen = lfs_path_namelen(newpath);
if (nlen > lfs->name_max) { if (nlen > lfs->name_max) {
return LFS_ERR_NAMETOOLONG; return LFS_ERR_NAMETOOLONG;
} }
@@ -4011,7 +4059,8 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
{LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT, {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT,
LFS_TYPE_DELETE, newid, 0), NULL}, LFS_TYPE_DELETE, newid, 0), NULL},
{LFS_MKTAG(LFS_TYPE_CREATE, 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(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd},
{LFS_MKTAG_IF(samepair, {LFS_MKTAG_IF(samepair,
LFS_TYPE_DELETE, newoldid, 0), NULL})); LFS_TYPE_DELETE, newoldid, 0), NULL}));
+7250 -188
View File
File diff suppressed because it is too large Load Diff