Commit message: Fixed wrong brackets: size*count + pad can overflow before the cast

=> Should fix #76 (most of the work has been done by Robin Mills in
   6e3855aed7ba8bb4731fc4087ca7f9078b2f3d97)

The problem with #76 is the contents of the 26th IFD, with the
following contents:
tag: 0x8649
type: 0x1
count: 0xffff ffff
offset: 0x4974

The issue is the size of count (uint32_t), as adding anything to it
causes an overflow. Especially the expression:
(size*count + pad+20)
results in an overflow and gives 20 as a result instead of
0x100000014, thus the condition in the if in the next line is false
and the program continues to run (until it crashes at io.read).

To properly account for the overflow, the brackets have to be removed,
as then the result is saved in the correctly sized type and not cast
after being calculated in the smaller type.

The brackets have also been removed from bigtiffimage.cpp, where the
same issue is present.
CVE: CVE-2017-17723
CWE: CWE-125
Vulnerability description: In Exiv2 0.26, there is a heap-based buffer over-read in the Exiv2::Image::byteSwap4 function in image.cpp. Remote attackers can exploit this vulnerability to disclose memory data or cause a denial of service via a crafted TIFF file.

Project: exiv2
File: image.cpp

    std::string binaryToHex(const byte *data, size_t size)
    {
        std::stringstream hexOutput;

        unsigned long tl = (unsigned long)((size / 16) * 16);
        unsigned long tl_offset = (unsigned long)(size - tl);

        for (unsigned long loop = 0; loop < (unsigned long)size; loop++) {
            if (data[loop] < 16) {
                hexOutput << "0";
            }
            hexOutput << std::hex << (int)data[loop];
            if ((loop % 8) == 7) {
                hexOutput << "  ";
            }
            if ((loop % 16) == 15 || loop == (tl + tl_offset - 1)) {
                int max = 15;
                if (loop >= tl) {
                    max = tl_offset - 1;
                    for (int offset = 0; offset < (int)(16 - tl_offset); offset++) {
                        if ((offset % 8) == 7) {
                            hexOutput << "  ";
                        }
                        hexOutput << "   ";
                    }
                }
                hexOutput << " ";
                for (int  offset = max; offset >= 0; offset--) {
                    if (offset == (max - 8)) {
                        hexOutput << "  ";
                    }
                    byte c = '.';
                    if (data[loop - offset] >= 0x20 && data[loop - offset] <= 0x7E) {
                        c = data[loop - offset] ;
                    }
                    hexOutput << (char) c ;
                }
                hexOutput << std::endl;
            }
        }

        hexOutput << std::endl << std::endl << std::endl;

        return hexOutput.str();
    }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::open(const byte* data, long size)
    {
        BasicIo::AutoPtr io(new MemIo(data, size));
        Image::AutoPtr image = open(io); // may throw
        if (image.get() == 0) throw Error(12);
        return image;
    }

Project: exiv2
File: image.cpp

    void Image::printStructure(std::ostream&, PrintStructureOption,int /*depth*/)
    {
        throw Error(13, io_->path());
    }

Project: exiv2
File: image.cpp

    void Image::setXmpPacket(const std::string& xmpPacket)
    {
        xmpPacket_ = xmpPacket;
        if ( XmpParser::decode(xmpData_, xmpPacket) ) {
            throw Error(54);
        }
        xmpPacket_ = xmpPacket;
    }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::create(int type,
                                        const std::wstring& wpath)
    {
        std::auto_ptr<FileIo> fileIo(new FileIo(wpath));
        // Create or overwrite the file, then close it
        if (fileIo->open("w+b") != 0) {
            throw WError(10, wpath, "w+b", strError().c_str());
        }
        fileIo->close();
        BasicIo::AutoPtr io(fileIo);
        Image::AutoPtr image = create(type, io);
        if (image.get() == 0) throw Error(13, type);
        return image;
    }

Project: exiv2
File: image.cpp

    AccessMode ImageFactory::checkMode(int type, MetadataId metadataId)
    {
        const Registry* r = find(registry, type);
        if (!r) throw Error(13, type);
        AccessMode am = amNone;
        switch (metadataId) {
        case mdNone:
            break;
        case mdExif:
            am = r->exifSupport_;
            break;
        case mdIptc:
            am = r->iptcSupport_;
            break;
        case mdXmp:
            am = r->xmpSupport_;
            break;
        case mdComment:
            am = r->commentSupport_;
            break;
        case mdIccProfile: break;

        // no default: let the compiler complain
        }
        return am;
    }

