-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEq1nRK9aeMoq1VSgcnJ2qBz9kQNkFAmPvZWkACgkQnJ2qBz9k
 QNkVtQf/V515KIb7lEkwOjlF7AQg5MS/c8zuodHISzWYhZyd0KSTb+qnF/QzLvVm
 cJ3cIPXrIyVw4Eeqh0qQvukOYCcBvUa1IBW5kePiy3mHiHRD2PRgaxSGBXTXqqPG
 xXagwllrn3/mG4ZKXlNYFrzgoshSFFeBdkLGEi+/L6DAe0B+mG+FIHON1eWylgOT
 j1D+/k9RNvRhRU8WtStcI4u9mnVPqUI2RSWUpjxuNzUPtyFflPVNCz+bkXXovPQ0
 ZQY2HeZcs7jsorCmeSHUzTt5bbj3BfhO3uWL4/wnHgp+88OBRUUyvMrNbOF97xd6
 KFqbJVQbSevasUSEvCS+3+EChzjGoA==
 =DaJi
 -----END PGP SIGNATURE-----

Merge tag 'fixes_for_v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull UDF and ext2 fixes from Jan Kara:

 - Rewrite of udf directory iteration code to address multiple syzbot
   reports

 - Fixes to udf extent handling and block mapping code to address
   several syzbot reports and filesystem corruption issues uncovered by
   fsx & fsstress

 - Convert udf to kmap_local()

 - Add sanity checks when loading udf bitmaps

 - Drop old VARCONV support which I've never seen used and which was
   broken for quite some years without anybody noticing

 - Finish conversion of ext2 to kmap_local()

 - One fix to mpage_writepages() on which other udf fixes depend

* tag 'fixes_for_v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (78 commits)
  udf: Avoid directory type conversion failure due to ENOMEM
  udf: Use unsigned variables for size calculations
  udf: remove reporting loc in debug output
  udf: Check consistency of Space Bitmap Descriptor
  udf: Fix file counting in LVID
  udf: Limit file size to 4TB
  udf: Don't return bh from udf_expand_dir_adinicb()
  udf: Convert udf_expand_file_adinicb() to avoid kmap_atomic()
  udf: Convert udf_adinicb_writepage() to memcpy_to_page()
  udf: Switch udf_adinicb_readpage() to kmap_local_page()
  udf: Move udf_adinicb_readpage() to inode.c
  udf: Mark aops implementation static
  udf: Switch to single address_space_operations
  udf: Add handling of in-ICB files to udf_bmap()
  udf: Convert all file types to use udf_write_end()
  udf: Convert in-ICB files to use udf_write_begin()
  udf: Convert in-ICB files to use udf_direct_IO()
  udf: Convert in-ICB files to use udf_writepages()
  udf: Unify .read_folio for normal and in-ICB files
  udf: Fix off-by-one error when discarding preallocation
  ...
This commit is contained in:
Linus Torvalds 2023-02-20 12:44:08 -08:00
commit 274978f173
20 changed files with 1381 additions and 1572 deletions

View File

