diff options
author | Robin Watts <robin.watts@artifex.com> | 2013-12-23 11:54:49 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2013-12-24 10:07:25 +0000 |
commit | 6b08b13fc4e95f9f0446a7199f1d9b5d9348d2f9 (patch) | |
tree | 01968e4ad4fa34c96d831581ec7f0920ff12a82e /source | |
parent | 3328de5f6432ee25525dca59d4e60d2603477b81 (diff) | |
download | mupdf-6b08b13fc4e95f9f0446a7199f1d9b5d9348d2f9.tar.xz |
Bug 694810: Implement late file repair for PDFs.
Currently, if we spot a bad xref as we are reading a PDF in, we can
repair that PDF by doing a long exhaustive read of the file. This
reconstructs the information that was in the xref, and the file can
be opened (and later saved) as normal.
If we hit an object that is not in the expected place however, we
cannot trigger a repair at that point - so xrefs with duff offsets
in (within the bounds of the file) will never be repaired.
This commit solves that by triggering a repair (just once) whenever
we fail to parse an object in the expected place.
Diffstat (limited to 'source')
-rw-r--r-- | source/pdf/pdf-parse.c | 14 | ||||
-rw-r--r-- | source/pdf/pdf-repair.c | 5 | ||||
-rw-r--r-- | source/pdf/pdf-xref.c | 36 |
3 files changed, 46 insertions, 9 deletions
diff --git a/source/pdf/pdf-parse.c b/source/pdf/pdf-parse.c index aaa45c0d..66e0fbe7 100644 --- a/source/pdf/pdf-parse.c +++ b/source/pdf/pdf-parse.c @@ -496,7 +496,7 @@ pdf_parse_stm_obj(pdf_document *doc, fz_stream *file, pdf_lexbuf *buf) pdf_obj * pdf_parse_ind_obj(pdf_document *doc, fz_stream *file, pdf_lexbuf *buf, - int *onum, int *ogen, int *ostmofs) + int *onum, int *ogen, int *ostmofs, int *try_repair) { pdf_obj *obj = NULL; int num = 0, gen = 0, stm_ofs; @@ -508,17 +508,29 @@ pdf_parse_ind_obj(pdf_document *doc, tok = pdf_lex(file, buf); if (tok != PDF_TOK_INT) + { + if (try_repair) + *try_repair = 1; fz_throw(ctx, FZ_ERROR_GENERIC, "expected object number"); + } num = buf->i; tok = pdf_lex(file, buf); if (tok != PDF_TOK_INT) + { + if (try_repair) + *try_repair = 1; fz_throw(ctx, FZ_ERROR_GENERIC, "expected generation number (%d ? obj)", num); + } gen = buf->i; tok = pdf_lex(file, buf); if (tok != PDF_TOK_OBJ) + { + if (try_repair) + *try_repair = 1; fz_throw(ctx, FZ_ERROR_GENERIC, "expected 'obj' keyword (%d %d ?)", num, gen); + } tok = pdf_lex(file, buf); diff --git a/source/pdf/pdf-repair.c b/source/pdf/pdf-repair.c index c742714d..e9a60986 100644 --- a/source/pdf/pdf-repair.c +++ b/source/pdf/pdf-repair.c @@ -234,7 +234,6 @@ pdf_repair_obj_stm(pdf_document *doc, int num, int gen) } } -/* Entered with file locked, remains locked throughout. */ void pdf_repair_xref(pdf_document *doc, pdf_lexbuf *buf) { @@ -267,6 +266,10 @@ pdf_repair_xref(pdf_document *doc, pdf_lexbuf *buf) fz_var(list); fz_var(obj); + if (doc->repair_attempted) + fz_throw(doc->ctx, FZ_ERROR_GENERIC, "Repair failed already - not trying again"); + doc->repair_attempted = 1; + doc->dirty = 1; /* Can't support incremental update after repair */ doc->freeze_updates = 1; diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c index 338dec94..f3deeebf 100644 --- a/source/pdf/pdf-xref.c +++ b/source/pdf/pdf-xref.c @@ -562,7 +562,7 @@ pdf_read_new_xref(pdf_document *doc, pdf_lexbuf *buf) { pdf_xref_entry *entry; int ofs = fz_tell(doc->file); - trailer = pdf_parse_ind_obj(doc, doc->file, buf, &num, &gen, &stm_ofs); + trailer = pdf_parse_ind_obj(doc, doc->file, buf, &num, &gen, &stm_ofs, NULL); entry = pdf_get_populating_xref_entry(doc, num); entry->ofs = ofs; entry->gen = gen; @@ -832,7 +832,7 @@ pdf_load_linear(pdf_document *doc) { pdf_xref_entry *entry; - dict = pdf_parse_ind_obj(doc, doc->file, &doc->lexbuf.base, &num, &gen, &stmofs); + dict = pdf_parse_ind_obj(doc, doc->file, &doc->lexbuf.base, &num, &gen, &stmofs, NULL); if (!pdf_is_dict(dict)) fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to read linearized dictionary"); o = pdf_dict_gets(dict, "Linearized"); @@ -1611,13 +1611,18 @@ void pdf_cache_object(pdf_document *doc, int num, int gen) { pdf_xref_entry *x; - int rnum, rgen; + int rnum, rgen, try_repair; fz_context *ctx = doc->ctx; + fz_var(try_repair); + if (num < 0 || num >= pdf_xref_len(doc)) fz_throw(ctx, FZ_ERROR_GENERIC, "object out of range (%d %d R); xref size %d", num, gen, pdf_xref_len(doc)); object_updated: + try_repair = 0; + rnum = num; + x = pdf_get_xref_entry(doc, num); if (x->obj) @@ -1634,18 +1639,35 @@ object_updated: fz_try(ctx) { x->obj = pdf_parse_ind_obj(doc, doc->file, &doc->lexbuf.base, - &rnum, &rgen, &x->stm_ofs); + &rnum, &rgen, &x->stm_ofs, &try_repair); } fz_catch(ctx) { - fz_rethrow_message(ctx, "cannot parse object (%d %d R)", num, gen); + if (!try_repair || fz_caught(ctx) == FZ_ERROR_TRYLATER) + fz_rethrow(ctx); } - if (rnum != num) + if (!try_repair && rnum != num) { pdf_drop_obj(x->obj); x->obj = NULL; - fz_rethrow_message(ctx, "found object (%d %d R) instead of (%d %d R)", rnum, rgen, num, gen); + try_repair = 1; + } + + if (try_repair) + { + fz_try(ctx) + { + pdf_repair_xref(doc, &doc->lexbuf.base); + } + fz_catch(ctx) + { + if (rnum == num) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse object (%d %d R)", num, gen); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "found object (%d %d R) instead of (%d %d R)", rnum, rgen, num, gen); + } + goto object_updated; } if (doc->crypt) |