Project: exiv2
File: image.cpp

    std::string stringFormat(const char* format, ...)
    {
        std::string result;

        int     need   = (int) std::strlen(format)*2;          // initial guess
        char*   buffer = NULL;
        int     again  =    4;
        int     rc     =   -1;

        while (rc < 0 && again--) {
            if ( buffer ) delete[] buffer;
            need  *= 2 ;
            buffer = new char[need];
            if ( buffer ) {
                va_list  args;                                 // variable arg list
                va_start(args, format);                        // args start after format
                rc=vsnprintf(buffer,(unsigned int)need, format, args);
                va_end(args);                                  // free the args
            }
        }

        if ( rc > 0 ) result = std::string(buffer) ;
        if ( buffer ) delete[] buffer;                         // free buffer
        return result;
    }

Project: exiv2
File: image.cpp

    std::string indent(int32_t d)
    {
        std::string result ;
        if ( d > 0 )
            while ( d--)
                result += "  ";
        return result;
    }

Project: exiv2
File: image.cpp

    std::string binaryToString(const byte* buff, size_t size, size_t start /*=0*/)
    {
        std::string result = "";
        size += start;

        while (start < size) {
            int   c             = (int) buff[start++] ;
            bool  bTrailingNull = c == 0 && start == size;
            if ( !bTrailingNull ) {
                if (c < ' ' || c >= 127) c = '.' ;
                result +=  (char) c ;
            }
        }
        return result;
    }

Project: exiv2
File: bigtiffimage.cpp

                uint64_t readData(int size) const
                {
                    const DataBuf data = Image::io().read(size);
                    assert(data.size_ != 0);

                    uint64_t result = 0;

                    if (size == 1)
                    {}
                    else if (size == 2)
                        result = byteSwap2(data, 0, doSwap_);
                    else if (size == 4)
                        result = byteSwap4(data, 0, doSwap_);
                    else if (size == 8)
                        result = byteSwap8(data, 0, doSwap_);
                    else
                        assert(!"unexpected size");

                    return result;
                }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::create(int type)
    {
        BasicIo::AutoPtr io(new MemIo);
        Image::AutoPtr image = create(type, io);
        if (image.get() == 0) throw Error(13, type);
        return image;
    }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::create(int type,
                                        const std::string& path)
    {
        std::auto_ptr<FileIo> fileIo(new FileIo(path));
        // Create or overwrite the file, then close it
        if (fileIo->open("w+b") != 0) {
            throw Error(10, path, "w+b", strError());
        }
        fileIo->close();
        BasicIo::AutoPtr io(fileIo);
        Image::AutoPtr image = create(type, io);
        if (image.get() == 0) throw Error(13, type);
        return image;
    }

Project: exiv2
File: image.cpp

    void Image::clearIccProfile()
    {
        iccProfile_.release();
    }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::open(const std::wstring& wpath, bool useCurl)
    {
        Image::AutoPtr image = open(ImageFactory::createIo(wpath, useCurl)); // may throw
        if (image.get() == 0) throw WError(11, wpath);
        return image;
    }

Project: exiv2
File: bigtiffimage.cpp

    Image::AutoPtr newBigTiffInstance(BasicIo::AutoPtr io, bool)
    {
        return Image::AutoPtr(new BigTiffImage(io));
    }

Project: exiv2
File: image.cpp

    std::string binaryToString(DataBuf& buf, size_t size, size_t start /*=0*/)
    {
        if ( size > (size_t) buf.size_ ) size = (size_t) buf.size_;
        return binaryToString(buf.pData_,size,start);
    }

Project: exiv2
File: image.cpp

    void Image::setIccProfile(Exiv2::DataBuf& iccProfile,bool bTestValid)
    {
        if ( bTestValid ) {
            if ( iccProfile.pData_ && ( iccProfile.size_ < (long) sizeof(long)) ) throw Error(53);
            long size = iccProfile.pData_ ? getULong(iccProfile.pData_, bigEndian): -1;
            if ( size!= iccProfile.size_ ) throw Error(53);
        }
        iccProfile_ = iccProfile;
    }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::open(const std::string& path, bool useCurl)
    {
        Image::AutoPtr image = open(ImageFactory::createIo(path, useCurl)); // may throw
        if (image.get() == 0) throw Error(11, path);
        return image;
    }

Project: exiv2
File: image.cpp

    void Image::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,uint32_t start,bool bSwap,char c,int depth)
    {
        depth++;
        bool bFirst  = true  ;

        // buffer
        const size_t dirSize = 32;
        DataBuf  dir(dirSize);
        bool bPrint = option == kpsBasic || option == kpsRecursive;

        do {
            // Read top of directory
            io.seek(start,BasicIo::beg);
            io.read(dir.pData_, 2);
            uint16_t   dirLength = byteSwap2(dir,0,bSwap);

            bool tooBig = dirLength > 500;
            if ( tooBig ) throw Error(55);

            if ( bFirst && bPrint ) {
                out << Internal::indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io.path() << std::endl;
                if ( tooBig ) out << Internal::indent(depth) << "dirLength = " << dirLength << std::endl;
            }

            // Read the dictionary
            for ( int i = 0 ; i < dirLength ; i ++ ) {
                if ( bFirst && bPrint ) {
                    out << Internal::indent(depth)
                        << " address |    tag                              |     "
                        << " type |    count |    offset | value\n";
                }
                bFirst = false;

                io.read(dir.pData_, 12);
                uint16_t tag    = byteSwap2(dir,0,bSwap);
                uint16_t type   = byteSwap2(dir,2,bSwap);
                uint32_t count  = byteSwap4(dir,4,bSwap);
                uint32_t offset = byteSwap4(dir,8,bSwap);

                // Break for unknown tag types else we may segfault.
                if ( !typeValid(type) ) {
                    std::cerr << "invalid type value detected in Image::printIFDStructure:  " << type << std::endl;
                    start = 0; // break from do loop
                    throw Error(56);
                    break; // break from for loop
                }

                std::string sp  = "" ; // output spacer

                //prepare to print the value
                uint32_t kount  = isPrintXMP(tag,option) ? count // haul in all the data
                                : isPrintICC(tag,option) ? count // ditto
                                : isStringType(type)     ? (count > 32 ? 32 : count) // restrict long arrays
                                : count > 5              ? 5
                                : count
                                ;
                uint32_t pad    = isStringType(type) ? 1 : 0;
                uint32_t size   = isStringType(type) ? 1
                                : is2ByteType(type)  ? 2
                                : is4ByteType(type)  ? 4
                                : is8ByteType(type)  ? 8
                                : 1
                                ;

                // if ( offset > io.size() ) offset = 0; // Denial of service?

                // #55 memory allocation crash test/data/POC8
                long long allocate = (long long) size*count + pad+20;
                if ( allocate > (long long) io.size() ) {
                    throw Error(57);
                }
                DataBuf  buf((long)allocate);  // allocate a buffer
                std::memcpy(buf.pData_,dir.pData_+8,4);  // copy dir[8:11] into buffer (short strings)
                const bool bOffsetIsPointer = count*size > 4;

                if ( bOffsetIsPointer ) {         // read into buffer
                    size_t   restore = io.tell();  // save
                    io.seek(offset,BasicIo::beg);  // position
                    io.read(buf.pData_,count*size);// read
                    io.seek(restore,BasicIo::beg); // restore
                }

                if ( bPrint ) {
                    const uint32_t address = start + 2 + i*12 ;
                    const std::string offsetString = bOffsetIsPointer?
                        Internal::stringFormat("%10u", offset):
                        "";

                    out << Internal::indent(depth)
                    << Internal::stringFormat("%8u | %#06x %-28s |%10s |%9u |%10s | "
                                              ,address,tag,tagName(tag).c_str(),typeName(type),count,offsetString.c_str());
                    if ( isShortType(type) ){
                        for ( size_t k = 0 ; k < kount ; k++ ) {
                            out << sp << byteSwap2(buf,k*size,bSwap);
                            sp = " ";
                        }
                    } else if ( isLongType(type) ){
                        for ( size_t k = 0 ; k < kount ; k++ ) {
                            out << sp << byteSwap4(buf,k*size,bSwap);
                            sp = " ";
                        }

                    } else if ( isRationalType(type) ){
                        for ( size_t k = 0 ; k < kount ; k++ ) {
                            uint32_t a = byteSwap4(buf,k*size+0,bSwap);
                            uint32_t b = byteSwap4(buf,k*size+4,bSwap);
                            out << sp << a << "/" << b;
                            sp = " ";
                        }
                    } else if ( isStringType(type) ) {
                        out << sp << Internal::binaryToString(buf, kount);
                    }

                    sp = kount == count ? "" : " ...";
                    out << sp << std::endl;

                    if ( option == kpsRecursive && (tag == 0x8769 /* ExifTag */ || tag == 0x014a/*SubIFDs*/  || type == tiffIfd) ) {
                        for ( size_t k = 0 ; k < count ; k++ ) {
                            size_t   restore = io.tell();
                            uint32_t offset = byteSwap4(buf,k*size,bSwap);
                            printIFDStructure(io,out,option,offset,bSwap,c,depth);
                            io.seek(restore,BasicIo::beg);
                        }
                    } else if ( option == kpsRecursive && tag == 0x83bb /* IPTCNAA */ ) {
                        size_t   restore = io.tell();  // save
                        io.seek(offset,BasicIo::beg);  // position
                        byte* bytes=new byte[count] ;  // allocate memory
                        io.read(bytes,count)        ;  // read
                        io.seek(restore,BasicIo::beg); // restore
                        IptcData::printStructure(out,bytes,count,depth);
                        delete[] bytes;                // free
                    }  else if ( option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) {
                        size_t   restore = io.tell();  // save

                        uint32_t jump= 10           ;
                        byte     bytes[20]          ;
                        const char* chars = (const char*) &bytes[0] ;
                        io.seek(offset,BasicIo::beg);  // position
                        io.read(bytes,jump    )     ;  // read
                        bytes[jump]=0               ;
                        if ( ::strcmp("Nikon",chars) == 0 ) {
                            // tag is an embedded tiff
                            byte* bytes=new byte[count-jump] ;  // allocate memory
                            io.read(bytes,count-jump)        ;  // read
                            MemIo memIo(bytes,count-jump)    ;  // create a file
                            printTiffStructure(memIo,out,option,depth);
                            delete[] bytes                   ;  // free
                        } else {
                            // tag is an IFD
                            io.seek(0,BasicIo::beg);  // position
                            printIFDStructure(io,out,option,offset,bSwap,c,depth);
                        }

                        io.seek(restore,BasicIo::beg); // restore
                    }
                }

                if ( isPrintXMP(tag,option) ) {
                    buf.pData_[count]=0;
                    out << (char*) buf.pData_;
                }
                if ( isPrintICC(tag,option) ) {
                    out.write((const char*)buf.pData_,count);
                }
            }
            if ( start ) {
                io.read(dir.pData_, 4);
                start = tooBig ? 0 : byteSwap4(dir,0,bSwap);
            }
        } while (start) ;

        if ( bPrint ) {
            out << Internal::indent(depth) << "END " << io.path() << std::endl;
        }
        out.flush();
        depth--;
    }