@ -461,9 +461,9 @@ static int ext2_handle_dirsync(struct inode *dir)
return err;
}
void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
struct page *page, void *page_addr, struct inode *inode,
int update_times)
int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
struct page *page, void *page_addr, struct inode *inode,
bool update_times)
{
loff_t pos = page_offset(page) +
(char *) de - (char *) page_addr;
@ -472,7 +472,10 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
lock_page(page);
err = ext2_prepare_chunk(page, pos, len);
BUG_ON(err);
if (err) {
unlock_page(page);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
ext2_set_de_type(de, inode);
ext2_commit_chunk(page, pos, len);
@ -480,7 +483,7 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
dir->i_mtime = dir->i_ctime = current_time(dir);
EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
ext2_handle_dirsync(dir);
return ext2_handle_dirsync(dir);
}
/*
@ -646,7 +649,7 @@ int ext2_make_empty(struct inode *inode, struct inode *parent)
unlock_page(page);
goto fail;
}
kaddr = kmap_atomic(page);
kaddr = kmap_local_page(page);
memset(kaddr, 0, chunk_size);
de = (struct ext2_dir_entry_2 *)kaddr;
de->name_len = 1;
@ -661,7 +664,7 @@ int ext2_make_empty(struct inode *inode, struct inode *parent)
de->inode = cpu_to_le32(parent->i_ino);
memcpy (de->name, "..\0", 4);
ext2_set_de_type (de, inode);
kunmap_atomic(kaddr);
kunmap_local(kaddr);
ext2_commit_chunk(page, 0, chunk_size);
err = ext2_handle_dirsync(inode);
fail:

View File

@ -734,8 +734,9 @@ extern int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page,
char *kaddr);
extern int ext2_empty_dir (struct inode *);
extern struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p, void **pa);
extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, void *,
struct inode *, int);
int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
struct page *page, void *page_addr, struct inode *inode,
bool update_times);
static inline void ext2_put_page(struct page *page, void *page_addr)
{
kunmap_local(page_addr);

View File

@ -370,8 +370,11 @@ static int ext2_rename (struct mnt_idmap * idmap,
err = PTR_ERR(new_de);
goto out_dir;
}
ext2_set_link(new_dir, new_de, new_page, page_addr, old_inode, 1);
err = ext2_set_link(new_dir, new_de, new_page, page_addr,
old_inode, true);
ext2_put_page(new_page, page_addr);
if (err)
goto out_dir;
new_inode->i_ctime = current_time(new_inode);
if (dir_de)
drop_nlink(new_inode);
@ -394,24 +397,24 @@ static int ext2_rename (struct mnt_idmap * idmap,
ext2_delete_entry(old_de, old_page, old_page_addr);
if (dir_de) {
if (old_dir != new_dir)
ext2_set_link(old_inode, dir_de, dir_page,
dir_page_addr, new_dir, 0);
if (old_dir != new_dir) {
err = ext2_set_link(old_inode, dir_de, dir_page,
dir_page_addr, new_dir, false);
}
ext2_put_page(dir_page, dir_page_addr);
inode_dec_link_count(old_dir);
}
ext2_put_page(old_page, old_page_addr);
return 0;
out_dir:
if (dir_de)
ext2_put_page(dir_page, dir_page_addr);
out_old:
ext2_put_page(old_page, old_page_addr);
out:
return err;
out_dir:
if (dir_de)
ext2_put_page(dir_page, dir_page_addr);
goto out_old;
}
const struct inode_operations ext2_dir_inode_operations = {

View File

@ -532,6 +532,8 @@ static int __mpage_writepage(struct page *page, struct writeback_control *wbc,
map_bh.b_size = 1 << blkbits;
if (mpd->get_block(inode, block_in_file, &map_bh, 1))
goto confused;
if (!buffer_mapped(&map_bh))
goto confused;
if (buffer_new(&map_bh))
clean_bdev_bh_alias(&map_bh);
if (buffer_boundary(&map_bh)) {

View File

@ -36,18 +36,41 @@ static int read_block_bitmap(struct super_block *sb,
unsigned long bitmap_nr)
{
struct buffer_head *bh = NULL;
int retval = 0;
int i;
int max_bits, off, count;
struct kernel_lb_addr loc;
loc.logicalBlockNum = bitmap->s_extPosition;
loc.partitionReferenceNum = UDF_SB(sb)->s_partition;
bh = udf_tread(sb, udf_get_lb_pblock(sb, &loc, block));
if (!bh)
retval = -EIO;
bh = sb_bread(sb, udf_get_lb_pblock(sb, &loc, block));
bitmap->s_block_bitmap[bitmap_nr] = bh;
return retval;
if (!bh)
return -EIO;
/* Check consistency of Space Bitmap buffer. */
max_bits = sb->s_blocksize * 8;
if (!bitmap_nr) {
off = sizeof(struct spaceBitmapDesc) << 3;
count = min(max_bits - off, bitmap->s_nr_groups);
} else {
/*
* Rough check if bitmap number is too big to have any bitmap
* blocks reserved.
*/
if (bitmap_nr >
(bitmap->s_nr_groups >> (sb->s_blocksize_bits + 3)) + 2)
return 0;
off = 0;
count = bitmap->s_nr_groups - bitmap_nr * max_bits +
(sizeof(struct spaceBitmapDesc) << 3);
count = min(count, max_bits);
}
for (i = 0; i < count; i++)
if (udf_test_bit(i + off, bh->b_data))
return -EFSCORRUPTED;
return 0;
}
static int __load_block_bitmap(struct super_block *sb,

View File

@ -39,26 +39,13 @@
static int udf_readdir(struct file *file, struct dir_context *ctx)
{
struct inode *dir = file_inode(file);
struct udf_inode_info *iinfo = UDF_I(dir);
struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL};
struct fileIdentDesc *fi = NULL;
struct fileIdentDesc cfi;
udf_pblk_t block, iblock;
loff_t nf_pos, emit_pos = 0;
int flen;
unsigned char *fname = NULL, *copy_name = NULL;
unsigned char *nameptr;
uint16_t liu;
uint8_t lfi;
loff_t size = udf_ext0_offset(dir) + dir->i_size;
struct buffer_head *tmp, *bha[16];
struct kernel_lb_addr eloc;
uint32_t elen;
sector_t offset;
int i, num, ret = 0;
struct extent_position epos = { NULL, 0, {0, 0} };
unsigned char *fname = NULL;
int ret = 0;
struct super_block *sb = dir->i_sb;
bool pos_valid = false;
struct udf_fileident_iter iter;
if (ctx->pos == 0) {
if (!dir_emit_dot(file, ctx))
@ -66,7 +53,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
ctx->pos = 1;
}
nf_pos = (ctx->pos - 1) << 2;
if (nf_pos >= size)
if (nf_pos >= dir->i_size)
goto out;
/*
@ -90,138 +77,57 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
goto out;
}
if (nf_pos == 0)
nf_pos = udf_ext0_offset(dir);
fibh.soffset = fibh.eoffset = nf_pos & (sb->s_blocksize - 1);
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
if (inode_bmap(dir, nf_pos >> sb->s_blocksize_bits,
&epos, &eloc, &elen, &offset)
!= (EXT_RECORDED_ALLOCATED >> 30)) {
ret = -ENOENT;
goto out;
}
block = udf_get_lb_pblock(sb, &eloc, offset);
if ((++offset << sb->s_blocksize_bits) < elen) {
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
epos.offset -= sizeof(struct short_ad);
else if (iinfo->i_alloc_type ==
ICBTAG_FLAG_AD_LONG)
epos.offset -= sizeof(struct long_ad);
} else {
offset = 0;
}
if (!(fibh.sbh = fibh.ebh = udf_tread(sb, block))) {
ret = -EIO;
goto out;
}
if (!(offset & ((16 >> (sb->s_blocksize_bits - 9)) - 1))) {
i = 16 >> (sb->s_blocksize_bits - 9);
if (i + offset > (elen >> sb->s_blocksize_bits))
i = (elen >> sb->s_blocksize_bits) - offset;
for (num = 0; i > 0; i--) {
block = udf_get_lb_pblock(sb, &eloc, offset + i);
tmp = udf_tgetblk(sb, block);
if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
bha[num++] = tmp;
else
brelse(tmp);
}
if (num) {
bh_readahead_batch(num, bha, REQ_RAHEAD);
for (i = 0; i < num; i++)
brelse(bha[i]);
}
}
}
while (nf_pos < size) {
for (ret = udf_fiiter_init(&iter, dir, nf_pos);
!ret && iter.pos < dir->i_size;
ret = udf_fiiter_advance(&iter)) {
struct kernel_lb_addr tloc;
loff_t cur_pos = nf_pos;
udf_pblk_t iblock;
/* Update file position only if we got past the current one */
if (nf_pos >= emit_pos) {
ctx->pos = (nf_pos >> 2) + 1;
pos_valid = true;
}
fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc,
&elen, &offset);
if (!fi)
goto out;
/* Still not at offset where user asked us to read from? */
if (cur_pos < emit_pos)
if (iter.pos < emit_pos)
continue;
liu = le16_to_cpu(cfi.lengthOfImpUse);
lfi = cfi.lengthFileIdent;
/* Update file position only if we got past the current one */
pos_valid = true;
ctx->pos = (iter.pos >> 2) + 1;
if (fibh.sbh == fibh.ebh) {
nameptr = udf_get_fi_ident(fi);
} else {
int poffset; /* Unpaded ending offset */
poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
if (poffset >= lfi) {
nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
} else {
if (!copy_name) {
copy_name = kmalloc(UDF_NAME_LEN,
GFP_NOFS);
if (!copy_name) {
ret = -ENOMEM;
goto out;
}
}
nameptr = copy_name;
memcpy(nameptr, udf_get_fi_ident(fi),
lfi - poffset);
memcpy(nameptr + lfi - poffset,
fibh.ebh->b_data, poffset);
}
}
if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
if (iter.fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
continue;
}
if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
if (iter.fi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) {
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
continue;
}
if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
if (iter.fi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
if (!dir_emit_dotdot(file, ctx))
goto out;
goto out_iter;
continue;
}
flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN);
flen = udf_get_filename(sb, iter.name,
iter.fi.lengthFileIdent, fname, UDF_NAME_LEN);
if (flen < 0)
continue;
tloc = lelb_to_cpu(cfi.icb.extLocation);
tloc = lelb_to_cpu(iter.fi.icb.extLocation);
iblock = udf_get_lb_pblock(sb, &tloc, 0);
if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN))
goto out;
} /* end while */
ctx->pos = (nf_pos >> 2) + 1;
pos_valid = true;
goto out_iter;
}
if (!ret) {
ctx->pos = (iter.pos >> 2) + 1;
pos_valid = true;
}
out_iter:
udf_fiiter_release(&iter);
out:
if (pos_valid)
file->f_version = inode_query_iversion(dir);
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
brelse(epos.bh);
kfree(fname);
kfree(copy_name);
return ret;
}

