Commit 274978f1 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents cd776a43 df97f64d
...@@ -461,9 +461,9 @@ static int ext2_handle_dirsync(struct inode *dir) ...@@ -461,9 +461,9 @@ static int ext2_handle_dirsync(struct inode *dir)
return err; return err;
} }
void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
struct page *page, void *page_addr, struct inode *inode, struct page *page, void *page_addr, struct inode *inode,
int update_times) bool update_times)
{ {
loff_t pos = page_offset(page) + loff_t pos = page_offset(page) +
(char *) de - (char *) page_addr; (char *) de - (char *) page_addr;
...@@ -472,7 +472,10 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, ...@@ -472,7 +472,10 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
lock_page(page); lock_page(page);
err = ext2_prepare_chunk(page, pos, len); 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); de->inode = cpu_to_le32(inode->i_ino);
ext2_set_de_type(de, inode); ext2_set_de_type(de, inode);
ext2_commit_chunk(page, pos, len); ext2_commit_chunk(page, pos, len);
...@@ -480,7 +483,7 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, ...@@ -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); dir->i_mtime = dir->i_ctime = current_time(dir);
EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir); 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) ...@@ -646,7 +649,7 @@ int ext2_make_empty(struct inode *inode, struct inode *parent)
unlock_page(page); unlock_page(page);
goto fail; goto fail;
} }
kaddr = kmap_atomic(page); kaddr = kmap_local_page(page);
memset(kaddr, 0, chunk_size); memset(kaddr, 0, chunk_size);
de = (struct ext2_dir_entry_2 *)kaddr; de = (struct ext2_dir_entry_2 *)kaddr;
de->name_len = 1; de->name_len = 1;
...@@ -661,7 +664,7 @@ int ext2_make_empty(struct inode *inode, struct inode *parent) ...@@ -661,7 +664,7 @@ int ext2_make_empty(struct inode *inode, struct inode *parent)
de->inode = cpu_to_le32(parent->i_ino); de->inode = cpu_to_le32(parent->i_ino);
memcpy (de->name, "..\0", 4); memcpy (de->name, "..\0", 4);
ext2_set_de_type (de, inode); ext2_set_de_type (de, inode);
kunmap_atomic(kaddr); kunmap_local(kaddr);
ext2_commit_chunk(page, 0, chunk_size); ext2_commit_chunk(page, 0, chunk_size);
err = ext2_handle_dirsync(inode); err = ext2_handle_dirsync(inode);
fail: fail:
......
...@@ -734,8 +734,9 @@ extern int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page, ...@@ -734,8 +734,9 @@ extern int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page,
char *kaddr); char *kaddr);
extern int ext2_empty_dir (struct inode *); extern int ext2_empty_dir (struct inode *);
extern struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p, void **pa); 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 *, int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
struct inode *, int); struct page *page, void *page_addr, struct inode *inode,
bool update_times);
static inline void ext2_put_page(struct page *page, void *page_addr) static inline void ext2_put_page(struct page *page, void *page_addr)
{ {
kunmap_local(page_addr); kunmap_local(page_addr);
......
...@@ -370,8 +370,11 @@ static int ext2_rename (struct mnt_idmap * idmap, ...@@ -370,8 +370,11 @@ static int ext2_rename (struct mnt_idmap * idmap,
err = PTR_ERR(new_de); err = PTR_ERR(new_de);
goto out_dir; 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); ext2_put_page(new_page, page_addr);
if (err)
goto out_dir;
new_inode->i_ctime = current_time(new_inode); new_inode->i_ctime = current_time(new_inode);
if (dir_de) if (dir_de)
drop_nlink(new_inode); drop_nlink(new_inode);
...@@ -394,24 +397,24 @@ static int ext2_rename (struct mnt_idmap * idmap, ...@@ -394,24 +397,24 @@ static int ext2_rename (struct mnt_idmap * idmap,
ext2_delete_entry(old_de, old_page, old_page_addr); ext2_delete_entry(old_de, old_page, old_page_addr);
if (dir_de) { if (dir_de) {
if (old_dir != new_dir) if (old_dir != new_dir) {
ext2_set_link(old_inode, dir_de, dir_page, err = ext2_set_link(old_inode, dir_de, dir_page,
dir_page_addr, new_dir, 0); dir_page_addr, new_dir, false);
}
ext2_put_page(dir_page, dir_page_addr); ext2_put_page(dir_page, dir_page_addr);
inode_dec_link_count(old_dir); inode_dec_link_count(old_dir);
} }
out_old:
ext2_put_page(old_page, old_page_addr); ext2_put_page(old_page, old_page_addr);
return 0; out:
return err;
out_dir: out_dir:
if (dir_de) if (dir_de)
ext2_put_page(dir_page, dir_page_addr); ext2_put_page(dir_page, dir_page_addr);
out_old: goto out_old;
ext2_put_page(old_page, old_page_addr);
out:
return err;
} }
const struct inode_operations ext2_dir_inode_operations = { const struct inode_operations ext2_dir_inode_operations = {
......
...@@ -532,6 +532,8 @@ static int __mpage_writepage(struct page *page, struct writeback_control *wbc, ...@@ -532,6 +532,8 @@ static int __mpage_writepage(struct page *page, struct writeback_control *wbc,
map_bh.b_size = 1 << blkbits; map_bh.b_size = 1 << blkbits;
if (mpd->get_block(inode, block_in_file, &map_bh, 1)) if (mpd->get_block(inode, block_in_file, &map_bh, 1))
goto confused; goto confused;
if (!buffer_mapped(&map_bh))
goto confused;
if (buffer_new(&map_bh)) if (buffer_new(&map_bh))
clean_bdev_bh_alias(&map_bh); clean_bdev_bh_alias(&map_bh);
if (buffer_boundary(&map_bh)) { if (buffer_boundary(&map_bh)) {
......
...@@ -36,18 +36,41 @@ static int read_block_bitmap(struct super_block *sb, ...@@ -36,18 +36,41 @@ static int read_block_bitmap(struct super_block *sb,
unsigned long bitmap_nr) unsigned long bitmap_nr)
{ {
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
int retval = 0; int i;
int max_bits, off, count;
struct kernel_lb_addr loc; struct kernel_lb_addr loc;
loc.logicalBlockNum = bitmap->s_extPosition; loc.logicalBlockNum = bitmap->s_extPosition;
loc.partitionReferenceNum = UDF_SB(sb)->s_partition; loc.partitionReferenceNum = UDF_SB(sb)->s_partition;
bh = udf_tread(sb, udf_get_lb_pblock(sb, &loc, block)); bh = sb_bread(sb, udf_get_lb_pblock(sb, &loc, block));
bitmap->s_block_bitmap[bitmap_nr] = bh;
if (!bh) if (!bh)
retval = -EIO; return -EIO;
bitmap->s_block_bitmap[bitmap_nr] = bh; /* Check consistency of Space Bitmap buffer. */
return retval; 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, static int __load_block_bitmap(struct super_block *sb,
......
...@@ -39,26 +39,13 @@ ...@@ -39,26 +39,13 @@
static int udf_readdir(struct file *file, struct dir_context *ctx) static int udf_readdir(struct file *file, struct dir_context *ctx)
{ {
struct inode *dir = file_inode(file); 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; loff_t nf_pos, emit_pos = 0;
int flen; int flen;
unsigned char *fname = NULL, *copy_name = NULL; unsigned char *fname = NULL;
unsigned char *nameptr; int ret = 0;
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} };
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
bool pos_valid = false; bool pos_valid = false;
struct udf_fileident_iter iter;
if (ctx->pos == 0) { if (ctx->pos == 0) {
if (!dir_emit_dot(file, ctx)) if (!dir_emit_dot(file, ctx))
...@@ -66,7 +53,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) ...@@ -66,7 +53,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
ctx->pos = 1; ctx->pos = 1;
} }
nf_pos = (ctx->pos - 1) << 2; nf_pos = (ctx->pos - 1) << 2;
if (nf_pos >= size) if (nf_pos >= dir->i_size)
goto out; goto out;
/* /*
...@@ -90,138 +77,57 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) ...@@ -90,138 +77,57 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
goto out; goto out;
} }
if (nf_pos == 0) for (ret = udf_fiiter_init(&iter, dir, nf_pos);
nf_pos = udf_ext0_offset(dir); !ret && iter.pos < dir->i_size;
ret = udf_fiiter_advance(&iter)) {
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) {
struct kernel_lb_addr tloc; 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? */ /* Still not at offset where user asked us to read from? */
if (cur_pos < emit_pos) if (iter.pos < emit_pos)
continue; continue;
liu = le16_to_cpu(cfi.lengthOfImpUse); /* Update file position only if we got past the current one */
lfi = cfi.lengthFileIdent; 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)) if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
continue; 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)) if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
continue; continue;
} }
if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) { if (iter.fi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
if (!dir_emit_dotdot(file, ctx)) if (!dir_emit_dotdot(file, ctx))
goto out; goto out_iter;
continue; 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) if (flen < 0)
continue; continue;
tloc = lelb_to_cpu(cfi.icb.extLocation); tloc = lelb_to_cpu(iter.fi.icb.extLocation);
iblock = udf_get_lb_pblock(sb, &tloc, 0); iblock = udf_get_lb_pblock(sb, &tloc, 0);
if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN)) if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN))
goto out; goto out_iter;
} /* end while */ }
ctx->pos = (nf_pos >> 2) + 1; if (!ret) {
ctx->pos = (iter.pos >> 2) + 1;
pos_valid = true; pos_valid = true;
}
out_iter:
udf_fiiter_release(&iter);
out: out:
if (pos_valid) if (pos_valid)
file->f_version = inode_query_iversion(dir); file->f_version = inode_query_iversion(dir);
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
brelse(epos.bh);
kfree(fname); kfree(fname);
kfree(copy_name);
return ret; return ret;
} }
......
...@@ -17,73 +17,133 @@ ...@@ -17,73 +17,133 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/bio.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, static int udf_verify_fi(struct udf_fileident_iter *iter)
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi,
struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t *elen,
sector_t *offset)
{ {
struct fileIdentDesc *fi; unsigned int len;
int i, num;
udf_pblk_t block; if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
struct buffer_head *tmp, *bha[16]; udf_err(iter->dir->i_sb,
struct udf_inode_info *iinfo = UDF_I(dir); "directory (ino %lu) has entry at pos %llu with incorrect tag %x\n",
iter->dir->i_ino, (unsigned long long)iter->pos,
fibh->soffset = fibh->eoffset; le16_to_cpu(iter->fi.descTag.tagIdent));
return -EFSCORRUPTED;
}
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;
}
/*
* 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;
}
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;
}
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) { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
fi = udf_get_fileident(iinfo->i_data - memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos,
(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)); sizeof(struct fileIdentDesc));
err = udf_verify_fi(iter);
return fi; 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;
} }
if (fibh->eoffset == dir->i_sb->s_blocksize) { off = iter->pos & (blksize - 1);
uint32_t lextoffset = epos->offset; len = min_t(int, sizeof(struct fileIdentDesc), blksize - off);
unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits; memcpy(&iter->fi, iter->bh[0]->b_data + off, len);
if (len < sizeof(struct fileIdentDesc))
if (udf_next_aext(dir, epos, eloc, elen, 1) != memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data,
(EXT_RECORDED_ALLOCATED >> 30)) sizeof(struct fileIdentDesc) - len);
return NULL; err = udf_verify_fi(iter);
if (err < 0)
block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); return err;
(*offset)++; /* Handle directory entry name */
nameoff = off + sizeof(struct fileIdentDesc) +
if ((*offset << blocksize_bits) >= *elen) le16_to_cpu(iter->fi.lengthOfImpUse);
*offset = 0; if (off + udf_dir_entry_len(&iter->fi) <= blksize) {
else iter->name = iter->bh[0]->b_data + nameoff;
epos->offset = lextoffset; } 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;
}
brelse(fibh->sbh); /* Readahead 8k once we are at 8k boundary */
fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); static void udf_readahead_dir(struct udf_fileident_iter *iter)
if (!fibh->sbh) {
return NULL; unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9);
fibh->soffset = fibh->eoffset = 0; struct buffer_head *tmp, *bha[16];
int i, num;
if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) { udf_pblk_t blk;
i = 16 >> (blocksize_bits - 9);
if (i + *offset > (*elen >> blocksize_bits)) if (iter->loffset & (ralen - 1))
i = (*elen >> blocksize_bits)-*offset; return;
for (num = 0; i > 0; i--) {
block = udf_get_lb_pblock(dir->i_sb, eloc, if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits))
*offset + i); ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset;
tmp = udf_tgetblk(dir->i_sb, block); num = 0;
if (tmp && !buffer_uptodate(tmp) && for (i = 0; i < ralen; i++) {
!buffer_locked(tmp)) 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; bha[num++] = tmp;
else else
brelse(tmp); brelse(tmp);
...@@ -93,107 +153,342 @@ struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos, ...@@ -93,107 +153,342 @@ struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
brelse(bha[i]); brelse(bha[i]);
} }
} }
} else if (fibh->sbh != fibh->ebh) {
brelse(fibh->sbh);
fibh->sbh = fibh->ebh;
}
fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, static struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter)
&(fibh->eoffset)); {
udf_pblk_t blk;
if (!fi) udf_readahead_dir(iter);
return NULL; blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset);
return sb_bread(iter->dir->i_sb, blk);
}
*nf_pos += fibh->eoffset - fibh->soffset; /*
* 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;
}
if (fibh->eoffset <= dir->i_sb->s_blocksize) { static int udf_fiiter_load_bhs(struct udf_fileident_iter *iter)
memcpy((uint8_t *)cfi, (uint8_t *)fi, {
sizeof(struct fileIdentDesc)); int blksize = 1 << iter->dir->i_blkbits;
} else if (fibh->eoffset > dir->i_sb->s_blocksize) { int off = iter->pos & (blksize - 1);
uint32_t lextoffset = epos->offset; int err;
struct fileIdentDesc *fi;
if (udf_next_aext(dir, epos, eloc, elen, 1) != /* Is there any further extent we can map from? */
(EXT_RECORDED_ALLOCATED >> 30)) if (!iter->bh[0] && iter->elen) {
return NULL; 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;
}
block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); 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);
(*offset)++; if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
err = udf_copy_fi(iter);
goto out;
}
if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
*offset = 0; &iter->eloc, &iter->elen, &iter->loffset) !=
else (EXT_RECORDED_ALLOCATED >> 30)) {
epos->offset = lextoffset; 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;
}
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;
}
int udf_fiiter_advance(struct udf_fileident_iter *iter)
{
unsigned int oldoff, len;
int blksize = 1 << iter->dir->i_blkbits;
int err;
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);
}
fibh->soffset -= dir->i_sb->s_blocksize; void udf_fiiter_release(struct udf_fileident_iter *iter)
fibh->eoffset -= dir->i_sb->s_blocksize; {
iter->dir = NULL;
brelse(iter->bh[0]);
brelse(iter->bh[1]);
iter->bh[0] = iter->bh[1] = NULL;
kfree(iter->namebuf);
iter->namebuf = NULL;
}
fibh->ebh = udf_tread(dir->i_sb, block); static void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2,
if (!fibh->ebh) int off, void *src, int len)
return NULL; {
int copy;
if (sizeof(struct fileIdentDesc) > -fibh->soffset) { if (off >= len1) {
int fi_len; 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);
}
}
memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset); static uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2,
memcpy((uint8_t *)cfi - fibh->soffset, int off, int len)
fibh->ebh->b_data, {
sizeof(struct fileIdentDesc) + fibh->soffset); int copy;
uint16_t crc = 0;
fi_len = udf_dir_entry_len(cfi); if (off >= len1) {
*nf_pos += fi_len - (fibh->eoffset - fibh->soffset); off -= len1;
fibh->eoffset = fibh->soffset + fi_len;
} else { } else {
memcpy((uint8_t *)cfi, (uint8_t *)fi, copy = min(off + len, len1) - off;
sizeof(struct fileIdentDesc)); 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);
} }
/* Got last entry outside of dir size - fs is corrupted! */ return crc;
if (*nf_pos > dir->i_size)
return NULL;
return fi;
} }
struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset) 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)
{ {
struct fileIdentDesc *fi; uint16_t crc;
int lengthThisIdent; int fioff = off;
uint8_t *ptr; int crcoff = off + sizeof(struct tag);
int padlen; 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);
}
if ((!buffer) || (!offset)) { crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen);
udf_debug("invalidparms, buffer=%p, offset=%p\n", fi->descTag.descCRC = cpu_to_le16(crc);
buffer, offset); fi->descTag.descCRCLength = cpu_to_le16(crclen);
return NULL; 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;
}
} }
ptr = buffer; udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse,
iter->name == iter->namebuf ? iter->name : NULL);
if ((*offset > 0) && (*offset < bufsize)) if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
ptr += *offset; mark_inode_dirty(iter->dir);
fi = (struct fileIdentDesc *)ptr; } else {
if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) { mark_buffer_dirty_inode(iter->bh[0], iter->dir);
udf_debug("0x%x != TAG_IDENT_FID\n", if (iter->bh[1])
le16_to_cpu(fi->descTag.tagIdent)); mark_buffer_dirty_inode(iter->bh[1], iter->dir);
udf_debug("offset: %d sizeof: %lu bufsize: %d\n",
*offset, (unsigned long)sizeof(struct fileIdentDesc),
bufsize);
return NULL;
} }
if ((*offset + sizeof(struct fileIdentDesc)) > bufsize) inode_inc_iversion(iter->dir);
lengthThisIdent = sizeof(struct fileIdentDesc); }
else
lengthThisIdent = sizeof(struct fileIdentDesc) +
fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
/* we need to figure padding, too! */ void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)
padlen = lengthThisIdent % UDF_NAME_PAD; {
if (padlen) struct udf_inode_info *iinfo = UDF_I(iter->dir);
lengthThisIdent += (UDF_NAME_PAD - padlen); int diff = new_elen - iter->elen;
*offset = *offset + lengthThisIdent;
/* 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);
}
return fi; /* 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, struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
......
...@@ -38,100 +38,55 @@ ...@@ -38,100 +38,55 @@
#include "udf_i.h" #include "udf_i.h"
#include "udf_sb.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; struct vm_area_struct *vma = vmf->vma;
char *kaddr; struct inode *inode = file_inode(vma->vm_file);
struct udf_inode_info *iinfo = UDF_I(inode); struct address_space *mapping = inode->i_mapping;
loff_t isize = i_size_read(inode); struct page *page = vmf->page;
loff_t size;
/* unsigned int end;
* We have to be careful here as truncate can change i_size under us. vm_fault_t ret = VM_FAULT_LOCKED;
* So just sample it once and use the same value everywhere. int err;
*/
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); sb_start_pagefault(inode->i_sb);
memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, i_size_read(inode)); file_update_time(vma->vm_file);
SetPageUptodate(page); filemap_invalidate_lock_shared(mapping);
kunmap_atomic(kaddr); lock_page(page);
mark_inode_dirty(inode); size = i_size_read(inode);
if (page->mapping != inode->i_mapping || page_offset(page) >= size) {
unlock_page(page); unlock_page(page);
ret = VM_FAULT_NOPAGE;
return 0; goto out_unlock;
} }
/* Space is already allocated for in-ICB file */
static int udf_adinicb_write_begin(struct file *file, if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
struct address_space *mapping, loff_t pos, goto out_dirty;
unsigned len, struct page **pagep, if (page->index == size >> PAGE_SHIFT)
void **fsdata) end = size & ~PAGE_MASK;
{ else
struct page *page; end = PAGE_SIZE;
err = __block_write_begin(page, 0, end, udf_get_block);
if (WARN_ON_ONCE(pos >= PAGE_SIZE)) if (!err)
return -EIO; err = block_commit_write(page, 0, end);
page = grab_cache_page_write_begin(mapping, 0); if (err < 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);
set_page_dirty(page);
unlock_page(page); unlock_page(page);
put_page(page); ret = block_page_mkwrite_return(err);
return copied; goto out_unlock;
}
out_dirty:
set_page_dirty(page);
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 = { static const struct vm_operations_struct udf_file_vm_ops = {
.dirty_folio = block_dirty_folio, .fault = filemap_fault,
.invalidate_folio = block_invalidate_folio, .map_pages = filemap_map_pages,
.read_folio = udf_adinicb_read_folio, .page_mkwrite = udf_page_mkwrite,
.writepage = udf_adinicb_writepage,
.write_begin = udf_adinicb_write_begin,
.write_end = udf_adinicb_write_end,
.direct_IO = udf_adinicb_direct_IO,
}; };
static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) 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) ...@@ -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 file *file = iocb->ki_filp;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
int err;
inode_lock(inode); inode_lock(inode);
...@@ -148,27 +102,23 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -148,27 +102,23 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (retval <= 0) if (retval <= 0)
goto out; goto out;
down_write(&iinfo->i_data_sem); if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
loff_t end = iocb->ki_pos + iov_iter_count(from); iocb->ki_pos + iov_iter_count(from))) {
filemap_invalidate_lock(inode->i_mapping);
if (inode->i_sb->s_blocksize < retval = udf_expand_file_adinicb(inode);
(udf_file_entry_alloc_offset(inode) + end)) { filemap_invalidate_unlock(inode->i_mapping);
err = udf_expand_file_adinicb(inode); if (retval)
if (err) { goto out;
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);
retval = __generic_file_write_iter(iocb, from); retval = __generic_file_write_iter(iocb, from);
out: 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); inode_unlock(inode);
if (retval > 0) { if (retval > 0) {
...@@ -243,11 +193,19 @@ static int udf_release_file(struct inode *inode, struct file *filp) ...@@ -243,11 +193,19 @@ static int udf_release_file(struct inode *inode, struct file *filp)
return 0; 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 = { const struct file_operations udf_file_operations = {
.read_iter = generic_file_read_iter, .read_iter = generic_file_read_iter,
.unlocked_ioctl = udf_ioctl, .unlocked_ioctl = udf_ioctl,
.open = generic_file_open, .open = generic_file_open,
.mmap = generic_file_mmap, .mmap = udf_file_mmap,
.write_iter = udf_file_write_iter, .write_iter = udf_file_write_iter,
.release = udf_release_file, .release = udf_release_file,
.fsync = generic_file_fsync, .fsync = generic_file_fsync,
......
...@@ -28,21 +28,7 @@ ...@@ -28,21 +28,7 @@
void udf_free_inode(struct inode *inode) void udf_free_inode(struct inode *inode)
{ {
struct super_block *sb = inode->i_sb; udf_free_blocks(inode->i_sb, NULL, &UDF_I(inode)->i_location, 0, 1);
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);
} }
struct inode *udf_new_inode(struct inode *dir, umode_t mode) 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) ...@@ -54,7 +40,6 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode)
uint32_t start = UDF_I(dir)->i_location.logicalBlockNum; uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
struct udf_inode_info *iinfo; struct udf_inode_info *iinfo;
struct udf_inode_info *dinfo = UDF_I(dir); struct udf_inode_info *dinfo = UDF_I(dir);
struct logicalVolIntegrityDescImpUse *lvidiu;
int err; int err;
inode = new_inode(sb); inode = new_inode(sb);
...@@ -92,18 +77,8 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode) ...@@ -92,18 +77,8 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode)
return ERR_PTR(err); return ERR_PTR(err);
} }
lvidiu = udf_sb_lvidiu(sb);
if (lvidiu) {
iinfo->i_unique = lvid_get_unique_id(sb); iinfo->i_unique = lvid_get_unique_id(sb);
inode->i_generation = iinfo->i_unique; 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);
}
inode_init_owner(&nop_mnt_idmap, inode, dir, mode); inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET)) if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET))
......
...@@ -52,21 +52,24 @@ ...@@ -52,21 +52,24 @@
#define FE_DELETE_PERMS (FE_PERM_U_DELETE | FE_PERM_G_DELETE | \ #define FE_DELETE_PERMS (FE_PERM_U_DELETE | FE_PERM_G_DELETE | \
FE_PERM_O_DELETE) FE_PERM_O_DELETE)
struct udf_map_rq;
static umode_t udf_convert_permissions(struct fileEntry *); static umode_t udf_convert_permissions(struct fileEntry *);
static int udf_update_inode(struct inode *, int); static int udf_update_inode(struct inode *, int);
static int udf_sync_inode(struct inode *inode); static int udf_sync_inode(struct inode *inode);
static int udf_alloc_i_data(struct inode *inode, size_t size); static int udf_alloc_i_data(struct inode *inode, size_t size);
static sector_t inode_getblk(struct inode *, sector_t, int *, int *); static int inode_getblk(struct inode *inode, struct udf_map_rq *map);
static int8_t udf_insert_aext(struct inode *, struct extent_position, static int udf_insert_aext(struct inode *, struct extent_position,
struct kernel_lb_addr, uint32_t); struct kernel_lb_addr, uint32_t);
static void udf_split_extents(struct inode *, int *, int, udf_pblk_t, static void udf_split_extents(struct inode *, int *, int, udf_pblk_t,
struct kernel_long_ad *, int *); struct kernel_long_ad *, int *);
static void udf_prealloc_extents(struct inode *, int, int, static void udf_prealloc_extents(struct inode *, int, int,
struct kernel_long_ad *, int *); struct kernel_long_ad *, int *);
static void udf_merge_extents(struct inode *, struct kernel_long_ad *, int *); static void udf_merge_extents(struct inode *, struct kernel_long_ad *, int *);
static void udf_update_extents(struct inode *, struct kernel_long_ad *, int, static int udf_update_extents(struct inode *, struct kernel_long_ad *, int,
int, struct extent_position *); int, struct extent_position *);
static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); static int udf_get_block_wb(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create);
static void __udf_clear_extent_cache(struct inode *inode) static void __udf_clear_extent_cache(struct inode *inode)
{ {
...@@ -182,14 +185,56 @@ static void udf_write_failed(struct address_space *mapping, loff_t to) ...@@ -182,14 +185,56 @@ static void udf_write_failed(struct address_space *mapping, loff_t to)
} }
} }
static int udf_adinicb_writepage(struct page *page,
struct writeback_control *wbc, void *data)
{
struct inode *inode = page->mapping->host;
struct udf_inode_info *iinfo = UDF_I(inode);
BUG_ON(!PageLocked(page));
memcpy_to_page(page, 0, iinfo->i_data + iinfo->i_lenEAttr,
i_size_read(inode));
unlock_page(page);
mark_inode_dirty(inode);
return 0;
}
static int udf_writepages(struct address_space *mapping, static int udf_writepages(struct address_space *mapping,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
return mpage_writepages(mapping, wbc, udf_get_block); struct inode *inode = mapping->host;
struct udf_inode_info *iinfo = UDF_I(inode);
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB)
return mpage_writepages(mapping, wbc, udf_get_block_wb);
return write_cache_pages(mapping, wbc, udf_adinicb_writepage, NULL);
}
static void udf_adinicb_readpage(struct page *page)
{
struct inode *inode = page->mapping->host;
char *kaddr;
struct udf_inode_info *iinfo = UDF_I(inode);
loff_t isize = i_size_read(inode);
kaddr = kmap_local_page(page);
memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr, isize);
memset(kaddr + isize, 0, PAGE_SIZE - isize);
flush_dcache_page(page);
SetPageUptodate(page);
kunmap_local(kaddr);
} }
static int udf_read_folio(struct file *file, struct folio *folio) static int udf_read_folio(struct file *file, struct folio *folio)
{ {
struct udf_inode_info *iinfo = UDF_I(file_inode(file));
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
udf_adinicb_readpage(&folio->page);
folio_unlock(folio);
return 0;
}
return mpage_read_folio(folio, udf_get_block); return mpage_read_folio(folio, udf_get_block);
} }
...@@ -202,12 +247,46 @@ static int udf_write_begin(struct file *file, struct address_space *mapping, ...@@ -202,12 +247,46 @@ static int udf_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, loff_t pos, unsigned len,
struct page **pagep, void **fsdata) struct page **pagep, void **fsdata)
{ {
struct udf_inode_info *iinfo = UDF_I(file_inode(file));
struct page *page;
int ret; int ret;
ret = block_write_begin(mapping, pos, len, pagep, udf_get_block); if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
ret = block_write_begin(mapping, pos, len, pagep,
udf_get_block);
if (unlikely(ret)) if (unlikely(ret))
udf_write_failed(mapping, pos + len); udf_write_failed(mapping, pos + len);
return ret; return ret;
}
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 int udf_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
struct inode *inode = file_inode(file);
loff_t last_pos;
if (UDF_I(inode)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB)
return generic_write_end(file, mapping, pos, len, copied, page,
fsdata);
last_pos = pos + copied;
if (last_pos > inode->i_size)
i_size_write(inode, last_pos);
set_page_dirty(page);
unlock_page(page);
put_page(page);
return copied;
} }
static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter) static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
...@@ -218,6 +297,9 @@ static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ...@@ -218,6 +297,9 @@ static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
size_t count = iov_iter_count(iter); size_t count = iov_iter_count(iter);
ssize_t ret; ssize_t ret;
/* Fallback to buffered IO for in-ICB files */
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
return 0;
ret = blockdev_direct_IO(iocb, inode, iter, udf_get_block); ret = blockdev_direct_IO(iocb, inode, iter, udf_get_block);
if (unlikely(ret < 0 && iov_iter_rw(iter) == WRITE)) if (unlikely(ret < 0 && iov_iter_rw(iter) == WRITE))
udf_write_failed(mapping, iocb->ki_pos + count); udf_write_failed(mapping, iocb->ki_pos + count);
...@@ -226,6 +308,10 @@ static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ...@@ -226,6 +308,10 @@ static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
static sector_t udf_bmap(struct address_space *mapping, sector_t block) static sector_t udf_bmap(struct address_space *mapping, sector_t block)
{ {
struct udf_inode_info *iinfo = UDF_I(mapping->host);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
return -EINVAL;
return generic_block_bmap(mapping, block, udf_get_block); return generic_block_bmap(mapping, block, udf_get_block);
} }
...@@ -236,7 +322,7 @@ const struct address_space_operations udf_aops = { ...@@ -236,7 +322,7 @@ const struct address_space_operations udf_aops = {
.readahead = udf_readahead, .readahead = udf_readahead,
.writepages = udf_writepages, .writepages = udf_writepages,
.write_begin = udf_write_begin, .write_begin = udf_write_begin,
.write_end = generic_write_end, .write_end = udf_write_end,
.direct_IO = udf_direct_IO, .direct_IO = udf_direct_IO,
.bmap = udf_bmap, .bmap = udf_bmap,
.migrate_folio = buffer_migrate_folio, .migrate_folio = buffer_migrate_folio,
...@@ -245,18 +331,17 @@ const struct address_space_operations udf_aops = { ...@@ -245,18 +331,17 @@ const struct address_space_operations udf_aops = {
/* /*
* Expand file stored in ICB to a normal one-block-file * Expand file stored in ICB to a normal one-block-file
* *
* This function requires i_data_sem for writing and releases it.
* This function requires i_mutex held * This function requires i_mutex held
*/ */
int udf_expand_file_adinicb(struct inode *inode) int udf_expand_file_adinicb(struct inode *inode)
{ {
struct page *page; struct page *page;
char *kaddr;
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
int err; int err;
WARN_ON_ONCE(!inode_is_locked(inode)); WARN_ON_ONCE(!inode_is_locked(inode));
if (!iinfo->i_lenAlloc) { if (!iinfo->i_lenAlloc) {
down_write(&iinfo->i_data_sem);
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
else else
...@@ -267,26 +352,13 @@ int udf_expand_file_adinicb(struct inode *inode) ...@@ -267,26 +352,13 @@ int udf_expand_file_adinicb(struct inode *inode)
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; return 0;
} }
/*
* Release i_data_sem so that we can lock a page - page lock ranks
* above i_data_sem. i_mutex still protects us against file changes.
*/
up_write(&iinfo->i_data_sem);
page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS); page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
if (!PageUptodate(page)) { if (!PageUptodate(page))
kaddr = kmap_atomic(page); udf_adinicb_readpage(page);
memset(kaddr + iinfo->i_lenAlloc, 0x00,
PAGE_SIZE - iinfo->i_lenAlloc);
memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr,
iinfo->i_lenAlloc);
flush_dcache_page(page);
SetPageUptodate(page);
kunmap_atomic(kaddr);
}
down_write(&iinfo->i_data_sem); down_write(&iinfo->i_data_sem);
memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00, memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00,
iinfo->i_lenAlloc); iinfo->i_lenAlloc);
...@@ -295,8 +367,6 @@ int udf_expand_file_adinicb(struct inode *inode) ...@@ -295,8 +367,6 @@ int udf_expand_file_adinicb(struct inode *inode)
iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
else else
iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
/* from now on we have normal address_space methods */
inode->i_data.a_ops = &udf_aops;
set_page_dirty(page); set_page_dirty(page);
unlock_page(page); unlock_page(page);
up_write(&iinfo->i_data_sem); up_write(&iinfo->i_data_sem);
...@@ -305,12 +375,10 @@ int udf_expand_file_adinicb(struct inode *inode) ...@@ -305,12 +375,10 @@ int udf_expand_file_adinicb(struct inode *inode)
/* Restore everything back so that we don't lose data... */ /* Restore everything back so that we don't lose data... */
lock_page(page); lock_page(page);
down_write(&iinfo->i_data_sem); down_write(&iinfo->i_data_sem);
kaddr = kmap_atomic(page); memcpy_to_page(page, 0, iinfo->i_data + iinfo->i_lenEAttr,
memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, inode->i_size); inode->i_size);
kunmap_atomic(kaddr);
unlock_page(page); unlock_page(page);
iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
inode->i_data.a_ops = &udf_adinicb_aops;
iinfo->i_lenAlloc = inode->i_size; iinfo->i_lenAlloc = inode->i_size;
up_write(&iinfo->i_data_sem); up_write(&iinfo->i_data_sem);
} }
...@@ -320,162 +388,103 @@ int udf_expand_file_adinicb(struct inode *inode) ...@@ -320,162 +388,103 @@ int udf_expand_file_adinicb(struct inode *inode)
return err; return err;
} }
struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, #define UDF_MAP_CREATE 0x01 /* Mapping can allocate new blocks */
udf_pblk_t *block, int *err) #define UDF_MAP_NOPREALLOC 0x02 /* Do not preallocate blocks */
{
udf_pblk_t newblock;
struct buffer_head *dbh = NULL;
struct kernel_lb_addr eloc;
uint8_t alloctype;
struct extent_position epos;
struct udf_fileident_bh sfibh, dfibh; #define UDF_BLK_MAPPED 0x01 /* Block was successfully mapped */
loff_t f_pos = udf_ext0_offset(inode); #define UDF_BLK_NEW 0x02 /* Block was freshly allocated */
int size = udf_ext0_offset(inode) + inode->i_size;
struct fileIdentDesc cfi, *sfi, *dfi;
struct udf_inode_info *iinfo = UDF_I(inode);
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
alloctype = ICBTAG_FLAG_AD_SHORT;
else
alloctype = ICBTAG_FLAG_AD_LONG;
if (!inode->i_size) { struct udf_map_rq {
iinfo->i_alloc_type = alloctype; sector_t lblk;
mark_inode_dirty(inode); udf_pblk_t pblk;
return NULL; int iflags; /* UDF_MAP_ flags determining behavior */
} int oflags; /* UDF_BLK_ flags reporting results */
};
/* alloc block, and copy data to it */ static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
*block = udf_new_block(inode->i_sb, inode, {
iinfo->i_location.partitionReferenceNum, int err;
iinfo->i_location.logicalBlockNum, err); struct udf_inode_info *iinfo = UDF_I(inode);
if (!(*block))
return NULL;
newblock = udf_get_pblock(inode->i_sb, *block,
iinfo->i_location.partitionReferenceNum,
0);
if (!newblock)
return NULL;
dbh = udf_tgetblk(inode->i_sb, newblock);
if (!dbh)
return NULL;
lock_buffer(dbh);
memset(dbh->b_data, 0x00, inode->i_sb->s_blocksize);
set_buffer_uptodate(dbh);
unlock_buffer(dbh);
mark_buffer_dirty_inode(dbh, inode);
sfibh.soffset = sfibh.eoffset =
f_pos & (inode->i_sb->s_blocksize - 1);
sfibh.sbh = sfibh.ebh = NULL;
dfibh.soffset = dfibh.eoffset = 0;
dfibh.sbh = dfibh.ebh = dbh;
while (f_pos < size) {
iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL,
NULL, NULL, NULL);
if (!sfi) {
brelse(dbh);
return NULL;
}
iinfo->i_alloc_type = alloctype;
sfi->descTag.tagLocation = cpu_to_le32(*block);
dfibh.soffset = dfibh.eoffset;
dfibh.eoffset += (sfibh.eoffset - sfibh.soffset);
dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset);
if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse,
udf_get_fi_ident(sfi))) {
iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
brelse(dbh);
return NULL;
}
}
mark_buffer_dirty_inode(dbh, inode);
memset(iinfo->i_data + iinfo->i_lenEAttr, 0, iinfo->i_lenAlloc); map->oflags = 0;
iinfo->i_lenAlloc = 0; if (!(map->iflags & UDF_MAP_CREATE)) {
eloc.logicalBlockNum = *block; struct kernel_lb_addr eloc;
eloc.partitionReferenceNum = uint32_t elen;
iinfo->i_location.partitionReferenceNum; sector_t offset;
iinfo->i_lenExtents = inode->i_size; struct extent_position epos = {};
epos.bh = NULL;
epos.block = iinfo->i_location;
epos.offset = udf_file_entry_alloc_offset(inode);
udf_add_aext(inode, &epos, &eloc, inode->i_size, 0);
/* UniqueID stuff */
down_read(&iinfo->i_data_sem);
if (inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset)
== (EXT_RECORDED_ALLOCATED >> 30)) {
map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc,
offset);
map->oflags |= UDF_BLK_MAPPED;
}
up_read(&iinfo->i_data_sem);
brelse(epos.bh); brelse(epos.bh);
mark_inode_dirty(inode);
return dbh;
}
static int udf_get_block(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create)
{
int err, new;
sector_t phys = 0;
struct udf_inode_info *iinfo;
if (!create) {
phys = udf_block_map(inode, block);
if (phys)
map_bh(bh_result, inode->i_sb, phys);
return 0; return 0;
} }
err = -EIO;
new = 0;
iinfo = UDF_I(inode);
down_write(&iinfo->i_data_sem); down_write(&iinfo->i_data_sem);
if (block == iinfo->i_next_alloc_block + 1) {
iinfo->i_next_alloc_block++;
iinfo->i_next_alloc_goal++;
}
/* /*
* Block beyond EOF and prealloc extents? Just discard preallocation * Block beyond EOF and prealloc extents? Just discard preallocation
* as it is not useful and complicates things. * as it is not useful and complicates things.
*/ */
if (((loff_t)block) << inode->i_blkbits > iinfo->i_lenExtents) if (((loff_t)map->lblk) << inode->i_blkbits >= iinfo->i_lenExtents)
udf_discard_prealloc(inode); udf_discard_prealloc(inode);
udf_clear_extent_cache(inode); udf_clear_extent_cache(inode);
phys = inode_getblk(inode, block, &err, &new); err = inode_getblk(inode, map);
if (!phys)
goto abort;
if (new)
set_buffer_new(bh_result);
map_bh(bh_result, inode->i_sb, phys);
abort:
up_write(&iinfo->i_data_sem); up_write(&iinfo->i_data_sem);
return err; return err;
} }
static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block, static int __udf_get_block(struct inode *inode, sector_t block,
int create, int *err) struct buffer_head *bh_result, int flags)
{ {
struct buffer_head *bh; int err;
struct buffer_head dummy; struct udf_map_rq map = {
.lblk = block,
dummy.b_state = 0; .iflags = flags,
dummy.b_blocknr = -1000; };
*err = udf_get_block(inode, block, &dummy, create);
if (!*err && buffer_mapped(&dummy)) { err = udf_map_block(inode, &map);
bh = sb_getblk(inode->i_sb, dummy.b_blocknr); if (err < 0)
if (buffer_new(&dummy)) { return err;
lock_buffer(bh); if (map.oflags & UDF_BLK_MAPPED) {
memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); map_bh(bh_result, inode->i_sb, map.pblk);
set_buffer_uptodate(bh); if (map.oflags & UDF_BLK_NEW)
unlock_buffer(bh); set_buffer_new(bh_result);
mark_buffer_dirty_inode(bh, inode);
}
return bh;
} }
return 0;
}
return NULL; int udf_get_block(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create)
{
int flags = create ? UDF_MAP_CREATE : 0;
/*
* We preallocate blocks only for regular files. It also makes sense
* for directories but there's a problem when to drop the
* preallocation. We might use some delayed work for that but I feel
* it's overengineering for a filesystem like UDF.
*/
if (!S_ISREG(inode->i_mode))
flags |= UDF_MAP_NOPREALLOC;
return __udf_get_block(inode, block, bh_result, flags);
}
/*
* We shouldn't be allocating blocks on page writeback since we allocate them
* on page fault. We can spot dirty buffers without allocated blocks though
* when truncate expands file. These however don't have valid data so we can
* safely ignore them. So never allocate blocks from page writeback.
*/
static int udf_get_block_wb(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create)
{
return __udf_get_block(inode, block, bh_result, 0);
} }
/* Extend the file with new blocks totaling 'new_block_bytes', /* Extend the file with new blocks totaling 'new_block_bytes',
...@@ -509,6 +518,7 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -509,6 +518,7 @@ static int udf_do_extend_file(struct inode *inode,
~(sb->s_blocksize - 1); ~(sb->s_blocksize - 1);
} }
add = 0;
/* Can we merge with the previous extent? */ /* Can we merge with the previous extent? */
if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
EXT_NOT_RECORDED_NOT_ALLOCATED) { EXT_NOT_RECORDED_NOT_ALLOCATED) {
...@@ -521,8 +531,10 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -521,8 +531,10 @@ static int udf_do_extend_file(struct inode *inode,
} }
if (fake) { if (fake) {
udf_add_aext(inode, last_pos, &last_ext->extLocation, err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1); last_ext->extLength, 1);
if (err < 0)
goto out_err;
count++; count++;
} else { } else {
struct kernel_lb_addr tmploc; struct kernel_lb_addr tmploc;
...@@ -539,6 +551,7 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -539,6 +551,7 @@ static int udf_do_extend_file(struct inode *inode,
if (new_block_bytes) if (new_block_bytes)
udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0); udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
} }
iinfo->i_lenExtents += add;
/* Managed to do everything necessary? */ /* Managed to do everything necessary? */
if (!new_block_bytes) if (!new_block_bytes)
...@@ -556,7 +569,8 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -556,7 +569,8 @@ static int udf_do_extend_file(struct inode *inode,
err = udf_add_aext(inode, last_pos, &last_ext->extLocation, err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1); last_ext->extLength, 1);
if (err) if (err)
return err; goto out_err;
iinfo->i_lenExtents += add;
count++; count++;
} }
if (new_block_bytes) { if (new_block_bytes) {
...@@ -565,7 +579,8 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -565,7 +579,8 @@ static int udf_do_extend_file(struct inode *inode,
err = udf_add_aext(inode, last_pos, &last_ext->extLocation, err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1); last_ext->extLength, 1);
if (err) if (err)
return err; goto out_err;
iinfo->i_lenExtents += new_block_bytes;
count++; count++;
} }
...@@ -579,6 +594,11 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -579,6 +594,11 @@ static int udf_do_extend_file(struct inode *inode,
return -EIO; return -EIO;
return count; return count;
out_err:
/* Remove extents we've created so far */
udf_clear_extent_cache(inode);
udf_truncate_extents(inode);
return err;
} }
/* Extend the final block of the file to final_block_len bytes */ /* Extend the final block of the file to final_block_len bytes */
...@@ -626,6 +646,7 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) ...@@ -626,6 +646,7 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
else else
BUG(); BUG();
down_write(&iinfo->i_data_sem);
/* /*
* When creating hole in file, just don't bother with preserving * When creating hole in file, just don't bother with preserving
* preallocation. It likely won't be very useful anyway. * preallocation. It likely won't be very useful anyway.
...@@ -668,14 +689,13 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) ...@@ -668,14 +689,13 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
if (err < 0) if (err < 0)
goto out; goto out;
err = 0; err = 0;
iinfo->i_lenExtents = newsize;
out: out:
brelse(epos.bh); brelse(epos.bh);
up_write(&iinfo->i_data_sem);
return err; return err;
} }
static sector_t inode_getblk(struct inode *inode, sector_t block, static int inode_getblk(struct inode *inode, struct udf_map_rq *map)
int *err, int *new)
{ {
struct kernel_long_ad laarr[EXTENT_MERGE_SIZE]; struct kernel_long_ad laarr[EXTENT_MERGE_SIZE];
struct extent_position prev_epos, cur_epos, next_epos; struct extent_position prev_epos, cur_epos, next_epos;
...@@ -684,21 +704,20 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -684,21 +704,20 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
struct kernel_lb_addr eloc, tmpeloc; struct kernel_lb_addr eloc, tmpeloc;
int c = 1; int c = 1;
loff_t lbcount = 0, b_off = 0; loff_t lbcount = 0, b_off = 0;
udf_pblk_t newblocknum, newblock = 0; udf_pblk_t newblocknum;
sector_t offset = 0; sector_t offset = 0;
int8_t etype; int8_t etype;
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
udf_pblk_t goal = 0, pgoal = iinfo->i_location.logicalBlockNum; udf_pblk_t goal = 0, pgoal = iinfo->i_location.logicalBlockNum;
int lastblock = 0; int lastblock = 0;
bool isBeyondEOF; bool isBeyondEOF;
int ret = 0;
*err = 0;
*new = 0;
prev_epos.offset = udf_file_entry_alloc_offset(inode); prev_epos.offset = udf_file_entry_alloc_offset(inode);
prev_epos.block = iinfo->i_location; prev_epos.block = iinfo->i_location;
prev_epos.bh = NULL; prev_epos.bh = NULL;
cur_epos = next_epos = prev_epos; cur_epos = next_epos = prev_epos;
b_off = (loff_t)block << inode->i_sb->s_blocksize_bits; b_off = (loff_t)map->lblk << inode->i_sb->s_blocksize_bits;
/* find the extent which contains the block we are looking for. /* find the extent which contains the block we are looking for.
alternate between laarr[0] and laarr[1] for locations of the alternate between laarr[0] and laarr[1] for locations of the
...@@ -757,15 +776,18 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -757,15 +776,18 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
elen = EXT_RECORDED_ALLOCATED | elen = EXT_RECORDED_ALLOCATED |
((elen + inode->i_sb->s_blocksize - 1) & ((elen + inode->i_sb->s_blocksize - 1) &
~(inode->i_sb->s_blocksize - 1)); ~(inode->i_sb->s_blocksize - 1));
iinfo->i_lenExtents =
ALIGN(iinfo->i_lenExtents,
inode->i_sb->s_blocksize);
udf_write_aext(inode, &cur_epos, &eloc, elen, 1); udf_write_aext(inode, &cur_epos, &eloc, elen, 1);
} }
newblock = udf_get_lb_pblock(inode->i_sb, &eloc, offset); map->oflags = UDF_BLK_MAPPED;
map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc, offset);
goto out_free; goto out_free;
} }
/* Are we beyond EOF and preallocated extent? */ /* Are we beyond EOF and preallocated extent? */
if (etype == -1) { if (etype == -1) {
int ret;
loff_t hole_len; loff_t hole_len;
isBeyondEOF = true; isBeyondEOF = true;
...@@ -785,18 +807,15 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -785,18 +807,15 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
/* Create extents for the hole between EOF and offset */ /* Create extents for the hole between EOF and offset */
hole_len = (loff_t)offset << inode->i_blkbits; hole_len = (loff_t)offset << inode->i_blkbits;
ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len); ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len);
if (ret < 0) { if (ret < 0)
*err = ret;
goto out_free; goto out_free;
}
c = 0; c = 0;
offset = 0; offset = 0;
count += ret; count += ret;
/* We are not covered by a preallocated extent? */ /*
if ((laarr[0].extLength & UDF_EXTENT_FLAG_MASK) != * Is there any real extent? - otherwise we overwrite the fake
EXT_NOT_RECORDED_ALLOCATED) { * one...
/* Is there any real extent? - otherwise we overwrite */
* the fake one... */
if (count) if (count)
c = !c; c = !c;
laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
...@@ -804,7 +823,6 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -804,7 +823,6 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
memset(&laarr[c].extLocation, 0x00, memset(&laarr[c].extLocation, 0x00,
sizeof(struct kernel_lb_addr)); sizeof(struct kernel_lb_addr));
count++; count++;
}
endnum = c + 1; endnum = c + 1;
lastblock = 1; lastblock = 1;
} else { } else {
...@@ -838,7 +856,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -838,7 +856,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
newblocknum = laarr[c].extLocation.logicalBlockNum + offset; newblocknum = laarr[c].extLocation.logicalBlockNum + offset;
else { /* otherwise, allocate a new block */ else { /* otherwise, allocate a new block */
if (iinfo->i_next_alloc_block == block) if (iinfo->i_next_alloc_block == map->lblk)
goal = iinfo->i_next_alloc_goal; goal = iinfo->i_next_alloc_goal;
if (!goal) { if (!goal) {
...@@ -848,11 +866,9 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -848,11 +866,9 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
newblocknum = udf_new_block(inode->i_sb, inode, newblocknum = udf_new_block(inode->i_sb, inode,
iinfo->i_location.partitionReferenceNum, iinfo->i_location.partitionReferenceNum,
goal, err); goal, &ret);
if (!newblocknum) { if (!newblocknum)
*err = -ENOSPC;
goto out_free; goto out_free;
}
if (isBeyondEOF) if (isBeyondEOF)
iinfo->i_lenExtents += inode->i_sb->s_blocksize; iinfo->i_lenExtents += inode->i_sb->s_blocksize;
} }
...@@ -863,11 +879,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -863,11 +879,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
* block */ * block */
udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum); udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum);
/* We preallocate blocks only for regular files. It also makes sense if (!(map->iflags & UDF_MAP_NOPREALLOC))
* for directories but there's a problem when to drop the
* preallocation. We might use some delayed work for that but I feel
* it's overengineering for a filesystem like UDF. */
if (S_ISREG(inode->i_mode))
udf_prealloc_extents(inode, c, lastblock, laarr, &endnum); udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
/* merge any continuous blocks in laarr */ /* merge any continuous blocks in laarr */
...@@ -876,28 +888,31 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, ...@@ -876,28 +888,31 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
/* write back the new extents, inserting new extents if the new number /* write back the new extents, inserting new extents if the new number
* of extents is greater than the old number, and deleting extents if * of extents is greater than the old number, and deleting extents if
* the new number of extents is less than the old number */ * the new number of extents is less than the old number */
udf_update_extents(inode, laarr, startnum, endnum, &prev_epos); ret = udf_update_extents(inode, laarr, startnum, endnum, &prev_epos);
if (ret < 0)
goto out_free;
newblock = udf_get_pblock(inode->i_sb, newblocknum, map->pblk = udf_get_pblock(inode->i_sb, newblocknum,
iinfo->i_location.partitionReferenceNum, 0); iinfo->i_location.partitionReferenceNum, 0);
if (!newblock) { if (!map->pblk) {
*err = -EIO; ret = -EFSCORRUPTED;
goto out_free; goto out_free;
} }
*new = 1; map->oflags = UDF_BLK_NEW | UDF_BLK_MAPPED;
iinfo->i_next_alloc_block = block; iinfo->i_next_alloc_block = map->lblk + 1;
iinfo->i_next_alloc_goal = newblocknum; iinfo->i_next_alloc_goal = newblocknum + 1;
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
if (IS_SYNC(inode)) if (IS_SYNC(inode))
udf_sync_inode(inode); udf_sync_inode(inode);
else else
mark_inode_dirty(inode); mark_inode_dirty(inode);
ret = 0;
out_free: out_free:
brelse(prev_epos.bh); brelse(prev_epos.bh);
brelse(cur_epos.bh); brelse(cur_epos.bh);
brelse(next_epos.bh); brelse(next_epos.bh);
return newblock; return ret;
} }
static void udf_split_extents(struct inode *inode, int *c, int offset, static void udf_split_extents(struct inode *inode, int *c, int offset,
...@@ -1081,22 +1096,7 @@ static void udf_merge_extents(struct inode *inode, struct kernel_long_ad *laarr, ...@@ -1081,22 +1096,7 @@ static void udf_merge_extents(struct inode *inode, struct kernel_long_ad *laarr,
if (((li->extLength & UDF_EXTENT_LENGTH_MASK) + if (((li->extLength & UDF_EXTENT_LENGTH_MASK) +
(lip1->extLength & UDF_EXTENT_LENGTH_MASK) + (lip1->extLength & UDF_EXTENT_LENGTH_MASK) +
blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { blocksize - 1) <= UDF_EXTENT_LENGTH_MASK) {
lip1->extLength = (lip1->extLength -
(li->extLength &
UDF_EXTENT_LENGTH_MASK) +
UDF_EXTENT_LENGTH_MASK) &
~(blocksize - 1);
li->extLength = (li->extLength &
UDF_EXTENT_FLAG_MASK) +
(UDF_EXTENT_LENGTH_MASK + 1) -
blocksize;
lip1->extLocation.logicalBlockNum =
li->extLocation.logicalBlockNum +
((li->extLength &
UDF_EXTENT_LENGTH_MASK) >>
blocksize_bits);
} else {
li->extLength = lip1->extLength + li->extLength = lip1->extLength +
(((li->extLength & (((li->extLength &
UDF_EXTENT_LENGTH_MASK) + UDF_EXTENT_LENGTH_MASK) +
...@@ -1159,21 +1159,30 @@ static void udf_merge_extents(struct inode *inode, struct kernel_long_ad *laarr, ...@@ -1159,21 +1159,30 @@ static void udf_merge_extents(struct inode *inode, struct kernel_long_ad *laarr,
} }
} }
static void udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr, static int udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr,
int startnum, int endnum, int startnum, int endnum,
struct extent_position *epos) struct extent_position *epos)
{ {
int start = 0, i; int start = 0, i;
struct kernel_lb_addr tmploc; struct kernel_lb_addr tmploc;
uint32_t tmplen; uint32_t tmplen;
int err;
if (startnum > endnum) { if (startnum > endnum) {
for (i = 0; i < (startnum - endnum); i++) for (i = 0; i < (startnum - endnum); i++)
udf_delete_aext(inode, *epos); udf_delete_aext(inode, *epos);
} else if (startnum < endnum) { } else if (startnum < endnum) {
for (i = 0; i < (endnum - startnum); i++) { for (i = 0; i < (endnum - startnum); i++) {
udf_insert_aext(inode, *epos, laarr[i].extLocation, err = udf_insert_aext(inode, *epos,
laarr[i].extLocation,
laarr[i].extLength); laarr[i].extLength);
/*
* If we fail here, we are likely corrupting the extent
* list and leaking blocks. At least stop early to
* limit the damage.
*/
if (err < 0)
return err;
udf_next_aext(inode, epos, &laarr[i].extLocation, udf_next_aext(inode, epos, &laarr[i].extLocation,
&laarr[i].extLength, 1); &laarr[i].extLength, 1);
start++; start++;
...@@ -1185,17 +1194,36 @@ static void udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr ...@@ -1185,17 +1194,36 @@ static void udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr
udf_write_aext(inode, epos, &laarr[i].extLocation, udf_write_aext(inode, epos, &laarr[i].extLocation,
laarr[i].extLength, 1); laarr[i].extLength, 1);
} }
return 0;
} }
struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block, struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
int create, int *err) int create, int *err)
{ {
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
struct udf_map_rq map = {
.lblk = block,
.iflags = UDF_MAP_NOPREALLOC | (create ? UDF_MAP_CREATE : 0),
};
bh = udf_getblk(inode, block, create, err); *err = udf_map_block(inode, &map);
if (!bh) if (*err || !(map.oflags & UDF_BLK_MAPPED))
return NULL; return NULL;
bh = sb_getblk(inode->i_sb, map.pblk);
if (!bh) {
*err = -ENOMEM;
return NULL;
}
if (map.oflags & UDF_BLK_NEW) {
lock_buffer(bh);
memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
set_buffer_uptodate(bh);
unlock_buffer(bh);
mark_buffer_dirty_inode(bh, inode);
return bh;
}
if (bh_read(bh, 0) >= 0) if (bh_read(bh, 0) >= 0)
return bh; return bh;
...@@ -1206,7 +1234,7 @@ struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block, ...@@ -1206,7 +1234,7 @@ struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
int udf_setsize(struct inode *inode, loff_t newsize) int udf_setsize(struct inode *inode, loff_t newsize)
{ {
int err; int err = 0;
struct udf_inode_info *iinfo; struct udf_inode_info *iinfo;
unsigned int bsize = i_blocksize(inode); unsigned int bsize = i_blocksize(inode);
...@@ -1216,28 +1244,25 @@ int udf_setsize(struct inode *inode, loff_t newsize) ...@@ -1216,28 +1244,25 @@ int udf_setsize(struct inode *inode, loff_t newsize)
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return -EPERM; return -EPERM;
filemap_invalidate_lock(inode->i_mapping);
iinfo = UDF_I(inode); iinfo = UDF_I(inode);
if (newsize > inode->i_size) { if (newsize > inode->i_size) {
down_write(&iinfo->i_data_sem);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
if (bsize < if (bsize >=
(udf_file_entry_alloc_offset(inode) + newsize)) { (udf_file_entry_alloc_offset(inode) + newsize)) {
err = udf_expand_file_adinicb(inode);
if (err)
return err;
down_write(&iinfo->i_data_sem); down_write(&iinfo->i_data_sem);
} else {
iinfo->i_lenAlloc = newsize; iinfo->i_lenAlloc = newsize;
up_write(&iinfo->i_data_sem);
goto set_size; goto set_size;
} }
err = udf_expand_file_adinicb(inode);
if (err)
goto out_unlock;
} }
err = udf_extend_file(inode, newsize); err = udf_extend_file(inode, newsize);
if (err) { if (err)
up_write(&iinfo->i_data_sem); goto out_unlock;
return err;
}
set_size: set_size:
up_write(&iinfo->i_data_sem);
truncate_setsize(inode, newsize); truncate_setsize(inode, newsize);
} else { } else {
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
...@@ -1254,14 +1279,14 @@ int udf_setsize(struct inode *inode, loff_t newsize) ...@@ -1254,14 +1279,14 @@ int udf_setsize(struct inode *inode, loff_t newsize)
err = block_truncate_page(inode->i_mapping, newsize, err = block_truncate_page(inode->i_mapping, newsize,
udf_get_block); udf_get_block);
if (err) if (err)
return err; goto out_unlock;
truncate_setsize(inode, newsize); truncate_setsize(inode, newsize);
down_write(&iinfo->i_data_sem); down_write(&iinfo->i_data_sem);
udf_clear_extent_cache(inode); udf_clear_extent_cache(inode);
err = udf_truncate_extents(inode); err = udf_truncate_extents(inode);
up_write(&iinfo->i_data_sem); up_write(&iinfo->i_data_sem);
if (err) if (err)
return err; goto out_unlock;
} }
update_time: update_time:
inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_mtime = inode->i_ctime = current_time(inode);
...@@ -1269,7 +1294,9 @@ int udf_setsize(struct inode *inode, loff_t newsize) ...@@ -1269,7 +1294,9 @@ int udf_setsize(struct inode *inode, loff_t newsize)
udf_sync_inode(inode); udf_sync_inode(inode);
else else
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; out_unlock:
filemap_invalidate_unlock(inode->i_mapping);
return err;
} }
/* /*
...@@ -1381,6 +1408,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) ...@@ -1381,6 +1408,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode)
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
iinfo->i_hidden = hidden_inode;
iinfo->i_unique = 0; iinfo->i_unique = 0;
iinfo->i_lenEAttr = 0; iinfo->i_lenEAttr = 0;
iinfo->i_lenExtents = 0; iinfo->i_lenExtents = 0;
...@@ -1537,9 +1565,6 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) ...@@ -1537,9 +1565,6 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode)
case ICBTAG_FILE_TYPE_REGULAR: case ICBTAG_FILE_TYPE_REGULAR:
case ICBTAG_FILE_TYPE_UNDEF: case ICBTAG_FILE_TYPE_UNDEF:
case ICBTAG_FILE_TYPE_VAT20: case ICBTAG_FILE_TYPE_VAT20:
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
inode->i_data.a_ops = &udf_adinicb_aops;
else
inode->i_data.a_ops = &udf_aops; inode->i_data.a_ops = &udf_aops;
inode->i_op = &udf_file_inode_operations; inode->i_op = &udf_file_inode_operations;
inode->i_fop = &udf_file_operations; inode->i_fop = &udf_file_operations;
...@@ -1671,7 +1696,7 @@ static int udf_update_inode(struct inode *inode, int do_sync) ...@@ -1671,7 +1696,7 @@ static int udf_update_inode(struct inode *inode, int do_sync)
unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
bh = udf_tgetblk(inode->i_sb, bh = sb_getblk(inode->i_sb,
udf_get_lb_pblock(inode->i_sb, &iinfo->i_location, 0)); udf_get_lb_pblock(inode->i_sb, &iinfo->i_location, 0));
if (!bh) { if (!bh) {
udf_debug("getblk failure\n"); udf_debug("getblk failure\n");
...@@ -1716,8 +1741,12 @@ static int udf_update_inode(struct inode *inode, int do_sync) ...@@ -1716,8 +1741,12 @@ static int udf_update_inode(struct inode *inode, int do_sync)
if (S_ISDIR(inode->i_mode) && inode->i_nlink > 0) if (S_ISDIR(inode->i_mode) && inode->i_nlink > 0)
fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1); fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1);
else {
if (iinfo->i_hidden)
fe->fileLinkCount = cpu_to_le16(0);
else else
fe->fileLinkCount = cpu_to_le16(inode->i_nlink); fe->fileLinkCount = cpu_to_le16(inode->i_nlink);
}
fe->informationLength = cpu_to_le64(inode->i_size); fe->informationLength = cpu_to_le64(inode->i_size);
...@@ -1888,8 +1917,13 @@ struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino, ...@@ -1888,8 +1917,13 @@ struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW)) if (!(inode->i_state & I_NEW)) {
if (UDF_I(inode)->i_hidden != hidden_inode) {
iput(inode);
return ERR_PTR(-EFSCORRUPTED);
}
return inode; return inode;
}
memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr)); memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr));
err = udf_read_inode(inode, hidden_inode); err = udf_read_inode(inode, hidden_inode);
...@@ -1922,7 +1956,7 @@ int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block, ...@@ -1922,7 +1956,7 @@ int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
neloc.logicalBlockNum = block; neloc.logicalBlockNum = block;
neloc.partitionReferenceNum = epos->block.partitionReferenceNum; neloc.partitionReferenceNum = epos->block.partitionReferenceNum;
bh = udf_tgetblk(sb, udf_get_lb_pblock(sb, &neloc, 0)); bh = sb_getblk(sb, udf_get_lb_pblock(sb, &neloc, 0));
if (!bh) if (!bh)
return -EIO; return -EIO;
lock_buffer(bh); lock_buffer(bh);
...@@ -2139,7 +2173,7 @@ int8_t udf_next_aext(struct inode *inode, struct extent_position *epos, ...@@ -2139,7 +2173,7 @@ int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,
epos->offset = sizeof(struct allocExtDesc); epos->offset = sizeof(struct allocExtDesc);
brelse(epos->bh); brelse(epos->bh);
block = udf_get_lb_pblock(inode->i_sb, &epos->block, 0); block = udf_get_lb_pblock(inode->i_sb, &epos->block, 0);
epos->bh = udf_tread(inode->i_sb, block); epos->bh = sb_bread(inode->i_sb, block);
if (!epos->bh) { if (!epos->bh) {
udf_debug("reading block %u failed!\n", block); udf_debug("reading block %u failed!\n", block);
return -1; return -1;
...@@ -2203,12 +2237,13 @@ int8_t udf_current_aext(struct inode *inode, struct extent_position *epos, ...@@ -2203,12 +2237,13 @@ int8_t udf_current_aext(struct inode *inode, struct extent_position *epos,
return etype; return etype;
} }
static int8_t udf_insert_aext(struct inode *inode, struct extent_position epos, static int udf_insert_aext(struct inode *inode, struct extent_position epos,
struct kernel_lb_addr neloc, uint32_t nelen) struct kernel_lb_addr neloc, uint32_t nelen)
{ {
struct kernel_lb_addr oeloc; struct kernel_lb_addr oeloc;
uint32_t oelen; uint32_t oelen;
int8_t etype; int8_t etype;
int err;
if (epos.bh) if (epos.bh)
get_bh(epos.bh); get_bh(epos.bh);
...@@ -2218,10 +2253,10 @@ static int8_t udf_insert_aext(struct inode *inode, struct extent_position epos, ...@@ -2218,10 +2253,10 @@ static int8_t udf_insert_aext(struct inode *inode, struct extent_position epos,
neloc = oeloc; neloc = oeloc;
nelen = (etype << 30) | oelen; nelen = (etype << 30) | oelen;
} }
udf_add_aext(inode, &epos, &neloc, nelen, 1); err = udf_add_aext(inode, &epos, &neloc, nelen, 1);
brelse(epos.bh); brelse(epos.bh);
return (nelen >> 30); return err;
} }
int8_t udf_delete_aext(struct inode *inode, struct extent_position epos) int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
...@@ -2339,28 +2374,3 @@ int8_t inode_bmap(struct inode *inode, sector_t block, ...@@ -2339,28 +2374,3 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
return etype; return etype;
} }
udf_pblk_t udf_block_map(struct inode *inode, sector_t block)
{
struct kernel_lb_addr eloc;
uint32_t elen;
sector_t offset;
struct extent_position epos = {};
udf_pblk_t ret;
down_read(&UDF_I(inode)->i_data_sem);
if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) ==
(EXT_RECORDED_ALLOCATED >> 30))
ret = udf_get_lb_pblock(inode->i_sb, &eloc, offset);
else
ret = 0;
up_read(&UDF_I(inode)->i_data_sem);
brelse(epos.bh);
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV))
return udf_fixed_to_variable(ret);
else
return ret;
}
...@@ -45,7 +45,7 @@ unsigned int udf_get_last_session(struct super_block *sb) ...@@ -45,7 +45,7 @@ unsigned int udf_get_last_session(struct super_block *sb)
return 0; 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); struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
unsigned long lblock = 0; unsigned long lblock = 0;
...@@ -54,8 +54,11 @@ unsigned long udf_get_last_block(struct super_block *sb) ...@@ -54,8 +54,11 @@ unsigned long udf_get_last_block(struct super_block *sb)
* The cdrom layer call failed or returned obviously bogus value? * The cdrom layer call failed or returned obviously bogus value?
* Try using the device size... * 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); lblock = sb_bdev_nr_blocks(sb);
}
if (lblock) if (lblock)
return lblock - 1; return lblock - 1;
......
...@@ -28,22 +28,6 @@ ...@@ -28,22 +28,6 @@
#include "udf_i.h" #include "udf_i.h"
#include "udf_sb.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, struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size,
uint32_t type, uint8_t loc) uint32_t type, uint8_t loc)
{ {
...@@ -216,7 +200,7 @@ struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block, ...@@ -216,7 +200,7 @@ struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block,
if (block == 0xFFFFFFFF) if (block == 0xFFFFFFFF)
return NULL; return NULL;
bh = udf_tread(sb, block); bh = sb_bread(sb, block);
if (!bh) { if (!bh) {
udf_err(sb, "read failed, block=%u, location=%u\n", udf_err(sb, "read failed, block=%u, location=%u\n",
block, location); block, location);
......
...@@ -41,283 +41,93 @@ static inline int udf_match(int len1, const unsigned char *name1, int len2, ...@@ -41,283 +41,93 @@ static inline int udf_match(int len1, const unsigned char *name1, int len2,
return !memcmp(name1, name2, len1); return !memcmp(name1, name2, len1);
} }
int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh,
uint8_t *impuse, uint8_t *fileident)
{
uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(struct tag);
uint16_t crc;
int offset;
uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse);
uint8_t lfi = cfi->lengthFileIdent;
int padlen = fibh->eoffset - fibh->soffset - liu - lfi -
sizeof(struct fileIdentDesc);
int adinicb = 0;
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
adinicb = 1;
offset = fibh->soffset + sizeof(struct fileIdentDesc);
if (impuse) {
if (adinicb || (offset + liu < 0)) {
memcpy((uint8_t *)sfi->impUse, impuse, liu);
} else if (offset >= 0) {
memcpy(fibh->ebh->b_data + offset, impuse, liu);
} else {
memcpy((uint8_t *)sfi->impUse, impuse, -offset);
memcpy(fibh->ebh->b_data, impuse - offset,
liu + offset);
}
}
offset += liu;
if (fileident) {
if (adinicb || (offset + lfi < 0)) {
memcpy(sfi->impUse + liu, fileident, lfi);
} else if (offset >= 0) {
memcpy(fibh->ebh->b_data + offset, fileident, lfi);
} else {
memcpy(sfi->impUse + liu, fileident, -offset);
memcpy(fibh->ebh->b_data, fileident - offset,
lfi + offset);
}
}
offset += lfi;
if (adinicb || (offset + padlen < 0)) {
memset(sfi->impUse + liu + lfi, 0x00, padlen);
} else if (offset >= 0) {
memset(fibh->ebh->b_data + offset, 0x00, padlen);
} else {
memset(sfi->impUse + liu + lfi, 0x00, -offset);
memset(fibh->ebh->b_data, 0x00, padlen + offset);
}
crc = crc_itu_t(0, (uint8_t *)cfi + sizeof(struct tag),
sizeof(struct fileIdentDesc) - sizeof(struct tag));
if (fibh->sbh == fibh->ebh) {
crc = crc_itu_t(crc, (uint8_t *)sfi->impUse,
crclen + sizeof(struct tag) -
sizeof(struct fileIdentDesc));
} else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) {
crc = crc_itu_t(crc, fibh->ebh->b_data +
sizeof(struct fileIdentDesc) +
fibh->soffset,
crclen + sizeof(struct tag) -
sizeof(struct fileIdentDesc));
} else {
crc = crc_itu_t(crc, (uint8_t *)sfi->impUse,
-fibh->soffset - sizeof(struct fileIdentDesc));
crc = crc_itu_t(crc, fibh->ebh->b_data, fibh->eoffset);
}
cfi->descTag.descCRC = cpu_to_le16(crc);
cfi->descTag.descCRCLength = cpu_to_le16(crclen);
cfi->descTag.tagChecksum = udf_tag_checksum(&cfi->descTag);
if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) {
memcpy((uint8_t *)sfi, (uint8_t *)cfi,
sizeof(struct fileIdentDesc));
} else {
memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset);
memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset,
sizeof(struct fileIdentDesc) + fibh->soffset);
}
if (adinicb) {
mark_inode_dirty(inode);
} else {
if (fibh->sbh != fibh->ebh)
mark_buffer_dirty_inode(fibh->ebh, inode);
mark_buffer_dirty_inode(fibh->sbh, inode);
}
inode_inc_iversion(inode);
return 0;
}
/** /**
* udf_find_entry - find entry in given directory. * udf_fiiter_find_entry - find entry in given directory.
* *
* @dir: directory inode to search in * @dir: directory inode to search in
* @child: qstr of the name * @child: qstr of the name
* @fibh: buffer head / inode with file identifier descriptor we found * @iter: iter to use for searching
* @cfi: found file identifier descriptor with given name
* *
* This function searches in the directory @dir for a file name @child. When * This function searches in the directory @dir for a file name @child. When
* found, @fibh points to the buffer head(s) (bh is NULL for in ICB * found, @iter points to the position in the directory with given entry.
* directories) containing the file identifier descriptor (FID). In that case
* the function returns pointer to the FID in the buffer or inode - but note
* that FID may be split among two buffers (blocks) so accessing it via that
* pointer isn't easily possible. This pointer can be used only as an iterator
* for other directory manipulation functions. For inspection of the FID @cfi
* can be used - the found FID is copied there.
* *
* Returns pointer to FID, NULL when nothing found, or error code. * Returns 0 on success, < 0 on error (including -ENOENT).
*/ */
static struct fileIdentDesc *udf_find_entry(struct inode *dir, static int udf_fiiter_find_entry(struct inode *dir, const struct qstr *child,
const struct qstr *child, struct udf_fileident_iter *iter)
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi)
{ {
struct fileIdentDesc *fi = NULL;
loff_t f_pos;
udf_pblk_t block;
int flen; int flen;
unsigned char *fname = NULL, *copy_name = NULL; unsigned char *fname = NULL;
unsigned char *nameptr; struct super_block *sb = dir->i_sb;
uint8_t lfi;
uint16_t liu;
loff_t size;
struct kernel_lb_addr eloc;
uint32_t elen;
sector_t offset;
struct extent_position epos = {};
struct udf_inode_info *dinfo = UDF_I(dir);
int isdotdot = child->len == 2 && int isdotdot = child->len == 2 &&
child->name[0] == '.' && child->name[1] == '.'; child->name[0] == '.' && child->name[1] == '.';
struct super_block *sb = dir->i_sb; int ret;
size = udf_ext0_offset(dir) + dir->i_size;
f_pos = udf_ext0_offset(dir);
fibh->sbh = fibh->ebh = NULL;
fibh->soffset = fibh->eoffset = f_pos & (sb->s_blocksize - 1);
if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
if (inode_bmap(dir, f_pos >> sb->s_blocksize_bits, &epos,
&eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) {
fi = ERR_PTR(-EIO);
goto out_err;
}
block = udf_get_lb_pblock(sb, &eloc, offset);
if ((++offset << sb->s_blocksize_bits) < elen) {
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
epos.offset -= sizeof(struct short_ad);
else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
epos.offset -= sizeof(struct long_ad);
} else
offset = 0;
fibh->sbh = fibh->ebh = udf_tread(sb, block);
if (!fibh->sbh) {
fi = ERR_PTR(-EIO);
goto out_err;
}
}
fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
if (!fname) { if (!fname)
fi = ERR_PTR(-ENOMEM); return -ENOMEM;
goto out_err;
}
while (f_pos < size) {
fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
&elen, &offset);
if (!fi) {
fi = ERR_PTR(-EIO);
goto out_err;
}
liu = le16_to_cpu(cfi->lengthOfImpUse); for (ret = udf_fiiter_init(iter, dir, 0);
lfi = cfi->lengthFileIdent; !ret && iter->pos < dir->i_size;
ret = udf_fiiter_advance(iter)) {
if (fibh->sbh == fibh->ebh) { if (iter->fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
nameptr = udf_get_fi_ident(fi);
} else {
int poffset; /* Unpaded ending offset */
poffset = fibh->soffset + sizeof(struct fileIdentDesc) +
liu + lfi;
if (poffset >= lfi)
nameptr = (uint8_t *)(fibh->ebh->b_data +
poffset - lfi);
else {
if (!copy_name) {
copy_name = kmalloc(UDF_NAME_LEN_CS0,
GFP_NOFS);
if (!copy_name) {
fi = ERR_PTR(-ENOMEM);
goto out_err;
}
}
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 (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE)) if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
continue; 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)) if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
continue; continue;
} }
if ((cfi->fileCharacteristics & FID_FILE_CHAR_PARENT) && if ((iter->fi.fileCharacteristics & FID_FILE_CHAR_PARENT) &&
isdotdot) isdotdot)
goto out_ok; goto out_ok;
if (!lfi) if (!iter->fi.lengthFileIdent)
continue; 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) { if (flen < 0) {
fi = ERR_PTR(flen); ret = flen;
goto out_err; goto out_err;
} }
if (udf_match(flen, fname, child->len, child->name)) if (udf_match(flen, fname, child->len, child->name))
goto out_ok; goto out_ok;
} }
if (!ret)
ret = -ENOENT;
fi = NULL;
out_err: out_err:
if (fibh->sbh != fibh->ebh) udf_fiiter_release(iter);
brelse(fibh->ebh);
brelse(fibh->sbh);
out_ok: out_ok:
brelse(epos.bh);
kfree(fname); kfree(fname);
kfree(copy_name);
return fi; return ret;
} }
static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags) unsigned int flags)
{ {
struct inode *inode = NULL; struct inode *inode = NULL;
struct fileIdentDesc cfi; struct udf_fileident_iter iter;
struct udf_fileident_bh fibh; int err;
struct fileIdentDesc *fi;
if (dentry->d_name.len > UDF_NAME_LEN) if (dentry->d_name.len > UDF_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); err = udf_fiiter_find_entry(dir, &dentry->d_name, &iter);
if (IS_ERR(fi)) if (err < 0 && err != -ENOENT)
return ERR_CAST(fi); return ERR_PTR(err);
if (fi) { if (err == 0) {
struct kernel_lb_addr loc; struct kernel_lb_addr loc;
if (fibh.sbh != fibh.ebh) loc = lelb_to_cpu(iter.fi.icb.extLocation);
brelse(fibh.ebh); udf_fiiter_release(&iter);
brelse(fibh.sbh);
loc = lelb_to_cpu(cfi.icb.extLocation);
inode = udf_iget(dir->i_sb, &loc); inode = udf_iget(dir->i_sb, &loc);
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(inode); return ERR_CAST(inode);
...@@ -326,281 +136,243 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, ...@@ -326,281 +136,243 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
return d_splice_alias(inode, dentry); return d_splice_alias(inode, dentry);
} }
static struct fileIdentDesc *udf_add_entry(struct inode *dir, static int udf_expand_dir_adinicb(struct inode *inode, udf_pblk_t *block)
struct dentry *dentry,
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi, int *err)
{ {
struct super_block *sb = dir->i_sb; udf_pblk_t newblock;
struct fileIdentDesc *fi = NULL; struct buffer_head *dbh = NULL;
unsigned char *name = NULL;
int namelen;
loff_t f_pos;
loff_t size = udf_ext0_offset(dir) + dir->i_size;
int nfidlen;
udf_pblk_t block;
struct kernel_lb_addr eloc; struct kernel_lb_addr eloc;
uint32_t elen = 0; struct extent_position epos;
sector_t offset; uint8_t alloctype;
struct extent_position epos = {}; struct udf_inode_info *iinfo = UDF_I(inode);
struct udf_inode_info *dinfo; struct udf_fileident_iter iter;
uint8_t *impuse;
int ret;
fibh->sbh = fibh->ebh = NULL; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS); alloctype = ICBTAG_FLAG_AD_SHORT;
if (!name) { else
*err = -ENOMEM; alloctype = ICBTAG_FLAG_AD_LONG;
goto out_err;
}
if (dentry) { if (!inode->i_size) {
if (!dentry->d_name.len) { iinfo->i_alloc_type = alloctype;
*err = -EINVAL; mark_inode_dirty(inode);
goto out_err; return 0;
}
namelen = udf_put_filename(sb, dentry->d_name.name,
dentry->d_name.len,
name, UDF_NAME_LEN_CS0);
if (!namelen) {
*err = -ENAMETOOLONG;
goto out_err;
}
} else {
namelen = 0;
} }
nfidlen = ALIGN(sizeof(struct fileIdentDesc) + namelen, UDF_NAME_PAD); /* alloc block, and copy data to it */
*block = udf_new_block(inode->i_sb, inode,
f_pos = udf_ext0_offset(dir); iinfo->i_location.partitionReferenceNum,
iinfo->i_location.logicalBlockNum, &ret);
fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1); if (!(*block))
dinfo = UDF_I(dir); return ret;
if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { newblock = udf_get_pblock(inode->i_sb, *block,
if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos, iinfo->i_location.partitionReferenceNum,
&eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) { 0);
block = udf_get_lb_pblock(dir->i_sb, if (newblock == 0xffffffff)
&dinfo->i_location, 0); return -EFSCORRUPTED;
fibh->soffset = fibh->eoffset = sb->s_blocksize; dbh = sb_getblk(inode->i_sb, newblock);
goto add; if (!dbh)
return -ENOMEM;
lock_buffer(dbh);
memcpy(dbh->b_data, iinfo->i_data, inode->i_size);
memset(dbh->b_data + inode->i_size, 0,
inode->i_sb->s_blocksize - inode->i_size);
set_buffer_uptodate(dbh);
unlock_buffer(dbh);
/* Drop inline data, add block instead */
iinfo->i_alloc_type = alloctype;
memset(iinfo->i_data + iinfo->i_lenEAttr, 0, iinfo->i_lenAlloc);
iinfo->i_lenAlloc = 0;
eloc.logicalBlockNum = *block;
eloc.partitionReferenceNum =
iinfo->i_location.partitionReferenceNum;
iinfo->i_lenExtents = inode->i_size;
epos.bh = NULL;
epos.block = iinfo->i_location;
epos.offset = udf_file_entry_alloc_offset(inode);
ret = udf_add_aext(inode, &epos, &eloc, inode->i_size, 0);
brelse(epos.bh);
if (ret < 0) {
brelse(dbh);
udf_free_blocks(inode->i_sb, inode, &eloc, 0, 1);
return ret;
} }
block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); mark_inode_dirty(inode);
if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
epos.offset -= sizeof(struct short_ad);
else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
epos.offset -= sizeof(struct long_ad);
} else
offset = 0;
fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); /* Now fixup tags in moved directory entries */
if (!fibh->sbh) { for (ret = udf_fiiter_init(&iter, inode, 0);
*err = -EIO; !ret && iter.pos < inode->i_size;
goto out_err; ret = udf_fiiter_advance(&iter)) {
iter.fi.descTag.tagLocation = cpu_to_le32(*block);
if (iter.fi.lengthOfImpUse != cpu_to_le16(0))
impuse = dbh->b_data + iter.pos +
sizeof(struct fileIdentDesc);
else
impuse = NULL;
udf_fiiter_write_fi(&iter, impuse);
} }
brelse(dbh);
/*
* We don't expect the iteration to fail as the directory has been
* already verified to be correct
*/
WARN_ON_ONCE(ret);
udf_fiiter_release(&iter);
block = dinfo->i_location.logicalBlockNum; return 0;
} }
while (f_pos < size) { static int udf_fiiter_add_entry(struct inode *dir, struct dentry *dentry,
fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, struct udf_fileident_iter *iter)
&elen, &offset); {
struct udf_inode_info *dinfo = UDF_I(dir);
int nfidlen, namelen = 0;
int ret;
int off, blksize = 1 << dir->i_blkbits;
udf_pblk_t block;
char name[UDF_NAME_LEN_CS0];
if (!fi) { if (dentry) {
*err = -EIO; if (!dentry->d_name.len)
goto out_err; return -EINVAL;
namelen = udf_put_filename(dir->i_sb, dentry->d_name.name,
dentry->d_name.len,
name, UDF_NAME_LEN_CS0);
if (!namelen)
return -ENAMETOOLONG;
} }
nfidlen = ALIGN(sizeof(struct fileIdentDesc) + namelen, UDF_NAME_PAD);
if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { for (ret = udf_fiiter_init(iter, dir, 0);
if (udf_dir_entry_len(cfi) == nfidlen) { !ret && iter->pos < dir->i_size;
cfi->descTag.tagSerialNum = cpu_to_le16(1); ret = udf_fiiter_advance(iter)) {
cfi->fileVersionNum = cpu_to_le16(1); if (iter->fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
cfi->fileCharacteristics = 0; if (udf_dir_entry_len(&iter->fi) == nfidlen) {
cfi->lengthFileIdent = namelen; iter->fi.descTag.tagSerialNum = cpu_to_le16(1);
cfi->lengthOfImpUse = cpu_to_le16(0); iter->fi.fileVersionNum = cpu_to_le16(1);
if (!udf_write_fi(dir, cfi, fi, fibh, NULL, iter->fi.fileCharacteristics = 0;
name)) iter->fi.lengthFileIdent = namelen;
goto out_ok; iter->fi.lengthOfImpUse = cpu_to_le16(0);
else { memcpy(iter->namebuf, name, namelen);
*err = -EIO; iter->name = iter->namebuf;
goto out_err; return 0;
} }
} }
} }
if (ret) {
udf_fiiter_release(iter);
return ret;
} }
add:
f_pos += nfidlen;
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
sb->s_blocksize - fibh->eoffset < nfidlen) { blksize - udf_ext0_offset(dir) - iter->pos < nfidlen) {
brelse(epos.bh); udf_fiiter_release(iter);
epos.bh = NULL; ret = udf_expand_dir_adinicb(dir, &block);
fibh->soffset -= udf_ext0_offset(dir); if (ret)
fibh->eoffset -= udf_ext0_offset(dir); return ret;
f_pos -= udf_ext0_offset(dir); ret = udf_fiiter_init(iter, dir, dir->i_size);
if (fibh->sbh != fibh->ebh) if (ret < 0)
brelse(fibh->ebh); return ret;
brelse(fibh->sbh);
fibh->sbh = fibh->ebh =
udf_expand_dir_adinicb(dir, &block, err);
if (!fibh->sbh)
goto out_err;
epos.block = dinfo->i_location;
epos.offset = udf_file_entry_alloc_offset(dir);
/* Load extent udf_expand_dir_adinicb() has created */
udf_current_aext(dir, &epos, &eloc, &elen, 1);
}
/* Entry fits into current block? */
if (sb->s_blocksize - fibh->eoffset >= nfidlen) {
fibh->soffset = fibh->eoffset;
fibh->eoffset += nfidlen;
if (fibh->sbh != fibh->ebh) {
brelse(fibh->sbh);
fibh->sbh = fibh->ebh;
} }
/* Get blocknumber to use for entry tag */
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
block = dinfo->i_location.logicalBlockNum; block = dinfo->i_location.logicalBlockNum;
fi = (struct fileIdentDesc *)
(dinfo->i_data + fibh->soffset -
udf_ext0_offset(dir) +
dinfo->i_lenEAttr);
} else {
block = eloc.logicalBlockNum +
((elen - 1) >>
dir->i_sb->s_blocksize_bits);
fi = (struct fileIdentDesc *)
(fibh->sbh->b_data + fibh->soffset);
}
} else {
/* Round up last extent in the file */
elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1);
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
epos.offset -= sizeof(struct short_ad);
else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
epos.offset -= sizeof(struct long_ad);
udf_write_aext(dir, &epos, &eloc, elen, 1);
dinfo->i_lenExtents = (dinfo->i_lenExtents + sb->s_blocksize
- 1) & ~(sb->s_blocksize - 1);
fibh->soffset = fibh->eoffset - sb->s_blocksize;
fibh->eoffset += nfidlen - sb->s_blocksize;
if (fibh->sbh != fibh->ebh) {
brelse(fibh->sbh);
fibh->sbh = fibh->ebh;
}
block = eloc.logicalBlockNum + ((elen - 1) >>
dir->i_sb->s_blocksize_bits);
fibh->ebh = udf_bread(dir,
f_pos >> dir->i_sb->s_blocksize_bits, 1, err);
if (!fibh->ebh)
goto out_err;
/* Extents could have been merged, invalidate our position */
brelse(epos.bh);
epos.bh = NULL;
epos.block = dinfo->i_location;
epos.offset = udf_file_entry_alloc_offset(dir);
if (!fibh->soffset) {
/* Find the freshly allocated block */
while (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
(EXT_RECORDED_ALLOCATED >> 30))
;
block = eloc.logicalBlockNum + ((elen - 1) >>
dir->i_sb->s_blocksize_bits);
brelse(fibh->sbh);
fibh->sbh = fibh->ebh;
fi = (struct fileIdentDesc *)(fibh->sbh->b_data);
} else { } else {
fi = (struct fileIdentDesc *) block = iter->eloc.logicalBlockNum +
(fibh->sbh->b_data + sb->s_blocksize + ((iter->elen - 1) >> dir->i_blkbits);
fibh->soffset);
} }
} off = iter->pos & (blksize - 1);
if (!off)
memset(cfi, 0, sizeof(struct fileIdentDesc)); off = blksize;
if (UDF_SB(sb)->s_udfrev >= 0x0200) /* Entry fits into current block? */
udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, if (blksize - udf_ext0_offset(dir) - off >= nfidlen)
goto store_fi;
ret = udf_fiiter_append_blk(iter);
if (ret) {
udf_fiiter_release(iter);
return ret;
}
/* Entry will be completely in the new block? Update tag location... */
if (!(iter->pos & (blksize - 1)))
block = iter->eloc.logicalBlockNum +
((iter->elen - 1) >> dir->i_blkbits);
store_fi:
memset(&iter->fi, 0, sizeof(struct fileIdentDesc));
if (UDF_SB(dir->i_sb)->s_udfrev >= 0x0200)
udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 3, 1, block,
sizeof(struct tag)); sizeof(struct tag));
else else
udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 2, 1, block,
sizeof(struct tag)); sizeof(struct tag));
cfi->fileVersionNum = cpu_to_le16(1); iter->fi.fileVersionNum = cpu_to_le16(1);
cfi->lengthFileIdent = namelen; iter->fi.lengthFileIdent = namelen;
cfi->lengthOfImpUse = cpu_to_le16(0); iter->fi.lengthOfImpUse = cpu_to_le16(0);
if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { memcpy(iter->namebuf, name, namelen);
iter->name = iter->namebuf;
dir->i_size += nfidlen; dir->i_size += nfidlen;
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
dinfo->i_lenAlloc += nfidlen; dinfo->i_lenAlloc += nfidlen;
else {
/* Find the last extent and truncate it to proper size */
while (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
(EXT_RECORDED_ALLOCATED >> 30))
;
elen -= dinfo->i_lenExtents - dir->i_size;
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
epos.offset -= sizeof(struct short_ad);
else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
epos.offset -= sizeof(struct long_ad);
udf_write_aext(dir, &epos, &eloc, elen, 1);
dinfo->i_lenExtents = dir->i_size;
}
mark_inode_dirty(dir);
goto out_ok;
} else { } else {
*err = -EIO; /* Truncate last extent to proper size */
goto out_err; udf_fiiter_update_elen(iter, iter->elen -
(dinfo->i_lenExtents - dir->i_size));
} }
mark_inode_dirty(dir);
out_err: return 0;
fi = NULL;
if (fibh->sbh != fibh->ebh)
brelse(fibh->ebh);
brelse(fibh->sbh);
out_ok:
brelse(epos.bh);
kfree(name);
return fi;
} }
static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi, static void udf_fiiter_delete_entry(struct udf_fileident_iter *iter)
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi)
{ {
cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED; iter->fi.fileCharacteristics |= FID_FILE_CHAR_DELETED;
if (UDF_QUERY_FLAG(iter->dir->i_sb, UDF_FLAG_STRICT))
memset(&iter->fi.icb, 0x00, sizeof(struct long_ad));
udf_fiiter_write_fi(iter, NULL);
}
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) static void udf_add_fid_counter(struct super_block *sb, bool dir, int val)
memset(&(cfi->icb), 0x00, sizeof(struct long_ad)); {
struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL); if (!lvidiu)
return;
mutex_lock(&UDF_SB(sb)->s_alloc_mutex);
if (dir)
le32_add_cpu(&lvidiu->numDirs, val);
else
le32_add_cpu(&lvidiu->numFiles, val);
udf_updated_lvid(sb);
mutex_unlock(&UDF_SB(sb)->s_alloc_mutex);
} }
static int udf_add_nondir(struct dentry *dentry, struct inode *inode) static int udf_add_nondir(struct dentry *dentry, struct inode *inode)
{ {
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
struct inode *dir = d_inode(dentry->d_parent); struct inode *dir = d_inode(dentry->d_parent);
struct udf_fileident_bh fibh; struct udf_fileident_iter iter;
struct fileIdentDesc cfi, *fi;
int err; int err;
fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); err = udf_fiiter_add_entry(dir, dentry, &iter);
if (unlikely(!fi)) { if (err) {
inode_dec_link_count(inode); inode_dec_link_count(inode);
discard_new_inode(inode); discard_new_inode(inode);
return err; return err;
} }
cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); iter.fi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse =
cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); udf_fiiter_write_fi(&iter, NULL);
dir->i_ctime = dir->i_mtime = current_time(dir); dir->i_ctime = dir->i_mtime = current_time(dir);
mark_inode_dirty(dir); mark_inode_dirty(dir);
if (fibh.sbh != fibh.ebh) udf_fiiter_release(&iter);
brelse(fibh.ebh); udf_add_fid_counter(dir->i_sb, false, 1);
brelse(fibh.sbh);
d_instantiate_new(dentry, inode); d_instantiate_new(dentry, inode);
return 0; return 0;
...@@ -614,9 +386,6 @@ static int udf_create(struct mnt_idmap *idmap, struct inode *dir, ...@@ -614,9 +386,6 @@ static int udf_create(struct mnt_idmap *idmap, struct inode *dir,
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_ERR(inode); return PTR_ERR(inode);
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
inode->i_data.a_ops = &udf_adinicb_aops;
else
inode->i_data.a_ops = &udf_aops; inode->i_data.a_ops = &udf_aops;
inode->i_op = &udf_file_inode_operations; inode->i_op = &udf_file_inode_operations;
inode->i_fop = &udf_file_operations; inode->i_fop = &udf_file_operations;
...@@ -633,9 +402,6 @@ static int udf_tmpfile(struct mnt_idmap *idmap, struct inode *dir, ...@@ -633,9 +402,6 @@ static int udf_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_ERR(inode); return PTR_ERR(inode);
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
inode->i_data.a_ops = &udf_adinicb_aops;
else
inode->i_data.a_ops = &udf_aops; inode->i_data.a_ops = &udf_aops;
inode->i_op = &udf_file_inode_operations; inode->i_op = &udf_file_inode_operations;
inode->i_fop = &udf_file_operations; inode->i_fop = &udf_file_operations;
...@@ -665,8 +431,7 @@ static int udf_mkdir(struct mnt_idmap *idmap, struct inode *dir, ...@@ -665,8 +431,7 @@ static int udf_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode) struct dentry *dentry, umode_t mode)
{ {
struct inode *inode; struct inode *inode;
struct udf_fileident_bh fibh; struct udf_fileident_iter iter;
struct fileIdentDesc cfi, *fi;
int err; int err;
struct udf_inode_info *dinfo = UDF_I(dir); struct udf_inode_info *dinfo = UDF_I(dir);
struct udf_inode_info *iinfo; struct udf_inode_info *iinfo;
...@@ -678,183 +443,113 @@ static int udf_mkdir(struct mnt_idmap *idmap, struct inode *dir, ...@@ -678,183 +443,113 @@ static int udf_mkdir(struct mnt_idmap *idmap, struct inode *dir,
iinfo = UDF_I(inode); iinfo = UDF_I(inode);
inode->i_op = &udf_dir_inode_operations; inode->i_op = &udf_dir_inode_operations;
inode->i_fop = &udf_dir_operations; inode->i_fop = &udf_dir_operations;
fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err); err = udf_fiiter_add_entry(inode, NULL, &iter);
if (!fi) { if (err) {
inode_dec_link_count(inode); clear_nlink(inode);
discard_new_inode(inode); discard_new_inode(inode);
goto out; return err;
} }
set_nlink(inode, 2); set_nlink(inode, 2);
cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
cfi.icb.extLocation = cpu_to_lelb(dinfo->i_location); iter.fi.icb.extLocation = cpu_to_lelb(dinfo->i_location);
*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse =
cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL); cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL);
cfi.fileCharacteristics = iter.fi.fileCharacteristics =
FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT; FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT;
udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL); udf_fiiter_write_fi(&iter, NULL);
brelse(fibh.sbh); udf_fiiter_release(&iter);
mark_inode_dirty(inode); mark_inode_dirty(inode);
fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); err = udf_fiiter_add_entry(dir, dentry, &iter);
if (!fi) { if (err) {
clear_nlink(inode); clear_nlink(inode);
mark_inode_dirty(inode);
discard_new_inode(inode); discard_new_inode(inode);
goto out; return err;
} }
cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); iter.fi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse =
cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY; iter.fi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); udf_fiiter_write_fi(&iter, NULL);
udf_fiiter_release(&iter);
udf_add_fid_counter(dir->i_sb, true, 1);
inc_nlink(dir); inc_nlink(dir);
dir->i_ctime = dir->i_mtime = current_time(dir); dir->i_ctime = dir->i_mtime = current_time(dir);
mark_inode_dirty(dir); mark_inode_dirty(dir);
d_instantiate_new(dentry, inode); d_instantiate_new(dentry, inode);
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
err = 0;
out: return 0;
return err;
} }
static int empty_dir(struct inode *dir) static int empty_dir(struct inode *dir)
{ {
struct fileIdentDesc *fi, cfi; struct udf_fileident_iter iter;
struct udf_fileident_bh fibh; int ret;
loff_t f_pos;
loff_t size = udf_ext0_offset(dir) + dir->i_size; for (ret = udf_fiiter_init(&iter, dir, 0);
udf_pblk_t block; !ret && iter.pos < dir->i_size;
struct kernel_lb_addr eloc; ret = udf_fiiter_advance(&iter)) {
uint32_t elen; if (iter.fi.lengthFileIdent &&
sector_t offset; !(iter.fi.fileCharacteristics & FID_FILE_CHAR_DELETED)) {
struct extent_position epos = {}; udf_fiiter_release(&iter);
struct udf_inode_info *dinfo = UDF_I(dir);
f_pos = udf_ext0_offset(dir);
fibh.soffset = fibh.eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
fibh.sbh = fibh.ebh = NULL;
else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits,
&epos, &eloc, &elen, &offset) ==
(EXT_RECORDED_ALLOCATED >> 30)) {
block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
epos.offset -= sizeof(struct short_ad);
else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
epos.offset -= sizeof(struct long_ad);
} else
offset = 0;
fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block);
if (!fibh.sbh) {
brelse(epos.bh);
return 0; return 0;
} }
} else {
brelse(epos.bh);
return 0;
} }
udf_fiiter_release(&iter);
while (f_pos < size) {
fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc,
&elen, &offset);
if (!fi) {
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
brelse(epos.bh);
return 0;
}
if (cfi.lengthFileIdent &&
(cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) {
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
brelse(epos.bh);
return 0;
}
}
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
brelse(epos.bh);
return 1; return 1;
} }
static int udf_rmdir(struct inode *dir, struct dentry *dentry) static int udf_rmdir(struct inode *dir, struct dentry *dentry)
{ {
int retval; int ret;
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct udf_fileident_bh fibh; struct udf_fileident_iter iter;
struct fileIdentDesc *fi, cfi;
struct kernel_lb_addr tloc; struct kernel_lb_addr tloc;
retval = -ENOENT; ret = udf_fiiter_find_entry(dir, &dentry->d_name, &iter);
fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); if (ret)
if (IS_ERR_OR_NULL(fi)) {
if (fi)
retval = PTR_ERR(fi);
goto out; goto out;
}
retval = -EIO; ret = -EFSCORRUPTED;
tloc = lelb_to_cpu(cfi.icb.extLocation); tloc = lelb_to_cpu(iter.fi.icb.extLocation);
if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino) if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino)
goto end_rmdir; goto end_rmdir;
retval = -ENOTEMPTY; ret = -ENOTEMPTY;
if (!empty_dir(inode)) if (!empty_dir(inode))
goto end_rmdir; goto end_rmdir;
retval = udf_delete_entry(dir, fi, &fibh, &cfi); udf_fiiter_delete_entry(&iter);
if (retval)
goto end_rmdir;
if (inode->i_nlink != 2) if (inode->i_nlink != 2)
udf_warn(inode->i_sb, "empty directory has nlink != 2 (%u)\n", udf_warn(inode->i_sb, "empty directory has nlink != 2 (%u)\n",
inode->i_nlink); inode->i_nlink);
clear_nlink(inode); clear_nlink(inode);
inode->i_size = 0; inode->i_size = 0;
inode_dec_link_count(dir); inode_dec_link_count(dir);
udf_add_fid_counter(dir->i_sb, true, -1);
inode->i_ctime = dir->i_ctime = dir->i_mtime = inode->i_ctime = dir->i_ctime = dir->i_mtime =
current_time(inode); current_time(inode);
mark_inode_dirty(dir); mark_inode_dirty(dir);
ret = 0;
end_rmdir: end_rmdir:
if (fibh.sbh != fibh.ebh) udf_fiiter_release(&iter);
brelse(fibh.ebh);
brelse(fibh.sbh);
out: out:
return retval; return ret;
} }
static int udf_unlink(struct inode *dir, struct dentry *dentry) static int udf_unlink(struct inode *dir, struct dentry *dentry)
{ {
int retval; int ret;
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct udf_fileident_bh fibh; struct udf_fileident_iter iter;
struct fileIdentDesc *fi;
struct fileIdentDesc cfi;
struct kernel_lb_addr tloc; struct kernel_lb_addr tloc;
retval = -ENOENT; ret = udf_fiiter_find_entry(dir, &dentry->d_name, &iter);
fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); if (ret)
if (IS_ERR_OR_NULL(fi)) {
if (fi)
retval = PTR_ERR(fi);
goto out; goto out;
}
retval = -EIO; ret = -EFSCORRUPTED;
tloc = lelb_to_cpu(cfi.icb.extLocation); tloc = lelb_to_cpu(iter.fi.icb.extLocation);
if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino) if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino)
goto end_unlink; goto end_unlink;
...@@ -863,22 +558,17 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry) ...@@ -863,22 +558,17 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry)
inode->i_ino, inode->i_nlink); inode->i_ino, inode->i_nlink);
set_nlink(inode, 1); set_nlink(inode, 1);
} }
retval = udf_delete_entry(dir, fi, &fibh, &cfi); udf_fiiter_delete_entry(&iter);
if (retval)
goto end_unlink;
dir->i_ctime = dir->i_mtime = current_time(dir); dir->i_ctime = dir->i_mtime = current_time(dir);
mark_inode_dirty(dir); mark_inode_dirty(dir);
inode_dec_link_count(inode); inode_dec_link_count(inode);
udf_add_fid_counter(dir->i_sb, false, -1);
inode->i_ctime = dir->i_ctime; inode->i_ctime = dir->i_ctime;
retval = 0; ret = 0;
end_unlink: end_unlink:
if (fibh.sbh != fibh.ebh) udf_fiiter_release(&iter);
brelse(fibh.ebh);
brelse(fibh.sbh);
out: out:
return retval; return ret;
} }
static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir, static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
...@@ -929,15 +619,20 @@ static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir, ...@@ -929,15 +619,20 @@ static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
iinfo->i_location.partitionReferenceNum; iinfo->i_location.partitionReferenceNum;
bsize = sb->s_blocksize; bsize = sb->s_blocksize;
iinfo->i_lenExtents = bsize; iinfo->i_lenExtents = bsize;
udf_add_aext(inode, &epos, &eloc, bsize, 0); err = udf_add_aext(inode, &epos, &eloc, bsize, 0);
brelse(epos.bh); brelse(epos.bh);
if (err < 0) {
udf_free_blocks(sb, inode, &eloc, 0, 1);
goto out_no_entry;
}
block = udf_get_pblock(sb, block, block = udf_get_pblock(sb, block,
iinfo->i_location.partitionReferenceNum, iinfo->i_location.partitionReferenceNum,
0); 0);
epos.bh = udf_tgetblk(sb, block); epos.bh = sb_getblk(sb, block);
if (unlikely(!epos.bh)) { if (unlikely(!epos.bh)) {
err = -ENOMEM; err = -ENOMEM;
udf_free_blocks(sb, inode, &eloc, 0, 1);
goto out_no_entry; goto out_no_entry;
} }
lock_buffer(epos.bh); lock_buffer(epos.bh);
...@@ -1038,28 +733,23 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir, ...@@ -1038,28 +733,23 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry) struct dentry *dentry)
{ {
struct inode *inode = d_inode(old_dentry); struct inode *inode = d_inode(old_dentry);
struct udf_fileident_bh fibh; struct udf_fileident_iter iter;
struct fileIdentDesc cfi, *fi;
int err; int err;
fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); err = udf_fiiter_add_entry(dir, dentry, &iter);
if (!fi) { if (err)
return err; return err;
} iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); iter.fi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location);
cfi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location);
if (UDF_SB(inode->i_sb)->s_lvid_bh) { if (UDF_SB(inode->i_sb)->s_lvid_bh) {
*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse =
cpu_to_le32(lvid_get_unique_id(inode->i_sb)); cpu_to_le32(lvid_get_unique_id(inode->i_sb));
} }
udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); udf_fiiter_write_fi(&iter, NULL);
if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) udf_fiiter_release(&iter);
mark_inode_dirty(dir);
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
inc_nlink(inode); inc_nlink(inode);
udf_add_fid_counter(dir->i_sb, false, 1);
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
mark_inode_dirty(inode); mark_inode_dirty(inode);
dir->i_ctime = dir->i_mtime = current_time(dir); dir->i_ctime = dir->i_mtime = current_time(dir);
...@@ -1079,78 +769,75 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir, ...@@ -1079,78 +769,75 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
{ {
struct inode *old_inode = d_inode(old_dentry); struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry); struct inode *new_inode = d_inode(new_dentry);
struct udf_fileident_bh ofibh, nfibh; struct udf_fileident_iter oiter, niter, diriter;
struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL; bool has_diriter = false;
struct fileIdentDesc ocfi, ncfi; int retval;
struct buffer_head *dir_bh = NULL;
int retval = -ENOENT;
struct kernel_lb_addr tloc; struct kernel_lb_addr tloc;
struct udf_inode_info *old_iinfo = UDF_I(old_inode);
if (flags & ~RENAME_NOREPLACE) if (flags & ~RENAME_NOREPLACE)
return -EINVAL; return -EINVAL;
ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter);
if (!ofi || IS_ERR(ofi)) { if (retval)
if (IS_ERR(ofi)) return retval;
retval = PTR_ERR(ofi);
goto end_rename;
}
if (ofibh.sbh != ofibh.ebh)
brelse(ofibh.ebh);
brelse(ofibh.sbh);
tloc = lelb_to_cpu(ocfi.icb.extLocation);
if (udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino)
goto end_rename;
nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi); tloc = lelb_to_cpu(oiter.fi.icb.extLocation);
if (IS_ERR(nfi)) { if (udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino) {
retval = PTR_ERR(nfi); retval = -ENOENT;
goto end_rename; goto out_oiter;
}
if (nfi && !new_inode) {
if (nfibh.sbh != nfibh.ebh)
brelse(nfibh.ebh);
brelse(nfibh.sbh);
nfi = NULL;
} }
if (S_ISDIR(old_inode->i_mode)) {
int offset = udf_ext0_offset(old_inode);
if (S_ISDIR(old_inode->i_mode)) {
if (new_inode) { if (new_inode) {
retval = -ENOTEMPTY; retval = -ENOTEMPTY;
if (!empty_dir(new_inode)) if (!empty_dir(new_inode))
goto end_rename; goto out_oiter;
} }
retval = -EIO; /*
if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { * We need to protect against old_inode getting converted from
dir_fi = udf_get_fileident( * ICB to normal directory.
old_iinfo->i_data - */
(old_iinfo->i_efe ? inode_lock_nested(old_inode, I_MUTEX_NONDIR2);
sizeof(struct extendedFileEntry) : retval = udf_fiiter_find_entry(old_inode, &dotdot_name,
sizeof(struct fileEntry)), &diriter);
old_inode->i_sb->s_blocksize, &offset); if (retval == -ENOENT) {
} else { udf_err(old_inode->i_sb,
dir_bh = udf_bread(old_inode, 0, 0, &retval); "directory (ino %lu) has no '..' entry\n",
if (!dir_bh) old_inode->i_ino);
goto end_rename; retval = -EFSCORRUPTED;
dir_fi = udf_get_fileident(dir_bh->b_data, }
old_inode->i_sb->s_blocksize, &offset); if (retval) {
} inode_unlock(old_inode);
if (!dir_fi) goto out_oiter;
goto end_rename; }
tloc = lelb_to_cpu(dir_fi->icb.extLocation); has_diriter = true;
tloc = lelb_to_cpu(diriter.fi.icb.extLocation);
if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) != if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) !=
old_dir->i_ino) old_dir->i_ino) {
goto end_rename; retval = -EFSCORRUPTED;
} udf_err(old_inode->i_sb,
if (!nfi) { "directory (ino %lu) has parent entry pointing to another inode (%lu != %u)\n",
nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, old_inode->i_ino, old_dir->i_ino,
&retval); udf_get_lb_pblock(old_inode->i_sb, &tloc, 0));
if (!nfi) goto out_oiter;
goto end_rename; }
}
retval = udf_fiiter_find_entry(new_dir, &new_dentry->d_name, &niter);
if (retval && retval != -ENOENT)
goto out_oiter;
/* Entry found but not passed by VFS? */
if (!retval && !new_inode) {
retval = -EFSCORRUPTED;
udf_fiiter_release(&niter);
goto out_oiter;
}
/* Entry not found? Need to add one... */
if (retval) {
udf_fiiter_release(&niter);
retval = udf_fiiter_add_entry(new_dir, new_dentry, &niter);
if (retval)
goto out_oiter;
} }
/* /*
...@@ -1163,31 +850,46 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir, ...@@ -1163,31 +850,46 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
/* /*
* ok, that's it * ok, that's it
*/ */
ncfi.fileVersionNum = ocfi.fileVersionNum; niter.fi.fileVersionNum = oiter.fi.fileVersionNum;
ncfi.fileCharacteristics = ocfi.fileCharacteristics; niter.fi.fileCharacteristics = oiter.fi.fileCharacteristics;
memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(ocfi.icb)); memcpy(&(niter.fi.icb), &(oiter.fi.icb), sizeof(oiter.fi.icb));
udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); udf_fiiter_write_fi(&niter, NULL);
udf_fiiter_release(&niter);
/* The old fid may have moved - find it again */ /*
ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); * The old entry may have moved due to new entry allocation. Find it
udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); * again.
*/
udf_fiiter_release(&oiter);
retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter);
if (retval) {
udf_err(old_dir->i_sb,
"failed to find renamed entry again in directory (ino %lu)\n",
old_dir->i_ino);
} else {
udf_fiiter_delete_entry(&oiter);
udf_fiiter_release(&oiter);
}
if (new_inode) { if (new_inode) {
new_inode->i_ctime = current_time(new_inode); new_inode->i_ctime = current_time(new_inode);
inode_dec_link_count(new_inode); inode_dec_link_count(new_inode);
udf_add_fid_counter(old_dir->i_sb, S_ISDIR(new_inode->i_mode),
-1);
} }
old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
new_dir->i_ctime = new_dir->i_mtime = current_time(new_dir); new_dir->i_ctime = new_dir->i_mtime = current_time(new_dir);
mark_inode_dirty(old_dir); mark_inode_dirty(old_dir);
mark_inode_dirty(new_dir); mark_inode_dirty(new_dir);
if (dir_fi) { if (has_diriter) {
dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location); diriter.fi.icb.extLocation =
udf_update_tag((char *)dir_fi, udf_dir_entry_len(dir_fi)); cpu_to_lelb(UDF_I(new_dir)->i_location);
if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) udf_update_tag((char *)&diriter.fi,
mark_inode_dirty(old_inode); udf_dir_entry_len(&diriter.fi));
else udf_fiiter_write_fi(&diriter, NULL);
mark_buffer_dirty_inode(dir_bh, old_inode); udf_fiiter_release(&diriter);
inode_unlock(old_inode);
inode_dec_link_count(old_dir); inode_dec_link_count(old_dir);
if (new_inode) if (new_inode)
...@@ -1197,22 +899,13 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir, ...@@ -1197,22 +899,13 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir,
mark_inode_dirty(new_dir); mark_inode_dirty(new_dir);
} }
} }
return 0;
if (ofi) { out_oiter:
if (ofibh.sbh != ofibh.ebh) if (has_diriter) {
brelse(ofibh.ebh); udf_fiiter_release(&diriter);
brelse(ofibh.sbh); inode_unlock(old_inode);
}
retval = 0;
end_rename:
brelse(dir_bh);
if (nfi) {
if (nfibh.sbh != nfibh.ebh)
brelse(nfibh.ebh);
brelse(nfibh.sbh);
} }
udf_fiiter_release(&oiter);
return retval; return retval;
} }
...@@ -1221,17 +914,15 @@ static struct dentry *udf_get_parent(struct dentry *child) ...@@ -1221,17 +914,15 @@ static struct dentry *udf_get_parent(struct dentry *child)
{ {
struct kernel_lb_addr tloc; struct kernel_lb_addr tloc;
struct inode *inode = NULL; struct inode *inode = NULL;
struct fileIdentDesc cfi; struct udf_fileident_iter iter;
struct udf_fileident_bh fibh; int err;
if (!udf_find_entry(d_inode(child), &dotdot_name, &fibh, &cfi))
return ERR_PTR(-EACCES);
if (fibh.sbh != fibh.ebh) err = udf_fiiter_find_entry(d_inode(child), &dotdot_name, &iter);
brelse(fibh.ebh); if (err)
brelse(fibh.sbh); return ERR_PTR(err);
tloc = lelb_to_cpu(cfi.icb.extLocation); tloc = lelb_to_cpu(iter.fi.icb.extLocation);
udf_fiiter_release(&iter);
inode = udf_iget(child->d_sb, &tloc); inode = udf_iget(child->d_sb, &tloc);
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(inode); return ERR_CAST(inode);
......
...@@ -54,6 +54,7 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, ...@@ -54,6 +54,7 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block,
struct udf_part_map *map; struct udf_part_map *map;
struct udf_virtual_data *vdata; struct udf_virtual_data *vdata;
struct udf_inode_info *iinfo = UDF_I(sbi->s_vat_inode); struct udf_inode_info *iinfo = UDF_I(sbi->s_vat_inode);
int err;
map = &sbi->s_partmaps[partition]; map = &sbi->s_partmaps[partition];
vdata = &map->s_type_specific.s_virtual; vdata = &map->s_type_specific.s_virtual;
...@@ -79,12 +80,10 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, ...@@ -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; index = vdata->s_start_offset / sizeof(uint32_t) + block;
} }
loc = udf_block_map(sbi->s_vat_inode, newblock); bh = udf_bread(sbi->s_vat_inode, newblock, 0, &err);
bh = sb_bread(sb, loc);
if (!bh) { if (!bh) {
udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%u,%u) VAT: %u[%u]\n", udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%u,%u)\n",
sb, block, partition, loc, index); sb, block, partition);
return 0xFFFFFFFF; return 0xFFFFFFFF;
} }
......
...@@ -86,6 +86,13 @@ enum { ...@@ -86,6 +86,13 @@ enum {
#define UDF_MAX_LVID_NESTING 1000 #define UDF_MAX_LVID_NESTING 1000
enum { UDF_MAX_LINKS = 0xffff }; 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 */ /* These are the "meat" - everything else is stuffing */
static int udf_fill_super(struct super_block *, void *, int); static int udf_fill_super(struct super_block *, void *, int);
...@@ -147,6 +154,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb) ...@@ -147,6 +154,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
ei->i_next_alloc_goal = 0; ei->i_next_alloc_goal = 0;
ei->i_strat4096 = 0; ei->i_strat4096 = 0;
ei->i_streamdir = 0; ei->i_streamdir = 0;
ei->i_hidden = 0;
init_rwsem(&ei->i_data_sem); init_rwsem(&ei->i_data_sem);
ei->cached_extent.lstart = -1; ei->cached_extent.lstart = -1;
spin_lock_init(&ei->i_extent_cache_lock); spin_lock_init(&ei->i_extent_cache_lock);
...@@ -733,7 +741,7 @@ static int udf_check_vsd(struct super_block *sb) ...@@ -733,7 +741,7 @@ static int udf_check_vsd(struct super_block *sb)
* added */ * added */
for (; !nsr && sector < VSD_MAX_SECTOR_OFFSET; sector += sectorsize) { for (; !nsr && sector < VSD_MAX_SECTOR_OFFSET; sector += sectorsize) {
/* Read a block */ /* Read a block */
bh = udf_tread(sb, sector >> sb->s_blocksize_bits); bh = sb_bread(sb, sector >> sb->s_blocksize_bits);
if (!bh) if (!bh)
break; break;
...@@ -1175,7 +1183,6 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) ...@@ -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 udf_part_map *map = &sbi->s_partmaps[p_index];
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
struct udf_inode_info *vati; struct udf_inode_info *vati;
uint32_t pos;
struct virtualAllocationTable20 *vat20; struct virtualAllocationTable20 *vat20;
sector_t blocks = sb_bdev_nr_blocks(sb); 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) ...@@ -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) { } else if (map->s_partition_type == UDF_VIRTUAL_MAP20) {
vati = UDF_I(sbi->s_vat_inode); vati = UDF_I(sbi->s_vat_inode);
if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
pos = udf_block_map(sbi->s_vat_inode, 0); int err = 0;
bh = sb_bread(sb, pos);
if (!bh) bh = udf_bread(sbi->s_vat_inode, 0, 0, &err);
return -EIO; if (!bh) {
if (!err)
err = -EFSCORRUPTED;
return err;
}
vat20 = (struct virtualAllocationTable20 *)bh->b_data; vat20 = (struct virtualAllocationTable20 *)bh->b_data;
} else { } else {
vat20 = (struct virtualAllocationTable20 *) vat20 = (struct virtualAllocationTable20 *)
...@@ -1838,10 +1849,6 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block, ...@@ -1838,10 +1849,6 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block,
uint16_t ident; uint16_t ident;
int ret; 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); bh = udf_read_tagged(sb, block, block, &ident);
if (!bh) if (!bh)
return -EAGAIN; return -EAGAIN;
...@@ -1860,10 +1867,10 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block, ...@@ -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 * Returns < 0 on error, 0 on success. -EAGAIN is special - try next set
* of anchors. * 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) struct kernel_lb_addr *fileset)
{ {
sector_t last[6]; udf_pblk_t last[6];
int i; int i;
struct udf_sb_info *sbi = UDF_SB(sb); struct udf_sb_info *sbi = UDF_SB(sb);
int last_count = 0; int last_count = 0;
...@@ -1923,46 +1930,6 @@ static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock, ...@@ -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); 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 * Check Volume Structure Descriptor, find Anchor block and load Volume
* Descriptor Sequence. * Descriptor Sequence.
...@@ -2003,7 +1970,7 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, ...@@ -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 */ /* Look for anchor block and load Volume Descriptor Sequence */
sbi->s_anchor = uopt->anchor; 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 (ret < 0) {
if (!silent && ret == -EAGAIN) if (!silent && ret == -EAGAIN)
udf_warn(sb, "No anchor found\n"); udf_warn(sb, "No anchor found\n");
...@@ -2297,7 +2264,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) ...@@ -2297,7 +2264,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
ret = -ENOMEM; ret = -ENOMEM;
goto error_out; goto error_out;
} }
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = UDF_MAX_FILESIZE;
sb->s_max_links = UDF_MAX_LINKS; sb->s_max_links = UDF_MAX_LINKS;
return 0; return 0;
...@@ -2454,7 +2421,7 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb, ...@@ -2454,7 +2421,7 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb,
if (bytes) { if (bytes) {
brelse(bh); brelse(bh);
newblock = udf_get_lb_pblock(sb, &loc, ++block); newblock = udf_get_lb_pblock(sb, &loc, ++block);
bh = udf_tread(sb, newblock); bh = sb_bread(sb, newblock);
if (!bh) { if (!bh) {
udf_debug("read failed\n"); udf_debug("read failed\n");
goto out; goto out;
......
...@@ -107,48 +107,40 @@ static int udf_symlink_filler(struct file *file, struct folio *folio) ...@@ -107,48 +107,40 @@ static int udf_symlink_filler(struct file *file, struct folio *folio)
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
unsigned char *symlink; unsigned char *symlink;
int err; int err = 0;
unsigned char *p = page_address(page); unsigned char *p = page_address(page);
struct udf_inode_info *iinfo; struct udf_inode_info *iinfo = UDF_I(inode);
uint32_t pos;
/* We don't support symlinks longer than one block */ /* We don't support symlinks longer than one block */
if (inode->i_size > inode->i_sb->s_blocksize) { if (inode->i_size > inode->i_sb->s_blocksize) {
err = -ENAMETOOLONG; 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) { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
symlink = iinfo->i_data + iinfo->i_lenEAttr; symlink = iinfo->i_data + iinfo->i_lenEAttr;
} else { } else {
bh = sb_bread(inode->i_sb, pos); bh = udf_bread(inode, 0, 0, &err);
if (!bh) { if (!bh) {
err = -EIO; if (!err)
goto out_unlock_inode; err = -EFSCORRUPTED;
goto out_err;
} }
symlink = bh->b_data; symlink = bh->b_data;
} }
err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE); err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
brelse(bh); brelse(bh);
if (err) if (err)
goto out_unlock_inode; goto out_err;
up_read(&iinfo->i_data_sem);
SetPageUptodate(page); SetPageUptodate(page);
unlock_page(page); unlock_page(page);
return 0; return 0;
out_unlock_inode: out_err:
up_read(&iinfo->i_data_sem);
SetPageError(page); SetPageError(page);
out_unmap: out_unlock:
unlock_page(page); unlock_page(page);
return err; return err;
} }
......
...@@ -125,7 +125,7 @@ void udf_discard_prealloc(struct inode *inode) ...@@ -125,7 +125,7 @@ void udf_discard_prealloc(struct inode *inode)
struct kernel_lb_addr eloc; struct kernel_lb_addr eloc;
uint32_t elen; uint32_t elen;
uint64_t lbcount = 0; uint64_t lbcount = 0;
int8_t etype = -1, netype; int8_t etype = -1;
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
int bsize = 1 << inode->i_blkbits; int bsize = 1 << inode->i_blkbits;
...@@ -136,7 +136,7 @@ void udf_discard_prealloc(struct inode *inode) ...@@ -136,7 +136,7 @@ void udf_discard_prealloc(struct inode *inode)
epos.block = iinfo->i_location; epos.block = iinfo->i_location;
/* Find the last extent in the file */ /* 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); brelse(prev_epos.bh);
prev_epos = epos; prev_epos = epos;
if (prev_epos.bh) if (prev_epos.bh)
...@@ -240,7 +240,7 @@ int udf_truncate_extents(struct inode *inode) ...@@ -240,7 +240,7 @@ int udf_truncate_extents(struct inode *inode)
brelse(epos.bh); brelse(epos.bh);
epos.offset = sizeof(struct allocExtDesc); epos.offset = sizeof(struct allocExtDesc);
epos.block = eloc; epos.block = eloc;
epos.bh = udf_tread(sb, epos.bh = sb_bread(sb,
udf_get_lb_pblock(sb, &eloc, 0)); udf_get_lb_pblock(sb, &eloc, 0));
/* Error reading indirect block? */ /* Error reading indirect block? */
if (!epos.bh) if (!epos.bh)
......
...@@ -44,7 +44,8 @@ struct udf_inode_info { ...@@ -44,7 +44,8 @@ struct udf_inode_info {
unsigned i_use : 1; /* unallocSpaceEntry */ unsigned i_use : 1; /* unallocSpaceEntry */
unsigned i_strat4096 : 1; unsigned i_strat4096 : 1;
unsigned i_streamdir : 1; unsigned i_streamdir : 1;
unsigned reserved : 25; unsigned i_hidden : 1; /* hidden system inode */
unsigned reserved : 24;
__u8 *i_data; __u8 *i_data;
struct kernel_lb_addr i_locStreamdir; struct kernel_lb_addr i_locStreamdir;
__u64 i_lenStreams; __u64 i_lenStreams;
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#define UDF_FLAG_STRICT 5 #define UDF_FLAG_STRICT 5
#define UDF_FLAG_UNDELETE 6 #define UDF_FLAG_UNDELETE 6
#define UDF_FLAG_UNHIDE 7 #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_UID_FORGET 11 /* save -1 for uid to disk */
#define UDF_FLAG_GID_FORGET 12 #define UDF_FLAG_GID_FORGET 12
#define UDF_FLAG_UID_SET 13 #define UDF_FLAG_UID_SET 13
...@@ -55,6 +54,8 @@ ...@@ -55,6 +54,8 @@
#define MF_DUPLICATE_MD 0x01 #define MF_DUPLICATE_MD 0x01
#define MF_MIRROR_FE_LOADED 0x02 #define MF_MIRROR_FE_LOADED 0x02
#define EFSCORRUPTED EUCLEAN
struct udf_meta_data { struct udf_meta_data {
__u32 s_meta_file_loc; __u32 s_meta_file_loc;
__u32 s_mirror_file_loc; __u32 s_mirror_file_loc;
......
...@@ -34,9 +34,6 @@ extern __printf(3, 4) void _udf_warn(struct super_block *sb, ...@@ -34,9 +34,6 @@ extern __printf(3, 4) void _udf_warn(struct super_block *sb,
#define udf_debug(fmt, ...) \ #define udf_debug(fmt, ...) \
pr_debug("%s:%d:%s: " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__) 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_LENGTH_MASK 0x3FFFFFFF
#define UDF_EXTENT_FLAG_MASK 0xC0000000 #define UDF_EXTENT_FLAG_MASK 0xC0000000
...@@ -83,14 +80,24 @@ extern const struct inode_operations udf_file_inode_operations; ...@@ -83,14 +80,24 @@ extern const struct inode_operations udf_file_inode_operations;
extern const struct file_operations udf_file_operations; extern const struct file_operations udf_file_operations;
extern const struct inode_operations udf_symlink_inode_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_aops;
extern const struct address_space_operations udf_adinicb_aops;
extern const struct address_space_operations udf_symlink_aops; extern const struct address_space_operations udf_symlink_aops;
struct udf_fileident_bh { struct udf_fileident_iter {
struct buffer_head *sbh; struct inode *dir; /* Directory we are working with */
struct buffer_head *ebh; loff_t pos; /* Logical position in a dir */
int soffset; struct buffer_head *bh[2]; /* Buffer containing 'pos' and possibly
int eoffset; * 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 { struct udf_vds_record {
...@@ -121,22 +128,16 @@ struct inode *udf_find_metadata_inode_efe(struct super_block *sb, ...@@ -121,22 +128,16 @@ struct inode *udf_find_metadata_inode_efe(struct super_block *sb,
u32 meta_file_loc, u32 partition_num); u32 meta_file_loc, u32 partition_num);
/* namei.c */ /* 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) static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
{ {
return ALIGN(sizeof(struct fileIdentDesc) + return ALIGN(sizeof(struct fileIdentDesc) +
le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent, le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent,
UDF_NAME_PAD); 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 */ /* file.c */
extern long udf_ioctl(struct file *, unsigned int, unsigned long); extern long udf_ioctl(struct file *, unsigned int, unsigned long);
/* inode.c */ /* inode.c */
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *, extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
bool hidden_inode); bool hidden_inode);
...@@ -151,16 +152,14 @@ static inline struct inode *udf_iget(struct super_block *sb, ...@@ -151,16 +152,14 @@ static inline struct inode *udf_iget(struct super_block *sb,
return __udf_iget(sb, ino, false); return __udf_iget(sb, ino, false);
} }
extern int udf_expand_file_adinicb(struct inode *); 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, extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
int create, int *err); int create, int *err);
extern int udf_setsize(struct inode *, loff_t); extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *); extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc); 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 *, extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
struct kernel_lb_addr *, uint32_t *, sector_t *); 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, extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
struct extent_position *epos); struct extent_position *epos);
extern int __udf_add_aext(struct inode *inode, 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 *, ...@@ -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); extern void udf_update_extra_perms(struct inode *inode, umode_t mode);
/* misc.c */ /* 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, extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t,
uint32_t, uint8_t); uint32_t, uint8_t);
extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_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); ...@@ -194,7 +190,7 @@ extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int);
/* lowlevel.c */ /* lowlevel.c */
extern unsigned int udf_get_last_session(struct super_block *); 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 */ /* partition.c */
extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, 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, ...@@ -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); uint16_t partition, uint32_t goal, int *err);
/* directory.c */ /* directory.c */
extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *, int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
struct udf_fileident_bh *, loff_t pos);
struct fileIdentDesc *, int udf_fiiter_advance(struct udf_fileident_iter *iter);
struct extent_position *, void udf_fiiter_release(struct udf_fileident_iter *iter);
struct kernel_lb_addr *, uint32_t *, void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse);
sector_t *); void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen);
extern struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int udf_fiiter_append_blk(struct udf_fileident_iter *iter);
int *offset);
extern struct long_ad *udf_get_filelongad(uint8_t *, int, uint32_t *, int); 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); extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment