162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * directory.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * PURPOSE
662306a36Sopenharmony_ci *	Directory related functions
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "udfdecl.h"
1162306a36Sopenharmony_ci#include "udf_i.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/fs.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/bio.h>
1662306a36Sopenharmony_ci#include <linux/crc-itu-t.h>
1762306a36Sopenharmony_ci#include <linux/iversion.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int udf_verify_fi(struct udf_fileident_iter *iter)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	unsigned int len;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
2462306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
2562306a36Sopenharmony_ci			"directory (ino %lu) has entry at pos %llu with incorrect tag %x\n",
2662306a36Sopenharmony_ci			iter->dir->i_ino, (unsigned long long)iter->pos,
2762306a36Sopenharmony_ci			le16_to_cpu(iter->fi.descTag.tagIdent));
2862306a36Sopenharmony_ci		return -EFSCORRUPTED;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	len = udf_dir_entry_len(&iter->fi);
3162306a36Sopenharmony_ci	if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) {
3262306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
3362306a36Sopenharmony_ci			"directory (ino %lu) has entry at pos %llu with unaligned length of impUse field\n",
3462306a36Sopenharmony_ci			iter->dir->i_ino, (unsigned long long)iter->pos);
3562306a36Sopenharmony_ci		return -EFSCORRUPTED;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	/*
3862306a36Sopenharmony_ci	 * This is in fact allowed by the spec due to long impUse field but
3962306a36Sopenharmony_ci	 * we don't support it. If there is real media with this large impUse
4062306a36Sopenharmony_ci	 * field, support can be added.
4162306a36Sopenharmony_ci	 */
4262306a36Sopenharmony_ci	if (len > 1 << iter->dir->i_blkbits) {
4362306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
4462306a36Sopenharmony_ci			"directory (ino %lu) has too big (%u) entry at pos %llu\n",
4562306a36Sopenharmony_ci			iter->dir->i_ino, len, (unsigned long long)iter->pos);
4662306a36Sopenharmony_ci		return -EFSCORRUPTED;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci	if (iter->pos + len > iter->dir->i_size) {
4962306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
5062306a36Sopenharmony_ci			"directory (ino %lu) has entry past directory size at pos %llu\n",
5162306a36Sopenharmony_ci			iter->dir->i_ino, (unsigned long long)iter->pos);
5262306a36Sopenharmony_ci		return -EFSCORRUPTED;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	if (udf_dir_entry_len(&iter->fi) !=
5562306a36Sopenharmony_ci	    sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) {
5662306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
5762306a36Sopenharmony_ci			"directory (ino %lu) has entry where CRC length (%u) does not match entry length (%u)\n",
5862306a36Sopenharmony_ci			iter->dir->i_ino,
5962306a36Sopenharmony_ci			(unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength),
6062306a36Sopenharmony_ci			(unsigned)(udf_dir_entry_len(&iter->fi) -
6162306a36Sopenharmony_ci							sizeof(struct tag)));
6262306a36Sopenharmony_ci		return -EFSCORRUPTED;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int udf_copy_fi(struct udf_fileident_iter *iter)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(iter->dir);
7062306a36Sopenharmony_ci	u32 blksize = 1 << iter->dir->i_blkbits;
7162306a36Sopenharmony_ci	u32 off, len, nameoff;
7262306a36Sopenharmony_ci	int err;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Skip copying when we are at EOF */
7562306a36Sopenharmony_ci	if (iter->pos >= iter->dir->i_size) {
7662306a36Sopenharmony_ci		iter->name = NULL;
7762306a36Sopenharmony_ci		return 0;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) {
8062306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
8162306a36Sopenharmony_ci			"directory (ino %lu) has entry straddling EOF\n",
8262306a36Sopenharmony_ci			iter->dir->i_ino);
8362306a36Sopenharmony_ci		return -EFSCORRUPTED;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
8662306a36Sopenharmony_ci		memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos,
8762306a36Sopenharmony_ci		       sizeof(struct fileIdentDesc));
8862306a36Sopenharmony_ci		err = udf_verify_fi(iter);
8962306a36Sopenharmony_ci		if (err < 0)
9062306a36Sopenharmony_ci			return err;
9162306a36Sopenharmony_ci		iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos +
9262306a36Sopenharmony_ci			sizeof(struct fileIdentDesc) +
9362306a36Sopenharmony_ci			le16_to_cpu(iter->fi.lengthOfImpUse);
9462306a36Sopenharmony_ci		return 0;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	off = iter->pos & (blksize - 1);
9862306a36Sopenharmony_ci	len = min_t(u32, sizeof(struct fileIdentDesc), blksize - off);
9962306a36Sopenharmony_ci	memcpy(&iter->fi, iter->bh[0]->b_data + off, len);
10062306a36Sopenharmony_ci	if (len < sizeof(struct fileIdentDesc))
10162306a36Sopenharmony_ci		memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data,
10262306a36Sopenharmony_ci		       sizeof(struct fileIdentDesc) - len);
10362306a36Sopenharmony_ci	err = udf_verify_fi(iter);
10462306a36Sopenharmony_ci	if (err < 0)
10562306a36Sopenharmony_ci		return err;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Handle directory entry name */
10862306a36Sopenharmony_ci	nameoff = off + sizeof(struct fileIdentDesc) +
10962306a36Sopenharmony_ci				le16_to_cpu(iter->fi.lengthOfImpUse);
11062306a36Sopenharmony_ci	if (off + udf_dir_entry_len(&iter->fi) <= blksize) {
11162306a36Sopenharmony_ci		iter->name = iter->bh[0]->b_data + nameoff;
11262306a36Sopenharmony_ci	} else if (nameoff >= blksize) {
11362306a36Sopenharmony_ci		iter->name = iter->bh[1]->b_data + (nameoff - blksize);
11462306a36Sopenharmony_ci	} else {
11562306a36Sopenharmony_ci		iter->name = iter->namebuf;
11662306a36Sopenharmony_ci		len = blksize - nameoff;
11762306a36Sopenharmony_ci		memcpy(iter->name, iter->bh[0]->b_data + nameoff, len);
11862306a36Sopenharmony_ci		memcpy(iter->name + len, iter->bh[1]->b_data,
11962306a36Sopenharmony_ci		       iter->fi.lengthFileIdent - len);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Readahead 8k once we are at 8k boundary */
12562306a36Sopenharmony_cistatic void udf_readahead_dir(struct udf_fileident_iter *iter)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9);
12862306a36Sopenharmony_ci	struct buffer_head *tmp, *bha[16];
12962306a36Sopenharmony_ci	int i, num;
13062306a36Sopenharmony_ci	udf_pblk_t blk;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (iter->loffset & (ralen - 1))
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits))
13662306a36Sopenharmony_ci		ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset;
13762306a36Sopenharmony_ci	num = 0;
13862306a36Sopenharmony_ci	for (i = 0; i < ralen; i++) {
13962306a36Sopenharmony_ci		blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc,
14062306a36Sopenharmony_ci					iter->loffset + i);
14162306a36Sopenharmony_ci		tmp = sb_getblk(iter->dir->i_sb, blk);
14262306a36Sopenharmony_ci		if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
14362306a36Sopenharmony_ci			bha[num++] = tmp;
14462306a36Sopenharmony_ci		else
14562306a36Sopenharmony_ci			brelse(tmp);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	if (num) {
14862306a36Sopenharmony_ci		bh_readahead_batch(num, bha, REQ_RAHEAD);
14962306a36Sopenharmony_ci		for (i = 0; i < num; i++)
15062306a36Sopenharmony_ci			brelse(bha[i]);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	udf_pblk_t blk;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	udf_readahead_dir(iter);
15962306a36Sopenharmony_ci	blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset);
16062306a36Sopenharmony_ci	return sb_bread(iter->dir->i_sb, blk);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/*
16462306a36Sopenharmony_ci * Updates loffset to point to next directory block; eloc, elen & epos are
16562306a36Sopenharmony_ci * updated if we need to traverse to the next extent as well.
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic int udf_fiiter_advance_blk(struct udf_fileident_iter *iter)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	iter->loffset++;
17062306a36Sopenharmony_ci	if (iter->loffset < DIV_ROUND_UP(iter->elen, 1<<iter->dir->i_blkbits))
17162306a36Sopenharmony_ci		return 0;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	iter->loffset = 0;
17462306a36Sopenharmony_ci	if (udf_next_aext(iter->dir, &iter->epos, &iter->eloc, &iter->elen, 1)
17562306a36Sopenharmony_ci			!= (EXT_RECORDED_ALLOCATED >> 30)) {
17662306a36Sopenharmony_ci		if (iter->pos == iter->dir->i_size) {
17762306a36Sopenharmony_ci			iter->elen = 0;
17862306a36Sopenharmony_ci			return 0;
17962306a36Sopenharmony_ci		}
18062306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
18162306a36Sopenharmony_ci			"extent after position %llu not allocated in directory (ino %lu)\n",
18262306a36Sopenharmony_ci			(unsigned long long)iter->pos, iter->dir->i_ino);
18362306a36Sopenharmony_ci		return -EFSCORRUPTED;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int udf_fiiter_load_bhs(struct udf_fileident_iter *iter)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int blksize = 1 << iter->dir->i_blkbits;
19162306a36Sopenharmony_ci	int off = iter->pos & (blksize - 1);
19262306a36Sopenharmony_ci	int err;
19362306a36Sopenharmony_ci	struct fileIdentDesc *fi;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Is there any further extent we can map from? */
19662306a36Sopenharmony_ci	if (!iter->bh[0] && iter->elen) {
19762306a36Sopenharmony_ci		iter->bh[0] = udf_fiiter_bread_blk(iter);
19862306a36Sopenharmony_ci		if (!iter->bh[0]) {
19962306a36Sopenharmony_ci			err = -ENOMEM;
20062306a36Sopenharmony_ci			goto out_brelse;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci		if (!buffer_uptodate(iter->bh[0])) {
20362306a36Sopenharmony_ci			err = -EIO;
20462306a36Sopenharmony_ci			goto out_brelse;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	/* There's no next block so we are done */
20862306a36Sopenharmony_ci	if (iter->pos >= iter->dir->i_size)
20962306a36Sopenharmony_ci		return 0;
21062306a36Sopenharmony_ci	/* Need to fetch next block as well? */
21162306a36Sopenharmony_ci	if (off + sizeof(struct fileIdentDesc) > blksize)
21262306a36Sopenharmony_ci		goto fetch_next;
21362306a36Sopenharmony_ci	fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off);
21462306a36Sopenharmony_ci	/* Need to fetch next block to get name? */
21562306a36Sopenharmony_ci	if (off + udf_dir_entry_len(fi) > blksize) {
21662306a36Sopenharmony_cifetch_next:
21762306a36Sopenharmony_ci		err = udf_fiiter_advance_blk(iter);
21862306a36Sopenharmony_ci		if (err)
21962306a36Sopenharmony_ci			goto out_brelse;
22062306a36Sopenharmony_ci		iter->bh[1] = udf_fiiter_bread_blk(iter);
22162306a36Sopenharmony_ci		if (!iter->bh[1]) {
22262306a36Sopenharmony_ci			err = -ENOMEM;
22362306a36Sopenharmony_ci			goto out_brelse;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		if (!buffer_uptodate(iter->bh[1])) {
22662306a36Sopenharmony_ci			err = -EIO;
22762306a36Sopenharmony_ci			goto out_brelse;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ciout_brelse:
23262306a36Sopenharmony_ci	brelse(iter->bh[0]);
23362306a36Sopenharmony_ci	brelse(iter->bh[1]);
23462306a36Sopenharmony_ci	iter->bh[0] = iter->bh[1] = NULL;
23562306a36Sopenharmony_ci	return err;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciint udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
23962306a36Sopenharmony_ci		    loff_t pos)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(dir);
24262306a36Sopenharmony_ci	int err = 0;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	iter->dir = dir;
24562306a36Sopenharmony_ci	iter->bh[0] = iter->bh[1] = NULL;
24662306a36Sopenharmony_ci	iter->pos = pos;
24762306a36Sopenharmony_ci	iter->elen = 0;
24862306a36Sopenharmony_ci	iter->epos.bh = NULL;
24962306a36Sopenharmony_ci	iter->name = NULL;
25062306a36Sopenharmony_ci	/*
25162306a36Sopenharmony_ci	 * When directory is verified, we don't expect directory iteration to
25262306a36Sopenharmony_ci	 * fail and it can be difficult to undo without corrupting filesystem.
25362306a36Sopenharmony_ci	 * So just do not allow memory allocation failures here.
25462306a36Sopenharmony_ci	 */
25562306a36Sopenharmony_ci	iter->namebuf = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL | __GFP_NOFAIL);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
25862306a36Sopenharmony_ci		err = udf_copy_fi(iter);
25962306a36Sopenharmony_ci		goto out;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
26362306a36Sopenharmony_ci		       &iter->eloc, &iter->elen, &iter->loffset) !=
26462306a36Sopenharmony_ci	    (EXT_RECORDED_ALLOCATED >> 30)) {
26562306a36Sopenharmony_ci		if (pos == dir->i_size)
26662306a36Sopenharmony_ci			return 0;
26762306a36Sopenharmony_ci		udf_err(dir->i_sb,
26862306a36Sopenharmony_ci			"position %llu not allocated in directory (ino %lu)\n",
26962306a36Sopenharmony_ci			(unsigned long long)pos, dir->i_ino);
27062306a36Sopenharmony_ci		err = -EFSCORRUPTED;
27162306a36Sopenharmony_ci		goto out;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci	err = udf_fiiter_load_bhs(iter);
27462306a36Sopenharmony_ci	if (err < 0)
27562306a36Sopenharmony_ci		goto out;
27662306a36Sopenharmony_ci	err = udf_copy_fi(iter);
27762306a36Sopenharmony_ciout:
27862306a36Sopenharmony_ci	if (err < 0)
27962306a36Sopenharmony_ci		udf_fiiter_release(iter);
28062306a36Sopenharmony_ci	return err;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint udf_fiiter_advance(struct udf_fileident_iter *iter)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	unsigned int oldoff, len;
28662306a36Sopenharmony_ci	int blksize = 1 << iter->dir->i_blkbits;
28762306a36Sopenharmony_ci	int err;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	oldoff = iter->pos & (blksize - 1);
29062306a36Sopenharmony_ci	len = udf_dir_entry_len(&iter->fi);
29162306a36Sopenharmony_ci	iter->pos += len;
29262306a36Sopenharmony_ci	if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
29362306a36Sopenharmony_ci		if (oldoff + len >= blksize) {
29462306a36Sopenharmony_ci			brelse(iter->bh[0]);
29562306a36Sopenharmony_ci			iter->bh[0] = NULL;
29662306a36Sopenharmony_ci			/* Next block already loaded? */
29762306a36Sopenharmony_ci			if (iter->bh[1]) {
29862306a36Sopenharmony_ci				iter->bh[0] = iter->bh[1];
29962306a36Sopenharmony_ci				iter->bh[1] = NULL;
30062306a36Sopenharmony_ci			} else {
30162306a36Sopenharmony_ci				err = udf_fiiter_advance_blk(iter);
30262306a36Sopenharmony_ci				if (err < 0)
30362306a36Sopenharmony_ci					return err;
30462306a36Sopenharmony_ci			}
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci		err = udf_fiiter_load_bhs(iter);
30762306a36Sopenharmony_ci		if (err < 0)
30862306a36Sopenharmony_ci			return err;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	return udf_copy_fi(iter);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_civoid udf_fiiter_release(struct udf_fileident_iter *iter)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	iter->dir = NULL;
31662306a36Sopenharmony_ci	brelse(iter->bh[0]);
31762306a36Sopenharmony_ci	brelse(iter->bh[1]);
31862306a36Sopenharmony_ci	iter->bh[0] = iter->bh[1] = NULL;
31962306a36Sopenharmony_ci	kfree(iter->namebuf);
32062306a36Sopenharmony_ci	iter->namebuf = NULL;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2,
32462306a36Sopenharmony_ci			     int off, void *src, int len)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	int copy;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (off >= len1) {
32962306a36Sopenharmony_ci		off -= len1;
33062306a36Sopenharmony_ci	} else {
33162306a36Sopenharmony_ci		copy = min(off + len, len1) - off;
33262306a36Sopenharmony_ci		memcpy(buf1 + off, src, copy);
33362306a36Sopenharmony_ci		src += copy;
33462306a36Sopenharmony_ci		len -= copy;
33562306a36Sopenharmony_ci		off = 0;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	if (len > 0) {
33862306a36Sopenharmony_ci		if (WARN_ON_ONCE(off + len > len2 || !buf2))
33962306a36Sopenharmony_ci			return;
34062306a36Sopenharmony_ci		memcpy(buf2 + off, src, len);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2,
34562306a36Sopenharmony_ci				int off, int len)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	int copy;
34862306a36Sopenharmony_ci	uint16_t crc = 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (off >= len1) {
35162306a36Sopenharmony_ci		off -= len1;
35262306a36Sopenharmony_ci	} else {
35362306a36Sopenharmony_ci		copy = min(off + len, len1) - off;
35462306a36Sopenharmony_ci		crc = crc_itu_t(crc, buf1 + off, copy);
35562306a36Sopenharmony_ci		len -= copy;
35662306a36Sopenharmony_ci		off = 0;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci	if (len > 0) {
35962306a36Sopenharmony_ci		if (WARN_ON_ONCE(off + len > len2 || !buf2))
36062306a36Sopenharmony_ci			return 0;
36162306a36Sopenharmony_ci		crc = crc_itu_t(crc, buf2 + off, len);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	return crc;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2,
36762306a36Sopenharmony_ci				int off, struct fileIdentDesc *fi,
36862306a36Sopenharmony_ci				uint8_t *impuse, uint8_t *name)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	uint16_t crc;
37162306a36Sopenharmony_ci	int fioff = off;
37262306a36Sopenharmony_ci	int crcoff = off + sizeof(struct tag);
37362306a36Sopenharmony_ci	unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag);
37462306a36Sopenharmony_ci	char zeros[UDF_NAME_PAD] = {};
37562306a36Sopenharmony_ci	int endoff = off + udf_dir_entry_len(fi);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi,
37862306a36Sopenharmony_ci			 sizeof(struct fileIdentDesc));
37962306a36Sopenharmony_ci	off += sizeof(struct fileIdentDesc);
38062306a36Sopenharmony_ci	if (impuse)
38162306a36Sopenharmony_ci		udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse,
38262306a36Sopenharmony_ci				 le16_to_cpu(fi->lengthOfImpUse));
38362306a36Sopenharmony_ci	off += le16_to_cpu(fi->lengthOfImpUse);
38462306a36Sopenharmony_ci	if (name) {
38562306a36Sopenharmony_ci		udf_copy_to_bufs(buf1, len1, buf2, len2, off, name,
38662306a36Sopenharmony_ci				 fi->lengthFileIdent);
38762306a36Sopenharmony_ci		off += fi->lengthFileIdent;
38862306a36Sopenharmony_ci		udf_copy_to_bufs(buf1, len1, buf2, len2, off, zeros,
38962306a36Sopenharmony_ci				 endoff - off);
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen);
39362306a36Sopenharmony_ci	fi->descTag.descCRC = cpu_to_le16(crc);
39462306a36Sopenharmony_ci	fi->descTag.descCRCLength = cpu_to_le16(crclen);
39562306a36Sopenharmony_ci	fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag));
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_civoid udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(iter->dir);
40362306a36Sopenharmony_ci	void *buf1, *buf2 = NULL;
40462306a36Sopenharmony_ci	int len1, len2 = 0, off;
40562306a36Sopenharmony_ci	int blksize = 1 << iter->dir->i_blkbits;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	off = iter->pos & (blksize - 1);
40862306a36Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
40962306a36Sopenharmony_ci		buf1 = iinfo->i_data + iinfo->i_lenEAttr;
41062306a36Sopenharmony_ci		len1 = iter->dir->i_size;
41162306a36Sopenharmony_ci	} else {
41262306a36Sopenharmony_ci		buf1 = iter->bh[0]->b_data;
41362306a36Sopenharmony_ci		len1 = blksize;
41462306a36Sopenharmony_ci		if (iter->bh[1]) {
41562306a36Sopenharmony_ci			buf2 = iter->bh[1]->b_data;
41662306a36Sopenharmony_ci			len2 = blksize;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse,
42162306a36Sopenharmony_ci			    iter->name == iter->namebuf ? iter->name : NULL);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
42462306a36Sopenharmony_ci		mark_inode_dirty(iter->dir);
42562306a36Sopenharmony_ci	} else {
42662306a36Sopenharmony_ci		mark_buffer_dirty_inode(iter->bh[0], iter->dir);
42762306a36Sopenharmony_ci		if (iter->bh[1])
42862306a36Sopenharmony_ci			mark_buffer_dirty_inode(iter->bh[1], iter->dir);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci	inode_inc_iversion(iter->dir);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_civoid udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(iter->dir);
43662306a36Sopenharmony_ci	int diff = new_elen - iter->elen;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* Skip update when we already went past the last extent */
43962306a36Sopenharmony_ci	if (!iter->elen)
44062306a36Sopenharmony_ci		return;
44162306a36Sopenharmony_ci	iter->elen = new_elen;
44262306a36Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
44362306a36Sopenharmony_ci		iter->epos.offset -= sizeof(struct short_ad);
44462306a36Sopenharmony_ci	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
44562306a36Sopenharmony_ci		iter->epos.offset -= sizeof(struct long_ad);
44662306a36Sopenharmony_ci	udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);
44762306a36Sopenharmony_ci	iinfo->i_lenExtents += diff;
44862306a36Sopenharmony_ci	mark_inode_dirty(iter->dir);
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci/* Append new block to directory. @iter is expected to point at EOF */
45262306a36Sopenharmony_ciint udf_fiiter_append_blk(struct udf_fileident_iter *iter)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(iter->dir);
45562306a36Sopenharmony_ci	int blksize = 1 << iter->dir->i_blkbits;
45662306a36Sopenharmony_ci	struct buffer_head *bh;
45762306a36Sopenharmony_ci	sector_t block;
45862306a36Sopenharmony_ci	uint32_t old_elen = iter->elen;
45962306a36Sopenharmony_ci	int err;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
46262306a36Sopenharmony_ci		return -EINVAL;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* Round up last extent in the file */
46562306a36Sopenharmony_ci	udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* Allocate new block and refresh mapping information */
46862306a36Sopenharmony_ci	block = iinfo->i_lenExtents >> iter->dir->i_blkbits;
46962306a36Sopenharmony_ci	bh = udf_bread(iter->dir, block, 1, &err);
47062306a36Sopenharmony_ci	if (!bh) {
47162306a36Sopenharmony_ci		udf_fiiter_update_elen(iter, old_elen);
47262306a36Sopenharmony_ci		return err;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci	if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
47562306a36Sopenharmony_ci		       &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
47662306a36Sopenharmony_ci		udf_err(iter->dir->i_sb,
47762306a36Sopenharmony_ci			"block %llu not allocated in directory (ino %lu)\n",
47862306a36Sopenharmony_ci			(unsigned long long)block, iter->dir->i_ino);
47962306a36Sopenharmony_ci		return -EFSCORRUPTED;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	if (!(iter->pos & (blksize - 1))) {
48262306a36Sopenharmony_ci		brelse(iter->bh[0]);
48362306a36Sopenharmony_ci		iter->bh[0] = bh;
48462306a36Sopenharmony_ci	} else {
48562306a36Sopenharmony_ci		iter->bh[1] = bh;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci	return 0;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistruct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
49162306a36Sopenharmony_ci			      int inc)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct short_ad *sa;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if ((!ptr) || (!offset)) {
49662306a36Sopenharmony_ci		pr_err("%s: invalidparms\n", __func__);
49762306a36Sopenharmony_ci		return NULL;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if ((*offset + sizeof(struct short_ad)) > maxoffset)
50162306a36Sopenharmony_ci		return NULL;
50262306a36Sopenharmony_ci	else {
50362306a36Sopenharmony_ci		sa = (struct short_ad *)ptr;
50462306a36Sopenharmony_ci		if (sa->extLength == 0)
50562306a36Sopenharmony_ci			return NULL;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (inc)
50962306a36Sopenharmony_ci		*offset += sizeof(struct short_ad);
51062306a36Sopenharmony_ci	return sa;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistruct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct long_ad *la;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if ((!ptr) || (!offset)) {
51862306a36Sopenharmony_ci		pr_err("%s: invalidparms\n", __func__);
51962306a36Sopenharmony_ci		return NULL;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if ((*offset + sizeof(struct long_ad)) > maxoffset)
52362306a36Sopenharmony_ci		return NULL;
52462306a36Sopenharmony_ci	else {
52562306a36Sopenharmony_ci		la = (struct long_ad *)ptr;
52662306a36Sopenharmony_ci		if (la->extLength == 0)
52762306a36Sopenharmony_ci			return NULL;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (inc)
53162306a36Sopenharmony_ci		*offset += sizeof(struct long_ad);
53262306a36Sopenharmony_ci	return la;
53362306a36Sopenharmony_ci}
534