View File

@ -17,183 +17,478 @@
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/bio.h>
#include <linux/crc-itu-t.h>
#include <linux/iversion.h>
struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi,
struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t *elen,
sector_t *offset)
static int udf_verify_fi(struct udf_fileident_iter *iter)
{
struct fileIdentDesc *fi;
int i, num;
udf_pblk_t block;
struct buffer_head *tmp, *bha[16];
struct udf_inode_info *iinfo = UDF_I(dir);
unsigned int len;
fibh->soffset = fibh->eoffset;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
fi = udf_get_fileident(iinfo->i_data -
(iinfo->i_efe ?
sizeof(struct extendedFileEntry) :
sizeof(struct fileEntry)),
dir->i_sb->s_blocksize,
&(fibh->eoffset));
if (!fi)
return NULL;
*nf_pos += fibh->eoffset - fibh->soffset;
memcpy((uint8_t *)cfi, (uint8_t *)fi,
sizeof(struct fileIdentDesc));
return fi;
if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
udf_err(iter->dir->i_sb,
"directory (ino %lu) has entry at pos %llu with incorrect tag %x\n",
iter->dir->i_ino, (unsigned long long)iter->pos,
le16_to_cpu(iter->fi.descTag.tagIdent));
return -EFSCORRUPTED;
}
if (fibh->eoffset == dir->i_sb->s_blocksize) {
uint32_t lextoffset = epos->offset;
unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits;
if (udf_next_aext(dir, epos, eloc, elen, 1) !=
(EXT_RECORDED_ALLOCATED >> 30))
return NULL;
block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
(*offset)++;
if ((*offset << blocksize_bits) >= *elen)
*offset = 0;
else
epos->offset = lextoffset;
brelse(fibh->sbh);
fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
if (!fibh->sbh)
return NULL;
fibh->soffset = fibh->eoffset = 0;
if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) {
i = 16 >> (blocksize_bits - 9);
if (i + *offset > (*elen >> blocksize_bits))
i = (*elen >> blocksize_bits)-*offset;
for (num = 0; i > 0; i--) {
block = udf_get_lb_pblock(dir->i_sb, eloc,
*offset + i);
tmp = udf_tgetblk(dir->i_sb, block);
if (tmp && !buffer_uptodate(tmp) &&
!buffer_locked(tmp))
bha[num++] = tmp;
else
brelse(tmp);
}
if (num) {
bh_readahead_batch(num, bha, REQ_RAHEAD);
for (i = 0; i < num; i++)
brelse(bha[i]);
}
}
} else if (fibh->sbh != fibh->ebh) {
brelse(fibh->sbh);
fibh->sbh = fibh->ebh;
len = udf_dir_entry_len(&iter->fi);
if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) {
udf_err(iter->dir->i_sb,
"directory (ino %lu) has entry at pos %llu with unaligned length of impUse field\n",
iter->dir->i_ino, (unsigned long long)iter->pos);
return -EFSCORRUPTED;
}
fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize,
&(fibh->eoffset));
if (!fi)
return NULL;
*nf_pos += fibh->eoffset - fibh->soffset;
if (fibh->eoffset <= dir->i_sb->s_blocksize) {
memcpy((uint8_t *)cfi, (uint8_t *)fi,
sizeof(struct fileIdentDesc));
} else if (fibh->eoffset > dir->i_sb->s_blocksize) {
uint32_t lextoffset = epos->offset;
if (udf_next_aext(dir, epos, eloc, elen, 1) !=
(EXT_RECORDED_ALLOCATED >> 30))
return NULL;
block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
(*offset)++;
if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
*offset = 0;
else
epos->offset = lextoffset;
fibh->soffset -= dir->i_sb->s_blocksize;
fibh->eoffset -= dir->i_sb->s_blocksize;
fibh->ebh = udf_tread(dir->i_sb, block);
if (!fibh->ebh)
return NULL;
if (sizeof(struct fileIdentDesc) > -fibh->soffset) {
int fi_len;
memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset);
memcpy((uint8_t *)cfi - fibh->soffset,
fibh->ebh->b_data,
sizeof(struct fileIdentDesc) + fibh->soffset);
fi_len = udf_dir_entry_len(cfi);
*nf_pos += fi_len - (fibh->eoffset - fibh->soffset);
fibh->eoffset = fibh->soffset + fi_len;
} else {
memcpy((uint8_t *)cfi, (uint8_t *)fi,
sizeof(struct fileIdentDesc));
}
/*
* This is in fact allowed by the spec due to long impUse field but
* we don't support it. If there is real media with this large impUse
* field, support can be added.
*/
if (len > 1 << iter->dir->i_blkbits) {
udf_err(iter->dir->i_sb,
"directory (ino %lu) has too big (%u) entry at pos %llu\n",
iter->dir->i_ino, len, (unsigned long long)iter->pos);
return -EFSCORRUPTED;
}
/* Got last entry outside of dir size - fs is corrupted! */
if (*nf_pos > dir->i_size)
return NULL;
return fi;
if (iter->pos + len > iter->dir->i_size) {
udf_err(iter->dir->i_sb,
"directory (ino %lu) has entry past directory size at pos %llu\n",
iter->dir->i_ino, (unsigned long long)iter->pos);
return -EFSCORRUPTED;
}
if (udf_dir_entry_len(&iter->fi) !=
sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) {
udf_err(iter->dir->i_sb,
"directory (ino %lu) has entry where CRC length (%u) does not match entry length (%u)\n",
iter->dir->i_ino,
(unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength),
(unsigned)(udf_dir_entry_len(&iter->fi) -
sizeof(struct tag)));
return -EFSCORRUPTED;
}
return 0;
}
struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset)
static int udf_copy_fi(struct udf_fileident_iter *iter)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
u32 blksize = 1 << iter->dir->i_blkbits;
u32 off, len, nameoff;
int err;
/* Skip copying when we are at EOF */
if (iter->pos >= iter->dir->i_size) {
iter->name = NULL;
return 0;
}
if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) {
udf_err(iter->dir->i_sb,
"directory (ino %lu) has entry straddling EOF\n",
iter->dir->i_ino);
return -EFSCORRUPTED;
}
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos,
sizeof(struct fileIdentDesc));
err = udf_verify_fi(iter);
if (err < 0)
return err;
iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos +
sizeof(struct fileIdentDesc) +
le16_to_cpu(iter->fi.lengthOfImpUse);
return 0;
}
off = iter->pos & (blksize - 1);
len = min_t(int, sizeof(struct fileIdentDesc), blksize - off);
memcpy(&iter->fi, iter->bh[0]->b_data + off, len);
if (len < sizeof(struct fileIdentDesc))
memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data,
sizeof(struct fileIdentDesc) - len);
err = udf_verify_fi(iter);
if (err < 0)
return err;
/* Handle directory entry name */
nameoff = off + sizeof(struct fileIdentDesc) +
le16_to_cpu(iter->fi.lengthOfImpUse);
if (off + udf_dir_entry_len(&iter->fi) <= blksize) {
iter->name = iter->bh[0]->b_data + nameoff;
} else if (nameoff >= blksize) {
iter->name = iter->bh[1]->b_data + (nameoff - blksize);
} else {
iter->name = iter->namebuf;
len = blksize - nameoff;
memcpy(iter->name, iter->bh[0]->b_data + nameoff, len);
memcpy(iter->name + len, iter->bh[1]->b_data,
iter->fi.lengthFileIdent - len);
}
return 0;
}
/* Readahead 8k once we are at 8k boundary */
static void udf_readahead_dir(struct udf_fileident_iter *iter)
{
unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9);
struct buffer_head *tmp, *bha[16];
int i, num;
udf_pblk_t blk;
if (iter->loffset & (ralen - 1))
return;
if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits))
ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset;
num = 0;
for (i = 0; i < ralen; i++) {
blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc,
iter->loffset + i);
tmp = sb_getblk(iter->dir->i_sb, blk);
if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
bha[num++] = tmp;
else
brelse(tmp);
}
if (num) {
bh_readahead_batch(num, bha, REQ_RAHEAD);
for (i = 0; i < num; i++)
brelse(bha[i]);
}
}
static struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter)
{
udf_pblk_t blk;
udf_readahead_dir(iter);
blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset);
return sb_bread(iter->dir->i_sb, blk);
}
/*
* Updates loffset to point to next directory block; eloc, elen & epos are
* updated if we need to traverse to the next extent as well.
*/
static int udf_fiiter_advance_blk(struct udf_fileident_iter *iter)
{
iter->loffset++;
if (iter->loffset < DIV_ROUND_UP(iter->elen, 1<<iter->dir->i_blkbits))
return 0;
iter->loffset = 0;
if (udf_next_aext(iter->dir, &iter->epos, &iter->eloc, &iter->elen, 1)
!= (EXT_RECORDED_ALLOCATED >> 30)) {
if (iter->pos == iter->dir->i_size) {
iter->elen = 0;
return 0;
}
udf_err(iter->dir->i_sb,
"extent after position %llu not allocated in directory (ino %lu)\n",
(unsigned long long)iter->pos, iter->dir->i_ino);
return -EFSCORRUPTED;
}
return 0;
}
static int udf_fiiter_load_bhs(struct udf_fileident_iter *iter)
{
int blksize = 1 << iter->dir->i_blkbits;
int off = iter->pos & (blksize - 1);
int err;
struct fileIdentDesc *fi;
int lengthThisIdent;
uint8_t *ptr;
int padlen;
if ((!buffer) || (!offset)) {
udf_debug("invalidparms, buffer=%p, offset=%p\n",
buffer, offset);
return NULL;
/* Is there any further extent we can map from? */
if (!iter->bh[0] && iter->elen) {
iter->bh[0] = udf_fiiter_bread_blk(iter);
if (!iter->bh[0]) {
err = -ENOMEM;
goto out_brelse;
}
if (!buffer_uptodate(iter->bh[0])) {
err = -EIO;
goto out_brelse;
}
}
/* There's no next block so we are done */
if (iter->pos >= iter->dir->i_size)
return 0;
/* Need to fetch next block as well? */
if (off + sizeof(struct fileIdentDesc) > blksize)
goto fetch_next;
fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off);
/* Need to fetch next block to get name? */
if (off + udf_dir_entry_len(fi) > blksize) {
fetch_next:
err = udf_fiiter_advance_blk(iter);
if (err)
goto out_brelse;
iter->bh[1] = udf_fiiter_bread_blk(iter);
if (!iter->bh[1]) {
err = -ENOMEM;
goto out_brelse;
}
if (!buffer_uptodate(iter->bh[1])) {
err = -EIO;
goto out_brelse;
}
}
return 0;
out_brelse:
brelse(iter->bh[0]);
brelse(iter->bh[1]);
iter->bh[0] = iter->bh[1] = NULL;
return err;
}
int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
loff_t pos)
{
struct udf_inode_info *iinfo = UDF_I(dir);
int err = 0;
iter->dir = dir;
iter->bh[0] = iter->bh[1] = NULL;
iter->pos = pos;
iter->elen = 0;
iter->epos.bh = NULL;
iter->name = NULL;
/*
* When directory is verified, we don't expect directory iteration to
* fail and it can be difficult to undo without corrupting filesystem.
* So just do not allow memory allocation failures here.
*/
iter->namebuf = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL | __GFP_NOFAIL);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
err = udf_copy_fi(iter);
goto out;
}
ptr = buffer;
if ((*offset > 0) && (*offset < bufsize))
ptr += *offset;
fi = (struct fileIdentDesc *)ptr;
if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
udf_debug("0x%x != TAG_IDENT_FID\n",
le16_to_cpu(fi->descTag.tagIdent));
udf_debug("offset: %d sizeof: %lu bufsize: %d\n",
*offset, (unsigned long)sizeof(struct fileIdentDesc),
bufsize);
return NULL;
if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
&iter->eloc, &iter->elen, &iter->loffset) !=
(EXT_RECORDED_ALLOCATED >> 30)) {
if (pos == dir->i_size)
return 0;
udf_err(dir->i_sb,
"position %llu not allocated in directory (ino %lu)\n",
(unsigned long long)pos, dir->i_ino);
err = -EFSCORRUPTED;
goto out;
}
if ((*offset + sizeof(struct fileIdentDesc)) > bufsize)
lengthThisIdent = sizeof(struct fileIdentDesc);
else
lengthThisIdent = sizeof(struct fileIdentDesc) +
fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
err = udf_fiiter_load_bhs(iter);
if (err < 0)
goto out;
err = udf_copy_fi(iter);
out:
if (err < 0)
udf_fiiter_release(iter);
return err;
}
/* we need to figure padding, too! */
padlen = lengthThisIdent % UDF_NAME_PAD;
if (padlen)
lengthThisIdent += (UDF_NAME_PAD - padlen);
*offset = *offset + lengthThisIdent;
int udf_fiiter_advance(struct udf_fileident_iter *iter)
{
unsigned int oldoff, len;
int blksize = 1 << iter->dir->i_blkbits;
int err;
return fi;
oldoff = iter->pos & (blksize - 1);
len = udf_dir_entry_len(&iter->fi);
iter->pos += len;
if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
if (oldoff + len >= blksize) {
brelse(iter->bh[0]);
iter->bh[0] = NULL;
/* Next block already loaded? */
if (iter->bh[1]) {
iter->bh[0] = iter->bh[1];
iter->bh[1] = NULL;
} else {
err = udf_fiiter_advance_blk(iter);
if (err < 0)
return err;
}
}
err = udf_fiiter_load_bhs(iter);
if (err < 0)
return err;
}
return udf_copy_fi(iter);
}
void udf_fiiter_release(struct udf_fileident_iter *iter)
{
iter->dir = NULL;
brelse(iter->bh[0]);
brelse(iter->bh[1]);
iter->bh[0] = iter->bh[1] = NULL;
kfree(iter->namebuf);
iter->namebuf = NULL;
}
static void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2,
int off, void *src, int len)
{
int copy;
if (off >= len1) {
off -= len1;
} else {
copy = min(off + len, len1) - off;
memcpy(buf1 + off, src, copy);
src += copy;
len -= copy;
off = 0;
}
if (len > 0) {
if (WARN_ON_ONCE(off + len > len2 || !buf2))
return;
memcpy(buf2 + off, src, len);
}
}
static uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2,
int off, int len)
{
int copy;
uint16_t crc = 0;
if (off >= len1) {
off -= len1;
} else {
copy = min(off + len, len1) - off;
crc = crc_itu_t(crc, buf1 + off, copy);
len -= copy;
off = 0;
}
if (len > 0) {
if (WARN_ON_ONCE(off + len > len2 || !buf2))
return 0;
crc = crc_itu_t(crc, buf2 + off, len);
}
return crc;
}
static void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2,
int off, struct fileIdentDesc *fi,
uint8_t *impuse, uint8_t *name)
{
uint16_t crc;
int fioff = off;
int crcoff = off + sizeof(struct tag);
unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag);
char zeros[UDF_NAME_PAD] = {};
int endoff = off + udf_dir_entry_len(fi);
udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi,
sizeof(struct fileIdentDesc));
off += sizeof(struct fileIdentDesc);
if (impuse)
udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse,
le16_to_cpu(fi->lengthOfImpUse));
off += le16_to_cpu(fi->lengthOfImpUse);
if (name) {
udf_copy_to_bufs(buf1, len1, buf2, len2, off, name,
fi->lengthFileIdent);
off += fi->lengthFileIdent;
udf_copy_to_bufs(buf1, len1, buf2, len2, off, zeros,
endoff - off);
}
crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen);
fi->descTag.descCRC = cpu_to_le16(crc);
fi->descTag.descCRCLength = cpu_to_le16(crclen);
fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag);
udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag));
}
void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
void *buf1, *buf2 = NULL;
int len1, len2 = 0, off;
int blksize = 1 << iter->dir->i_blkbits;
off = iter->pos & (blksize - 1);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
buf1 = iinfo->i_data + iinfo->i_lenEAttr;
len1 = iter->dir->i_size;
} else {
buf1 = iter->bh[0]->b_data;
len1 = blksize;
if (iter->bh[1]) {
buf2 = iter->bh[1]->b_data;
len2 = blksize;
}
}
udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse,
iter->name == iter->namebuf ? iter->name : NULL);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
mark_inode_dirty(iter->dir);
} else {
mark_buffer_dirty_inode(iter->bh[0], iter->dir);
if (iter->bh[1])
mark_buffer_dirty_inode(iter->bh[1], iter->dir);
}
inode_inc_iversion(iter->dir);
}
void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
int diff = new_elen - iter->elen;
/* Skip update when we already went past the last extent */
if (!iter->elen)
return;
iter->elen = new_elen;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
iter->epos.offset -= sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
iter->epos.offset -= sizeof(struct long_ad);
udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);
iinfo->i_lenExtents += diff;
mark_inode_dirty(iter->dir);
}
/* Append new block to directory. @iter is expected to point at EOF */
int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
int blksize = 1 << iter->dir->i_blkbits;
struct buffer_head *bh;
sector_t block;
uint32_t old_elen = iter->elen;
int err;
if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
return -EINVAL;
/* Round up last extent in the file */
udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));
/* Allocate new block and refresh mapping information */
block = iinfo->i_lenExtents >> iter->dir->i_blkbits;
bh = udf_bread(iter->dir, block, 1, &err);
if (!bh) {
udf_fiiter_update_elen(iter, old_elen);
return err;
}
if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
&iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
udf_err(iter->dir->i_sb,
"block %llu not allocated in directory (ino %lu)\n",
(unsigned long long)block, iter->dir->i_ino);
return -EFSCORRUPTED;
}
if (!(iter->pos & (blksize - 1))) {
brelse(iter->bh[0]);
iter->bh[0] = bh;
} else {
iter->bh[1] = bh;
}
return 0;
}
struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,