Project: exiv2
File: image.cpp

    Image::AutoPtr ImageFactory::open(BasicIo::AutoPtr io)
    {
        if (io->open() != 0) {
            throw Error(9, io->path(), strError());
        }
        for (unsigned int i = 0; registry[i].imageType_ != ImageType::none; ++i) {
            if (registry[i].isThisType_(*io, false)) {
                return registry[i].newInstance_(io, false);
            }
        }
        return Image::AutoPtr();
    } // ImageFactory::open

Project: exiv2
File: image.cpp

    BasicIo::AutoPtr ImageFactory::createIo(const std::string& path, bool useCurl)
    {
        Protocol fProt = fileProtocol(path);
#if EXV_USE_SSH == 1
        if (fProt == pSsh || fProt == pSftp) {
            return BasicIo::AutoPtr(new SshIo(path)); // may throw
        }
#endif
#if EXV_USE_CURL == 1
        if (useCurl && (fProt == pHttp || fProt == pHttps || fProt == pFtp)) {
            return BasicIo::AutoPtr(new CurlIo(path)); // may throw
        }
#endif
        if (fProt == pHttp)
            return BasicIo::AutoPtr(new HttpIo(path)); // may throw
        if (fProt == pFileUri)
            return BasicIo::AutoPtr(new FileIo(pathOfFileUrl(path)));
        if (fProt == pStdin || fProt == pDataUri)
            return BasicIo::AutoPtr(new XPathIo(path)); // may throw

        return BasicIo::AutoPtr(new FileIo(path));

        (void)(useCurl);
    } // ImageFactory::createIo

