Commit f2844803 authored by Jan Kara's avatar Jan Kara
Browse files

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: default avatarJan Kara <jack@suse.cz>
parent 6ec01a80
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -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,
+110 −0
Original line number Diff line number Diff line
@@ -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,
+2 −0
Original line number Diff line number Diff line
@@ -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 *,