View File

@ -38,100 +38,55 @@
#include "udf_i.h"
#include "udf_sb.h"
static void __udf_adinicb_readpage(struct page *page)
static vm_fault_t udf_page_mkwrite(struct vm_fault *vmf)
{
struct inode *inode = page->mapping->host;
char *kaddr;
struct udf_inode_info *iinfo = UDF_I(inode);
loff_t isize = i_size_read(inode);
struct vm_area_struct *vma = vmf->vma;
struct inode *inode = file_inode(vma->vm_file);
struct address_space *mapping = inode->i_mapping;
struct page *page = vmf->page;
loff_t size;
unsigned int end;
vm_fault_t ret = VM_FAULT_LOCKED;
int err;
/*
* We have to be careful here as truncate can change i_size under us.
* So just sample it once and use the same value everywhere.
*/
kaddr = kmap_atomic(page);
memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr, isize);
memset(kaddr + isize, 0, PAGE_SIZE - isize);
flush_dcache_page(page);
SetPageUptodate(page);
kunmap_atomic(kaddr);
}
static int udf_adinicb_read_folio(struct file *file, struct folio *folio)
{
BUG_ON(!folio_test_locked(folio));
__udf_adinicb_readpage(&folio->page);
folio_unlock(folio);
return 0;
}
static int udf_adinicb_writepage(struct page *page,
struct writeback_control *wbc)
{
struct inode *inode = page->mapping->host;
char *kaddr;
struct udf_inode_info *iinfo = UDF_I(inode);
BUG_ON(!PageLocked(page));
kaddr = kmap_atomic(page);
memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, i_size_read(inode));
SetPageUptodate(page);
kunmap_atomic(kaddr);
mark_inode_dirty(inode);
unlock_page(page);
return 0;
}
static int udf_adinicb_write_begin(struct file *file,
struct address_space *mapping, loff_t pos,
unsigned len, struct page **pagep,
void **fsdata)
{
struct page *page;
if (WARN_ON_ONCE(pos >= PAGE_SIZE))
return -EIO;
page = grab_cache_page_write_begin(mapping, 0);
if (!page)
return -ENOMEM;
*pagep = page;
if (!PageUptodate(page))
__udf_adinicb_readpage(page);
return 0;
}
static ssize_t udf_adinicb_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
/* Fallback to buffered I/O. */
return 0;
}
static int udf_adinicb_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
struct inode *inode = page->mapping->host;
loff_t last_pos = pos + copied;
if (last_pos > inode->i_size)
i_size_write(inode, last_pos);
sb_start_pagefault(inode->i_sb);
file_update_time(vma->vm_file);
filemap_invalidate_lock_shared(mapping);
lock_page(page);
size = i_size_read(inode);
if (page->mapping != inode->i_mapping || page_offset(page) >= size) {
unlock_page(page);
ret = VM_FAULT_NOPAGE;
goto out_unlock;
}
/* Space is already allocated for in-ICB file */
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
goto out_dirty;
if (page->index == size >> PAGE_SHIFT)
end = size & ~PAGE_MASK;
else
end = PAGE_SIZE;
err = __block_write_begin(page, 0, end, udf_get_block);
if (!err)
err = block_commit_write(page, 0, end);
if (err < 0) {
unlock_page(page);
ret = block_page_mkwrite_return(err);
goto out_unlock;
}
out_dirty:
set_page_dirty(page);
unlock_page(page);
put_page(page);
return copied;
wait_for_stable_page(page);
out_unlock:
filemap_invalidate_unlock_shared(mapping);
sb_end_pagefault(inode->i_sb);
return ret;
}
const struct address_space_operations udf_adinicb_aops = {
.dirty_folio = block_dirty_folio,
.invalidate_folio = block_invalidate_folio,
.read_folio = udf_adinicb_read_folio,
.writepage = udf_adinicb_writepage,
.write_begin = udf_adinicb_write_begin,
.write_end = udf_adinicb_write_end,
.direct_IO = udf_adinicb_direct_IO,
static const struct vm_operations_struct udf_file_vm_ops = {
.fault = filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = udf_page_mkwrite,
};
static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
@ -140,7 +95,6 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct udf_inode_info *iinfo = UDF_I(inode);
int err;
inode_lock(inode);
@ -148,27 +102,23 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (retval <= 0)
goto out;
down_write(&iinfo->i_data_sem);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
loff_t end = iocb->ki_pos + iov_iter_count(from);
if (inode->i_sb->s_blocksize <
(udf_file_entry_alloc_offset(inode) + end)) {
err = udf_expand_file_adinicb(inode);
if (err) {
inode_unlock(inode);
udf_debug("udf_expand_adinicb: err=%d\n", err);
return err;
}
} else {
iinfo->i_lenAlloc = max(end, inode->i_size);
up_write(&iinfo->i_data_sem);
}
} else
up_write(&iinfo->i_data_sem);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
iocb->ki_pos + iov_iter_count(from))) {
filemap_invalidate_lock(inode->i_mapping);
retval = udf_expand_file_adinicb(inode);
filemap_invalidate_unlock(inode->i_mapping);
if (retval)
goto out;
}
retval = __generic_file_write_iter(iocb, from);
out:
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && retval > 0) {
down_write(&iinfo->i_data_sem);
iinfo->i_lenAlloc = inode->i_size;
up_write(&iinfo->i_data_sem);
}
inode_unlock(inode);
if (retval > 0) {
@ -243,11 +193,19 @@ static int udf_release_file(struct inode *inode, struct file *filp)
return 0;
}
static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)
{
file_accessed(file);
vma->vm_ops = &udf_file_vm_ops;
return 0;
}
const struct file_operations udf_file_operations = {
.read_iter = generic_file_read_iter,
.unlocked_ioctl = udf_ioctl,
.open = generic_file_open,
.mmap = generic_file_mmap,
.mmap = udf_file_mmap,
.write_iter = udf_file_write_iter,
.release = udf_release_file,
.fsync = generic_file_fsync,

View File

@ -28,21 +28,7 @@
void udf_free_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct udf_sb_info *sbi = UDF_SB(sb);
struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
if (lvidiu) {
mutex_lock(&sbi->s_alloc_mutex);
if (S_ISDIR(inode->i_mode))
le32_add_cpu(&lvidiu->numDirs, -1);
else
le32_add_cpu(&lvidiu->numFiles, -1);
udf_updated_lvid(sb);
mutex_unlock(&sbi->s_alloc_mutex);
}
udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1);
udf_free_blocks(inode->i_sb, NULL, &UDF_I(inode)->i_location, 0, 1);
}
struct inode *udf_new_inode(struct inode *dir, umode_t mode)
@ -54,7 +40,6 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode)
uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
struct udf_inode_info *iinfo;
struct udf_inode_info *dinfo = UDF_I(dir);
struct logicalVolIntegrityDescImpUse *lvidiu;
int err;
inode = new_inode(sb);
@ -92,18 +77,8 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode)
return ERR_PTR(err);
}
lvidiu = udf_sb_lvidiu(sb);
if (lvidiu) {
iinfo->i_unique = lvid_get_unique_id(sb);
inode->i_generation = iinfo->i_unique;
mutex_lock(&sbi->s_alloc_mutex);
if (S_ISDIR(mode))
le32_add_cpu(&lvidiu->numDirs, 1);
else
le32_add_cpu(&lvidiu->numFiles, 1);
udf_updated_lvid(sb);
mutex_unlock(&sbi->s_alloc_mutex);
}
iinfo->i_unique = lvid_get_unique_id(sb);
inode->i_generation = iinfo->i_unique;
inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET))

