brintos

brintos / linux-shallow public Read only

0
0
Text · 14.0 KiB · 632453a Raw
543 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * directory.c4 *5 * PURPOSE6 *	Directory related functions7 *8 */9 10#include "udfdecl.h"11#include "udf_i.h"12 13#include <linux/fs.h>14#include <linux/string.h>15#include <linux/bio.h>16#include <linux/crc-itu-t.h>17#include <linux/iversion.h>18 19static int udf_verify_fi(struct udf_fileident_iter *iter)20{21	unsigned int len;22 23	if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {24		udf_err(iter->dir->i_sb,25			"directory (ino %lu) has entry at pos %llu with incorrect tag %x\n",26			iter->dir->i_ino, (unsigned long long)iter->pos,27			le16_to_cpu(iter->fi.descTag.tagIdent));28		return -EFSCORRUPTED;29	}30	len = udf_dir_entry_len(&iter->fi);31	if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) {32		udf_err(iter->dir->i_sb,33			"directory (ino %lu) has entry at pos %llu with unaligned length of impUse field\n",34			iter->dir->i_ino, (unsigned long long)iter->pos);35		return -EFSCORRUPTED;36	}37	/*38	 * This is in fact allowed by the spec due to long impUse field but39	 * we don't support it. If there is real media with this large impUse40	 * field, support can be added.41	 */42	if (len > 1 << iter->dir->i_blkbits) {43		udf_err(iter->dir->i_sb,44			"directory (ino %lu) has too big (%u) entry at pos %llu\n",45			iter->dir->i_ino, len, (unsigned long long)iter->pos);46		return -EFSCORRUPTED;47	}48	if (iter->pos + len > iter->dir->i_size) {49		udf_err(iter->dir->i_sb,50			"directory (ino %lu) has entry past directory size at pos %llu\n",51			iter->dir->i_ino, (unsigned long long)iter->pos);52		return -EFSCORRUPTED;53	}54	if (udf_dir_entry_len(&iter->fi) !=55	    sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) {56		udf_err(iter->dir->i_sb,57			"directory (ino %lu) has entry where CRC length (%u) does not match entry length (%u)\n",58			iter->dir->i_ino,59			(unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength),60			(unsigned)(udf_dir_entry_len(&iter->fi) -61							sizeof(struct tag)));62		return -EFSCORRUPTED;63	}64	return 0;65}66 67static int udf_copy_fi(struct udf_fileident_iter *iter)68{69	struct udf_inode_info *iinfo = UDF_I(iter->dir);70	u32 blksize = 1 << iter->dir->i_blkbits;71	u32 off, len, nameoff;72	int err;73 74	/* Skip copying when we are at EOF */75	if (iter->pos >= iter->dir->i_size) {76		iter->name = NULL;77		return 0;78	}79	if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) {80		udf_err(iter->dir->i_sb,81			"directory (ino %lu) has entry straddling EOF\n",82			iter->dir->i_ino);83		return -EFSCORRUPTED;84	}85	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {86		memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos,87		       sizeof(struct fileIdentDesc));88		err = udf_verify_fi(iter);89		if (err < 0)90			return err;91		iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos +92			sizeof(struct fileIdentDesc) +93			le16_to_cpu(iter->fi.lengthOfImpUse);94		return 0;95	}96 97	off = iter->pos & (blksize - 1);98	len = min_t(u32, sizeof(struct fileIdentDesc), blksize - off);99	memcpy(&iter->fi, iter->bh[0]->b_data + off, len);100	if (len < sizeof(struct fileIdentDesc))101		memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data,102		       sizeof(struct fileIdentDesc) - len);103	err = udf_verify_fi(iter);104	if (err < 0)105		return err;106 107	/* Handle directory entry name */108	nameoff = off + sizeof(struct fileIdentDesc) +109				le16_to_cpu(iter->fi.lengthOfImpUse);110	if (off + udf_dir_entry_len(&iter->fi) <= blksize) {111		iter->name = iter->bh[0]->b_data + nameoff;112	} else if (nameoff >= blksize) {113		iter->name = iter->bh[1]->b_data + (nameoff - blksize);114	} else {115		iter->name = iter->namebuf;116		len = blksize - nameoff;117		memcpy(iter->name, iter->bh[0]->b_data + nameoff, len);118		memcpy(iter->name + len, iter->bh[1]->b_data,119		       iter->fi.lengthFileIdent - len);120	}121	return 0;122}123 124/* Readahead 8k once we are at 8k boundary */125static void udf_readahead_dir(struct udf_fileident_iter *iter)126{127	unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9);128	struct buffer_head *tmp, *bha[16];129	int i, num;130	udf_pblk_t blk;131 132	if (iter->loffset & (ralen - 1))133		return;134 135	if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits))136		ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset;137	num = 0;138	for (i = 0; i < ralen; i++) {139		blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc,140					iter->loffset + i);141		tmp = sb_getblk(iter->dir->i_sb, blk);142		if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))143			bha[num++] = tmp;144		else145			brelse(tmp);146	}147	if (num) {148		bh_readahead_batch(num, bha, REQ_RAHEAD);149		for (i = 0; i < num; i++)150			brelse(bha[i]);151	}152}153 154static struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter)155{156	udf_pblk_t blk;157 158	udf_readahead_dir(iter);159	blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset);160	return sb_bread(iter->dir->i_sb, blk);161}162 163/*164 * Updates loffset to point to next directory block; eloc, elen & epos are165 * updated if we need to traverse to the next extent as well.166 */167static int udf_fiiter_advance_blk(struct udf_fileident_iter *iter)168{169	int8_t etype = -1;170	int err = 0;171 172	iter->loffset++;173	if (iter->loffset < DIV_ROUND_UP(iter->elen, 1<<iter->dir->i_blkbits))174		return 0;175 176	iter->loffset = 0;177	err = udf_next_aext(iter->dir, &iter->epos, &iter->eloc,178			    &iter->elen, &etype, 1);179	if (err < 0)180		return err;181	else if (err == 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {182		if (iter->pos == iter->dir->i_size) {183			iter->elen = 0;184			return 0;185		}186		udf_err(iter->dir->i_sb,187			"extent after position %llu not allocated in directory (ino %lu)\n",188			(unsigned long long)iter->pos, iter->dir->i_ino);189		return -EFSCORRUPTED;190	}191	return 0;192}193 194static int udf_fiiter_load_bhs(struct udf_fileident_iter *iter)195{196	int blksize = 1 << iter->dir->i_blkbits;197	int off = iter->pos & (blksize - 1);198	int err;199	struct fileIdentDesc *fi;200 201	/* Is there any further extent we can map from? */202	if (!iter->bh[0] && iter->elen) {203		iter->bh[0] = udf_fiiter_bread_blk(iter);204		if (!iter->bh[0]) {205			err = -ENOMEM;206			goto out_brelse;207		}208		if (!buffer_uptodate(iter->bh[0])) {209			err = -EIO;210			goto out_brelse;211		}212	}213	/* There's no next block so we are done */214	if (iter->pos >= iter->dir->i_size)215		return 0;216	/* Need to fetch next block as well? */217	if (off + sizeof(struct fileIdentDesc) > blksize)218		goto fetch_next;219	fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off);220	/* Need to fetch next block to get name? */221	if (off + udf_dir_entry_len(fi) > blksize) {222fetch_next:223		err = udf_fiiter_advance_blk(iter);224		if (err)225			goto out_brelse;226		iter->bh[1] = udf_fiiter_bread_blk(iter);227		if (!iter->bh[1]) {228			err = -ENOMEM;229			goto out_brelse;230		}231		if (!buffer_uptodate(iter->bh[1])) {232			err = -EIO;233			goto out_brelse;234		}235	}236	return 0;237out_brelse:238	brelse(iter->bh[0]);239	brelse(iter->bh[1]);240	iter->bh[0] = iter->bh[1] = NULL;241	return err;242}243 244int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,245		    loff_t pos)246{247	struct udf_inode_info *iinfo = UDF_I(dir);248	int err = 0;249	int8_t etype;250 251	iter->dir = dir;252	iter->bh[0] = iter->bh[1] = NULL;253	iter->pos = pos;254	iter->elen = 0;255	iter->epos.bh = NULL;256	iter->name = NULL;257	/*258	 * When directory is verified, we don't expect directory iteration to259	 * fail and it can be difficult to undo without corrupting filesystem.260	 * So just do not allow memory allocation failures here.261	 */262	iter->namebuf = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL | __GFP_NOFAIL);263 264	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {265		err = udf_copy_fi(iter);266		goto out;267	}268 269	err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,270			 &iter->eloc, &iter->elen, &iter->loffset, &etype);271	if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {272		if (pos == dir->i_size)273			return 0;274		udf_err(dir->i_sb,275			"position %llu not allocated in directory (ino %lu)\n",276			(unsigned long long)pos, dir->i_ino);277		err = -EFSCORRUPTED;278		goto out;279	}280	err = udf_fiiter_load_bhs(iter);281	if (err < 0)282		goto out;283	err = udf_copy_fi(iter);284out:285	if (err < 0)286		udf_fiiter_release(iter);287	return err;288}289 290int udf_fiiter_advance(struct udf_fileident_iter *iter)291{292	unsigned int oldoff, len;293	int blksize = 1 << iter->dir->i_blkbits;294	int err;295 296	oldoff = iter->pos & (blksize - 1);297	len = udf_dir_entry_len(&iter->fi);298	iter->pos += len;299	if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {300		if (oldoff + len >= blksize) {301			brelse(iter->bh[0]);302			iter->bh[0] = NULL;303			/* Next block already loaded? */304			if (iter->bh[1]) {305				iter->bh[0] = iter->bh[1];306				iter->bh[1] = NULL;307			} else {308				err = udf_fiiter_advance_blk(iter);309				if (err < 0)310					return err;311			}312		}313		err = udf_fiiter_load_bhs(iter);314		if (err < 0)315			return err;316	}317	return udf_copy_fi(iter);318}319 320void udf_fiiter_release(struct udf_fileident_iter *iter)321{322	iter->dir = NULL;323	brelse(iter->bh[0]);324	brelse(iter->bh[1]);325	iter->bh[0] = iter->bh[1] = NULL;326	kfree(iter->namebuf);327	iter->namebuf = NULL;328}329 330static void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2,331			     int off, void *src, int len)332{333	int copy;334 335	if (off >= len1) {336		off -= len1;337	} else {338		copy = min(off + len, len1) - off;339		memcpy(buf1 + off, src, copy);340		src += copy;341		len -= copy;342		off = 0;343	}344	if (len > 0) {345		if (WARN_ON_ONCE(off + len > len2 || !buf2))346			return;347		memcpy(buf2 + off, src, len);348	}349}350 351static uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2,352				int off, int len)353{354	int copy;355	uint16_t crc = 0;356 357	if (off >= len1) {358		off -= len1;359	} else {360		copy = min(off + len, len1) - off;361		crc = crc_itu_t(crc, buf1 + off, copy);362		len -= copy;363		off = 0;364	}365	if (len > 0) {366		if (WARN_ON_ONCE(off + len > len2 || !buf2))367			return 0;368		crc = crc_itu_t(crc, buf2 + off, len);369	}370	return crc;371}372 373static void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2,374				int off, struct fileIdentDesc *fi,375				uint8_t *impuse, uint8_t *name)376{377	uint16_t crc;378	int fioff = off;379	int crcoff = off + sizeof(struct tag);380	unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag);381	char zeros[UDF_NAME_PAD] = {};382	int endoff = off + udf_dir_entry_len(fi);383 384	udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi,385			 sizeof(struct fileIdentDesc));386	off += sizeof(struct fileIdentDesc);387	if (impuse)388		udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse,389				 le16_to_cpu(fi->lengthOfImpUse));390	off += le16_to_cpu(fi->lengthOfImpUse);391	if (name) {392		udf_copy_to_bufs(buf1, len1, buf2, len2, off, name,393				 fi->lengthFileIdent);394		off += fi->lengthFileIdent;395		udf_copy_to_bufs(buf1, len1, buf2, len2, off, zeros,396				 endoff - off);397	}398 399	crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen);400	fi->descTag.descCRC = cpu_to_le16(crc);401	fi->descTag.descCRCLength = cpu_to_le16(crclen);402	fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag);403 404	udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag));405}406 407void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)408{409	struct udf_inode_info *iinfo = UDF_I(iter->dir);410	void *buf1, *buf2 = NULL;411	int len1, len2 = 0, off;412	int blksize = 1 << iter->dir->i_blkbits;413 414	off = iter->pos & (blksize - 1);415	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {416		buf1 = iinfo->i_data + iinfo->i_lenEAttr;417		len1 = iter->dir->i_size;418	} else {419		buf1 = iter->bh[0]->b_data;420		len1 = blksize;421		if (iter->bh[1]) {422			buf2 = iter->bh[1]->b_data;423			len2 = blksize;424		}425	}426 427	udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse,428			    iter->name == iter->namebuf ? iter->name : NULL);429 430	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {431		mark_inode_dirty(iter->dir);432	} else {433		mark_buffer_dirty_inode(iter->bh[0], iter->dir);434		if (iter->bh[1])435			mark_buffer_dirty_inode(iter->bh[1], iter->dir);436	}437	inode_inc_iversion(iter->dir);438}439 440void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)441{442	struct udf_inode_info *iinfo = UDF_I(iter->dir);443	int diff = new_elen - iter->elen;444 445	/* Skip update when we already went past the last extent */446	if (!iter->elen)447		return;448	iter->elen = new_elen;449	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)450		iter->epos.offset -= sizeof(struct short_ad);451	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)452		iter->epos.offset -= sizeof(struct long_ad);453	udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);454	iinfo->i_lenExtents += diff;455	mark_inode_dirty(iter->dir);456}457 458/* Append new block to directory. @iter is expected to point at EOF */459int udf_fiiter_append_blk(struct udf_fileident_iter *iter)460{461	struct udf_inode_info *iinfo = UDF_I(iter->dir);462	int blksize = 1 << iter->dir->i_blkbits;463	struct buffer_head *bh;464	sector_t block;465	uint32_t old_elen = iter->elen;466	int err;467	int8_t etype;468 469	if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))470		return -EINVAL;471 472	/* Round up last extent in the file */473	udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));474 475	/* Allocate new block and refresh mapping information */476	block = iinfo->i_lenExtents >> iter->dir->i_blkbits;477	bh = udf_bread(iter->dir, block, 1, &err);478	if (!bh) {479		udf_fiiter_update_elen(iter, old_elen);480		return err;481	}482	err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,483		   &iter->loffset, &etype);484	if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {485		udf_err(iter->dir->i_sb,486			"block %llu not allocated in directory (ino %lu)\n",487			(unsigned long long)block, iter->dir->i_ino);488		return -EFSCORRUPTED;489	}490	if (!(iter->pos & (blksize - 1))) {491		brelse(iter->bh[0]);492		iter->bh[0] = bh;493	} else {494		iter->bh[1] = bh;495	}496	return 0;497}498 499struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,500			      int inc)501{502	struct short_ad *sa;503 504	if ((!ptr) || (!offset)) {505		pr_err("%s: invalidparms\n", __func__);506		return NULL;507	}508 509	if ((*offset + sizeof(struct short_ad)) > maxoffset)510		return NULL;511	else {512		sa = (struct short_ad *)ptr;513		if (sa->extLength == 0)514			return NULL;515	}516 517	if (inc)518		*offset += sizeof(struct short_ad);519	return sa;520}521 522struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)523{524	struct long_ad *la;525 526	if ((!ptr) || (!offset)) {527		pr_err("%s: invalidparms\n", __func__);528		return NULL;529	}530 531	if ((*offset + sizeof(struct long_ad)) > maxoffset)532		return NULL;533	else {534		la = (struct long_ad *)ptr;535		if (la->extLength == 0)536			return NULL;537	}538 539	if (inc)540		*offset += sizeof(struct long_ad);541	return la;542}543