Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ static inline uint32_t get_free_blocks(struct super_block *sb, uint32_t len)
bh = sb_bread(sb, ret + i);
if (!bh) {
pr_err("get_free_blocks: sb_bread failed for block %d\n", ret + i);
/* Restore all len blocks - bitmap was cleared atomically */
bitmap_set(sbi->bfree_bitmap, ret, len);
sbi->nr_free_blocks += len;
return -EIO;
return 0; /* Return 0 to indicate failure (0 is reserved) */
}
memset(bh->b_data, 0, SIMPLEFS_BLOCK_SIZE);
mark_buffer_dirty(bh);
Expand Down
113 changes: 99 additions & 14 deletions file.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,91 @@ static int simplefs_writepage(struct page *page, struct writeback_control *wbc)
* the data into the page cache. This function checks if the write operation
* can complete and allocates the necessary blocks through block_write_begin().
*/
#if SIMPLEFS_AT_LEAST(6, 12, 0)
#if SIMPLEFS_AT_LEAST(6, 15, 0)
static int simplefs_write_begin(const struct kiocb *iocb,
struct address_space *mapping,
loff_t pos,
unsigned int len,
struct folio **foliop,
void **fsdata)
{
struct file *file = iocb->ki_filp;
struct simplefs_sb_info *sbi = SIMPLEFS_SB(file->f_inode->i_sb);
int err;
uint32_t nr_allocs = 0;

if (pos + len > SIMPLEFS_MAX_FILESIZE)
return -ENOSPC;

nr_allocs = max(pos + len, file->f_inode->i_size) / SIMPLEFS_BLOCK_SIZE;
if (nr_allocs > file->f_inode->i_blocks - 1)
nr_allocs -= file->f_inode->i_blocks - 1;
else
nr_allocs = 0;
if (nr_allocs > sbi->nr_free_blocks)
return -ENOSPC;

err = block_write_begin(mapping, pos, len, foliop, simplefs_file_get_block);
if (err < 0)
pr_err("newly allocated blocks reclaim not implemented yet\n");
return err;
}
#elif SIMPLEFS_AT_LEAST(6, 12, 0)
static int simplefs_write_begin(struct file *file,
struct address_space *mapping,
loff_t pos,
unsigned int len,
struct folio **foliop,
void **fsdata)
{
struct simplefs_sb_info *sbi = SIMPLEFS_SB(file->f_inode->i_sb);
int err;
uint32_t nr_allocs = 0;

if (pos + len > SIMPLEFS_MAX_FILESIZE)
return -ENOSPC;

nr_allocs = max(pos + len, file->f_inode->i_size) / SIMPLEFS_BLOCK_SIZE;
if (nr_allocs > file->f_inode->i_blocks - 1)
nr_allocs -= file->f_inode->i_blocks - 1;
else
nr_allocs = 0;
if (nr_allocs > sbi->nr_free_blocks)
return -ENOSPC;

err = block_write_begin(mapping, pos, len, foliop, simplefs_file_get_block);
if (err < 0)
pr_err("newly allocated blocks reclaim not implemented yet\n");
return err;
}
#elif SIMPLEFS_AT_LEAST(5, 19, 0)
static int simplefs_write_begin(struct file *file,
struct address_space *mapping,
loff_t pos,
unsigned int len,
struct page **pagep,
void **fsdata)
{
struct simplefs_sb_info *sbi = SIMPLEFS_SB(file->f_inode->i_sb);
int err;
uint32_t nr_allocs = 0;

if (pos + len > SIMPLEFS_MAX_FILESIZE)
return -ENOSPC;

nr_allocs = max(pos + len, file->f_inode->i_size) / SIMPLEFS_BLOCK_SIZE;
if (nr_allocs > file->f_inode->i_blocks - 1)
nr_allocs -= file->f_inode->i_blocks - 1;
else
nr_allocs = 0;
if (nr_allocs > sbi->nr_free_blocks)
return -ENOSPC;

err = block_write_begin(mapping, pos, len, pagep, simplefs_file_get_block);
if (err < 0)
pr_err("newly allocated blocks reclaim not implemented yet\n");
return err;
}
#else
static int simplefs_write_begin(struct file *file,
struct address_space *mapping,
Expand All @@ -135,13 +206,11 @@ static int simplefs_write_begin(struct file *file,
unsigned int flags,
struct page **pagep,
void **fsdata)
#endif
{
struct simplefs_sb_info *sbi = SIMPLEFS_SB(file->f_inode->i_sb);
int err;
uint32_t nr_allocs = 0;

/* Check if the write can be completed (enough space?) */
if (pos + len > SIMPLEFS_MAX_FILESIZE)
return -ENOSPC;

Expand All @@ -153,33 +222,38 @@ static int simplefs_write_begin(struct file *file,
if (nr_allocs > sbi->nr_free_blocks)
return -ENOSPC;

/* prepare the write */
#if SIMPLEFS_AT_LEAST(6, 12, 0)
err = block_write_begin(mapping, pos, len, foliop, simplefs_file_get_block);
#elif SIMPLEFS_AT_LEAST(5, 19, 0)
err = block_write_begin(mapping, pos, len, pagep, simplefs_file_get_block);
#else
err = block_write_begin(mapping, pos, len, flags, pagep,
simplefs_file_get_block);
#endif
/* if this failed, reclaim newly allocated blocks */
if (err < 0)
pr_err("newly allocated blocks reclaim not implemented yet\n");
return err;
}
#endif

/* Called by the VFS after writing data from a write() syscall to the page
* cache. This function updates inode metadata and truncates the file if
* necessary.
*/
#if SIMPLEFS_AT_LEAST(6, 12, 0)
#if SIMPLEFS_AT_LEAST(6, 15, 0)
static int simplefs_write_end(const struct kiocb *iocb,
struct address_space *mapping,
loff_t pos,
unsigned int len,
unsigned int copied,
struct folio *foliop,
void *fsdata)
{
struct inode *inode = iocb->ki_filp->f_inode;
#elif SIMPLEFS_AT_LEAST(6, 12, 0)
static int simplefs_write_end(struct file *file,
struct address_space *mapping,
loff_t pos,
unsigned int len,
unsigned int copied,
struct folio *foliop,
void *fsdata)
{
struct inode *inode = file->f_inode;
#else
static int simplefs_write_end(struct file *file,
struct address_space *mapping,
Expand All @@ -188,9 +262,9 @@ static int simplefs_write_end(struct file *file,
unsigned int copied,
struct page *page,
void *fsdata)
#endif
{
struct inode *inode = file->f_inode;
#endif
struct simplefs_inode_info *ci = SIMPLEFS_INODE(inode);
struct super_block *sb = inode->i_sb;
#if SIMPLEFS_AT_LEAST(6, 6, 0)
Expand All @@ -199,7 +273,10 @@ static int simplefs_write_end(struct file *file,
uint32_t nr_blocks_old;

/* Complete the write() */
#if SIMPLEFS_AT_LEAST(6, 12, 0)
#if SIMPLEFS_AT_LEAST(6, 15, 0)
int ret =
generic_write_end(iocb, mapping, pos, len, copied, foliop, fsdata);
#elif SIMPLEFS_AT_LEAST(6, 12, 0)
int ret =
generic_write_end(file, mapping, pos, len, copied, foliop, fsdata);
#else
Expand Down Expand Up @@ -242,9 +319,15 @@ static int simplefs_write_end(struct file *file,
/* Read ei_block to remove unused blocks */
bh_index = sb_bread(sb, ci->ei_block);
if (!bh_index) {
#if SIMPLEFS_AT_LEAST(6, 15, 0)
pr_err("Failed to truncate '%s'. Lost %llu blocks\n",
iocb->ki_filp->f_path.dentry->d_name.name,
nr_blocks_old - inode->i_blocks);
#else
pr_err("Failed to truncate '%s'. Lost %llu blocks\n",
file->f_path.dentry->d_name.name,
nr_blocks_old - inode->i_blocks);
#endif
goto end;
}
index = (struct simplefs_file_ei_block *) bh_index->b_data;
Expand Down Expand Up @@ -486,7 +569,9 @@ const struct address_space_operations simplefs_aops = {
#else
.readpage = simplefs_readpage,
#endif
#if !SIMPLEFS_AT_LEAST(6, 15, 0)
.writepage = simplefs_writepage,
#endif
.write_begin = simplefs_write_begin,
.write_end = simplefs_write_end,
};
Expand Down
71 changes: 56 additions & 15 deletions inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ static struct dentry *simplefs_lookup(struct inode *dir,
/* Iterate blocks in extent */
for (bi = 0; bi < eblock->extents[ei].ee_len; bi++) {
bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi);
if (!bh2)
if (!bh2) {
brelse(bh);
return ERR_PTR(-EIO);
}

dblock = (struct simplefs_dir_block *) bh2->b_data;

Expand Down Expand Up @@ -375,22 +377,26 @@ static void simplefs_set_file_into_dir(struct simplefs_dir_block *dblock,
uint32_t inode_no,
const char *name)
{
int fi;
int fi = 0;
if (dblock->nr_files != 0 && dblock->files[0].inode != 0) {
for (fi = 0; fi < SIMPLEFS_FILES_PER_BLOCK - 1; fi++) {
if (dblock->files[fi].nr_blk != 1)
break;
}
dblock->files[fi + 1].inode = inode_no;
dblock->files[fi + 1].nr_blk = dblock->files[fi].nr_blk - 1;
strncpy(dblock->files[fi + 1].filename, name, SIMPLEFS_FILENAME_LEN);
strncpy(dblock->files[fi + 1].filename, name,
SIMPLEFS_FILENAME_LEN - 1);
dblock->files[fi + 1].filename[SIMPLEFS_FILENAME_LEN - 1] = '\0';
dblock->files[fi].nr_blk = 1;
} else if (dblock->nr_files == 0) {
dblock->files[fi].inode = inode_no;
strncpy(dblock->files[fi].filename, name, SIMPLEFS_FILENAME_LEN);
dblock->files[0].inode = inode_no;
strncpy(dblock->files[0].filename, name, SIMPLEFS_FILENAME_LEN - 1);
dblock->files[0].filename[SIMPLEFS_FILENAME_LEN - 1] = '\0';
} else {
dblock->files[0].inode = inode_no;
strncpy(dblock->files[fi].filename, name, SIMPLEFS_FILENAME_LEN);
strncpy(dblock->files[0].filename, name, SIMPLEFS_FILENAME_LEN - 1);
dblock->files[0].filename[SIMPLEFS_FILENAME_LEN - 1] = '\0';
}
dblock->nr_files++;
}
Expand Down Expand Up @@ -475,6 +481,12 @@ static int simplefs_create(struct inode *dir,
dir_nr_files = eblock->nr_files;
avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock);

/* Validate avail index is within bounds */
if (avail >= SIMPLEFS_MAX_EXTENTS) {
ret = -EMLINK;
goto iput;
}

/* if there is not any empty space, alloc new one */
if (!dir_nr_files && !eblock->extents[avail].ee_start) {
ret = simplefs_put_new_ext(sb, avail, eblock);
Expand Down Expand Up @@ -924,7 +936,16 @@ static int simplefs_rename(struct inode *old_dir,
return ret;
}

#if SIMPLEFS_AT_LEAST(6, 3, 0)
#if SIMPLEFS_AT_LEAST(6, 15, 0)
static struct dentry *simplefs_mkdir(struct mnt_idmap *id,
struct inode *dir,
struct dentry *dentry,
umode_t mode)
{
int ret = simplefs_create(id, dir, dentry, mode | S_IFDIR, 0);
return ret ? ERR_PTR(ret) : NULL;
}
#elif SIMPLEFS_AT_LEAST(6, 3, 0)
static int simplefs_mkdir(struct mnt_idmap *id,
struct inode *dir,
struct dentry *dentry,
Expand Down Expand Up @@ -1003,6 +1024,12 @@ static int simplefs_link(struct dentry *old_dentry,
int dir_nr_files = eblock->nr_files;
avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock);

/* Validate avail index is within bounds */
if (avail >= SIMPLEFS_MAX_EXTENTS) {
ret = -EMLINK;
goto end;
Copy link

@cubic-dev-ai cubic-dev-ai bot Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning -EMLINK via the new avail >= SIMPLEFS_MAX_EXTENTS branch leaks the inode allocated by simplefs_new_inode() because the goto end path never frees the inode or its block.

Prompt for AI agents
Address the following comment on inode.c at line 1030:

<comment>Returning -EMLINK via the new `avail &gt;= SIMPLEFS_MAX_EXTENTS` branch leaks the inode allocated by `simplefs_new_inode()` because the `goto end` path never frees the inode or its block.</comment>

<file context>
@@ -1003,6 +1024,12 @@ static int simplefs_link(struct dentry *old_dentry,
+    /* Validate avail index is within bounds */
+    if (avail &gt;= SIMPLEFS_MAX_EXTENTS) {
+        ret = -EMLINK;
+        goto end;
+    }
+
</file context>
Fix with Cubic

}

/* if there is not any empty space, alloc new one */
if (!dir_nr_files && !eblock->extents[avail].ee_start) {
ret = simplefs_put_new_ext(sb, avail, eblock);
Expand Down Expand Up @@ -1035,6 +1062,7 @@ static int simplefs_link(struct dentry *old_dentry,
/* write the file info into simplefs_dir_block */
simplefs_set_file_into_dir(dblock, old_inode->i_ino, dentry->d_name.name);

eblock->extents[avail].nr_files++;
eblock->nr_files++;
mark_buffer_dirty(bh2);
mark_buffer_dirty(bh);
Expand Down Expand Up @@ -1086,31 +1114,41 @@ static int simplefs_symlink(struct inode *dir,
uint32_t avail;

/* Check if symlink content is not too long */
if (l > sizeof(ci->i_data))
return -ENAMETOOLONG;
if (l > sizeof(ci->i_data)) {
ret = -ENAMETOOLONG;
goto iput;
}

/* fill directory data block */
bh = sb_bread(sb, ci_dir->ei_block);
if (!bh)
return -EIO;
if (!bh) {
ret = -EIO;
goto iput;
}
eblock = (struct simplefs_file_ei_block *) bh->b_data;

if (eblock->nr_files == SIMPLEFS_MAX_SUBFILES) {
ret = -EMLINK;
printk(KERN_INFO "directory is full");
goto end;
goto iput;
}

int dir_nr_files = eblock->nr_files;
avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock);

/* Validate avail index is within bounds */
if (avail >= SIMPLEFS_MAX_EXTENTS) {
ret = -EMLINK;
goto iput;
}

/* if there is not any empty space, alloc new one */
if (!dir_nr_files && !eblock->extents[avail].ee_start) {
ret = simplefs_put_new_ext(sb, avail, eblock);
switch (ret) {
case -ENOSPC:
ret = -ENOSPC;
goto end;
goto iput;
case -EIO:
ret = -EIO;
goto put_block;
Expand All @@ -1136,6 +1174,7 @@ static int simplefs_symlink(struct inode *dir,
/* write the file info into simplefs_dir_block */
simplefs_set_file_into_dir(dblock, inode->i_ino, dentry->d_name.name);

eblock->extents[avail].nr_files++;
eblock->nr_files++;
mark_buffer_dirty(bh2);
mark_buffer_dirty(bh);
Expand All @@ -1155,8 +1194,10 @@ static int simplefs_symlink(struct inode *dir,
eblock->extents[ei].ee_len);
memset(&eblock->extents[ei], 0, sizeof(struct simplefs_extent));
}

end:
iput:
put_blocks(SIMPLEFS_SB(sb), ci->ei_block, 1);
put_inode(SIMPLEFS_SB(sb), inode->i_ino);
iput(inode);
brelse(bh);
return ret;
}
Expand Down