File diff suppressed because it is too large Load Diff

View File

@ -45,7 +45,7 @@ unsigned int udf_get_last_session(struct super_block *sb)
return 0;
}
unsigned long udf_get_last_block(struct super_block *sb)
udf_pblk_t udf_get_last_block(struct super_block *sb)
{
struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
unsigned long lblock = 0;
@ -54,8 +54,11 @@ unsigned long udf_get_last_block(struct super_block *sb)
* The cdrom layer call failed or returned obviously bogus value?
* Try using the device size...
*/
if (!cdi || cdrom_get_last_written(cdi, &lblock) || lblock == 0)
if (!cdi || cdrom_get_last_written(cdi, &lblock) || lblock == 0) {
if (sb_bdev_nr_blocks(sb) > ~(udf_pblk_t)0)
return 0;
lblock = sb_bdev_nr_blocks(sb);
}
if (lblock)
return lblock - 1;

View File

@ -28,22 +28,6 @@
#include "udf_i.h"
#include "udf_sb.h"
struct buffer_head *udf_tgetblk(struct super_block *sb, udf_pblk_t block)
{
if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
return sb_getblk(sb, udf_fixed_to_variable(block));
else
return sb_getblk(sb, block);
}
struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block)
{
if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
return sb_bread(sb, udf_fixed_to_variable(block));
else
return sb_bread(sb, block);
}
struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size,
uint32_t type, uint8_t loc)
{
@ -216,7 +200,7 @@ struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block,
if (block == 0xFFFFFFFF)
return NULL;
bh = udf_tread(sb, block);
bh = sb_bread(sb, block);
if (!bh) {
udf_err(sb, "read failed, block=%u, location=%u\n",
block, location);

File diff suppressed because it is too large Load Diff

View File

@ -54,6 +54,7 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block,
struct udf_part_map *map;
struct udf_virtual_data *vdata;
struct udf_inode_info *iinfo = UDF_I(sbi->s_vat_inode);
int err;
map = &sbi->s_partmaps[partition];
vdata = &map->s_type_specific.s_virtual;
@ -79,12 +80,10 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block,
index = vdata->s_start_offset / sizeof(uint32_t) + block;
}
loc = udf_block_map(sbi->s_vat_inode, newblock);
bh = sb_bread(sb, loc);
bh = udf_bread(sbi->s_vat_inode, newblock, 0, &err);
if (!bh) {
udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%u,%u) VAT: %u[%u]\n",
sb, block, partition, loc, index);
udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%u,%u)\n",
sb, block, partition);
return 0xFFFFFFFF;
}

