mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
commit fa65653e575fbd958bdf5fb9c4a71a324e39510d upstream. Detect when a directory entry is (possibly partially) beyond directory size and return EIO in that case since it means the filesystem is corrupted. Otherwise directory operations can further corrupt the directory and possibly also oops the kernel. CC: Anatoly Trosinenko <anatoly.trosinenko@gmail.com> CC: stable@vger.kernel.org Reported-and-tested-by: Anatoly Trosinenko <anatoly.trosinenko@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
245 lines
5.9 KiB
C
245 lines
5.9 KiB
C
/*
|
|
* directory.c
|
|
*
|
|
* PURPOSE
|
|
* Directory related functions
|
|
*
|
|
* COPYRIGHT
|
|
* This file is distributed under the terms of the GNU General Public
|
|
* License (GPL). Copies of the GPL can be obtained from:
|
|
* ftp://prep.ai.mit.edu/pub/gnu/GPL
|
|
* Each contributing author retains all rights to their own work.
|
|
*/
|
|
|
|
#include "udfdecl.h"
|
|
#include "udf_i.h"
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/string.h>
|
|
#include <linux/bio.h>
|
|
|
|
struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
|
|
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;
|
|
int i, num, block;
|
|
struct buffer_head *tmp, *bha[16];
|
|
struct udf_inode_info *iinfo = UDF_I(dir);
|
|
|
|
fibh->soffset = fibh->eoffset;
|
|
|
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
|
fi = udf_get_fileident(iinfo->i_ext.i_data -
|
|
(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));
|
|
|
|
return fi;
|
|
}
|
|
|
|
if (fibh->eoffset == dir->i_sb->s_blocksize) {
|
|
int lextoffset = epos->offset;
|
|
unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits;
|
|
|
|
if (udf_next_aext(dir, epos, eloc, elen, 1) !=
|
|
(EXT_RECORDED_ALLOCATED >> 30))
|
|
return NULL;
|
|
|
|
block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
|
|
|
|
(*offset)++;
|
|
|
|
if ((*offset << blocksize_bits) >= *elen)
|
|
*offset = 0;
|
|
else
|
|
epos->offset = lextoffset;
|
|
|
|
brelse(fibh->sbh);
|
|
fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
|
|
if (!fibh->sbh)
|
|
return NULL;
|
|
fibh->soffset = fibh->eoffset = 0;
|
|
|
|
if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) {
|
|
i = 16 >> (blocksize_bits - 9);
|
|
if (i + *offset > (*elen >> blocksize_bits))
|
|
i = (*elen >> blocksize_bits)-*offset;
|
|
for (num = 0; i > 0; i--) {
|
|
block = udf_get_lb_pblock(dir->i_sb, eloc,
|
|
*offset + i);
|
|
tmp = udf_tgetblk(dir->i_sb, block);
|
|
if (tmp && !buffer_uptodate(tmp) &&
|
|
!buffer_locked(tmp))
|
|
bha[num++] = tmp;
|
|
else
|
|
brelse(tmp);
|
|
}
|
|
if (num) {
|
|
ll_rw_block(REQ_OP_READ, REQ_RAHEAD, num, bha);
|
|
for (i = 0; i < num; 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,
|
|
&(fibh->eoffset));
|
|
|
|
if (!fi)
|
|
return NULL;
|
|
|
|
*nf_pos += fibh->eoffset - fibh->soffset;
|
|
|
|
if (fibh->eoffset <= dir->i_sb->s_blocksize) {
|
|
memcpy((uint8_t *)cfi, (uint8_t *)fi,
|
|
sizeof(struct fileIdentDesc));
|
|
} else if (fibh->eoffset > dir->i_sb->s_blocksize) {
|
|
int lextoffset = epos->offset;
|
|
|
|
if (udf_next_aext(dir, epos, eloc, elen, 1) !=
|
|
(EXT_RECORDED_ALLOCATED >> 30))
|
|
return NULL;
|
|
|
|
block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
|
|
|
|
(*offset)++;
|
|
|
|
if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
|
|
*offset = 0;
|
|
else
|
|
epos->offset = lextoffset;
|
|
|
|
fibh->soffset -= dir->i_sb->s_blocksize;
|
|
fibh->eoffset -= dir->i_sb->s_blocksize;
|
|
|
|
fibh->ebh = udf_tread(dir->i_sb, block);
|
|
if (!fibh->ebh)
|
|
return NULL;
|
|
|
|
if (sizeof(struct fileIdentDesc) > -fibh->soffset) {
|
|
int fi_len;
|
|
|
|
memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset);
|
|
memcpy((uint8_t *)cfi - fibh->soffset,
|
|
fibh->ebh->b_data,
|
|
sizeof(struct fileIdentDesc) + fibh->soffset);
|
|
|
|
fi_len = (sizeof(struct fileIdentDesc) +
|
|
cfi->lengthFileIdent +
|
|
le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3;
|
|
|
|
*nf_pos += fi_len - (fibh->eoffset - fibh->soffset);
|
|
fibh->eoffset = fibh->soffset + fi_len;
|
|
} else {
|
|
memcpy((uint8_t *)cfi, (uint8_t *)fi,
|
|
sizeof(struct fileIdentDesc));
|
|
}
|
|
}
|
|
/* Got last entry outside of dir size - fs is corrupted! */
|
|
if (*nf_pos > dir->i_size)
|
|
return NULL;
|
|
return fi;
|
|
}
|
|
|
|
struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset)
|
|
{
|
|
struct fileIdentDesc *fi;
|
|
int lengthThisIdent;
|
|
uint8_t *ptr;
|
|
int padlen;
|
|
|
|
if ((!buffer) || (!offset)) {
|
|
udf_debug("invalidparms, buffer=%p, offset=%p\n",
|
|
buffer, offset);
|
|
return NULL;
|
|
}
|
|
|
|
ptr = buffer;
|
|
|
|
if ((*offset > 0) && (*offset < bufsize))
|
|
ptr += *offset;
|
|
fi = (struct fileIdentDesc *)ptr;
|
|
if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
|
|
udf_debug("0x%x != TAG_IDENT_FID\n",
|
|
le16_to_cpu(fi->descTag.tagIdent));
|
|
udf_debug("offset: %u sizeof: %lu bufsize: %u\n",
|
|
*offset, (unsigned long)sizeof(struct fileIdentDesc),
|
|
bufsize);
|
|
return NULL;
|
|
}
|
|
if ((*offset + sizeof(struct fileIdentDesc)) > bufsize)
|
|
lengthThisIdent = sizeof(struct fileIdentDesc);
|
|
else
|
|
lengthThisIdent = sizeof(struct fileIdentDesc) +
|
|
fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
|
|
|
|
/* we need to figure padding, too! */
|
|
padlen = lengthThisIdent % UDF_NAME_PAD;
|
|
if (padlen)
|
|
lengthThisIdent += (UDF_NAME_PAD - padlen);
|
|
*offset = *offset + lengthThisIdent;
|
|
|
|
return fi;
|
|
}
|
|
|
|
struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
|
|
int inc)
|
|
{
|
|
struct short_ad *sa;
|
|
|
|
if ((!ptr) || (!offset)) {
|
|
pr_err("%s: invalidparms\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if ((*offset + sizeof(struct short_ad)) > maxoffset)
|
|
return NULL;
|
|
else {
|
|
sa = (struct short_ad *)ptr;
|
|
if (sa->extLength == 0)
|
|
return NULL;
|
|
}
|
|
|
|
if (inc)
|
|
*offset += sizeof(struct short_ad);
|
|
return sa;
|
|
}
|
|
|
|
struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)
|
|
{
|
|
struct long_ad *la;
|
|
|
|
if ((!ptr) || (!offset)) {
|
|
pr_err("%s: invalidparms\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if ((*offset + sizeof(struct long_ad)) > maxoffset)
|
|
return NULL;
|
|
else {
|
|
la = (struct long_ad *)ptr;
|
|
if (la->extLength == 0)
|
|
return NULL;
|
|
}
|
|
|
|
if (inc)
|
|
*offset += sizeof(struct long_ad);
|
|
return la;
|
|
}
|