\n
-----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:
commit
274978f173
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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)) {
|
||||
|
@ -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,
|
||||
|
148
fs/udf/dir.c
148
fs/udf/dir.c
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
176
fs/udf/file.c
176
fs/udf/file.c
@ -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,
|
||||
|
@ -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))
|
||||
|
602
fs/udf/inode.c
602
fs/udf/inode.c
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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);
|
||||
|
1089
fs/udf/namei.c
1089
fs/udf/namei.c
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user