udf: Implement adding of dir entries using new iteration code

Implement function udf_fiiter_add_entry() adding new directory entries
using new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Jan Kara 2022-10-06 13:25:16 +02:00
parent 6ec01a8020
commit f284480340
3 changed files with 169 additions and 0 deletions

View file

@ -413,6 +413,63 @@ void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)
inode_inc_iversion(iter->dir);
}
void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
int diff = new_elen - iter->elen;
/* Skip update when we already went past the last extent */
if (!iter->elen)
return;
iter->elen = new_elen;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
iter->epos.offset -= sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
iter->epos.offset -= sizeof(struct long_ad);
udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);
iinfo->i_lenExtents += diff;
mark_inode_dirty(iter->dir);
}
/* Append new block to directory. @iter is expected to point at EOF */
int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
int blksize = 1 << iter->dir->i_blkbits;
struct buffer_head *bh;
sector_t block;
uint32_t old_elen = iter->elen;
int err;
if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
return -EINVAL;
/* Round up last extent in the file */
udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));
/* Allocate new block and refresh mapping information */
block = iinfo->i_lenExtents >> iter->dir->i_blkbits;
bh = udf_bread(iter->dir, block, 1, &err);
if (!bh) {
udf_fiiter_update_elen(iter, old_elen);
return err;
}
if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
&iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
udf_err(iter->dir->i_sb,
"block %llu not allocated in directory (ino %lu)\n",
(unsigned long long)block, iter->dir->i_ino);
return -EFSCORRUPTED;
}
if (!(iter->pos & (blksize - 1))) {
brelse(iter->bh[0]);
iter->bh[0] = bh;
} else {
iter->bh[1] = bh;
}
return 0;
}
struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi,

View file

@ -472,6 +472,116 @@ static struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
return dbh;
}
static int udf_fiiter_add_entry(struct inode *dir, struct dentry *dentry,
struct udf_fileident_iter *iter)
{
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 (dentry) {
if (!dentry->d_name.len)
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);
for (ret = udf_fiiter_init(iter, dir, 0);
!ret && iter->pos < dir->i_size;
ret = udf_fiiter_advance(iter)) {
if (iter->fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
if (udf_dir_entry_len(&iter->fi) == nfidlen) {
iter->fi.descTag.tagSerialNum = cpu_to_le16(1);
iter->fi.fileVersionNum = cpu_to_le16(1);
iter->fi.fileCharacteristics = 0;
iter->fi.lengthFileIdent = namelen;
iter->fi.lengthOfImpUse = cpu_to_le16(0);
memcpy(iter->namebuf, name, namelen);
iter->name = iter->namebuf;
return 0;
}
}
}
if (ret) {
udf_fiiter_release(iter);
return ret;
}
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
blksize - udf_ext0_offset(dir) - iter->pos < nfidlen) {
struct buffer_head *retbh;
udf_fiiter_release(iter);
/*
* FIXME: udf_expand_dir_adinicb does not need to return bh
* once other users are gone
*/
retbh = udf_expand_dir_adinicb(dir, &block, &ret);
if (!retbh)
return ret;
brelse(retbh);
ret = udf_fiiter_init(iter, dir, dir->i_size);
if (ret < 0)
return ret;
}
/* Get blocknumber to use for entry tag */
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
block = dinfo->i_location.logicalBlockNum;
} else {
block = iter->eloc.logicalBlockNum +
((iter->elen - 1) >> dir->i_blkbits);
}
off = iter->pos & (blksize - 1);
if (!off)
off = blksize;
/* Entry fits into current 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));
else
udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 2, 1, block,
sizeof(struct tag));
iter->fi.fileVersionNum = cpu_to_le16(1);
iter->fi.lengthFileIdent = namelen;
iter->fi.lengthOfImpUse = cpu_to_le16(0);
memcpy(iter->namebuf, name, namelen);
iter->name = iter->namebuf;
dir->i_size += nfidlen;
if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
dinfo->i_lenAlloc += nfidlen;
} else {
/* Truncate last extent to proper size */
udf_fiiter_update_elen(iter, iter->elen -
(dinfo->i_lenExtents - dir->i_size));
}
mark_inode_dirty(dir);
return 0;
}
static struct fileIdentDesc *udf_add_entry(struct inode *dir,
struct dentry *dentry,
struct udf_fileident_bh *fibh,

View file

@ -264,6 +264,8 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
int udf_fiiter_advance(struct udf_fileident_iter *iter);
void udf_fiiter_release(struct udf_fileident_iter *iter);
void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse);
void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen);
int udf_fiiter_append_blk(struct udf_fileident_iter *iter);
extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *,
struct udf_fileident_bh *,
struct fileIdentDesc *,