From f0a7bb7bdc77b404c79592be684f3c5d234751cf Mon Sep 17 00:00:00 2001 From: Su Laus Date: Sun, 12 Mar 2023 21:05:56 +0000 Subject: Optimize relative seeking with TIFFSetDirectory --- libtiff/tif_dir.c | 50 +++- libtiff/tif_dirread.c | 7 +- libtiff/tif_open.c | 1 + libtiff/tiffiop.h | 4 +- test/test_directory.c | 578 +++++++++++++++++++++++++++++++++++++---- test/test_ifd_loop_detection.c | 40 ++- 6 files changed, 608 insertions(+), 72 deletions(-) diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c index 56ecbf39..293d6fde 100644 --- a/libtiff/tif_dir.c +++ b/libtiff/tif_dir.c @@ -1702,6 +1702,12 @@ int TIFFCreateCustomDirectory(TIFF *tif, const TIFFFieldArray *infoarray) tif->tif_curoff = 0; tif->tif_row = (uint32_t)-1; tif->tif_curstrip = (uint32_t)-1; + /* invalidate directory index */ + tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER; + /* invalidate IFD loop lists */ + _TIFFCleanupIFDOffsetAndNumberMaps(tif); + /* To be able to return from SubIFD or custom-IFD to main-IFD */ + tif->tif_setdirectory_force_absolute = TRUE; return 0; } @@ -2022,14 +2028,31 @@ tdir_t TIFFNumberOfDirectories(TIFF *tif) int TIFFSetDirectory(TIFF *tif, tdir_t dirn) { uint64_t nextdiroff; - tdir_t nextdirnum; + tdir_t nextdirnum = 0; tdir_t n; - - if (!(tif->tif_flags & TIFF_BIGTIFF)) + /* Fast path when we just advance relative to the current directory: + * start at the current dir offset and continue to seek from there. + * Check special cases when relative is not allowed: + * - jump back from SubIFD or custom directory + * - right after TIFFWriteDirectory() jump back to that directory + * using TIFFSetDirectory() */ + const int relative = (dirn >= tif->tif_curdir) && (tif->tif_diroff != 0) && + !tif->tif_setdirectory_force_absolute; + + if (relative) + { + nextdiroff = tif->tif_diroff; + dirn -= tif->tif_curdir; + nextdirnum = tif->tif_curdir; + } + else if (!(tif->tif_flags & TIFF_BIGTIFF)) nextdiroff = tif->tif_header.classic.tiff_diroff; else nextdiroff = tif->tif_header.big.tiff_diroff; - nextdirnum = 0; + + /* Reset to relative stepping */ + tif->tif_setdirectory_force_absolute = FALSE; + for (n = dirn; n > 0 && nextdiroff != 0; n--) if (!TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum)) return (0); @@ -2039,12 +2062,15 @@ int TIFFSetDirectory(TIFF *tif, tdir_t dirn) return (0); tif->tif_nextdiroff = nextdiroff; - /* - * Set curdir to the actual directory index. The - * -1 decrement is because TIFFReadDirectory will increment - * tif_curdir after successfully reading the directory. - */ - tif->tif_curdir = (dirn - n); + + /* Set curdir to the actual directory index. */ + if (relative) + tif->tif_curdir += dirn - n; + else + tif->tif_curdir = dirn - n; + + /* The -1 decrement is because TIFFReadDirectory will increment + * tif_curdir after successfully reading the directory. */ if (tif->tif_curdir == 0) tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER; else @@ -2108,6 +2134,8 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff) tif->tif_curdir = 0; /* first directory of new chain */ /* add this offset to new IFD list */ _TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir, diroff); + /* To be able to return from SubIFD or custom-IFD to main-IFD */ + tif->tif_setdirectory_force_absolute = TRUE; } return (retval); } @@ -2250,6 +2278,6 @@ int TIFFUnlinkDirectory(TIFF *tif, tdir_t dirn) tif->tif_row = (uint32_t)-1; tif->tif_curstrip = (uint32_t)-1; tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER; - _TIFFCleanupIFDOffsetAndNumberMaps(tif); + _TIFFCleanupIFDOffsetAndNumberMaps(tif); /* invalidate IFD loop lists */ return (1); } diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c index ef21f7f5..d4cc11d0 100644 --- a/libtiff/tif_dirread.c +++ b/libtiff/tif_dirread.c @@ -4067,8 +4067,11 @@ int TIFFReadDirectory(TIFF *tif) if (tif->tif_nextdiroff == 0) { - /* In this special case, tif_diroff needs also to be set to 0. */ + /* In this special case, tif_diroff needs also to be set to 0. + * Furthermore, TIFFSetDirectory() needs to be switched to + * absolute stepping. */ tif->tif_diroff = tif->tif_nextdiroff; + tif->tif_setdirectory_force_absolute = TRUE; return 0; /* last offset, thus no checking necessary */ } @@ -5131,6 +5134,8 @@ int TIFFReadCustomDirectory(TIFF *tif, toff_t diroff, } /*-- if (!dp->tdir_ignore) */ } } + /* To be able to return from SubIFD or custom-IFD to main-IFD */ + tif->tif_setdirectory_force_absolute = TRUE; if (dir) _TIFFfreeExt(tif, dir); return 1; diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c index e84ef6d2..aa16a64f 100644 --- a/libtiff/tif_open.c +++ b/libtiff/tif_open.c @@ -485,6 +485,7 @@ TIFF *TIFFClientOpenExt(const char *name, const char *mode, goto bad; tif->tif_diroff = 0; tif->tif_lastdiroff = 0; + tif->tif_setdirectory_force_absolute = FALSE; return (tif); } /* diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h index c419608d..fbf7b070 100644 --- a/libtiff/tiffiop.h +++ b/libtiff/tiffiop.h @@ -148,7 +148,9 @@ struct tiff uint64_t tif_lastdiroff; /* file offset of last directory written so far */ TIFFHashSet *tif_map_dir_offset_to_number; TIFFHashSet *tif_map_dir_number_to_offset; - TIFFDirectory tif_dir; /* internal rep of current directory */ + int tif_setdirectory_force_absolute; /* switch between relative and absolute + stepping in TIFFSetDirectory() */ + TIFFDirectory tif_dir; /* internal rep of current directory */ TIFFDirectory tif_customdir; /* custom IFDs are separated from the main ones */ union diff --git a/test/test_directory.c b/test/test_directory.c index 12a6fd4e..1cd14274 100644 --- a/test/test_directory.c +++ b/test/test_directory.c @@ -23,12 +23,18 @@ * written is the same, and that their offsets match. The test is then repeated * using BigTIFF files. * + * Furthermore, arbitrary directory loading using TIFFSetDirectory() is checked, + * especially after the update with "relative" movement possibility. Also + * TIFFUnlinkDirectory() is tested. + * */ #include "tif_config.h" #include #include +#include +#include #include "tiffio.h" @@ -45,9 +51,10 @@ const uint16_t photometric = PHOTOMETRIC_RGB; const uint16_t rows_per_strip = 1; const uint16_t planarconfig = PLANARCONFIG_CONTIG; -int write_data_to_current_directory(TIFF *tif) +int write_data_to_current_directory(TIFF *tif, int i) { unsigned char buf[SPP] = {0, 127, 255}; + char auxString[128]; if (!tif) { @@ -89,6 +96,13 @@ int write_data_to_current_directory(TIFF *tif) fprintf(stderr, "Can't set PhotometricInterpretation tag.\n"); return 1; } + /* Write IFD identification number to ASCII string of PageName tag. */ + sprintf(auxString, "%d th. IFD", i); + if (!TIFFSetField(tif, TIFFTAG_PAGENAME, auxString)) + { + fprintf(stderr, "Can't set TIFFTAG_PAGENAME tag.\n"); + return 1; + } /* Write dummy pixel data. */ if (TIFFWriteScanline(tif, buf, 0, 0) == -1) @@ -100,7 +114,8 @@ int write_data_to_current_directory(TIFF *tif) return 0; } -int write_directory_to_closed_file(const char *filename, bool is_big_tiff) +int write_directory_to_closed_file(const char *filename, bool is_big_tiff, + int i) { TIFF *tif; tif = TIFFOpen(filename, is_big_tiff ? "a8" : "a"); @@ -110,16 +125,18 @@ int write_directory_to_closed_file(const char *filename, bool is_big_tiff) return 1; } - if (write_data_to_current_directory(tif)) + if (write_data_to_current_directory(tif, i)) { - fprintf(stderr, "Can't write data to current directory.\n"); + fprintf(stderr, "Can't write data to directory %d of %s.\n", i, + filename); TIFFClose(tif); return 1; } if (!TIFFWriteDirectory(tif)) { - fprintf(stderr, "TIFFWriteDirectory() failed.\n"); + fprintf(stderr, "TIFFWriteDirectory() failed for directory %d of %s.\n", + i, filename); TIFFClose(tif); return 1; } @@ -149,9 +166,57 @@ int count_directories(const char *filename, int *count) return 0; } +/* Compare 'requested_dir_number' with number written in PageName tag + * into the IFD to identify that IFD. */ +int is_requested_directory(TIFF *tif, int requested_dir_number, + const char *filename) +{ + char *ptr = NULL; + char *auxStr = NULL; + + if (!TIFFGetField(tif, TIFFTAG_PAGENAME, &ptr)) + { + fprintf(stderr, "Can't get TIFFTAG_PAGENAME tag.\n"); + return 0; + } + + /* Check for reading errors */ + if (ptr != NULL) + auxStr = strchr(ptr, ' '); + + if (ptr == NULL || auxStr == NULL || strncmp(auxStr, " th.", 4)) + { + ptr = ptr == NULL ? "(null)" : ptr; + fprintf(stderr, + "Error reading IFD directory number from PageName tag: %s\n", + ptr); + return 0; + } + + /* Retrieve IFD identification number from ASCII string */ + const int nthIFD = atoi(ptr); + if (nthIFD == requested_dir_number) + { + return 1; + } + fprintf(stderr, "Expected directory %d from %s was not loaded but: %s\n", + requested_dir_number, filename, ptr); + + return 0; +} + +enum DirWalkMode +{ + DirWalkMode_ReadDirectory, + DirWalkMode_SetDirectory, + DirWalkMode_SetDirectory_Reverse, +}; /* Gets a list of the directory offsets in a file. Assumes the file has at least - * N_DIRECTORIES directories */ -int get_dir_offsets(const char *filename, uint64_t *offsets) + * N_DIRECTORIES directories. + * There are three methods to test walking through the IFD chain. + * This tests the optimization of faster SetDirectory(). */ +int get_dir_offsets(const char *filename, uint64_t *offsets, + enum DirWalkMode dirWalkMode) { TIFF *tif; int i; @@ -165,8 +230,29 @@ int get_dir_offsets(const char *filename, uint64_t *offsets) for (i = 0; i < N_DIRECTORIES; i++) { - offsets[i] = TIFFCurrentDirOffset(tif); - if (!TIFFReadDirectory(tif) && i < (N_DIRECTORIES - 1)) + tdir_t dirn = (dirWalkMode == DirWalkMode_SetDirectory_Reverse) + ? (N_DIRECTORIES - i - 1) + : i; + + if (dirWalkMode != DirWalkMode_ReadDirectory && + !TIFFSetDirectory(tif, dirn) && dirn < (N_DIRECTORIES - 1)) + { + fprintf(stderr, "Can't set %d.th directory from %s\n", i, filename); + TIFFClose(tif); + return 3; + } + + offsets[dirn] = TIFFCurrentDirOffset(tif); + + /* Check, if dirn is requested directory */ + if (!is_requested_directory(tif, dirn, filename)) + { + TIFFClose(tif); + return 4; + } + + if (dirWalkMode == DirWalkMode_ReadDirectory && + !TIFFReadDirectory(tif) && i < (N_DIRECTORIES - 1)) { fprintf(stderr, "Can't read %d.th directory from %s\n", i, filename); @@ -179,14 +265,364 @@ int get_dir_offsets(const char *filename, uint64_t *offsets) return 0; } -/* Checks that rewriting a directory does not break the directory linked list +/* Checks that TIFFSetDirectory() work well after update for relative seeking + * to following directories. + * + * There are several issues especially when SubIFDs and custom directories are + * involved. There are no real directory number for those and TIFFSetDirectory() + * cannot be used. However, TIFFSetDirectory() is needed to switch back to the + * main-IFD chain. Furthermore, IFD-loop handling needs to be supported in any + * cases. + * Also the case where directly after TIFFWriteDirectory() that directory + * is re-read using TIFFSetDirectory() is tested. + */ +int test_arbitrary_directrory_loading(bool is_big_tiff) +{ + const char *filename = "test_arbitrary_directrory_loading.tif"; + TIFF *tif; + uint64_t offsets_base[N_DIRECTORIES]; + int expected_original_dirnumber; + + /* Create a file and write N_DIRECTORIES (10) directories to it */ + tif = TIFFOpen(filename, is_big_tiff ? "w8" : "w"); + if (!tif) + { + fprintf(stderr, "Can't create %s\n", filename); + return 1; + } + TIFFSetDirectory(tif, 0); + for (int i = 0; i < N_DIRECTORIES; i++) + { + if (write_data_to_current_directory(tif, i)) + { + fprintf(stderr, "Can't write data to current directory in %s\n", + filename); + goto failure; + } + if (!TIFFWriteDirectory(tif)) + { + fprintf(stderr, "Can't write directory to %s\n", filename); + goto failure; + } + if (i >= 2 && i <= 4) + { + if (i == 3) + { + /* Invalidate directory - TIFFSetSubDirectory() will fail */ + if (TIFFSetSubDirectory(tif, 0)) + { + fprintf(stderr, + "Unexpected return at invalidate directory %d " + "within %s.\n", + i, filename); + goto failure; + } + } + /* Test jump back to just written directory. */ + if (!TIFFSetDirectory(tif, i)) + { + fprintf(stderr, + "Can't set directory %d within %s " + "right after TIFFWriteDirectory().\n", + i, filename); + goto failure; + } + if (i == 4) + { + /* Invalidate directory - TIFFSetSubDirectory() will fail */ + if (TIFFSetSubDirectory(tif, 0)) + { + fprintf(stderr, + "Unexpected return at invalidate directory %d " + "within %s.\n", + i, filename); + goto failure; + } + } + /*Check if correct directory is loaded */ + if (!is_requested_directory(tif, i, filename)) + { + goto failure; + } + /* Reset to next directory to go on with writing. */ + TIFFCreateDirectory(tif); + } + } + TIFFClose(tif); + + /* Reopen prepared testfile */ + tif = TIFFOpen(filename, "r+"); + if (!tif) + { + fprintf(stderr, "Can't create %s\n", filename); + return 1; + } + + /* Get directory offsets */ + if (get_dir_offsets(filename, offsets_base, DirWalkMode_ReadDirectory)) + { + fprintf(stderr, "Error reading directory offsets from %s.\n", filename); + goto failure; + } + + /* Set last directory and then one after, which should fail. */ + if (!TIFFSetDirectory(tif, N_DIRECTORIES - 1)) + { + fprintf(stderr, "Can't set last directory %d within %s\n", + N_DIRECTORIES - 1, filename); + goto failure; + } + if (TIFFSetDirectory(tif, N_DIRECTORIES + 1)) + { + fprintf(stderr, + "End of IFD chain not detected. Set non existing directory %d " + "within %s\n", + N_DIRECTORIES + 1, filename); + goto failure; + } + + if (!TIFFSetDirectory(tif, 2)) + { + fprintf(stderr, "Can't set directory %d within %s\n", 2, filename); + goto failure; + } + uint64_t off2 = TIFFCurrentDirOffset(tif); + /* Note that dirnum = 2 is deleted here since TIFFUnlinkDirectory() + * starts with 1 instead of 0. */ + if (!TIFFUnlinkDirectory(tif, 3)) + { + fprintf(stderr, "Can't unlink directory %d within %s\n", 3, filename); + goto failure; + } + /* Move to the unlinked IFD. This sets tif_curdir=0 because unlinked IFD + * offset is not in the IFD loop list. This indicates a SubIFD chain. */ + if (!TIFFSetSubDirectory(tif, off2)) + { + fprintf(stderr, + "Can't set sub-directory at offset 0x%" PRIx64 " (%" PRIu64 + ") within %s\n", + off2, off2, filename); + goto failure; + } + /*Check if correct directory is loaded */ + expected_original_dirnumber = 2; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + /* This should move back to the main-IFD chain and load the third + * directory which has IFD number 4, due to the deleted third IFD. */ + if (!TIFFSetDirectory(tif, 3)) + { + fprintf(stderr, "Can't set new directory %d within %s\n", 3, filename); + goto failure; + } + expected_original_dirnumber = 4; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + /* Test backwards jump. */ + if (!TIFFSetDirectory(tif, 2)) + { + fprintf(stderr, "Can't set new directory %d within %s\n", 2, filename); + goto failure; + } + expected_original_dirnumber = 3; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + + /* Second UnlinkDirectory -> two IFDs are missing in the main-IFD chain + * then, orignal dirnum 2 and 3 */ + if (!TIFFUnlinkDirectory(tif, 3)) + { + fprintf(stderr, "Can't unlink directory %d within %s\n", 3, filename); + goto failure; + } + if (!TIFFSetDirectory(tif, 2)) + { + fprintf(stderr, + "Can't set new directory %d after second " + "TIFFUnlinkDirectory(3) within %s\n", + 2, filename); + goto failure; + } + /* which should now be the previous dir-3. */ + expected_original_dirnumber = 4; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + + /* Check, if third original directory could be loaded and the following, + * still chained one. This is like for a SubIFD. */ + if (!TIFFSetSubDirectory(tif, offsets_base[2])) + { + fprintf(stderr, + "Can't set sub-directory at offset 0x%" PRIx64 " (%" PRIu64 + ") within %s\n", + offsets_base[2], offsets_base[2], filename); + goto failure; + } + if (!TIFFReadDirectory(tif)) + { + fprintf(stderr, + "Can't read directory after directory at offset 0x%" PRIx64 + " (%" PRIu64 ") within %s\n", + offsets_base[2], offsets_base[2], filename); + goto failure; + } + /* Check if correct directory is loaded, which was unlinked the second + * time. + */ + expected_original_dirnumber = 3; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + + /* Load unlinked directory like a SubIFD and then go back to the + * main-IFD chain using TIFFSetDirectory(). Also check two loads of the + * same directory. */ + if (!TIFFSetSubDirectory(tif, offsets_base[2])) + { + fprintf(stderr, + "Can't set sub-directory at offset 0x%" PRIx64 " (%" PRIu64 + ") within %s\n", + offsets_base[2], offsets_base[2], filename); + goto failure; + } + if (!TIFFSetDirectory(tif, 3)) + { + fprintf(stderr, "Can't set new directory %d within %s\n", 3, filename); + goto failure; + } + if (!TIFFSetDirectory(tif, 3)) + { + fprintf(stderr, "Can't set new directory %d a second time within %s\n", + 3, filename); + goto failure; + } + /*Check if correct directory is loaded. Because two original IFDs are + * unlinked / missing, the original dirnumber is now 5. */ + expected_original_dirnumber = 5; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + + /* Another sequence for testing. */ + if (!TIFFSetDirectory(tif, 2)) + { + fprintf(stderr, "Can't set new directory %d a second time within %s\n", + 2, filename); + goto failure; + } + if (!TIFFSetDirectory(tif, 3)) + { + fprintf(stderr, "Can't set new directory %d a second time within %s\n", + 3, filename); + goto failure; + } + if (!TIFFSetSubDirectory(tif, offsets_base[2])) + { + fprintf(stderr, + "Can't set sub-directory at offset 0x%" PRIx64 " (%" PRIu64 + ") within %s\n", + offsets_base[2], offsets_base[2], filename); + goto failure; + } + expected_original_dirnumber = 2; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + if (!TIFFSetDirectory(tif, 3)) + { + fprintf(stderr, "Can't set new directory %d a second time within %s\n", + 3, filename); + goto failure; + } + /*Check if correct directory is loaded. Because two original IFDs are + * unlinked / missing, the original dirnumber is now 5. */ + expected_original_dirnumber = 5; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + + /* Third UnlinkDirectory -> three IFDs are missing in the main-IFD chain + * then, orignal dirnum 0, 2 and 3 + * Furthermore, this test checks that TIFFUnlinkDirectory() can unlink + * the first directory dirnum = 0 and a following TIFFSetDirectory(0) + * does not load the unlinked directory. */ + if (!TIFFUnlinkDirectory(tif, 1)) + { + fprintf(stderr, "Can't unlink directory %d within %s\n", 0, filename); + goto failure; + } + /* Now three directories are missing (0,2,3) and thus directory 0 is + * original directory 1 and directory 2 is original directory 5. */ + if (!TIFFSetDirectory(tif, 0)) + { + fprintf(stderr, + "Can't set new directory %d after third " + "TIFFUnlinkDirectory(1) within %s\n", + 0, filename); + goto failure; + } + expected_original_dirnumber = 1; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + if (!TIFFSetDirectory(tif, 2)) + { + fprintf(stderr, + "Can't set new directory %d after third " + "TIFFUnlinkDirectory(1) within %s\n", + 2, filename); + goto failure; + } + expected_original_dirnumber = 5; + if (!is_requested_directory(tif, expected_original_dirnumber, filename)) + { + goto failure; + } + + /* TIFFUnlinkDirectory(0) is not allowed, because dirnum starts for + * this function with 1 instead of 0. + * An error return is expected here. */ + if (TIFFUnlinkDirectory(tif, 0)) + { + fprintf(stderr, "TIFFUnlinkDirectory(0) did not return an error.\n"); + goto failure; + } + + TIFFClose(tif); + unlink(filename); + return 0; + +failure: + if (tif) + { + TIFFClose(tif); + } + unlink(filename); + return 1; +} + +/* Checks that rewriting a directory does not break the directory linked + * list * - * This could happen because TIFFRewriteDirectory relies on the traversal of the - * directory linked list in order to move the rewritten directory to the end of - * the file. This means the `lastdir_offset` optimization should be skipped, - * otherwise the linked list will be broken at the point where it connected to - * the rewritten directory, resulting in the loss of the directories that come - * after it. + * This could happen because TIFFRewriteDirectory relies on the traversal of + * the directory linked list in order to move the rewritten directory to the + * end of the file. This means the `lastdir_offset` optimization should be + * skipped, otherwise the linked list will be broken at the point where it + * connected to the rewritten directory, resulting in the loss of the + * directories that come after it. */ int test_rewrite_lastdir_offset(bool is_big_tiff) { @@ -203,7 +639,7 @@ int test_rewrite_lastdir_offset(bool is_big_tiff) } for (i = 0; i < N_DIRECTORIES; i++) { - if (write_data_to_current_directory(tif)) + if (write_data_to_current_directory(tif, i)) { fprintf(stderr, "Can't write data to current directory in %s\n", filename); @@ -219,8 +655,15 @@ int test_rewrite_lastdir_offset(bool is_big_tiff) /* Without closing it, go to the fifth directory */ TIFFSetDirectory(tif, 4); + /* Check, if dirn is requested directory */ + if (!is_requested_directory(tif, 4, filename)) + { + TIFFClose(tif); + return 4; + } + /* Rewrite the fifth directory by calling TIFFRewriteDirectory */ - if (write_data_to_current_directory(tif)) + if (write_data_to_current_directory(tif, 4)) { fprintf(stderr, "Can't write data to fifth directory in %s\n", filename); @@ -243,10 +686,10 @@ int test_rewrite_lastdir_offset(bool is_big_tiff) } if (count != N_DIRECTORIES) { - fprintf( - stderr, - "Unexpected number of directories in %s. Expected %i, found %i.\n", - filename, N_DIRECTORIES, count); + fprintf(stderr, + "Unexpected number of directories in %s. Expected %i, " + "found %i.\n", + filename, N_DIRECTORIES, count); goto failure; } @@ -257,7 +700,6 @@ failure: if (tif) { TIFFClose(tif); - tif = NULL; } unlink(filename); return 1; @@ -270,12 +712,12 @@ int test_lastdir_offset(bool is_big_tiff) const char *filename_optimized = "test_directory_optimized.tif"; const char *filename_non_optimized = "test_directory_non_optimized.tif"; int i, count_optimized, count_non_optimized; - uint64_t offsets_optimized[N_DIRECTORIES]; - uint64_t offsets_non_optimized[N_DIRECTORIES]; + uint64_t offsets_base[N_DIRECTORIES]; + uint64_t offsets_comparison[N_DIRECTORIES]; TIFF *tif; - /* First file: open it and add multiple directories. This uses the lastdir - * optimization. */ + /* First file: open it and add multiple directories. This uses the + * lastdir optimization. */ tif = TIFFOpen(filename_optimized, is_big_tiff ? "w8" : "w"); if (!tif) { @@ -284,7 +726,7 @@ int test_lastdir_offset(bool is_big_tiff) } for (i = 0; i < N_DIRECTORIES; i++) { - if (write_data_to_current_directory(tif)) + if (write_data_to_current_directory(tif, i)) { fprintf(stderr, "Can't write data to current directory in %s\n", filename_optimized); @@ -304,7 +746,8 @@ int test_lastdir_offset(bool is_big_tiff) * lastdir optimization. */ for (i = 0; i < N_DIRECTORIES; i++) { - if (write_directory_to_closed_file(filename_non_optimized, is_big_tiff)) + if (write_directory_to_closed_file(filename_non_optimized, is_big_tiff, + i)) { fprintf(stderr, "Can't write directory to %s\n", filename_non_optimized); @@ -321,10 +764,10 @@ int test_lastdir_offset(bool is_big_tiff) } if (count_optimized != N_DIRECTORIES) { - fprintf( - stderr, - "Unexpected number of directories in %s. Expected %i, found %i.\n", - filename_optimized, N_DIRECTORIES, count_optimized); + fprintf(stderr, + "Unexpected number of directories in %s. Expected %i, " + "found %i.\n", + filename_optimized, N_DIRECTORIES, count_optimized); goto failure; } if (count_directories(filename_non_optimized, &count_non_optimized)) @@ -335,35 +778,52 @@ int test_lastdir_offset(bool is_big_tiff) } if (count_non_optimized != N_DIRECTORIES) { - fprintf( - stderr, - "Unexpected number of directories in %s. Expected %i, found %i.\n", - filename_non_optimized, N_DIRECTORIES, count_non_optimized); + fprintf(stderr, + "Unexpected number of directories in %s. Expected %i, " + "found %i.\n", + filename_non_optimized, N_DIRECTORIES, count_non_optimized); goto failure; } /* Check that both files have the same directory offsets */ - if (get_dir_offsets(filename_optimized, offsets_optimized)) + /* In parallel of comparing offsets between optimized base file and + * non-optimized file, test also three methods of walking through the + * IFD chain within get_dir_offsets(). This tests the optimization of + * faster SetDirectory(). */ + if (get_dir_offsets(filename_optimized, offsets_base, + DirWalkMode_ReadDirectory)) { fprintf(stderr, "Error reading directory offsets from %s.\n", filename_optimized); goto failure; } - if (get_dir_offsets(filename_non_optimized, offsets_non_optimized)) - { - fprintf(stderr, "Error reading directory offsets from %s.\n", - filename_non_optimized); - goto failure; - } - for (i = 0; i < N_DIRECTORIES; i++) + for (int file_i = 0; file_i < 2; ++file_i) { - if (offsets_optimized[i] != offsets_non_optimized[i]) + const char *filename = + (file_i == 0) ? filename_optimized : filename_non_optimized; + + for (enum DirWalkMode mode = DirWalkMode_ReadDirectory; + mode <= DirWalkMode_SetDirectory_Reverse; ++mode) { - fprintf(stderr, - "Unexpected directory offset for directory %i, expected " - "offset %" PRIu64 " but got %" PRIu64 ".\n", - i, offsets_non_optimized[i], offsets_optimized[i]); - goto failure; + if (get_dir_offsets(filename, offsets_comparison, mode)) + { + fprintf(stderr, + "Error reading directory offsets from %s in mode %d.\n", + filename, mode); + goto failure; + } + for (i = 0; i < N_DIRECTORIES; i++) + { + if (offsets_base[i] != offsets_comparison[i]) + { + fprintf(stderr, + "Unexpected directory offset for directory %i, " + "expected " + "offset %" PRIu64 " but got %" PRIu64 ".\n", + i, offsets_base[i], offsets_comparison[i]); + goto failure; + } + } } } @@ -376,7 +836,6 @@ failure: if (tif) { TIFFClose(tif); - tif = NULL; } unlink(filename_optimized); unlink(filename_non_optimized); @@ -406,5 +865,20 @@ int main() fprintf(stderr, "Failed during BigTIFF RewriteDirectory test.\n"); return 1; } + + /* Finally test arbitrary directory loading */ + if (test_arbitrary_directrory_loading(false)) + { + fprintf(stderr, + "Failed during non-BigTIFF ArbitraryDirectoryLoading test.\n"); + return 1; + } + if (test_arbitrary_directrory_loading(true)) + { + fprintf(stderr, + "Failed during BigTIFF ArbitraryDirectoryLoading test.\n"); + return 1; + } + return 0; } diff --git a/test/test_ifd_loop_detection.c b/test/test_ifd_loop_detection.c index 02421f66..866da39a 100644 --- a/test/test_ifd_loop_detection.c +++ b/test/test_ifd_loop_detection.c @@ -49,6 +49,7 @@ int is_requested_directory(TIFF *tif, int requested_dir_number, fprintf(stderr, "Can't get TIFFTAG_PAGENAME tag.\n"); return 0; } + /* Check for reading errors */ if (ptr != NULL) auxStr = strchr(ptr, ' '); @@ -107,12 +108,12 @@ int test_subifd_loop(void) } if (i != 4) { - fprintf(stderr, "(20) Expected fifth TIFFReadDirectory() to fail\n"); + fprintf(stderr, "(30) Expected fifth TIFFReadDirectory() to fail\n"); ret = 1; } if (!is_requested_directory(tif, 4, filename)) { - fprintf(stderr, "(21) Expected fifth main IFD to be loaded\n"); + fprintf(stderr, "(31) Expected fifth main IFD to be loaded\n"); ret = 1; } @@ -139,7 +140,7 @@ int test_subifd_loop(void) ret = 1; if (!is_requested_directory(tif, 200 + i, filename)) { - fprintf(stderr, "(22) Expected SubIFD %d to be loaded.\n", i); + fprintf(stderr, "(32) Expected SubIFD %d to be loaded.\n", i); ret = 1; } /* Now test SubIFD loop detection. @@ -153,7 +154,7 @@ int test_subifd_loop(void) { fprintf( stderr, - "(23) Expected third SubIFD-TIFFReadDirectory() to fail\n"); + "(33) Expected third SubIFD-TIFFReadDirectory() to fail\n"); ret = 1; } } @@ -162,13 +163,13 @@ int test_subifd_loop(void) ret = 1; if (!is_requested_directory(tif, 3, filename)) { - fprintf(stderr, "(24) Expected fourth main IFD to be loaded\n"); + fprintf(stderr, "(34) Expected fourth main IFD to be loaded\n"); ret = 1; } } else { - fprintf(stderr, "(25) No or wrong expected SubIFDs within main IFD\n"); + fprintf(stderr, "(35) No or wrong expected SubIFDs within main IFD\n"); ret = 1; } @@ -299,6 +300,31 @@ int main() fprintf(stderr, "(17) Expected TIFFReadDirectory() to fail\n"); ret = 1; } + if (TIFFSetDirectory(tif, 0) != 1) + { + fprintf(stderr, "(18) Expected TIFFSetDirectory() to succeed\n"); + ret = 1; + } + if (TIFFSetDirectory(tif, 1) != 1) + { + fprintf(stderr, "(19) Expected TIFFSetDirectory() to succeed\n"); + ret = 1; + } + if (TIFFSetDirectory(tif, 2)) + { + fprintf(stderr, "(20) Expected TIFFSetDirectory() to fail\n"); + ret = 1; + } + if (TIFFSetDirectory(tif, 1) != 1) + { + fprintf(stderr, "(21) Expected TIFFSetDirectory() to succeed\n"); + ret = 1; + } + if (TIFFSetDirectory(tif, 0) != 1) + { + fprintf(stderr, "(22) Expected TIFFSetDirectory() to succeed\n"); + ret = 1; + } TIFFClose(tif); } { @@ -309,7 +335,7 @@ int main() { fprintf( stderr, - "(18) Expected TIFFNumberOfDirectories() to return 2. Got %d\n", + "(23) Expected TIFFNumberOfDirectories() to return 2. Got %d\n", n); ret = 1; } -- cgit v1.2.1