Project: exiv2
File: bigtiffimage.cpp

                BigTiffImage(BasicIo::AutoPtr io):
                    Image(ImageType::bigtiff, mdExif, io),
                    header_(),
                    dataSize_(0),
                    doSwap_(false)
                {
                    header_ = readHeader(Image::io());
                    assert(header_.isValid());

                    doSwap_ =  (isLittleEndianPlatform() && header_.byteOrder() == bigEndian)
                          ||   (isBigEndianPlatform()    && header_.byteOrder() == littleEndian);

                    dataSize_ = header_.format() == Header::StandardTiff? 4 : 8;
                }

Project: exiv2
File: bigtiffimage.cpp

                void printIFD(std::ostream& out, PrintStructureOption option, uint64_t dir_offset, int depth)
                {
                    BasicIo& io = Image::io();

                    depth++;
                    bool bFirst  = true;

                    // buffer
                    bool bPrint = true;

                    do
                    {
                        // Read top of directory
                        io.seek(dir_offset, BasicIo::beg);

                        const uint64_t entries = readData(header_.format() == Header::StandardTiff? 2: 8);
                        const bool tooBig = entries > 500;

                        if ( bFirst && bPrint )
                        {
                            out << Internal::indent(depth) << Internal::stringFormat("STRUCTURE OF BIGTIFF FILE ") << io.path() << std::endl;
                            if (tooBig)
                                out << Internal::indent(depth) << "entries = " << entries << std::endl;
                        }

                        if (tooBig)
                            break;

                        // Read the dictionary
                        for ( uint64_t i = 0; i < entries; i ++ )
                        {
                            if ( bFirst && bPrint )
                                out << Internal::indent(depth)
                                    << " address |    tag                           |     "
                                    << " type |    count |    offset | value\n";

                            bFirst = false;

                            const uint16_t tag   = (uint16_t) readData(2);
                            const uint16_t type  = (uint16_t) readData(2);
                            const uint64_t count = readData(dataSize_);
                            const DataBuf  data  = io.read(dataSize_);        // Read data as raw value. what should be done about it will be decided depending on type

                            std::string sp = "" ; // output spacer

                            //prepare to print the value
                            // TODO: figure out what's going on with kount
                            const uint64_t kount  = isStringType(type)? (count > 32 ? 32 : count) // restrict long arrays
                                                            : count > 5              ? 5
                                                            : count
                                                            ;
                            const uint32_t pad    = isStringType(type) ? 1 : 0;
                            const uint32_t size   = isStringType(type) ? 1
                                                  : is2ByteType(type)  ? 2
                                                  : is4ByteType(type)  ? 4
                                                  : is8ByteType(type)  ? 8
                                                  : 1;

                			// #55 memory allocation crash test/data/POC8
                			long long allocate = (long long) size*count + pad;
                			if ( allocate > (long long) io.size() ) {
                    			throw Error(57);
                			}

                            DataBuf buf((long)allocate);

                            const uint64_t offset = header_.format() == Header::StandardTiff?
                                    byteSwap4(data, 0, doSwap_):
                                    byteSwap8(data, 0, doSwap_);

                            // big data? Use 'data' as pointer to real data
                            const bool usePointer = (size_t) count*size > (size_t) dataSize_;

                            if ( usePointer )                          // read into buffer
                            {
                                size_t   restore = io.tell();          // save
                                io.seek(offset, BasicIo::beg);         // position
                                io.read(buf.pData_, (long) count * size);     // read
                                io.seek(restore, BasicIo::beg);        // restore
                            }
                            else  // use 'data' as data :)
                                std::memcpy(buf.pData_, data.pData_, (size_t) count * size);     // copy data

                            if ( bPrint )
                            {
                                const uint64_t entrySize = header_.format() == Header::StandardTiff? 12: 20;
                                const uint64_t address = dir_offset + 2 + i * entrySize;
                                const std::string offsetString = usePointer?
                                    Internal::stringFormat("%10u", offset):
                                    "";

                                out << Internal::indent(depth)
                                    << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%10s | ",
                                        address, tag, tagName(tag).c_str(), typeName(type), count, offsetString.c_str());

                                if ( isShortType(type) )
                                {
                                    for ( size_t k = 0 ; k < kount ; k++ )
                                    {
                                        out << sp << byteSwap2(buf, k*size, doSwap_);
                                        sp = " ";
                                    }
                                }
                                else if ( isLongType(type) )
                                {
                                    for ( size_t k = 0 ; k < kount ; k++ )
                                    {
                                        out << sp << byteSwap4(buf, k*size, doSwap_);
                                        sp = " ";
                                    }
                                }
                                else if ( isLongLongType(type) )
                                {
                                    for ( size_t k = 0 ; k < kount ; k++ )
                                    {
                                        out << sp << byteSwap8(buf, k*size, doSwap_);
                                        sp = " ";
                                    }
                                }
                                else if ( isRationalType(type) )
                                {
                                    for ( size_t k = 0 ; k < kount ; k++ )
                                    {
                                        uint32_t a = byteSwap4(buf, k*size+0, doSwap_);
                                        uint32_t b = byteSwap4(buf, k*size+4, doSwap_);
                                        out << sp << a << "/" << b;
                                        sp = " ";
                                    }
                                }
                                else if ( isStringType(type) )
                                    out << sp << Internal::binaryToString(buf, (size_t) kount);

                                sp = kount == count ? "" : " ...";
                                out << sp << std::endl;

                                if ( option == kpsRecursive &&
                                        (tag == 0x8769 /* ExifTag */ || tag == 0x014a/*SubIFDs*/ || type == tiffIfd || type == tiffIfd8) )
                                {
                                    for ( size_t k = 0 ; k < count ; k++ )
                                    {
                                        const size_t restore = io.tell();
                                        const uint64_t ifdOffset = type == tiffIfd8?
                                            byteSwap8(buf, k*size, doSwap_):
                                            byteSwap4(buf, k*size, doSwap_);

                                        std::cerr << "tag = " << Internal::stringFormat("%#x",tag) << std::endl;
                                        printIFD(out, option, ifdOffset, depth);
                                        io.seek(restore, BasicIo::beg);
                                    }
                                }
                                else if ( option == kpsRecursive && tag == 0x83bb /* IPTCNAA */ )
                                {
                                    const size_t restore = io.tell();      // save
                                    io.seek(offset, BasicIo::beg);         // position
                                    byte* bytes=new byte[(size_t)count] ;  // allocate memory
                                    io.read(bytes,(long)count)        ;    // read
                                    io.seek(restore, BasicIo::beg);        // restore
                                    IptcData::printStructure(out,bytes,(size_t)count,depth);
                                    delete[] bytes;                        // free
                                }
                                else if ( option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10)
                                {
                                    size_t   restore = io.tell();  // save

                                    uint32_t jump= 10           ;
                                    byte     bytes[20]          ;
                                    const char* chars = (const char*) &bytes[0] ;
                                    io.seek(dir_offset, BasicIo::beg);  // position
                                    io.read(bytes,jump    )     ;  // read
                                    bytes[jump]=0               ;
                                    if ( ::strcmp("Nikon",chars) == 0 )
                                    {
                                        // tag is an embedded tiff
                                        byte* bytes=new byte[(size_t)count-jump] ;  // allocate memory
                                        io.read(bytes,(long)  count-jump)        ;  // read
                                        MemIo memIo(bytes,(long)count-jump)      ;  // create a file
                                        std::cerr << "Nikon makernote" << std::endl;
                                        // printTiffStructure(memIo,out,option,depth);  TODO: fix it
                                        delete[] bytes                   ;  // free
                                    }
                                    else
                                    {
                                        // tag is an IFD
                                        io.seek(0, BasicIo::beg);  // position
                                        std::cerr << "makernote" << std::endl;
                                        printIFD(out,option,offset,depth);
                                    }

                                    io.seek(restore,BasicIo::beg); // restore
                                }
                            }
                        }

                        const uint64_t nextDirOffset = readData(dataSize_);

                        dir_offset = tooBig ? 0 : nextDirOffset;
                        out.flush();
                    } while (dir_offset != 0);

                    if ( bPrint )
                        out << Internal::indent(depth) << "END " << io.path() << std::endl;

                    depth--;
                }