Wrap TIFF parser functions into a struct.

This commit is contained in:
KAMADA Ken'ichi 2021-06-05 19:32:24 +09:00
parent 8232c6cd51
commit 7365bc564f
3 changed files with 123 additions and 108 deletions

View File

@ -94,7 +94,7 @@ pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
pub use reader::{Exif, Reader}; pub use reader::{Exif, Reader};
pub use tag::{Context, Tag}; pub use tag::{Context, Tag};
pub use tiff::{DateTime, Field, In}; pub use tiff::{DateTime, Field, In};
pub use tiff::parse_exif_compat03 as parse_exif; pub use tiff::parse_exif;
pub use value::Value; pub use value::Value;
pub use value::{Rational, SRational}; pub use value::{Rational, SRational};

View File

@ -71,16 +71,15 @@ impl Reader {
/// Parses the Exif attributes from raw Exif data. /// Parses the Exif attributes from raw Exif data.
/// If an error occurred, `exif::Error` is returned. /// If an error occurred, `exif::Error` is returned.
pub fn read_raw(&self, data: Vec<u8>) -> Result<Exif, Error> { pub fn read_raw(&self, data: Vec<u8>) -> Result<Exif, Error> {
let buf = data; let mut parser = tiff::Parser::new();
let (entries, le) = tiff::parse_exif(&buf)?; parser.parse(&data)?;
let entry_map = entries.iter().enumerate() let entry_map = parser.entries.iter().enumerate()
.map(|(i, e)| (e.ifd_num_tag(), i)).collect(); .map(|(i, e)| (e.ifd_num_tag(), i)).collect();
Ok(Exif { Ok(Exif {
buf: buf, buf: data,
entries: entries, entries: parser.entries,
entry_map: entry_map, entry_map: entry_map,
little_endian: le, little_endian: parser.little_endian,
}) })
} }

View File

@ -150,51 +150,67 @@ impl fmt::Display for In {
/// Returns a Vec of Exif fields and a bool. /// Returns a Vec of Exif fields and a bool.
/// The boolean value is true if the data is little endian. /// The boolean value is true if the data is little endian.
/// If an error occurred, `exif::Error` is returned. /// If an error occurred, `exif::Error` is returned.
pub fn parse_exif_compat03(data: &[u8]) -> Result<(Vec<Field>, bool), Error> { pub fn parse_exif(data: &[u8]) -> Result<(Vec<Field>, bool), Error> {
parse_exif(data).map(|(entries, le)| { let mut parser = Parser::new();
let fields = entries.into_iter() parser.parse(data)?;
.map(|e| e.into_field(data, le)).collect(); let (entries, le) = (parser.entries, parser.little_endian);
(fields, le) Ok((entries.into_iter().map(|e| e.into_field(data, le)).collect(), le))
})
} }
pub fn parse_exif(data: &[u8]) -> Result<(Vec<IfdEntry>, bool), Error> { #[derive(Debug)]
pub struct Parser {
pub entries: Vec<IfdEntry>,
pub little_endian: bool,
}
impl Parser {
pub fn new() -> Self {
Self { entries: Vec::new(), little_endian: false }
}
pub fn parse(&mut self, data: &[u8]) -> Result<(), Error> {
// Check the byte order and call the real parser. // Check the byte order and call the real parser.
if data.len() < 8 { if data.len() < 8 {
return Err(Error::InvalidFormat("Truncated TIFF header")); return Err(Error::InvalidFormat("Truncated TIFF header"));
} }
match BigEndian::loadu16(data, 0) { match BigEndian::loadu16(data, 0) {
TIFF_BE => parse_exif_sub::<BigEndian>(data).map(|v| (v, false)), TIFF_BE => {
TIFF_LE => parse_exif_sub::<LittleEndian>(data).map(|v| (v, true)), self.little_endian = false;
self.parse_sub::<BigEndian>(data)
},
TIFF_LE => {
self.little_endian = true;
self.parse_sub::<LittleEndian>(data)
},
_ => Err(Error::InvalidFormat("Invalid TIFF byte order")), _ => Err(Error::InvalidFormat("Invalid TIFF byte order")),
} }
} }
fn parse_exif_sub<E>(data: &[u8]) fn parse_sub<E>(&mut self, data: &[u8])
-> Result<Vec<IfdEntry>, Error> where E: Endian { -> Result<(), Error> where E: Endian {
// Parse the rest of the header (42 and the IFD offset). // Parse the rest of the header (42 and the IFD offset).
if E::loadu16(data, 2) != TIFF_FORTY_TWO { if E::loadu16(data, 2) != TIFF_FORTY_TWO {
return Err(Error::InvalidFormat("Invalid forty two")); return Err(Error::InvalidFormat("Invalid forty two"));
} }
let mut ifd_offset = E::loadu32(data, 4) as usize; let mut ifd_offset = E::loadu32(data, 4) as usize;
let mut ifd_num_ck = Some(0); let mut ifd_num_ck = Some(0);
let mut entries = Vec::new();
while ifd_offset != 0 { while ifd_offset != 0 {
let ifd_num = ifd_num_ck.ok_or(Error::InvalidFormat("Too many IFDs"))?; let ifd_num = ifd_num_ck
.ok_or(Error::InvalidFormat("Too many IFDs"))?;
// Limit the number of IFDs to defend against resource exhaustion // Limit the number of IFDs to defend against resource exhaustion
// attacks. // attacks.
if ifd_num >= 8 { if ifd_num >= 8 {
return Err(Error::InvalidFormat("Limit the IFD count to 8")); return Err(Error::InvalidFormat("Limit the IFD count to 8"));
} }
ifd_offset = parse_ifd::<E>( ifd_offset = self.parse_ifd::<E>(
&mut entries, data, ifd_offset, Context::Tiff, ifd_num)?; data, ifd_offset, Context::Tiff, ifd_num)?;
ifd_num_ck = ifd_num.checked_add(1); ifd_num_ck = ifd_num.checked_add(1);
} }
Ok(entries) Ok(())
} }
// Parse IFD [EXIF23 4.6.2]. // Parse IFD [EXIF23 4.6.2].
fn parse_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8], fn parse_ifd<E>(&mut self, data: &[u8],
offset: usize, ctx: Context, ifd_num: u16) offset: usize, ctx: Context, ifd_num: u16)
-> Result<usize, Error> where E: Endian { -> Result<usize, Error> where E: Endian {
// Count (the number of the entries). // Count (the number of the entries).
@ -229,13 +245,13 @@ fn parse_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8],
// recursively defined. // recursively defined.
let tag = Tag(ctx, tag); let tag = Tag(ctx, tag);
match tag { match tag {
Tag::ExifIFDPointer => parse_child_ifd::<E>( Tag::ExifIFDPointer => self.parse_child_ifd::<E>(
entries, data, &mut val, Context::Exif, ifd_num)?, data, &mut val, Context::Exif, ifd_num)?,
Tag::GPSInfoIFDPointer => parse_child_ifd::<E>( Tag::GPSInfoIFDPointer => self.parse_child_ifd::<E>(
entries, data, &mut val, Context::Gps, ifd_num)?, data, &mut val, Context::Gps, ifd_num)?,
Tag::InteropIFDPointer => parse_child_ifd::<E>( Tag::InteropIFDPointer => self.parse_child_ifd::<E>(
entries, data, &mut val, Context::Interop, ifd_num)?, data, &mut val, Context::Interop, ifd_num)?,
_ => entries.push(IfdEntry { field: Field { _ => self.entries.push(IfdEntry { field: Field {
tag: tag, ifd_num: In(ifd_num), value: val }.into()}), tag: tag, ifd_num: In(ifd_num), value: val }.into()}),
} }
} }
@ -244,11 +260,11 @@ fn parse_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8],
if data.len() - offset - 2 - count * 12 < 4 { if data.len() - offset - 2 - count * 12 < 4 {
return Err(Error::InvalidFormat("Truncated next IFD offset")); return Err(Error::InvalidFormat("Truncated next IFD offset"));
} }
let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize; let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12);
Ok(next_ifd_offset) Ok(next_ifd_offset as usize)
} }
fn parse_child_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8], fn parse_child_ifd<E>(&mut self, data: &[u8],
pointer: &mut Value, ctx: Context, ifd_num: u16) pointer: &mut Value, ctx: Context, ifd_num: u16)
-> Result<(), Error> where E: Endian { -> Result<(), Error> where E: Endian {
// The pointer is not yet parsed, so do it here. // The pointer is not yet parsed, so do it here.
@ -259,11 +275,12 @@ fn parse_child_ifd<E>(entries: &mut Vec<IfdEntry>, data: &[u8],
// element of the field. // element of the field.
let ofs = pointer.get_uint(0).ok_or( let ofs = pointer.get_uint(0).ok_or(
Error::InvalidFormat("Invalid pointer"))? as usize; Error::InvalidFormat("Invalid pointer"))? as usize;
match parse_ifd::<E>(entries, data, ofs, ctx, ifd_num)? { match self.parse_ifd::<E>(data, ofs, ctx, ifd_num)? {
0 => Ok(()), 0 => Ok(()),
_ => Err(Error::InvalidFormat("Unexpected next IFD")), _ => Err(Error::InvalidFormat("Unexpected next IFD")),
} }
} }
}
pub fn is_tiff(buf: &[u8]) -> bool { pub fn is_tiff(buf: &[u8]) -> bool {
buf.starts_with(&TIFF_BE_SIG) || buf.starts_with(&TIFF_LE_SIG) buf.starts_with(&TIFF_BE_SIG) || buf.starts_with(&TIFF_LE_SIG)
@ -567,10 +584,9 @@ mod tests {
fn unknown_field() { fn unknown_field() {
let data = b"MM\0\x2a\0\0\0\x08\ let data = b"MM\0\x2a\0\0\0\x08\
\0\x01\x01\0\xff\xff\0\0\0\x01\0\x14\0\0\0\0\0\0"; \0\x01\x01\0\xff\xff\0\0\0\x01\0\x14\0\0\0\0\0\0";
let (v, le) = parse_exif(data).unwrap(); let (v, _le) = parse_exif(data).unwrap();
assert_eq!(v.len(), 1); assert_eq!(v.len(), 1);
assert_pat!(v[0].ref_field(data, le).value, assert_pat!(v[0].value, Value::Unknown(0xffff, 1, 0x12));
Value::Unknown(0xffff, 1, 0x12));
} }
#[test] #[test]