View File

@ -86,6 +86,13 @@ enum {
#define UDF_MAX_LVID_NESTING 1000
enum { UDF_MAX_LINKS = 0xffff };
/*
* We limit filesize to 4TB. This is arbitrary as the on-disk format supports
* more but because the file space is described by a linked list of extents,
* each of which can have at most 1GB, the creation and handling of extents
* gets unusably slow beyond certain point...
*/
#define UDF_MAX_FILESIZE (1ULL << 42)
/* These are the "meat" - everything else is stuffing */
static int udf_fill_super(struct super_block *, void *, int);
@ -147,6 +154,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
ei->i_next_alloc_goal = 0;
ei->i_strat4096 = 0;
ei->i_streamdir = 0;
ei->i_hidden = 0;
init_rwsem(&ei->i_data_sem);
ei->cached_extent.lstart = -1;
spin_lock_init(&ei->i_extent_cache_lock);
@ -733,7 +741,7 @@ static int udf_check_vsd(struct super_block *sb)
* added */
for (; !nsr && sector < VSD_MAX_SECTOR_OFFSET; sector += sectorsize) {
/* Read a block */
bh = udf_tread(sb, sector >> sb->s_blocksize_bits);
bh = sb_bread(sb, sector >> sb->s_blocksize_bits);
if (!bh)
break;
@ -1175,7 +1183,6 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
struct udf_part_map *map = &sbi->s_partmaps[p_index];
struct buffer_head *bh = NULL;
struct udf_inode_info *vati;
uint32_t pos;
struct virtualAllocationTable20 *vat20;
sector_t blocks = sb_bdev_nr_blocks(sb);
@ -1197,10 +1204,14 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
} else if (map->s_partition_type == UDF_VIRTUAL_MAP20) {
vati = UDF_I(sbi->s_vat_inode);
if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
pos = udf_block_map(sbi->s_vat_inode, 0);
bh = sb_bread(sb, pos);
if (!bh)
return -EIO;
int err = 0;
bh = udf_bread(sbi->s_vat_inode, 0, 0, &err);
if (!bh) {
if (!err)
err = -EFSCORRUPTED;
return err;
}
vat20 = (struct virtualAllocationTable20 *)bh->b_data;
} else {
vat20 = (struct virtualAllocationTable20 *)
@ -1838,10 +1849,6 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block,
uint16_t ident;
int ret;
if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV) &&
udf_fixed_to_variable(block) >= sb_bdev_nr_blocks(sb))
return -EAGAIN;
bh = udf_read_tagged(sb, block, block, &ident);
if (!bh)
return -EAGAIN;
@ -1860,10 +1867,10 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block,
* Returns < 0 on error, 0 on success. -EAGAIN is special - try next set
* of anchors.
*/
static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock,
static int udf_scan_anchors(struct super_block *sb, udf_pblk_t *lastblock,
struct kernel_lb_addr *fileset)
{
sector_t last[6];
udf_pblk_t last[6];
int i;
struct udf_sb_info *sbi = UDF_SB(sb);
int last_count = 0;
@ -1923,46 +1930,6 @@ static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock,
return udf_check_anchor_block(sb, sbi->s_session + 512, fileset);
}
/*
* Find an anchor volume descriptor and load Volume Descriptor Sequence from
* area specified by it. The function expects sbi->s_lastblock to be the last
* block on the media.
*
* Return <0 on error, 0 if anchor found. -EAGAIN is special meaning anchor
* was not found.
*/
static int udf_find_anchor(struct super_block *sb,
struct kernel_lb_addr *fileset)
{
struct udf_sb_info *sbi = UDF_SB(sb);
sector_t lastblock = sbi->s_last_block;
int ret;
ret = udf_scan_anchors(sb, &lastblock, fileset);
if (ret != -EAGAIN)
goto out;
/* No anchor found? Try VARCONV conversion of block numbers */
UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
lastblock = udf_variable_to_fixed(sbi->s_last_block);
/* Firstly, we try to not convert number of the last block */
ret = udf_scan_anchors(sb, &lastblock, fileset);
if (ret != -EAGAIN)
goto out;
lastblock = sbi->s_last_block;
/* Secondly, we try with converted number of the last block */
ret = udf_scan_anchors(sb, &lastblock, fileset);
if (ret < 0) {
/* VARCONV didn't help. Clear it. */
UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV);
}
out:
if (ret == 0)
sbi->s_last_block = lastblock;
return ret;
}
/*
* Check Volume Structure Descriptor, find Anchor block and load Volume
* Descriptor Sequence.
@ -2003,7 +1970,7 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
/* Look for anchor block and load Volume Descriptor Sequence */
sbi->s_anchor = uopt->anchor;
ret = udf_find_anchor(sb, fileset);
ret = udf_scan_anchors(sb, &sbi->s_last_block, fileset);
if (ret < 0) {
if (!silent && ret == -EAGAIN)
udf_warn(sb, "No anchor found\n");
@ -2297,7 +2264,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
ret = -ENOMEM;
goto error_out;
}
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_maxbytes = UDF_MAX_FILESIZE;
sb->s_max_links = UDF_MAX_LINKS;
return 0;
@ -2454,7 +2421,7 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb,
if (bytes) {
brelse(bh);
newblock = udf_get_lb_pblock(sb, &loc, ++block);
bh = udf_tread(sb, newblock);
bh = sb_bread(sb, newblock);
if (!bh) {
udf_debug("read failed\n");
goto out;

View File

@ -107,48 +107,40 @@ static int udf_symlink_filler(struct file *file, struct folio *folio)
struct inode *inode = page->mapping->host;
struct buffer_head *bh = NULL;
unsigned char *symlink;
int err;
int err = 0;
unsigned char *p = page_address(page);
struct udf_inode_info *iinfo;
uint32_t pos;
struct udf_inode_info *iinfo = UDF_I(inode);
/* We don't support symlinks longer than one block */
if (inode->i_size > inode->i_sb->s_blocksize) {
err = -ENAMETOOLONG;
goto out_unmap;
goto out_unlock;
}
iinfo = UDF_I(inode);
pos = udf_block_map(inode, 0);
down_read(&iinfo->i_data_sem);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
symlink = iinfo->i_data + iinfo->i_lenEAttr;
} else {
bh = sb_bread(inode->i_sb, pos);
bh = udf_bread(inode, 0, 0, &err);
if (!bh) {
err = -EIO;
goto out_unlock_inode;
if (!err)
err = -EFSCORRUPTED;
goto out_err;
}
symlink = bh->b_data;
}
err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
brelse(bh);
if (err)
goto out_unlock_inode;
goto out_err;
up_read(&iinfo->i_data_sem);
SetPageUptodate(page);
unlock_page(page);
return 0;
out_unlock_inode:
up_read(&iinfo->i_data_sem);
out_err:
SetPageError(page);
out_unmap:
out_unlock:
unlock_page(page);
return err;
}

