Merge pull request #1046 from littlefs-project/fix-trailing-slashes
paths: Revisit path parsing, fix trailing slash behavior
This commit is contained in:
@@ -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
|
||||||
name += strspn(name, "/");
|
if (lfs_tag_type3(tag) == LFS_TYPE_DIR) {
|
||||||
|
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}));
|
||||||
|
|||||||
+7256
-194
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user