Skip to content

Commit

Permalink
Merge pull request #1046 from littlefs-project/fix-trailing-slashes
Browse files Browse the repository at this point in the history
paths: Revisit path parsing, fix trailing slash behavior
  • Loading branch information
geky authored Dec 6, 2024
2 parents 78f9a5f + 999ef66 commit 2fcecc8
Show file tree
Hide file tree
Showing 2 changed files with 7,338 additions and 227 deletions.
89 changes: 69 additions & 20 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -1461,32 +1476,46 @@ 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);
dir->tail[0] = lfs->root[0];
dir->tail[1] = lfs->root[1];

// empty paths are not allowed
if (*name == '\0') {
return LFS_ERR_INVAL;
}

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 '..'
if ((namelen == 1 && memcmp(name, ".", 1) == 0) ||
(namelen == 2 && memcmp(name, "..", 2) == 0)) {
// skip '.'
if (namelen == 1 && memcmp(name, ".", 1) == 0) {
name += namelen;
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
const char *suffix = name + namelen;
lfs_size_t sufflen;
Expand All @@ -1498,7 +1527,9 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
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;
if (depth == 0) {
name = suffix + sufflen;
Expand All @@ -1512,14 +1543,14 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
}

// found path
if (name[0] == '\0') {
if (*name == '\0') {
return tag;
}

// 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;
}
Expand All @@ -1539,8 +1570,7 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
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) {
Expand Down Expand Up @@ -2604,12 +2634,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;
}
Expand Down Expand Up @@ -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
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;
}
Expand All @@ -3078,8 +3108,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_NOTDIR;
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;
Expand Down Expand Up @@ -3837,6 +3873,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);
}

Expand Down Expand Up @@ -3939,7 +3981,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;
}

Expand All @@ -3950,8 +3992,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;
}
Expand Down Expand Up @@ -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_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}));
Expand Down
Loading

0 comments on commit 2fcecc8

Please # to comment.