Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tarfs reliability fixes #160

Merged
merged 7 commits into from
Feb 13, 2024
57 changes: 33 additions & 24 deletions src/tarfs/tarfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,39 +98,44 @@ static int tarfs_readdir(struct file *file, struct dir_context *ctx)
int ret = 0;
char *name_buffer = NULL;
u64 name_len = 0;
u64 cur = ctx->pos;
u64 size = i_size_read(inode) / sizeof(disk_dentry) * sizeof(disk_dentry);
loff_t orig_pos = ctx->pos;

/* cur must be aligned to a directory entry. */
/* ctx->pos must be aligned to a directory entry. */
if (ctx->pos % sizeof(struct tarfs_direntry))
return -ENOENT;

/* Make sure we can't overflow the read offset. */
if (offset + size < offset)
return -ERANGE;

/* Make sure the increment of cur won't overflow by limiting size. */
/* Make sure the increment of ctx->pos won't overflow by limiting size. */
if (size >= U64_MAX - sizeof(disk_dentry))
return -ERANGE;

for (cur = ctx->pos; cur < size; cur += sizeof(disk_dentry)) {
for (; ctx->pos < size; ctx->pos += sizeof(disk_dentry)) {
u64 disk_len;
u8 type;

ret = tarfs_dev_read(inode->i_sb, offset + cur, &disk_dentry, sizeof(disk_dentry));
ret = tarfs_dev_read(inode->i_sb, offset + ctx->pos, &disk_dentry, sizeof(disk_dentry));
if (ret)
break;

disk_len = le64_to_cpu(disk_dentry.namelen);
if (disk_len > name_len) {
kfree(name_buffer);

if (disk_len > SIZE_MAX)
return -ENOMEM;

name_buffer = kmalloc(disk_len, GFP_KERNEL);
if (!name_buffer)
return -ENOMEM;
name_buffer = NULL;

if (disk_len > SIZE_MAX) {
ret = -ENOMEM;
break;
}

name_buffer = kmalloc(disk_len, GFP_NOFS);
if (!name_buffer) {
ret = -ENOMEM;
break;
}
name_len = disk_len;
}

Expand Down Expand Up @@ -162,11 +167,7 @@ static int tarfs_readdir(struct file *file, struct dir_context *ctx)
}

kfree(name_buffer);

if (!ret)
ctx->pos = cur;

return ret;
return ctx->pos != orig_pos ? 0 : ret;
}

static int tarfs_readpage(struct file *file, struct page *page)
Expand All @@ -178,8 +179,11 @@ static int tarfs_readpage(struct file *file, struct page *page)
int ret;

buf = kmap_local_page(page);
if (!buf)
if (!buf) {
SetPageError(page);
unlock_page(page);
return -ENOMEM;
}

offset = page_offset(page);
size = i_size_read(inode);
Expand Down Expand Up @@ -331,19 +335,24 @@ static struct inode *tarfs_iget(struct super_block *sb, u64 ino)
return inode;

discard:
discard_new_inode(inode);
iget_failed(inode);
return ERR_PTR(ret);
}

static int tarfs_strcmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
static int tarfs_strcmp(struct super_block *sb, u64 pos, const char *str,
size_t size)
{
struct buffer_head *bh;
unsigned long offset;
size_t segment;
bool matched;
const struct tarfs_state *state = sb->s_fs_info;

/* If the string doesn't fit in the data size, it doesn't match. */
if (pos + size < pos || pos + size > state->data_size)
return 0;

/* compare string up to a block at a time. */
/* Compare string up to a block at a time. */
while (size) {
offset = pos & (TARFS_BSIZE - 1);
segment = min_t(size_t, size, TARFS_BSIZE - offset);
Expand Down Expand Up @@ -448,7 +457,7 @@ static struct inode *tarfs_alloc_inode(struct super_block *sb)
{
struct tarfs_inode_info *info;

info = alloc_inode_sb(sb, tarfs_inode_cachep, GFP_KERNEL);
info = alloc_inode_sb(sb, tarfs_inode_cachep, GFP_NOFS);
if (!info)
return NULL;

Expand Down Expand Up @@ -514,7 +523,7 @@ static int tarfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_xattr = xattr_handlers;

scount = bdev_nr_sectors(sb->s_bdev);
if (!scount)
if (scount < TARFS_BSIZE / SECTOR_SIZE)
return -ENXIO;

state = kmalloc(sizeof(*state), GFP_KERNEL);
Expand Down
Loading