summaryrefslogtreecommitdiff
path: root/source/pdf
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-12-23 11:54:49 +0000
committerRobin Watts <robin.watts@artifex.com>2013-12-24 10:07:25 +0000
commit6b08b13fc4e95f9f0446a7199f1d9b5d9348d2f9 (patch)
tree01968e4ad4fa34c96d831581ec7f0920ff12a82e /source/pdf
parent3328de5f6432ee25525dca59d4e60d2603477b81 (diff)
downloadmupdf-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/pdf')
-rw-r--r--source/pdf/pdf-parse.c14
-rw-r--r--source/pdf/pdf-repair.c5
-rw-r--r--source/pdf/pdf-xref.c36
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)