View File

@ -125,7 +125,7 @@ void udf_discard_prealloc(struct inode *inode)
struct kernel_lb_addr eloc;
uint32_t elen;
uint64_t lbcount = 0;
int8_t etype = -1, netype;
int8_t etype = -1;
struct udf_inode_info *iinfo = UDF_I(inode);
int bsize = 1 << inode->i_blkbits;
@ -136,7 +136,7 @@ void udf_discard_prealloc(struct inode *inode)
epos.block = iinfo->i_location;
/* Find the last extent in the file */
while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 0)) != -1) {
while (udf_next_aext(inode, &epos, &eloc, &elen, 0) != -1) {
brelse(prev_epos.bh);
prev_epos = epos;
if (prev_epos.bh)
@ -240,7 +240,7 @@ int udf_truncate_extents(struct inode *inode)
brelse(epos.bh);
epos.offset = sizeof(struct allocExtDesc);
epos.block = eloc;
epos.bh = udf_tread(sb,
epos.bh = sb_bread(sb,
udf_get_lb_pblock(sb, &eloc, 0));
/* Error reading indirect block? */
if (!epos.bh)

View File

@ -44,7 +44,8 @@ struct udf_inode_info {
unsigned i_use : 1; /* unallocSpaceEntry */
unsigned i_strat4096 : 1;
unsigned i_streamdir : 1;
unsigned reserved : 25;
unsigned i_hidden : 1; /* hidden system inode */
unsigned reserved : 24;
__u8 *i_data;
struct kernel_lb_addr i_locStreamdir;
__u64 i_lenStreams;

View File

@ -23,7 +23,6 @@
#define UDF_FLAG_STRICT 5
#define UDF_FLAG_UNDELETE 6
#define UDF_FLAG_UNHIDE 7
#define UDF_FLAG_VARCONV 8
#define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */
#define UDF_FLAG_GID_FORGET 12
#define UDF_FLAG_UID_SET 13
@ -55,6 +54,8 @@
#define MF_DUPLICATE_MD 0x01
#define MF_MIRROR_FE_LOADED 0x02
#define EFSCORRUPTED EUCLEAN
struct udf_meta_data {
__u32 s_meta_file_loc;
__u32 s_mirror_file_loc;

View File

@ -34,9 +34,6 @@ extern __printf(3, 4) void _udf_warn(struct super_block *sb,
#define udf_debug(fmt, ...) \
pr_debug("%s:%d:%s: " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) )
#define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) )
#define UDF_EXTENT_LENGTH_MASK 0x3FFFFFFF
#define UDF_EXTENT_FLAG_MASK 0xC0000000
@ -83,14 +80,24 @@ extern const struct inode_operations udf_file_inode_operations;
extern const struct file_operations udf_file_operations;
extern const struct inode_operations udf_symlink_inode_operations;
extern const struct address_space_operations udf_aops;
extern const struct address_space_operations udf_adinicb_aops;
extern const struct address_space_operations udf_symlink_aops;
struct udf_fileident_bh {
struct buffer_head *sbh;
struct buffer_head *ebh;
int soffset;
int eoffset;
struct udf_fileident_iter {
struct inode *dir; /* Directory we are working with */
loff_t pos; /* Logical position in a dir */
struct buffer_head *bh[2]; /* Buffer containing 'pos' and possibly
* next buffer if entry straddles
* blocks */
struct kernel_lb_addr eloc; /* Start of extent containing 'pos' */
uint32_t elen; /* Length of extent containing 'pos' */
sector_t loffset; /* Block offset of 'pos' within above
* extent */
struct extent_position epos; /* Position after the above extent */
struct fileIdentDesc fi; /* Copied directory entry */
uint8_t *name; /* Pointer to entry name */
uint8_t *namebuf; /* Storage for entry name in case
* the name is split between two blocks
*/
};
struct udf_vds_record {
@ -121,22 +128,16 @@ struct inode *udf_find_metadata_inode_efe(struct super_block *sb,
u32 meta_file_loc, u32 partition_num);
/* namei.c */
extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *,
struct fileIdentDesc *, struct udf_fileident_bh *,
uint8_t *, uint8_t *);
static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
{
return ALIGN(sizeof(struct fileIdentDesc) +
le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent,
UDF_NAME_PAD);
}
static inline uint8_t *udf_get_fi_ident(struct fileIdentDesc *fi)
{
return ((uint8_t *)(fi + 1)) + le16_to_cpu(fi->lengthOfImpUse);
}
/* file.c */
extern long udf_ioctl(struct file *, unsigned int, unsigned long);
/* inode.c */
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
bool hidden_inode);
@ -151,16 +152,14 @@ static inline struct inode *udf_iget(struct super_block *sb,
return __udf_iget(sb, ino, false);
}
extern int udf_expand_file_adinicb(struct inode *);
extern struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
udf_pblk_t *block, int *err);
extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
int create, int *err);
extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
extern udf_pblk_t udf_block_map(struct inode *inode, sector_t block);
extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
struct kernel_lb_addr *, uint32_t *, sector_t *);
int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
struct extent_position *epos);
extern int __udf_add_aext(struct inode *inode, struct extent_position *epos,
@ -177,9 +176,6 @@ extern int8_t udf_current_aext(struct inode *, struct extent_position *,
extern void udf_update_extra_perms(struct inode *inode, umode_t mode);
/* misc.c */
extern struct buffer_head *udf_tgetblk(struct super_block *sb,
udf_pblk_t block);
extern struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block);
extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t,
uint32_t, uint8_t);
extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t,
@ -194,7 +190,7 @@ extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int);
/* lowlevel.c */
extern unsigned int udf_get_last_session(struct super_block *);
extern unsigned long udf_get_last_block(struct super_block *);
udf_pblk_t udf_get_last_block(struct super_block *);
/* partition.c */
extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t,
@ -243,14 +239,13 @@ extern udf_pblk_t udf_new_block(struct super_block *sb, struct inode *inode,
uint16_t partition, uint32_t goal, int *err);
/* directory.c */
extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *,
struct udf_fileident_bh *,
struct fileIdentDesc *,
struct extent_position *,
struct kernel_lb_addr *, uint32_t *,
sector_t *);
extern struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize,
int *offset);
int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
loff_t pos);
int udf_fiiter_advance(struct udf_fileident_iter *iter);
void udf_fiiter_release(struct udf_fileident_iter *iter);
void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse);
void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen);
int udf_fiiter_append_blk(struct udf_fileident_iter *iter);
extern struct long_ad *udf_get_filelongad(uint8_t *, int, uint32_t *, int);
extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int);