Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add previewBuffer type as a workaround for Sony preview images #2008

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/exiv2/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ namespace Exiv2 {
unsignedLongLong =16, //!< Exif LONG LONG type, 64-bit (8-byte) unsigned integer.
signedLongLong =17, //!< Exif LONG LONG type, 64-bit (8-byte) signed integer.
tiffIfd8 =18, //!< TIFF IFD type, 64-bit (8-byte) unsigned integer.
previewBuffer =0xffff, //!< Used when the metadata contains a reference to a preview image that is not stored with the metadata, but somewhere else in the file. (Sony does this. See https://github.com/Exiv2/exiv2/issues/2001)
string =0x10000, //!< IPTC string type.
date =0x10001, //!< IPTC date type.
time =0x10002, //!< IPTC time type.
Expand Down
87 changes: 87 additions & 0 deletions include/exiv2/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,93 @@ namespace Exiv2 {

}; // class TimeValue

/*!
@brief %Value for preview images

This class is used to handle Sony's preview images, which are not
stored in the metadata but instead refer to a location in the main
file. Since the actual preview is not available while we're parsing
the metadata, we create this placeholder value instead.

See: https://github.com/Exiv2/exiv2/issues/2001
*/
class EXIV2API PreviewBufferValue : public Value {
public:
//! Shortcut for a %PreviewBufferValue auto pointer.
typedef std::unique_ptr<PreviewBufferValue> UniquePtr;

//! @name Creators
//@{
//! Default constructor.
PreviewBufferValue();
//! Virtual destructor.
~PreviewBufferValue() override = default;
//@}

//! @name Manipulators
//@{
/*!
@brief Read the value from a character buffer.

@note The byte order is required by the interface but not used by this
method, so just use the default.

@param buf Pointer to the data buffer to read from
@param len Number of bytes in the data buffer
@param byteOrder Byte order. Not needed.

@return 0 if successful<BR>
1 in case of an unsupported time format
*/
int read(const byte* buf, long len, ByteOrder byteOrder = invalidByteOrder) override;
/*!
@brief Set the value to that of the string buf.

@param buf String containing the preview.

@return 0 if successful<BR>
1 in case of an unsupported time format
*/
int read(const std::string& buf) override;
//@}

//! @name Accessors
//@{
/*!
@brief Write value to a character data buffer.

The user must ensure that the buffer has enough memory. Otherwise
the call results in undefined behaviour.

@note The byte order is required by the interface but not used by this
method, so just use the default.

@param buf Data buffer to write to.
@param byteOrder Byte order. Not used.
@return Number of characters written.
*/
long copy(byte* buf, ByteOrder byteOrder = invalidByteOrder) const override;
long count() const override;
long size() const override;
std::ostream& write(std::ostream& os) const override;
//! Returns number of seconds in the day in UTC.
long toLong(long n = 0) const override;
//! Returns number of seconds in the day in UTC converted to float.
float toFloat(long n = 0) const override;
//! Returns number of seconds in the day in UTC converted to Rational.
Rational toRational(long n = 0) const override;
//@}

private:
//! @name Accessors
//@{
//! Internal virtual copy constructor.
PreviewBufferValue* clone_() const override;
//@}

size_t size_;
}; // class PreviewBufferValue

//! Template to determine the TypeId for a type T
template<typename T> TypeId getType();

Expand Down
17 changes: 11 additions & 6 deletions src/tiffvisitor_int.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,12 @@ namespace Exiv2 {
// #1143
if ( object->tag() == 0x2001 && std::string(groupName(object->group())) == "Sony1" ) {
isize=size;

// This a workaround for Sony preview images, which are stored in the body
// of the main file, not in the metadata. Since we don't have access to the
// rest of the file here, we just store a placeholder (of type PreviewBufferValue).
// See https://github.com/Exiv2/exiv2/issues/2001
typeId = previewBuffer;
} else {
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Offset of directory " << groupName(object->group())
Expand Down Expand Up @@ -1595,17 +1601,16 @@ namespace Exiv2 {
}
Value::UniquePtr v = Value::create(typeId);
enforce(v.get() != nullptr, kerCorruptedMetadata);
if ( !isize ) {
if (typeId != previewBuffer) {
v->read(pData, size, byteOrder());
} else {
// Prevent large memory allocations: https://github.com/Exiv2/exiv2/issues/1881
enforce(isize <= 1024 * 1024, kerCorruptedMetadata);

// #1143 Write a "hollow" buffer for the preview image
// Sadly: we don't know the exact location of the image in the source (it's near offset)
// And neither TiffReader nor TiffEntryBase have access to the BasicIo object being processed
std::vector<byte> buffer(isize);
v->read(buffer.data() ,isize, byteOrder());
//
// typeId == previewBuffer, so v is a PreviewBufferValue, which just stores the size without
// attempting to read the bytes.
v->read(0, isize, byteOrder());
}

object->setValue(std::move(v));
Expand Down
1 change: 1 addition & 0 deletions src/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace {
{ Exiv2::tiffFloat, "Float", 4 },
{ Exiv2::tiffDouble, "Double", 8 },
{ Exiv2::tiffIfd, "Ifd", 4 },
{ Exiv2::previewBuffer, "PreviewBuffer", 1 },
{ Exiv2::string, "String", 1 },
{ Exiv2::date, "Date", 8 },
{ Exiv2::time, "Time", 11 },
Expand Down
62 changes: 62 additions & 0 deletions src/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ namespace Exiv2 {
case langAlt:
value = UniquePtr(new LangAltValue);
break;
case previewBuffer:
value = UniquePtr(new PreviewBufferValue);
break;
default:
value = UniquePtr(new DataValue(typeId));
break;
Expand Down Expand Up @@ -1205,4 +1208,63 @@ namespace Exiv2 {
return {toLong(n), 1};
}

PreviewBufferValue::PreviewBufferValue()
: Value(previewBuffer)
{
}

int PreviewBufferValue::read(const byte*, long len, ByteOrder /*byteOrder*/)
{
if (len < 0) {
return 1;
}
size_ = static_cast<size_t>(len);
return 0;
}

int PreviewBufferValue::read(const std::string& buf)
{
size_ = buf.size();
return 0;
}

long PreviewBufferValue::copy(byte* /*buf*/, ByteOrder /*byteOrder*/) const
{
return size();
}

long PreviewBufferValue::count() const
{
return size();
}

long PreviewBufferValue::size() const
{
return static_cast<long>(size_);
}

PreviewBufferValue* PreviewBufferValue::clone_() const
{
return new PreviewBufferValue(*this);
}

std::ostream& PreviewBufferValue::write(std::ostream& os) const
{
return os;
}

long PreviewBufferValue::toLong(long /*n*/) const
{
return 0;
}

float PreviewBufferValue::toFloat(long n) const
{
return static_cast<float>(toLong(n));
}

Rational PreviewBufferValue::toRational(long n) const
{
return {toLong(n), 1};
}
} // namespace Exiv2
8 changes: 2 additions & 6 deletions tests/bugfixes/github/test_issue_1881.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,5 @@ class SonyPreviewImageLargeAllocation(metaclass=CaseMeta):
filename2 = path("$tmp_path/issue_1881_coverage.jpg")
commands = ["$exiv2 -q -d I rm $filename1", "$exiv2 -q -d I rm $filename2"]
stdout = ["",""]
stderr = [
"""Exiv2 exception in erase action for file $filename1:
$kerCorruptedMetadata
""",
""]
retval = [1,0]
stderr = ["",""]
retval = [0,0]