1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5 
6 #include "internal_file.h"
7 
8 #include "internal_attr.h"
9 #include "internal_constants.h"
10 #include "internal_structs.h"
11 #include "internal_xdr.h"
12 
13 #include <limits.h>
14 #include <math.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <stdio.h>
19 
20 /**************************************/
21 
22 struct _internal_exr_seq_scratch
23 {
24     uint8_t* scratch;
25     uint64_t curpos;
26     int64_t  navail;
27     uint64_t fileoff;
28 
29     exr_result_t (*sequential_read) (
30         struct _internal_exr_seq_scratch*, void*, uint64_t);
31 
32     struct _internal_exr_context* ctxt;
33 };
34 
35 static inline int
scratch_attr_too_big(struct _internal_exr_seq_scratch * scr,int32_t attrsz,int64_t fsize)36 scratch_attr_too_big (
37     struct _internal_exr_seq_scratch* scr, int32_t attrsz, int64_t fsize)
38 {
39     int64_t acmp = (int64_t) attrsz;
40     if (fsize > 0 && (acmp > scr->navail))
41     {
42         int64_t test = acmp - scr->navail;
43         int64_t foff = (int64_t) scr->fileoff;
44         if ((foff + test) > fsize) return 1;
45     }
46     return 0;
47 }
48 
49 #define SCRATCH_BUFFER_SIZE 4096
50 
51 static exr_result_t
scratch_seq_read(struct _internal_exr_seq_scratch * scr,void * buf,uint64_t sz)52 scratch_seq_read (struct _internal_exr_seq_scratch* scr, void* buf, uint64_t sz)
53 {
54     uint8_t*     outbuf  = buf;
55     uint64_t     nCopied = 0;
56     uint64_t     notdone = sz;
57     exr_result_t rv      = -1;
58 
59     while (notdone > 0)
60     {
61         if (scr->navail > 0)
62         {
63             uint64_t nLeft = (uint64_t) scr->navail;
64             uint64_t nCopy = notdone;
65             if (nCopy > nLeft) nCopy = nLeft;
66             memcpy (outbuf, scr->scratch + scr->curpos, nCopy);
67             scr->curpos += nCopy;
68             scr->navail -= (int64_t) nCopy;
69             notdone -= nCopy;
70             outbuf += nCopy;
71             nCopied += nCopy;
72         }
73         else if (notdone > SCRATCH_BUFFER_SIZE)
74         {
75             uint64_t nPages  = notdone / SCRATCH_BUFFER_SIZE;
76             int64_t  nread   = 0;
77             uint64_t nToRead = nPages * SCRATCH_BUFFER_SIZE;
78             rv               = scr->ctxt->do_read (
79                 scr->ctxt,
80                 outbuf,
81                 nToRead,
82                 &(scr->fileoff),
83                 &nread,
84                 EXR_MUST_READ_ALL);
85             if (nread > 0)
86             {
87                 notdone -= (uint64_t) nread;
88                 outbuf += nread;
89                 nCopied += (uint64_t) nread;
90             }
91             else
92             {
93                 break;
94             }
95         }
96         else
97         {
98             int64_t nread = 0;
99             rv            = scr->ctxt->do_read (
100                 scr->ctxt,
101                 scr->scratch,
102                 SCRATCH_BUFFER_SIZE,
103                 &(scr->fileoff),
104                 &nread,
105                 EXR_ALLOW_SHORT_READ);
106             if (nread > 0)
107             {
108                 scr->navail = nread;
109                 scr->curpos = 0;
110             }
111             else
112             {
113                 if (nread == 0)
114                     rv = scr->ctxt->report_error (
115                         scr->ctxt,
116                         EXR_ERR_READ_IO,
117                         "End of file attempting to read header");
118                 break;
119             }
120         }
121     }
122     if (rv == -1)
123     {
124         if (nCopied == sz)
125             rv = EXR_ERR_SUCCESS;
126         else
127             rv = EXR_ERR_READ_IO;
128     }
129     return rv;
130 }
131 
132 /**************************************/
133 
134 static exr_result_t
priv_init_scratch(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scr,uint64_t offset)135 priv_init_scratch (
136     struct _internal_exr_context*     ctxt,
137     struct _internal_exr_seq_scratch* scr,
138     uint64_t                          offset)
139 {
140     scr->curpos          = 0;
141     scr->navail          = 0;
142     scr->fileoff         = offset;
143     scr->sequential_read = &scratch_seq_read;
144     scr->ctxt            = ctxt;
145     scr->scratch         = ctxt->alloc_fn (SCRATCH_BUFFER_SIZE);
146     if (scr->scratch == NULL)
147         return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
148     return EXR_ERR_SUCCESS;
149 }
150 
151 /**************************************/
152 
153 static void
priv_destroy_scratch(struct _internal_exr_seq_scratch * scr)154 priv_destroy_scratch (struct _internal_exr_seq_scratch* scr)
155 {
156     struct _internal_exr_context* pctxt = scr->ctxt;
157     if (scr->scratch) pctxt->free_fn (scr->scratch);
158 }
159 
160 /**************************************/
161 
162 static exr_result_t
check_bad_attrsz(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,int32_t attrsz,int32_t eltsize,const char * aname,const char * tname,int32_t * outsz)163 check_bad_attrsz (
164     struct _internal_exr_context*     ctxt,
165     struct _internal_exr_seq_scratch* scratch,
166     int32_t                           attrsz,
167     int32_t                           eltsize,
168     const char*                       aname,
169     const char*                       tname,
170     int32_t*                          outsz)
171 {
172     int32_t n = attrsz;
173 
174     *outsz = n;
175     if (attrsz < 0)
176         return ctxt->print_error (
177             ctxt,
178             EXR_ERR_ATTR_SIZE_MISMATCH,
179             "Attribute '%s', type '%s': Invalid negative size %d",
180             aname,
181             tname,
182             attrsz);
183 
184     if (scratch_attr_too_big (scratch, attrsz, ctxt->file_size))
185         return ctxt->print_error (
186             ctxt,
187             EXR_ERR_ATTR_SIZE_MISMATCH,
188             "Attribute '%s', type '%s': Invalid size %d",
189             aname,
190             tname,
191             attrsz);
192 
193     if (eltsize > 1)
194     {
195         n = attrsz / eltsize;
196         if (attrsz != (int32_t) (n * eltsize))
197             return ctxt->print_error (
198                 ctxt,
199                 EXR_ERR_ATTR_SIZE_MISMATCH,
200                 "Attribute '%s': Invalid size %d (exp '%s' size 4 * n, found odd bytes %d)",
201                 aname,
202                 attrsz,
203                 tname,
204                 (attrsz % eltsize));
205         *outsz = n;
206     }
207 
208     return EXR_ERR_SUCCESS;
209 }
210 
211 /**************************************/
212 
213 static exr_result_t
read_text(struct _internal_exr_context * ctxt,char text[256],int32_t * outlen,int32_t maxlen,struct _internal_exr_seq_scratch * scratch,const char * type)214 read_text (
215     struct _internal_exr_context*     ctxt,
216     char                              text[256],
217     int32_t*                          outlen,
218     int32_t                           maxlen,
219     struct _internal_exr_seq_scratch* scratch,
220     const char*                       type)
221 {
222     char         b;
223     exr_result_t rv      = EXR_ERR_SUCCESS;
224     int32_t      namelen = *outlen;
225 
226     while (namelen <= maxlen)
227     {
228         rv = scratch->sequential_read (scratch, &b, 1);
229         if (rv != EXR_ERR_SUCCESS) return rv;
230         text[namelen] = b;
231         if (b == '\0') break;
232         ++namelen;
233     }
234     *outlen = namelen;
235     if (namelen > maxlen)
236     {
237         text[maxlen - 1] = '\0';
238         return ctxt->print_error (
239             ctxt,
240             EXR_ERR_NAME_TOO_LONG,
241             "Invalid %s encountered: start '%s' (max %d)",
242             type,
243             text,
244             maxlen);
245     }
246     return EXR_ERR_SUCCESS;
247 }
248 
249 /**************************************/
250 
251 static exr_result_t
extract_attr_chlist(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_chlist_t * attrdata,const char * aname,const char * tname,int32_t attrsz)252 extract_attr_chlist (
253     struct _internal_exr_context*     ctxt,
254     struct _internal_exr_seq_scratch* scratch,
255     exr_attr_chlist_t*                attrdata,
256     const char*                       aname,
257     const char*                       tname,
258     int32_t                           attrsz)
259 {
260     char         chname[256];
261     int32_t      chlen;
262     int32_t      ptype, xsamp, ysamp;
263     uint8_t      flags[4];
264     int32_t      maxlen = ctxt->max_name_length;
265     exr_result_t rv;
266 
267     rv = check_bad_attrsz (ctxt, scratch, attrsz, 1, aname, tname, &chlen);
268 
269     while (rv == EXR_ERR_SUCCESS && attrsz > 0)
270     {
271         chlen = 0;
272         rv    = read_text (ctxt, chname, &chlen, maxlen, scratch, aname);
273         if (rv != EXR_ERR_SUCCESS) break;
274         attrsz -= chlen + 1;
275 
276         if (chlen == 0) break;
277 
278         if (attrsz < 16)
279         {
280             return ctxt->print_error (
281                 ctxt,
282                 EXR_ERR_ATTR_SIZE_MISMATCH,
283                 "Out of data parsing '%s', last channel '%s'",
284                 aname,
285                 chname);
286         }
287 
288         rv = scratch->sequential_read (scratch, &ptype, 4);
289         if (rv != EXR_ERR_SUCCESS) break;
290         rv = scratch->sequential_read (scratch, &flags, 4);
291         if (rv != EXR_ERR_SUCCESS) break;
292         rv = scratch->sequential_read (scratch, &xsamp, 4);
293         if (rv != EXR_ERR_SUCCESS) break;
294         rv = scratch->sequential_read (scratch, &ysamp, 4);
295         if (rv != EXR_ERR_SUCCESS) break;
296 
297         attrsz -= 16;
298         ptype = (int32_t) one_to_native32 ((uint32_t) ptype);
299         xsamp = (int32_t) one_to_native32 ((uint32_t) xsamp);
300         ysamp = (int32_t) one_to_native32 ((uint32_t) ysamp);
301 
302         rv = exr_attr_chlist_add_with_length (
303             (exr_context_t) ctxt,
304             attrdata,
305             chname,
306             chlen,
307             (exr_pixel_type_t) ptype,
308             flags[0],
309             xsamp,
310             ysamp);
311     }
312     return rv;
313 }
314 
315 /**************************************/
316 
317 static exr_result_t
extract_attr_uint8(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,uint8_t * attrdata,const char * aname,const char * tname,int32_t attrsz,uint8_t maxval)318 extract_attr_uint8 (
319     struct _internal_exr_context*     ctxt,
320     struct _internal_exr_seq_scratch* scratch,
321     uint8_t*                          attrdata,
322     const char*                       aname,
323     const char*                       tname,
324     int32_t                           attrsz,
325     uint8_t                           maxval)
326 {
327     if (attrsz != 1)
328         return ctxt->print_error (
329             ctxt,
330             EXR_ERR_ATTR_SIZE_MISMATCH,
331             "Attribute '%s': Invalid size %d (exp '%s' size 1)",
332             aname,
333             attrsz,
334             tname);
335 
336     if (scratch->sequential_read (scratch, attrdata, sizeof (uint8_t)))
337         return ctxt->print_error (
338             ctxt, EXR_ERR_READ_IO, "Unable to read '%s' %s data", aname, tname);
339 
340     if (*attrdata >= maxval)
341         return ctxt->print_error (
342             ctxt,
343             EXR_ERR_INVALID_ATTR,
344             "Attribute '%s' (type '%s'): Invalid value %d (max allowed %d)",
345             aname,
346             tname,
347             (int) *attrdata,
348             (int) maxval);
349 
350     return EXR_ERR_SUCCESS;
351 }
352 
353 /**************************************/
354 
355 static exr_result_t
extract_attr_64bit(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,void * attrdata,const char * aname,const char * tname,int32_t attrsz,int32_t num)356 extract_attr_64bit (
357     struct _internal_exr_context*     ctxt,
358     struct _internal_exr_seq_scratch* scratch,
359     void*                             attrdata,
360     const char*                       aname,
361     const char*                       tname,
362     int32_t                           attrsz,
363     int32_t                           num)
364 {
365     exr_result_t rv;
366     if (attrsz != 8 * num)
367         return ctxt->print_error (
368             ctxt,
369             EXR_ERR_ATTR_SIZE_MISMATCH,
370             "Attribute '%s': Invalid size %d (exp '%s' size 8 * %d (%d))",
371             aname,
372             attrsz,
373             tname,
374             num,
375             8 * num);
376 
377     rv = scratch->sequential_read (scratch, attrdata, 8 * (uint64_t) num);
378     if (rv != EXR_ERR_SUCCESS)
379         return ctxt->print_error (
380             ctxt, rv, "Unable to read '%s' %s data", aname, tname);
381 
382     priv_to_native64 (attrdata, num);
383     return rv;
384 }
385 
386 /**************************************/
387 
388 static exr_result_t
extract_attr_32bit(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,void * attrdata,const char * aname,const char * tname,int32_t attrsz,int32_t num)389 extract_attr_32bit (
390     struct _internal_exr_context*     ctxt,
391     struct _internal_exr_seq_scratch* scratch,
392     void*                             attrdata,
393     const char*                       aname,
394     const char*                       tname,
395     int32_t                           attrsz,
396     int32_t                           num)
397 {
398     exr_result_t rv;
399     if (attrsz != 4 * num)
400         return ctxt->print_error (
401             ctxt,
402             EXR_ERR_ATTR_SIZE_MISMATCH,
403             "Attribute '%s': Invalid size %d (exp '%s' size 4 * %d (%d))",
404             aname,
405             attrsz,
406             tname,
407             num,
408             4 * num);
409 
410     rv = scratch->sequential_read (scratch, attrdata, 4 * (uint64_t) num);
411     if (rv != EXR_ERR_SUCCESS)
412         return ctxt->print_error (
413             ctxt, rv, "Unable to read '%s' %s data", aname, tname);
414 
415     priv_to_native32 (attrdata, num);
416     return rv;
417 }
418 
419 /**************************************/
420 
421 static exr_result_t
extract_attr_float_vector(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_float_vector_t * attrdata,const char * aname,const char * tname,int32_t attrsz)422 extract_attr_float_vector (
423     struct _internal_exr_context*     ctxt,
424     struct _internal_exr_seq_scratch* scratch,
425     exr_attr_float_vector_t*          attrdata,
426     const char*                       aname,
427     const char*                       tname,
428     int32_t                           attrsz)
429 {
430     int32_t      n  = 0;
431     exr_result_t rv = check_bad_attrsz (
432         ctxt, scratch, attrsz, (int) sizeof (float), aname, tname, &n);
433 
434     /* in case of duplicate attr name in header (mostly fuzz testing) */
435     exr_attr_float_vector_destroy ((exr_context_t) ctxt, attrdata);
436 
437     if (rv == EXR_ERR_SUCCESS && n > 0)
438     {
439         rv = exr_attr_float_vector_init ((exr_context_t) ctxt, attrdata, n);
440         if (rv != EXR_ERR_SUCCESS) return rv;
441 
442         rv = scratch->sequential_read (
443             scratch, EXR_CONST_CAST (void*, attrdata->arr), (uint64_t) attrsz);
444         if (rv != EXR_ERR_SUCCESS)
445         {
446             exr_attr_float_vector_destroy ((exr_context_t) ctxt, attrdata);
447             return ctxt->print_error (
448                 ctxt,
449                 EXR_ERR_READ_IO,
450                 "Unable to read '%s' %s data",
451                 aname,
452                 tname);
453         }
454 
455         priv_to_native32 (attrdata, n);
456     }
457 
458     return rv;
459 }
460 
461 /**************************************/
462 
463 static exr_result_t
extract_attr_string(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_string_t * attrdata,const char * aname,const char * tname,int32_t attrsz,char * strptr)464 extract_attr_string (
465     struct _internal_exr_context*     ctxt,
466     struct _internal_exr_seq_scratch* scratch,
467     exr_attr_string_t*                attrdata,
468     const char*                       aname,
469     const char*                       tname,
470     int32_t                           attrsz,
471     char*                             strptr)
472 {
473     exr_result_t rv =
474         scratch->sequential_read (scratch, (void*) strptr, (uint64_t) attrsz);
475 
476     if (rv != EXR_ERR_SUCCESS)
477         return ctxt->print_error (
478             ctxt, rv, "Unable to read '%s' %s data", aname, tname);
479 
480     strptr[attrsz] = '\0';
481 
482     return exr_attr_string_init_static_with_length (
483         (exr_context_t) ctxt, attrdata, strptr, attrsz);
484 }
485 
486 /**************************************/
487 
488 static exr_result_t
extract_attr_string_vector(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_string_vector_t * attrdata,const char * aname,const char * tname,int32_t attrsz)489 extract_attr_string_vector (
490     struct _internal_exr_context*     ctxt,
491     struct _internal_exr_seq_scratch* scratch,
492     exr_attr_string_vector_t*         attrdata,
493     const char*                       aname,
494     const char*                       tname,
495     int32_t                           attrsz)
496 {
497     exr_result_t       rv;
498     int32_t            n, nstr, nalloced, nlen, pulled = 0;
499     exr_attr_string_t *nlist, *clist, nil = { 0 };
500 
501     rv = check_bad_attrsz (ctxt, scratch, attrsz, 1, aname, tname, &n);
502     if (rv != EXR_ERR_SUCCESS) return rv;
503 
504     nstr     = 0;
505     nalloced = 0;
506     clist    = NULL;
507     while (pulled < attrsz)
508     {
509         nlen = 0;
510         rv   = scratch->sequential_read (scratch, &nlen, sizeof (int32_t));
511         if (rv != EXR_ERR_SUCCESS)
512         {
513             rv = ctxt->print_error (
514                 ctxt,
515                 rv,
516                 "Attribute '%s': Unable to read string length",
517                 aname);
518             goto extract_string_vector_fail;
519         }
520 
521         pulled += sizeof (int32_t);
522         nlen = (int32_t) one_to_native32 ((uint32_t) nlen);
523         if (nlen < 0 || (ctxt->file_size > 0 && nlen > ctxt->file_size))
524         {
525             rv = ctxt->print_error (
526                 ctxt,
527                 EXR_ERR_INVALID_ATTR,
528                 "Attribute '%s': Invalid size (%d) encountered parsing string vector",
529                 aname,
530                 nlen);
531             goto extract_string_vector_fail;
532         }
533 
534         if (nalloced == 0)
535         {
536             clist = ctxt->alloc_fn (4 * sizeof (exr_attr_string_t));
537             if (clist == NULL)
538             {
539                 rv = ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
540                 goto extract_string_vector_fail;
541             }
542             nalloced = 4;
543         }
544         if ((nstr + 1) >= nalloced)
545         {
546             nalloced *= 2;
547             nlist = ctxt->alloc_fn (
548                 (size_t) (nalloced) * sizeof (exr_attr_string_t));
549             if (nlist == NULL)
550             {
551                 rv = ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
552                 goto extract_string_vector_fail;
553             }
554             for (int32_t i = 0; i < nstr; ++i)
555                 *(nlist + i) = clist[i];
556             ctxt->free_fn (clist);
557             clist = nlist;
558         }
559         nlist  = clist + nstr;
560         *nlist = nil;
561         nstr += 1;
562         rv = exr_attr_string_init ((exr_context_t) ctxt, nlist, nlen);
563         if (rv != EXR_ERR_SUCCESS) goto extract_string_vector_fail;
564 
565         rv = scratch->sequential_read (
566             scratch, EXR_CONST_CAST (void*, nlist->str), (uint64_t) nlen);
567         if (rv != EXR_ERR_SUCCESS)
568         {
569             rv = ctxt->print_error (
570                 ctxt,
571                 rv,
572                 "Attribute '%s': Unable to read string of length (%d)",
573                 aname,
574                 nlen);
575             goto extract_string_vector_fail;
576         }
577         *((EXR_CONST_CAST (char*, nlist->str)) + nlen) = '\0';
578         pulled += nlen;
579     }
580 
581     // just in case someone injected a duplicate attribute name into the header
582     exr_attr_string_vector_destroy ((exr_context_t) ctxt, attrdata);
583     attrdata->n_strings  = nstr;
584     attrdata->alloc_size = nalloced;
585     attrdata->strings    = clist;
586     return EXR_ERR_SUCCESS;
587 extract_string_vector_fail:
588     for (int32_t i = 0; i < nstr; ++i)
589         exr_attr_string_destroy ((exr_context_t) ctxt, clist + i);
590     if (clist) ctxt->free_fn (clist);
591 
592     return rv;
593 }
594 
595 /**************************************/
596 
597 static exr_result_t
extract_attr_tiledesc(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_tiledesc_t * attrdata,const char * aname,const char * tname,int32_t attrsz)598 extract_attr_tiledesc (
599     struct _internal_exr_context*     ctxt,
600     struct _internal_exr_seq_scratch* scratch,
601     exr_attr_tiledesc_t*              attrdata,
602     const char*                       aname,
603     const char*                       tname,
604     int32_t                           attrsz)
605 {
606     exr_result_t rv;
607     if (attrsz != (int32_t) sizeof (*attrdata))
608         return ctxt->print_error (
609             ctxt,
610             EXR_ERR_ATTR_SIZE_MISMATCH,
611             "Attribute '%s': Invalid size %d (exp '%s' size %d)",
612             aname,
613             attrsz,
614             tname,
615             (int32_t) sizeof (*attrdata));
616 
617     rv = scratch->sequential_read (scratch, attrdata, sizeof (*attrdata));
618     if (rv != EXR_ERR_SUCCESS)
619         return ctxt->print_error (
620             ctxt, rv, "Unable to read '%s' %s data", aname, tname);
621 
622     attrdata->x_size = one_to_native32 (attrdata->x_size);
623     attrdata->y_size = one_to_native32 (attrdata->y_size);
624 
625     if ((int) EXR_GET_TILE_LEVEL_MODE (*attrdata) >= (int) EXR_TILE_LAST_TYPE)
626         return ctxt->print_error (
627             ctxt,
628             EXR_ERR_INVALID_ATTR,
629             "Attribute '%s': Invalid tile level specification encountered: found enum %d",
630             aname,
631             (int) EXR_GET_TILE_LEVEL_MODE (*attrdata));
632 
633     if ((int) EXR_GET_TILE_ROUND_MODE (*attrdata) >=
634         (int) EXR_TILE_ROUND_LAST_TYPE)
635         return ctxt->print_error (
636             ctxt,
637             EXR_ERR_INVALID_ATTR,
638             "Attribute '%s': Invalid tile rounding specification encountered: found enum %d",
639             aname,
640             (int) EXR_GET_TILE_ROUND_MODE (*attrdata));
641 
642     return rv;
643 }
644 
645 /**************************************/
646 
647 static exr_result_t
extract_attr_opaque(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_opaquedata_t * attrdata,const char * aname,const char * tname,int32_t attrsz)648 extract_attr_opaque (
649     struct _internal_exr_context*     ctxt,
650     struct _internal_exr_seq_scratch* scratch,
651     exr_attr_opaquedata_t*            attrdata,
652     const char*                       aname,
653     const char*                       tname,
654     int32_t                           attrsz)
655 {
656     int32_t      n;
657     exr_result_t rv;
658 
659     rv = check_bad_attrsz (ctxt, scratch, attrsz, 1, aname, tname, &n);
660     if (rv != EXR_ERR_SUCCESS) return rv;
661 
662     exr_attr_opaquedata_destroy ((exr_context_t) ctxt, attrdata);
663     rv = exr_attr_opaquedata_init (
664         (exr_context_t) ctxt, attrdata, (uint64_t) attrsz);
665     if (rv != EXR_ERR_SUCCESS) return rv;
666 
667     rv = scratch->sequential_read (
668         scratch, (void*) attrdata->packed_data, (uint64_t) attrsz);
669     if (rv != EXR_ERR_SUCCESS)
670     {
671         exr_attr_opaquedata_destroy ((exr_context_t) ctxt, attrdata);
672         return ctxt->print_error (
673             ctxt,
674             EXR_ERR_READ_IO,
675             "Attribute '%s': Unable to read opaque %s data (%d bytes)",
676             aname,
677             tname,
678             attrsz);
679     }
680     return rv;
681 }
682 
683 /**************************************/
684 
685 static exr_result_t
extract_attr_preview(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch,exr_attr_preview_t * attrdata,const char * aname,const char * tname,int32_t attrsz)686 extract_attr_preview (
687     struct _internal_exr_context*     ctxt,
688     struct _internal_exr_seq_scratch* scratch,
689     exr_attr_preview_t*               attrdata,
690     const char*                       aname,
691     const char*                       tname,
692     int32_t                           attrsz)
693 {
694     uint64_t     bytes;
695     uint32_t     sz[2];
696     exr_result_t rv;
697     int64_t      fsize = ctxt->file_size;
698 
699     /* mostly for fuzzing, but just in case there's a duplicate name */
700     exr_attr_preview_destroy ((exr_context_t) ctxt, attrdata);
701 
702     if (attrsz < 8)
703         return ctxt->print_error (
704             ctxt,
705             EXR_ERR_ATTR_SIZE_MISMATCH,
706             "Attribute '%s': Invalid size %d (exp '%s' size >= 8)",
707             aname,
708             attrsz,
709             tname);
710 
711     rv = scratch->sequential_read (scratch, sz, sizeof (uint32_t) * 2);
712     if (rv != EXR_ERR_SUCCESS)
713         return ctxt->print_error (
714             ctxt, rv, "Attribute '%s': Unable to read preview sizes", aname);
715 
716     sz[0] = one_to_native32 (sz[0]);
717     sz[1] = one_to_native32 (sz[1]);
718     bytes = 4 * sz[0] * sz[1];
719     if ((uint64_t) attrsz != (8 + bytes))
720         return ctxt->print_error (
721             ctxt,
722             EXR_ERR_INVALID_ATTR,
723             "Attribute '%s': Invalid size %d (exp '%s' %u x %u * 4 + sizevals)",
724             aname,
725             attrsz,
726             tname,
727             sz[0],
728             sz[1]);
729 
730     if (bytes == 0 || (fsize > 0 && bytes >= (uint64_t) fsize))
731     {
732         return ctxt->print_error (
733             ctxt,
734             EXR_ERR_ATTR_SIZE_MISMATCH,
735             "Attribute '%s', type '%s': Invalid size for preview %u x %u",
736             aname,
737             tname,
738             sz[0],
739             sz[1]);
740     }
741 
742     rv = exr_attr_preview_init ((exr_context_t) ctxt, attrdata, sz[0], sz[1]);
743     if (rv != EXR_ERR_SUCCESS) return rv;
744 
745     if (bytes > 0)
746     {
747         rv = scratch->sequential_read (
748             scratch, EXR_CONST_CAST (void*, attrdata->rgba), sz[0] * sz[1] * 4);
749         if (rv != EXR_ERR_SUCCESS)
750         {
751             exr_attr_preview_destroy ((exr_context_t) ctxt, attrdata);
752             return ctxt->print_error (
753                 ctxt,
754                 rv,
755                 "Attribute '%s': Unable to read preview data (%d bytes)",
756                 aname,
757                 attrsz);
758         }
759     }
760 
761     return rv;
762 }
763 
764 /**************************************/
765 
766 static exr_result_t
check_populate_channels(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)767 check_populate_channels (
768     struct _internal_exr_context*     ctxt,
769     struct _internal_exr_part*        curpart,
770     struct _internal_exr_seq_scratch* scratch,
771     const char*                       tname,
772     int32_t                           attrsz)
773 {
774     exr_attr_chlist_t tmpchans = { 0 };
775     exr_result_t      rv;
776 
777     if (curpart->channels)
778         return ctxt->print_error (
779             ctxt,
780             EXR_ERR_INVALID_ATTR,
781             "Duplicate copy of required attribute 'channels' encountered");
782 
783     if (0 != strcmp (tname, "chlist"))
784         return ctxt->print_error (
785             ctxt,
786             EXR_ERR_ATTR_TYPE_MISMATCH,
787             "Required attribute 'channels': Invalid type '%s'",
788             tname);
789 
790     rv = extract_attr_chlist (
791         ctxt, scratch, &(tmpchans), EXR_REQ_CHANNELS_STR, tname, attrsz);
792     if (rv != EXR_ERR_SUCCESS)
793     {
794         exr_attr_chlist_destroy ((exr_context_t) ctxt, &(tmpchans));
795         return rv;
796     }
797 
798     rv = exr_attr_list_add_static_name (
799         (exr_context_t) ctxt,
800         &(curpart->attributes),
801         EXR_REQ_CHANNELS_STR,
802         EXR_ATTR_CHLIST,
803         0,
804         NULL,
805         &(curpart->channels));
806 
807     if (rv != EXR_ERR_SUCCESS)
808     {
809         exr_attr_chlist_destroy ((exr_context_t) ctxt, &tmpchans);
810         return ctxt->print_error (
811             ctxt,
812             rv,
813             "Unable to initialize attribute '%s', type 'chlist'",
814             EXR_REQ_CHANNELS_STR);
815     }
816 
817     exr_attr_chlist_destroy ((exr_context_t) ctxt, curpart->channels->chlist);
818     *(curpart->channels->chlist) = tmpchans;
819     return rv;
820 }
821 
822 /**************************************/
823 
824 static exr_result_t
check_populate_compression(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)825 check_populate_compression (
826     struct _internal_exr_context*     ctxt,
827     struct _internal_exr_part*        curpart,
828     struct _internal_exr_seq_scratch* scratch,
829     const char*                       tname,
830     int32_t                           attrsz)
831 {
832     uint8_t      data;
833     exr_result_t rv;
834 
835     if (curpart->compression)
836         return ctxt->print_error (
837             ctxt,
838             EXR_ERR_INVALID_ATTR,
839             "Duplicate copy of required attribute '%s' encountered",
840             EXR_REQ_COMP_STR);
841 
842     if (0 != strcmp (tname, EXR_REQ_COMP_STR))
843         return ctxt->print_error (
844             ctxt,
845             EXR_ERR_ATTR_TYPE_MISMATCH,
846             "Required attribute '%s': Invalid type '%s'",
847             EXR_REQ_COMP_STR,
848             tname);
849 
850     rv = extract_attr_uint8 (
851         ctxt,
852         scratch,
853         &data,
854         EXR_REQ_COMP_STR,
855         tname,
856         attrsz,
857         (uint8_t) EXR_COMPRESSION_LAST_TYPE);
858     if (rv != EXR_ERR_SUCCESS) return rv;
859 
860     rv = exr_attr_list_add_static_name (
861         (exr_context_t) ctxt,
862         &(curpart->attributes),
863         EXR_REQ_COMP_STR,
864         EXR_ATTR_COMPRESSION,
865         0,
866         NULL,
867         &(curpart->compression));
868     if (rv != EXR_ERR_SUCCESS)
869         return ctxt->print_error (
870             ctxt,
871             rv,
872             "Unable to initialize attribute '%s', type 'compression'",
873             EXR_REQ_COMP_STR);
874 
875     curpart->compression->uc = data;
876     curpart->comp_type       = (exr_compression_t) data;
877     return rv;
878 }
879 
880 /**************************************/
881 
882 static exr_result_t
check_populate_dataWindow(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)883 check_populate_dataWindow (
884     struct _internal_exr_context*     ctxt,
885     struct _internal_exr_part*        curpart,
886     struct _internal_exr_seq_scratch* scratch,
887     const char*                       tname,
888     int32_t                           attrsz)
889 {
890     exr_attr_box2i_t tmpdata = { 0 };
891     exr_result_t     rv;
892 
893     if (curpart->dataWindow)
894         return ctxt->print_error (
895             ctxt,
896             EXR_ERR_INVALID_ATTR,
897             "Duplicate copy of required attribute '%s' encountered",
898             EXR_REQ_DATA_STR);
899 
900     if (0 != strcmp (tname, "box2i"))
901         return ctxt->print_error (
902             ctxt,
903             EXR_ERR_ATTR_TYPE_MISMATCH,
904             "Required attribute '%s': Invalid type '%s'",
905             EXR_REQ_DATA_STR,
906             tname);
907 
908     rv = extract_attr_32bit (
909         ctxt, scratch, &(tmpdata), EXR_REQ_DATA_STR, tname, attrsz, 4);
910     if (rv != EXR_ERR_SUCCESS) return rv;
911 
912     rv = exr_attr_list_add_static_name (
913         (exr_context_t) ctxt,
914         &(curpart->attributes),
915         EXR_REQ_DATA_STR,
916         EXR_ATTR_BOX2I,
917         0,
918         NULL,
919         &(curpart->dataWindow));
920     if (rv != EXR_ERR_SUCCESS)
921         return ctxt->print_error (
922             ctxt,
923             rv,
924             "Unable to initialize attribute '%s', type 'box2i'",
925             EXR_REQ_DATA_STR);
926 
927     *(curpart->dataWindow->box2i) = tmpdata;
928     curpart->data_window          = tmpdata;
929     return rv;
930 }
931 
932 /**************************************/
933 
934 static exr_result_t
check_populate_displayWindow(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)935 check_populate_displayWindow (
936     struct _internal_exr_context*     ctxt,
937     struct _internal_exr_part*        curpart,
938     struct _internal_exr_seq_scratch* scratch,
939     const char*                       tname,
940     int32_t                           attrsz)
941 {
942     exr_attr_box2i_t tmpdata = { 0 };
943     exr_result_t     rv;
944 
945     if (curpart->displayWindow)
946         return ctxt->print_error (
947             ctxt,
948             EXR_ERR_INVALID_ATTR,
949             "Duplicate copy of required attribute '%s' encountered",
950             EXR_REQ_DISP_STR);
951 
952     if (0 != strcmp (tname, "box2i"))
953         return ctxt->print_error (
954             ctxt,
955             EXR_ERR_ATTR_TYPE_MISMATCH,
956             "Required attribute '%s': Invalid type '%s'",
957             EXR_REQ_DISP_STR,
958             tname);
959 
960     rv = extract_attr_32bit (
961         ctxt, scratch, &(tmpdata), EXR_REQ_DISP_STR, tname, attrsz, 4);
962     if (rv != EXR_ERR_SUCCESS) return rv;
963 
964     rv = exr_attr_list_add_static_name (
965         (exr_context_t) ctxt,
966         &(curpart->attributes),
967         EXR_REQ_DISP_STR,
968         EXR_ATTR_BOX2I,
969         0,
970         NULL,
971         &(curpart->displayWindow));
972     if (rv != EXR_ERR_SUCCESS)
973         return ctxt->print_error (
974             ctxt,
975             rv,
976             "Unable to initialize attribute '%s', type 'box2i'",
977             EXR_REQ_DISP_STR);
978 
979     *(curpart->displayWindow->box2i) = tmpdata;
980     curpart->display_window          = tmpdata;
981     return rv;
982 }
983 
984 /**************************************/
985 
986 static exr_result_t
check_populate_lineOrder(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)987 check_populate_lineOrder (
988     struct _internal_exr_context*     ctxt,
989     struct _internal_exr_part*        curpart,
990     struct _internal_exr_seq_scratch* scratch,
991     const char*                       tname,
992     int32_t                           attrsz)
993 {
994     uint8_t      data;
995     exr_result_t rv;
996 
997     if (curpart->lineOrder)
998         return ctxt->print_error (
999             ctxt,
1000             EXR_ERR_INVALID_ATTR,
1001             "Duplicate copy of required attribute '%s' encountered",
1002             EXR_REQ_LO_STR);
1003 
1004     if (0 != strcmp (tname, EXR_REQ_LO_STR))
1005         return ctxt->print_error (
1006             ctxt,
1007             EXR_ERR_ATTR_TYPE_MISMATCH,
1008             "Required attribute '%s': Invalid type '%s'",
1009             EXR_REQ_LO_STR,
1010             tname);
1011 
1012     rv = extract_attr_uint8 (
1013         ctxt,
1014         scratch,
1015         &data,
1016         EXR_REQ_LO_STR,
1017         tname,
1018         attrsz,
1019         (uint8_t) EXR_LINEORDER_LAST_TYPE);
1020     if (rv != EXR_ERR_SUCCESS) return rv;
1021 
1022     rv = exr_attr_list_add_static_name (
1023         (exr_context_t) ctxt,
1024         &(curpart->attributes),
1025         EXR_REQ_LO_STR,
1026         EXR_ATTR_LINEORDER,
1027         0,
1028         NULL,
1029         &(curpart->lineOrder));
1030     if (rv != EXR_ERR_SUCCESS)
1031         return ctxt->print_error (
1032             ctxt,
1033             rv,
1034             "Unable to initialize attribute '%s', type 'lineOrder'",
1035             EXR_REQ_LO_STR);
1036 
1037     curpart->lineOrder->uc = data;
1038     curpart->lineorder     = data;
1039     return rv;
1040 }
1041 
1042 /**************************************/
1043 
1044 static exr_result_t
check_populate_pixelAspectRatio(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1045 check_populate_pixelAspectRatio (
1046     struct _internal_exr_context*     ctxt,
1047     struct _internal_exr_part*        curpart,
1048     struct _internal_exr_seq_scratch* scratch,
1049     const char*                       tname,
1050     int32_t                           attrsz)
1051 {
1052     exr_result_t rv;
1053     union
1054     {
1055         uint32_t ival;
1056         float    fval;
1057     } tpun;
1058 
1059     if (curpart->pixelAspectRatio)
1060         return ctxt->print_error (
1061             ctxt,
1062             EXR_ERR_INVALID_ATTR,
1063             "Duplicate copy of required attribute '%s' encountered",
1064             EXR_REQ_PAR_STR);
1065 
1066     if (0 != strcmp (tname, "float"))
1067         return ctxt->print_error (
1068             ctxt,
1069             EXR_ERR_ATTR_TYPE_MISMATCH,
1070             "Required attribute '%s': Invalid type '%s'",
1071             EXR_REQ_PAR_STR,
1072             tname);
1073 
1074     if (attrsz != sizeof (float))
1075         return ctxt->print_error (
1076             ctxt,
1077             EXR_ERR_ATTR_SIZE_MISMATCH,
1078             "Required attribute '%s': Invalid size %d (exp 4)",
1079             EXR_REQ_PAR_STR,
1080             attrsz);
1081 
1082     rv = scratch->sequential_read (scratch, &(tpun.ival), sizeof (uint32_t));
1083     if (rv != EXR_ERR_SUCCESS)
1084         return ctxt->print_error (
1085             ctxt,
1086             rv,
1087             "Attribute '%s': Unable to read data (%d bytes)",
1088             EXR_REQ_PAR_STR,
1089             attrsz);
1090 
1091     tpun.ival = one_to_native32 (tpun.ival);
1092 
1093     rv = exr_attr_list_add_static_name (
1094         (exr_context_t) ctxt,
1095         &(curpart->attributes),
1096         EXR_REQ_PAR_STR,
1097         EXR_ATTR_FLOAT,
1098         0,
1099         NULL,
1100         &(curpart->pixelAspectRatio));
1101     if (rv != EXR_ERR_SUCCESS)
1102         return ctxt->print_error (
1103             ctxt,
1104             rv,
1105             "Unable to initialize attribute '%s', type 'float'",
1106             EXR_REQ_PAR_STR);
1107 
1108     curpart->pixelAspectRatio->f = tpun.fval;
1109     return rv;
1110 }
1111 
1112 /**************************************/
1113 
1114 static exr_result_t
check_populate_screenWindowCenter(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1115 check_populate_screenWindowCenter (
1116     struct _internal_exr_context*     ctxt,
1117     struct _internal_exr_part*        curpart,
1118     struct _internal_exr_seq_scratch* scratch,
1119     const char*                       tname,
1120     int32_t                           attrsz)
1121 {
1122     exr_result_t   rv;
1123     exr_attr_v2f_t tmpdata;
1124 
1125     if (curpart->screenWindowCenter)
1126         return ctxt->print_error (
1127             ctxt,
1128             EXR_ERR_INVALID_ATTR,
1129             "Duplicate copy of required attribute '%s' encountered",
1130             EXR_REQ_SCR_WC_STR);
1131 
1132     if (0 != strcmp (tname, "v2f"))
1133         return ctxt->print_error (
1134             ctxt,
1135             EXR_ERR_ATTR_TYPE_MISMATCH,
1136             "Required attribute '%s': Invalid type '%s'",
1137             EXR_REQ_SCR_WC_STR,
1138             tname);
1139 
1140     if (attrsz != sizeof (exr_attr_v2f_t))
1141         return ctxt->print_error (
1142             ctxt,
1143             EXR_ERR_ATTR_SIZE_MISMATCH,
1144             "Required attribute '%s': Invalid size %d (exp %" PRIu64 ")",
1145             EXR_REQ_SCR_WC_STR,
1146             attrsz,
1147             (uint64_t) sizeof (exr_attr_v2f_t));
1148 
1149     rv = scratch->sequential_read (scratch, &tmpdata, sizeof (exr_attr_v2f_t));
1150     if (rv != EXR_ERR_SUCCESS)
1151         return ctxt->print_error (
1152             ctxt,
1153             rv,
1154             "Attribute '%s': Unable to read data (%d bytes)",
1155             EXR_REQ_SCR_WC_STR,
1156             attrsz);
1157 
1158     priv_to_native32 (&tmpdata, 2);
1159 
1160     rv = exr_attr_list_add_static_name (
1161         (exr_context_t) ctxt,
1162         &(curpart->attributes),
1163         EXR_REQ_SCR_WC_STR,
1164         EXR_ATTR_V2F,
1165         0,
1166         NULL,
1167         &(curpart->screenWindowCenter));
1168     if (rv != EXR_ERR_SUCCESS)
1169         return ctxt->print_error (
1170             ctxt,
1171             rv,
1172             "Unable to initialize attribute '%s', type 'v2f'",
1173             EXR_REQ_SCR_WC_STR);
1174 
1175     *(curpart->screenWindowCenter->v2f) = tmpdata;
1176     return rv;
1177 }
1178 
1179 /**************************************/
1180 
1181 static exr_result_t
check_populate_screenWindowWidth(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1182 check_populate_screenWindowWidth (
1183     struct _internal_exr_context*     ctxt,
1184     struct _internal_exr_part*        curpart,
1185     struct _internal_exr_seq_scratch* scratch,
1186     const char*                       tname,
1187     int32_t                           attrsz)
1188 {
1189     exr_result_t rv;
1190     union
1191     {
1192         uint32_t ival;
1193         float    fval;
1194     } tpun;
1195 
1196     if (curpart->screenWindowWidth)
1197         return ctxt->print_error (
1198             ctxt,
1199             EXR_ERR_INVALID_ATTR,
1200             "Duplicate copy of required attribute '%s' encountered",
1201             EXR_REQ_SCR_WW_STR);
1202 
1203     if (0 != strcmp (tname, "float"))
1204         return ctxt->print_error (
1205             ctxt,
1206             EXR_ERR_ATTR_TYPE_MISMATCH,
1207             "Required attribute '%s': Invalid type '%s'",
1208             EXR_REQ_SCR_WW_STR,
1209             tname);
1210 
1211     if (attrsz != sizeof (float))
1212         return ctxt->print_error (
1213             ctxt,
1214             EXR_ERR_ATTR_SIZE_MISMATCH,
1215             "Required attribute '%s': Invalid size %d (exp 4)",
1216             EXR_REQ_SCR_WW_STR,
1217             attrsz);
1218 
1219     rv = scratch->sequential_read (scratch, &(tpun.ival), sizeof (uint32_t));
1220     if (rv != EXR_ERR_SUCCESS)
1221         return ctxt->print_error (
1222             ctxt,
1223             rv,
1224             "Attribute '%s': Unable to read data (%d bytes)",
1225             EXR_REQ_SCR_WW_STR,
1226             attrsz);
1227 
1228     tpun.ival = one_to_native32 (tpun.ival);
1229 
1230     rv = exr_attr_list_add_static_name (
1231         (exr_context_t) ctxt,
1232         &(curpart->attributes),
1233         EXR_REQ_SCR_WW_STR,
1234         EXR_ATTR_FLOAT,
1235         0,
1236         NULL,
1237         &(curpart->screenWindowWidth));
1238     if (rv != EXR_ERR_SUCCESS)
1239         return ctxt->print_error (
1240             ctxt,
1241             rv,
1242             "Unable to initialize attribute '%s', type 'float'",
1243             EXR_REQ_SCR_WW_STR);
1244 
1245     curpart->screenWindowWidth->f = tpun.fval;
1246     return rv;
1247 }
1248 
1249 /**************************************/
1250 
1251 static exr_result_t
check_populate_tiles(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1252 check_populate_tiles (
1253     struct _internal_exr_context*     ctxt,
1254     struct _internal_exr_part*        curpart,
1255     struct _internal_exr_seq_scratch* scratch,
1256     const char*                       tname,
1257     int32_t                           attrsz)
1258 {
1259     exr_result_t        rv;
1260     exr_attr_tiledesc_t tmpdata = { 0 };
1261 
1262     if (curpart->tiles)
1263         return ctxt->print_error (
1264             ctxt,
1265             EXR_ERR_INVALID_ATTR,
1266             "Duplicate copy of required attribute 'tiles' encountered");
1267 
1268     if (0 != strcmp (tname, "tiledesc"))
1269         return ctxt->print_error (
1270             ctxt,
1271             EXR_ERR_ATTR_TYPE_MISMATCH,
1272             "Required attribute 'tiles': Invalid type '%s'",
1273             tname);
1274 
1275     if (attrsz != sizeof (exr_attr_tiledesc_t))
1276         return ctxt->print_error (
1277             ctxt,
1278             EXR_ERR_ATTR_TYPE_MISMATCH,
1279             "Required attribute 'tiles': Invalid size %d (exp %" PRIu64 ")",
1280             attrsz,
1281             (uint64_t) sizeof (exr_attr_tiledesc_t));
1282 
1283     rv = scratch->sequential_read (scratch, &tmpdata, sizeof (tmpdata));
1284     if (rv != EXR_ERR_SUCCESS)
1285         return ctxt->report_error (ctxt, rv, "Unable to read 'tiles' data");
1286 
1287     tmpdata.x_size = one_to_native32 (tmpdata.x_size);
1288     tmpdata.y_size = one_to_native32 (tmpdata.y_size);
1289 
1290     rv = exr_attr_list_add_static_name (
1291         (exr_context_t) ctxt,
1292         &(curpart->attributes),
1293         EXR_REQ_TILES_STR,
1294         EXR_ATTR_TILEDESC,
1295         0,
1296         NULL,
1297         &(curpart->tiles));
1298     if (rv != EXR_ERR_SUCCESS)
1299         return ctxt->print_error (
1300             ctxt,
1301             rv,
1302             "Unable to initialize attribute '%s', type 'tiledesc'",
1303             EXR_REQ_TILES_STR);
1304 
1305     *(curpart->tiles->tiledesc) = tmpdata;
1306     return rv;
1307 }
1308 
1309 /**************************************/
1310 
1311 static exr_result_t
check_populate_name(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1312 check_populate_name (
1313     struct _internal_exr_context*     ctxt,
1314     struct _internal_exr_part*        curpart,
1315     struct _internal_exr_seq_scratch* scratch,
1316     const char*                       tname,
1317     int32_t                           attrsz)
1318 {
1319     exr_result_t rv;
1320     uint8_t*     outstr;
1321     int32_t      n;
1322 
1323     rv = check_bad_attrsz (
1324         ctxt, scratch, attrsz, 1, EXR_REQ_NAME_STR, tname, &n);
1325     if (rv != EXR_ERR_SUCCESS) return rv;
1326 
1327     if (curpart->name)
1328         return ctxt->print_error (
1329             ctxt,
1330             EXR_ERR_INVALID_ATTR,
1331             "Duplicate copy of required attribute 'name' encountered");
1332 
1333     if (0 != strcmp (tname, "string"))
1334         return ctxt->print_error (
1335             ctxt,
1336             EXR_ERR_ATTR_TYPE_MISMATCH,
1337             "attribute 'name': Invalid type '%s'",
1338             tname);
1339 
1340     rv = exr_attr_list_add_static_name (
1341         (exr_context_t) ctxt,
1342         &(curpart->attributes),
1343         EXR_REQ_NAME_STR,
1344         EXR_ATTR_STRING,
1345         attrsz + 1,
1346         &outstr,
1347         &(curpart->name));
1348     if (rv != EXR_ERR_SUCCESS)
1349     {
1350         return ctxt->print_error (
1351             ctxt,
1352             rv,
1353             "Unable to initialize attribute '%s', type 'string'",
1354             EXR_REQ_NAME_STR);
1355     }
1356 
1357     rv = scratch->sequential_read (scratch, outstr, (uint64_t) attrsz);
1358     if (rv != EXR_ERR_SUCCESS)
1359     {
1360         exr_attr_list_remove (
1361             (exr_context_t) ctxt, &(curpart->attributes), curpart->name);
1362         curpart->name = NULL;
1363         return ctxt->report_error (ctxt, rv, "Unable to read 'name' data");
1364     }
1365     outstr[attrsz] = '\0';
1366 
1367     rv = exr_attr_string_init_static_with_length (
1368         (exr_context_t) ctxt,
1369         curpart->name->string,
1370         (const char*) outstr,
1371         attrsz);
1372     if (rv != EXR_ERR_SUCCESS)
1373     {
1374         exr_attr_list_remove (
1375             (exr_context_t) ctxt, &(curpart->attributes), curpart->name);
1376         curpart->name = NULL;
1377         return ctxt->report_error (ctxt, rv, "Unable to read 'name' data");
1378     }
1379 
1380     return rv;
1381 }
1382 
1383 /**************************************/
1384 
1385 static exr_result_t
check_populate_type(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1386 check_populate_type (
1387     struct _internal_exr_context*     ctxt,
1388     struct _internal_exr_part*        curpart,
1389     struct _internal_exr_seq_scratch* scratch,
1390     const char*                       tname,
1391     int32_t                           attrsz)
1392 {
1393     exr_result_t rv;
1394     uint8_t*     outstr;
1395     int32_t      n;
1396 
1397     rv = check_bad_attrsz (
1398         ctxt, scratch, attrsz, 1, EXR_REQ_TYPE_STR, tname, &n);
1399     if (rv != EXR_ERR_SUCCESS) return rv;
1400 
1401     if (curpart->type)
1402         return ctxt->print_error (
1403             ctxt,
1404             EXR_ERR_INVALID_ATTR,
1405             "Duplicate copy of required attribute 'type' encountered");
1406 
1407     if (0 != strcmp (tname, "string"))
1408         return ctxt->print_error (
1409             ctxt,
1410             EXR_ERR_ATTR_TYPE_MISMATCH,
1411             "Required attribute 'type': Invalid type '%s'",
1412             tname);
1413 
1414     rv = exr_attr_list_add_static_name (
1415         (exr_context_t) ctxt,
1416         &(curpart->attributes),
1417         EXR_REQ_TYPE_STR,
1418         EXR_ATTR_STRING,
1419         attrsz + 1,
1420         &outstr,
1421         &(curpart->type));
1422     if (rv != EXR_ERR_SUCCESS)
1423     {
1424         return ctxt->print_error (
1425             ctxt,
1426             rv,
1427             "Unable to initialize attribute '%s', type 'string'",
1428             EXR_REQ_TYPE_STR);
1429     }
1430 
1431     rv = scratch->sequential_read (scratch, outstr, (uint64_t) attrsz);
1432     if (rv != EXR_ERR_SUCCESS)
1433     {
1434         exr_attr_list_remove (
1435             (exr_context_t) ctxt, &(curpart->attributes), curpart->type);
1436         curpart->type = NULL;
1437         return ctxt->report_error (ctxt, rv, "Unable to read 'name' data");
1438     }
1439     outstr[attrsz] = '\0';
1440 
1441     rv = exr_attr_string_init_static_with_length (
1442         (exr_context_t) ctxt,
1443         curpart->type->string,
1444         (const char*) outstr,
1445         attrsz);
1446     if (rv != EXR_ERR_SUCCESS)
1447     {
1448         exr_attr_list_remove (
1449             (exr_context_t) ctxt, &(curpart->attributes), curpart->type);
1450         curpart->type = NULL;
1451         return ctxt->report_error (ctxt, rv, "Unable to read 'name' data");
1452     }
1453 
1454     if (strcmp ((const char*) outstr, "scanlineimage") == 0)
1455         curpart->storage_mode = EXR_STORAGE_SCANLINE;
1456     else if (strcmp ((const char*) outstr, "tiledimage") == 0)
1457         curpart->storage_mode = EXR_STORAGE_TILED;
1458     else if (strcmp ((const char*) outstr, "deepscanline") == 0)
1459         curpart->storage_mode = EXR_STORAGE_DEEP_SCANLINE;
1460     else if (strcmp ((const char*) outstr, "deeptile") == 0)
1461         curpart->storage_mode = EXR_STORAGE_DEEP_TILED;
1462     else
1463     {
1464         rv = ctxt->print_error (
1465             ctxt,
1466             EXR_ERR_INVALID_ATTR,
1467             "attribute 'type': Invalid type string '%s'",
1468             outstr);
1469         exr_attr_list_remove (
1470             (exr_context_t) ctxt, &(curpart->attributes), curpart->type);
1471         curpart->type = NULL;
1472     }
1473 
1474     return rv;
1475 }
1476 
1477 /**************************************/
1478 
1479 static exr_result_t
check_populate_version(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1480 check_populate_version (
1481     struct _internal_exr_context*     ctxt,
1482     struct _internal_exr_part*        curpart,
1483     struct _internal_exr_seq_scratch* scratch,
1484     const char*                       tname,
1485     int32_t                           attrsz)
1486 {
1487     exr_result_t rv;
1488 
1489     if (curpart->version)
1490         return ctxt->print_error (
1491             ctxt,
1492             EXR_ERR_INVALID_ATTR,
1493             "Duplicate copy of required attribute 'version' encountered");
1494 
1495     if (0 != strcmp (tname, "int"))
1496         return ctxt->print_error (
1497             ctxt,
1498             EXR_ERR_ATTR_TYPE_MISMATCH,
1499             "attribute 'version': Invalid type '%s'",
1500             tname);
1501 
1502     if (attrsz != sizeof (int32_t))
1503         return ctxt->print_error (
1504             ctxt,
1505             EXR_ERR_INVALID_ATTR,
1506             "attribute 'version': Invalid size %d (exp 4)",
1507             attrsz);
1508 
1509     rv = scratch->sequential_read (scratch, &attrsz, sizeof (int32_t));
1510     if (rv != EXR_ERR_SUCCESS)
1511         return ctxt->report_error (ctxt, rv, "Unable to read version data");
1512 
1513     attrsz = (int32_t) one_to_native32 ((uint32_t) attrsz);
1514     if (attrsz != 1)
1515         return ctxt->print_error (
1516             ctxt, EXR_ERR_INVALID_ATTR, "Invalid version %d: expect 1", attrsz);
1517 
1518     rv = exr_attr_list_add_static_name (
1519         (exr_context_t) ctxt,
1520         &(curpart->attributes),
1521         EXR_REQ_VERSION_STR,
1522         EXR_ATTR_INT,
1523         0,
1524         NULL,
1525         &(curpart->version));
1526     if (rv != EXR_ERR_SUCCESS)
1527         return ctxt->print_error (
1528             ctxt,
1529             rv,
1530             "Unable to initialize attribute '%s', type 'int'",
1531             EXR_REQ_VERSION_STR);
1532     curpart->version->i = attrsz;
1533     return rv;
1534 }
1535 
1536 /**************************************/
1537 
1538 static exr_result_t
check_populate_chunk_count(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * tname,int32_t attrsz)1539 check_populate_chunk_count (
1540     struct _internal_exr_context*     ctxt,
1541     struct _internal_exr_part*        curpart,
1542     struct _internal_exr_seq_scratch* scratch,
1543     const char*                       tname,
1544     int32_t                           attrsz)
1545 {
1546     exr_result_t rv;
1547 
1548     if (curpart->chunkCount)
1549         return ctxt->print_error (
1550             ctxt,
1551             EXR_ERR_INVALID_ATTR,
1552             "Duplicate copy of required attribute 'chunkCount' encountered");
1553 
1554     if (0 != strcmp (tname, "int"))
1555         return ctxt->print_error (
1556             ctxt,
1557             EXR_ERR_ATTR_TYPE_MISMATCH,
1558             "attribute 'chunkCount': Invalid type '%s'",
1559             tname);
1560 
1561     if (attrsz != sizeof (int32_t))
1562         return ctxt->print_error (
1563             ctxt,
1564             EXR_ERR_INVALID_ATTR,
1565             "Required attribute 'chunkCount': Invalid size %d (exp 4)",
1566             attrsz);
1567 
1568     rv = scratch->sequential_read (scratch, &attrsz, sizeof (int32_t));
1569     if (rv != EXR_ERR_SUCCESS)
1570         return ctxt->report_error (ctxt, rv, "Unable to read chunkCount data");
1571 
1572     rv = exr_attr_list_add_static_name (
1573         (exr_context_t) ctxt,
1574         &(curpart->attributes),
1575         EXR_REQ_CHUNK_COUNT_STR,
1576         EXR_ATTR_INT,
1577         0,
1578         NULL,
1579         &(curpart->chunkCount));
1580     if (rv != EXR_ERR_SUCCESS)
1581         return ctxt->print_error (
1582             ctxt,
1583             rv,
1584             "Unable to initialize attribute '%s', type 'int'",
1585             EXR_REQ_CHUNK_COUNT_STR);
1586 
1587     attrsz                 = (int32_t) one_to_native32 ((uint32_t) attrsz);
1588     curpart->chunkCount->i = attrsz;
1589     curpart->chunk_count   = attrsz;
1590     return rv;
1591 }
1592 
1593 /**************************************/
1594 
1595 static exr_result_t
check_req_attr(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,struct _internal_exr_seq_scratch * scratch,const char * aname,const char * tname,int32_t attrsz)1596 check_req_attr (
1597     struct _internal_exr_context*     ctxt,
1598     struct _internal_exr_part*        curpart,
1599     struct _internal_exr_seq_scratch* scratch,
1600     const char*                       aname,
1601     const char*                       tname,
1602     int32_t                           attrsz)
1603 {
1604     switch (aname[0])
1605     {
1606         case 'c':
1607             if (0 == strcmp (aname, EXR_REQ_CHANNELS_STR))
1608                 return check_populate_channels (
1609                     ctxt, curpart, scratch, tname, attrsz);
1610             if (0 == strcmp (aname, EXR_REQ_COMP_STR))
1611                 return check_populate_compression (
1612                     ctxt, curpart, scratch, tname, attrsz);
1613             if (0 == strcmp (aname, EXR_REQ_CHUNK_COUNT_STR))
1614                 return check_populate_chunk_count (
1615                     ctxt, curpart, scratch, tname, attrsz);
1616             break;
1617         case 'd':
1618             if (0 == strcmp (aname, EXR_REQ_DATA_STR))
1619                 return check_populate_dataWindow (
1620                     ctxt, curpart, scratch, tname, attrsz);
1621             if (0 == strcmp (aname, EXR_REQ_DISP_STR))
1622                 return check_populate_displayWindow (
1623                     ctxt, curpart, scratch, tname, attrsz);
1624             break;
1625         case 'l':
1626             if (0 == strcmp (aname, EXR_REQ_LO_STR))
1627                 return check_populate_lineOrder (
1628                     ctxt, curpart, scratch, tname, attrsz);
1629             break;
1630         case 'n':
1631             if (0 == strcmp (aname, EXR_REQ_NAME_STR))
1632                 return check_populate_name (
1633                     ctxt, curpart, scratch, tname, attrsz);
1634             break;
1635         case 'p':
1636             if (0 == strcmp (aname, EXR_REQ_PAR_STR))
1637                 return check_populate_pixelAspectRatio (
1638                     ctxt, curpart, scratch, tname, attrsz);
1639             break;
1640         case 's':
1641             if (0 == strcmp (aname, EXR_REQ_SCR_WC_STR))
1642                 return check_populate_screenWindowCenter (
1643                     ctxt, curpart, scratch, tname, attrsz);
1644             if (0 == strcmp (aname, EXR_REQ_SCR_WW_STR))
1645                 return check_populate_screenWindowWidth (
1646                     ctxt, curpart, scratch, tname, attrsz);
1647             break;
1648         case 't':
1649             if (0 == strcmp (aname, EXR_REQ_TILES_STR))
1650                 return check_populate_tiles (
1651                     ctxt, curpart, scratch, tname, attrsz);
1652             if (0 == strcmp (aname, EXR_REQ_TYPE_STR))
1653                 return check_populate_type (
1654                     ctxt, curpart, scratch, tname, attrsz);
1655             break;
1656         case 'v':
1657             if (0 == strcmp (aname, EXR_REQ_VERSION_STR))
1658                 return check_populate_version (
1659                     ctxt, curpart, scratch, tname, attrsz);
1660             break;
1661         default: break;
1662     }
1663 
1664     return EXR_ERR_UNKNOWN;
1665 }
1666 
1667 /**************************************/
1668 
1669 static exr_result_t
pull_attr(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,uint8_t init_byte,struct _internal_exr_seq_scratch * scratch)1670 pull_attr (
1671     struct _internal_exr_context*     ctxt,
1672     struct _internal_exr_part*        curpart,
1673     uint8_t                           init_byte,
1674     struct _internal_exr_seq_scratch* scratch)
1675 {
1676     char             name[256], type[256];
1677     exr_result_t     rv;
1678     int32_t          namelen = 0, typelen = 0;
1679     int32_t          attrsz = 0;
1680     exr_attribute_t* nattr  = NULL;
1681     uint8_t*         strptr = NULL;
1682     const int32_t    maxlen = ctxt->max_name_length;
1683 
1684     name[0] = (char) init_byte;
1685     namelen = 1;
1686 
1687     rv = read_text (ctxt, name, &namelen, maxlen, scratch, "attribute name");
1688     if (rv != EXR_ERR_SUCCESS) return rv;
1689     rv = read_text (ctxt, type, &typelen, maxlen, scratch, "attribute type");
1690     if (rv != EXR_ERR_SUCCESS) return rv;
1691 
1692     if (namelen == 0)
1693         return ctxt->report_error (
1694             ctxt,
1695             EXR_ERR_FILE_BAD_HEADER,
1696             "Invalid empty string encountered parsing attribute name");
1697 
1698     if (typelen == 0)
1699         return ctxt->print_error (
1700             ctxt,
1701             EXR_ERR_FILE_BAD_HEADER,
1702             "Invalid empty string encountered parsing attribute type for attribute '%s'",
1703             name);
1704 
1705     rv = scratch->sequential_read (scratch, &attrsz, sizeof (int32_t));
1706     if (rv != EXR_ERR_SUCCESS)
1707         return ctxt->print_error (
1708             ctxt,
1709             rv,
1710             "Unable to read attribute size for attribute '%s', type '%s'",
1711             name,
1712             type);
1713     attrsz = (int32_t) one_to_native32 ((uint32_t) attrsz);
1714 
1715     rv = check_req_attr (ctxt, curpart, scratch, name, type, attrsz);
1716     if (rv != EXR_ERR_UNKNOWN) return rv;
1717 
1718     /* not a required attr, just a normal one, optimize for string type to avoid double malloc */
1719     if (!strcmp (type, "string"))
1720     {
1721         int32_t n;
1722         rv = check_bad_attrsz (ctxt, scratch, attrsz, 1, name, type, &n);
1723         if (rv != EXR_ERR_SUCCESS) return rv;
1724 
1725         rv = exr_attr_list_add (
1726             (exr_context_t) ctxt,
1727             &(curpart->attributes),
1728             name,
1729             EXR_ATTR_STRING,
1730             n + 1,
1731             &strptr,
1732             &nattr);
1733     }
1734     else
1735     {
1736         rv = exr_attr_list_add_by_type (
1737             (exr_context_t) ctxt,
1738             &(curpart->attributes),
1739             name,
1740             type,
1741             0,
1742             NULL,
1743             &nattr);
1744     }
1745 
1746     if (rv != EXR_ERR_SUCCESS)
1747         return ctxt->print_error (
1748             ctxt,
1749             rv,
1750             "Unable to initialize attribute '%s', type '%s'",
1751             name,
1752             type);
1753 
1754     switch (nattr->type)
1755     {
1756         case EXR_ATTR_BOX2I:
1757             rv = extract_attr_32bit (
1758                 ctxt, scratch, nattr->box2i, name, type, attrsz, 4);
1759             break;
1760         case EXR_ATTR_BOX2F:
1761             rv = extract_attr_32bit (
1762                 ctxt, scratch, nattr->box2f, name, type, attrsz, 4);
1763             break;
1764         case EXR_ATTR_CHLIST:
1765             rv = extract_attr_chlist (
1766                 ctxt, scratch, nattr->chlist, name, type, attrsz);
1767             break;
1768         case EXR_ATTR_CHROMATICITIES:
1769             rv = extract_attr_32bit (
1770                 ctxt, scratch, nattr->chromaticities, name, type, attrsz, 8);
1771             break;
1772         case EXR_ATTR_COMPRESSION:
1773             rv = extract_attr_uint8 (
1774                 ctxt,
1775                 scratch,
1776                 &(nattr->uc),
1777                 name,
1778                 type,
1779                 attrsz,
1780                 (uint8_t) EXR_COMPRESSION_LAST_TYPE);
1781             break;
1782         case EXR_ATTR_ENVMAP:
1783             rv = extract_attr_uint8 (
1784                 ctxt,
1785                 scratch,
1786                 &(nattr->uc),
1787                 name,
1788                 type,
1789                 attrsz,
1790                 (uint8_t) EXR_ENVMAP_LAST_TYPE);
1791             break;
1792         case EXR_ATTR_LINEORDER:
1793             rv = extract_attr_uint8 (
1794                 ctxt,
1795                 scratch,
1796                 &(nattr->uc),
1797                 name,
1798                 type,
1799                 attrsz,
1800                 (uint8_t) EXR_LINEORDER_LAST_TYPE);
1801             break;
1802         case EXR_ATTR_DOUBLE:
1803             rv = extract_attr_64bit (
1804                 ctxt, scratch, &(nattr->d), name, type, attrsz, 1);
1805             break;
1806         case EXR_ATTR_FLOAT:
1807             rv = extract_attr_32bit (
1808                 ctxt, scratch, &(nattr->f), name, type, attrsz, 1);
1809             break;
1810         case EXR_ATTR_FLOAT_VECTOR:
1811             rv = extract_attr_float_vector (
1812                 ctxt, scratch, nattr->floatvector, name, type, attrsz);
1813             break;
1814         case EXR_ATTR_INT:
1815             rv = extract_attr_32bit (
1816                 ctxt, scratch, &(nattr->i), name, type, attrsz, 1);
1817             break;
1818         case EXR_ATTR_KEYCODE:
1819             rv = extract_attr_32bit (
1820                 ctxt, scratch, nattr->keycode, name, type, attrsz, 7);
1821             break;
1822         case EXR_ATTR_M33F:
1823             rv = extract_attr_32bit (
1824                 ctxt, scratch, nattr->m33f->m, name, type, attrsz, 9);
1825             break;
1826         case EXR_ATTR_M33D:
1827             rv = extract_attr_64bit (
1828                 ctxt, scratch, nattr->m33d->m, name, type, attrsz, 9);
1829             break;
1830         case EXR_ATTR_M44F:
1831             rv = extract_attr_32bit (
1832                 ctxt, scratch, nattr->m44f->m, name, type, attrsz, 16);
1833             break;
1834         case EXR_ATTR_M44D:
1835             rv = extract_attr_64bit (
1836                 ctxt, scratch, nattr->m44d->m, name, type, attrsz, 16);
1837             break;
1838         case EXR_ATTR_PREVIEW:
1839             rv = extract_attr_preview (
1840                 ctxt, scratch, nattr->preview, name, type, attrsz);
1841             break;
1842         case EXR_ATTR_RATIONAL:
1843             rv = extract_attr_32bit (
1844                 ctxt, scratch, nattr->rational, name, type, attrsz, 2);
1845             break;
1846         case EXR_ATTR_STRING:
1847             rv = extract_attr_string (
1848                 ctxt,
1849                 scratch,
1850                 nattr->string,
1851                 name,
1852                 type,
1853                 attrsz,
1854                 (char*) strptr);
1855             break;
1856         case EXR_ATTR_STRING_VECTOR:
1857             rv = extract_attr_string_vector (
1858                 ctxt, scratch, nattr->stringvector, name, type, attrsz);
1859             break;
1860         case EXR_ATTR_TILEDESC:
1861             rv = extract_attr_tiledesc (
1862                 ctxt, scratch, nattr->tiledesc, name, type, attrsz);
1863             break;
1864         case EXR_ATTR_TIMECODE:
1865             rv = extract_attr_32bit (
1866                 ctxt, scratch, nattr->timecode, name, type, attrsz, 2);
1867             break;
1868         case EXR_ATTR_V2I:
1869             rv = extract_attr_32bit (
1870                 ctxt, scratch, nattr->v2i->arr, name, type, attrsz, 2);
1871             break;
1872         case EXR_ATTR_V2F:
1873             rv = extract_attr_32bit (
1874                 ctxt, scratch, nattr->v2f->arr, name, type, attrsz, 2);
1875             break;
1876         case EXR_ATTR_V2D:
1877             rv = extract_attr_64bit (
1878                 ctxt, scratch, nattr->v2d->arr, name, type, attrsz, 2);
1879             break;
1880         case EXR_ATTR_V3I:
1881             rv = extract_attr_32bit (
1882                 ctxt, scratch, nattr->v3i->arr, name, type, attrsz, 3);
1883             break;
1884         case EXR_ATTR_V3F:
1885             rv = extract_attr_32bit (
1886                 ctxt, scratch, nattr->v3f->arr, name, type, attrsz, 3);
1887             break;
1888         case EXR_ATTR_V3D:
1889             rv = extract_attr_64bit (
1890                 ctxt, scratch, nattr->v3d->arr, name, type, attrsz, 3);
1891             break;
1892         case EXR_ATTR_OPAQUE:
1893             rv = extract_attr_opaque (
1894                 ctxt, scratch, nattr->opaque, name, type, attrsz);
1895             break;
1896         case EXR_ATTR_UNKNOWN:
1897         case EXR_ATTR_LAST_KNOWN_TYPE:
1898         default:
1899             rv = ctxt->print_error (
1900                 ctxt,
1901                 EXR_ERR_INVALID_ARGUMENT,
1902                 "Invalid type '%s' for attribute '%s'",
1903                 type,
1904                 name);
1905             break;
1906     }
1907     if (rv != EXR_ERR_SUCCESS)
1908     {
1909         exr_attr_list_remove (
1910             (exr_context_t) ctxt, &(curpart->attributes), nattr);
1911     }
1912 
1913     return rv;
1914 }
1915 
1916 /**************************************/
1917 
1918 /*  floor( log(x) / log(2) ) */
1919 static int32_t
floor_log2(int64_t x)1920 floor_log2 (int64_t x)
1921 {
1922     int32_t y = 0;
1923     while (x > 1)
1924     {
1925         y += 1;
1926         x >>= 1;
1927     }
1928     return y;
1929 }
1930 
1931 /**************************************/
1932 
1933 /*  ceil( log(x) / log(2) ) */
1934 static int32_t
ceil_log2(int64_t x)1935 ceil_log2 (int64_t x)
1936 {
1937     int32_t y = 0, r = 0;
1938     while (x > 1)
1939     {
1940         if (x & 1) r = 1;
1941         y += 1;
1942         x >>= 1;
1943     }
1944     return y + r;
1945 }
1946 
1947 /**************************************/
1948 
1949 static int64_t
calc_level_size(int64_t mind,int64_t maxd,int level,exr_tile_round_mode_t rounding)1950 calc_level_size (
1951     int64_t mind, int64_t maxd, int level, exr_tile_round_mode_t rounding)
1952 {
1953     int64_t dsize   = (int64_t) maxd - (int64_t) mind + 1;
1954     int64_t b       = ((int64_t) 1) << level;
1955     int64_t retsize = dsize / b;
1956 
1957     if (rounding == EXR_TILE_ROUND_UP && retsize * b < dsize) retsize += 1;
1958 
1959     if (retsize < 1) retsize = 1;
1960     return retsize;
1961 }
1962 
1963 /**************************************/
1964 
1965 exr_result_t
internal_exr_compute_tile_information(struct _internal_exr_context * ctxt,struct _internal_exr_part * curpart,int rebuild)1966 internal_exr_compute_tile_information (
1967     struct _internal_exr_context* ctxt,
1968     struct _internal_exr_part*    curpart,
1969     int                           rebuild)
1970 {
1971     exr_result_t rv = EXR_ERR_SUCCESS;
1972     if (curpart->storage_mode == EXR_STORAGE_SCANLINE ||
1973         curpart->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
1974         return EXR_ERR_SUCCESS;
1975 
1976     if (rebuild && (!curpart->dataWindow || !curpart->tiles))
1977         return EXR_ERR_SUCCESS;
1978 
1979     if (!curpart->tiles)
1980         return ctxt->standard_error (ctxt, EXR_ERR_MISSING_REQ_ATTR);
1981 
1982     if (rebuild)
1983     {
1984         if (curpart->tile_level_tile_count_x)
1985         {
1986             ctxt->free_fn (curpart->tile_level_tile_count_x);
1987             curpart->tile_level_tile_count_x = NULL;
1988         }
1989     }
1990 
1991     if (curpart->tile_level_tile_count_x == NULL)
1992     {
1993         const exr_attr_box2i_t     dw       = curpart->data_window;
1994         const exr_attr_tiledesc_t* tiledesc = curpart->tiles->tiledesc;
1995         int64_t                    w, h;
1996         int32_t                    numX, numY;
1997         int32_t*                   levcntX = NULL;
1998         int32_t*                   levcntY = NULL;
1999         int32_t*                   levszX  = NULL;
2000         int32_t*                   levszY  = NULL;
2001 
2002         w = ((int64_t) dw.max.x) - ((int64_t) dw.min.x) + 1;
2003         h = ((int64_t) dw.max.y) - ((int64_t) dw.min.y) + 1;
2004 
2005         if (tiledesc->x_size == 0 || tiledesc->y_size == 0)
2006             return ctxt->standard_error (ctxt, EXR_ERR_INVALID_ATTR);
2007         switch (EXR_GET_TILE_LEVEL_MODE ((*tiledesc)))
2008         {
2009             case EXR_TILE_ONE_LEVEL: numX = numY = 1; break;
2010             case EXR_TILE_MIPMAP_LEVELS:
2011                 if (EXR_GET_TILE_ROUND_MODE ((*tiledesc)) ==
2012                     EXR_TILE_ROUND_DOWN)
2013                 {
2014                     numX = floor_log2 (w > h ? w : h) + 1;
2015                     numY = numX;
2016                 }
2017                 else
2018                 {
2019                     numX = ceil_log2 (w > h ? w : h) + 1;
2020                     numY = numX;
2021                 }
2022                 break;
2023             case EXR_TILE_RIPMAP_LEVELS:
2024                 if (EXR_GET_TILE_ROUND_MODE ((*tiledesc)) ==
2025                     EXR_TILE_ROUND_DOWN)
2026                 {
2027                     numX = floor_log2 (w) + 1;
2028                     numY = floor_log2 (h) + 1;
2029                 }
2030                 else
2031                 {
2032                     numX = ceil_log2 (w) + 1;
2033                     numY = ceil_log2 (h) + 1;
2034                 }
2035                 break;
2036             case EXR_TILE_LAST_TYPE:
2037             default:
2038                 return ctxt->standard_error (
2039                     ctxt, EXR_ERR_INVALID_ATTR);
2040         }
2041 
2042         curpart->num_tile_levels_x = numX;
2043         curpart->num_tile_levels_y = numY;
2044         levcntX                    = (int32_t*) ctxt->alloc_fn (
2045             2 * (size_t) (numX + numY) * sizeof (int32_t));
2046         if (levcntX == NULL)
2047             return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
2048         levszX  = levcntX + numX;
2049         levcntY = levszX + numX;
2050         levszY  = levcntY + numY;
2051 
2052         for (int32_t l = 0; l < numX; ++l)
2053         {
2054             int64_t sx = calc_level_size (
2055                 dw.min.x, dw.max.x, l, EXR_GET_TILE_ROUND_MODE ((*tiledesc)));
2056             if (sx < 0 || sx > (int64_t) INT32_MAX)
2057                 return ctxt->print_error (
2058                     ctxt,
2059                     EXR_ERR_INVALID_ATTR,
2060                     "Invalid data window x dims (%d, %d) resulting in invalid tile level size (%" PRId64
2061                     ") for level %d",
2062                     dw.min.x,
2063                     dw.max.x,
2064                     sx,
2065                     l);
2066             levcntX[l] =
2067                 (int32_t) (((uint64_t) sx + tiledesc->x_size - 1) / tiledesc->x_size);
2068             levszX[l] = (int32_t) sx;
2069         }
2070 
2071         for (int32_t l = 0; l < numY; ++l)
2072         {
2073             int64_t sy = calc_level_size (
2074                 dw.min.y, dw.max.y, l, EXR_GET_TILE_ROUND_MODE ((*tiledesc)));
2075             if (sy < 0 || sy > (int64_t) INT32_MAX)
2076                 return ctxt->print_error (
2077                     ctxt,
2078                     EXR_ERR_INVALID_ATTR,
2079                     "Invalid data window y dims (%d, %d) resulting in invalid tile level size (%" PRId64
2080                     ") for level %d",
2081                     dw.min.y,
2082                     dw.max.y,
2083                     sy,
2084                     l);
2085             levcntY[l] =
2086                 (int32_t) (((uint64_t) sy + tiledesc->y_size - 1) / tiledesc->y_size);
2087             levszY[l] = (int32_t) sy;
2088         }
2089 
2090         curpart->tile_level_tile_count_x = levcntX;
2091         curpart->tile_level_tile_count_y = levcntY;
2092         curpart->tile_level_tile_size_x  = levszX;
2093         curpart->tile_level_tile_size_y  = levszY;
2094     }
2095     return rv;
2096 }
2097 
2098 /**************************************/
2099 
2100 int32_t
internal_exr_compute_chunk_offset_size(struct _internal_exr_part * curpart)2101 internal_exr_compute_chunk_offset_size (struct _internal_exr_part* curpart)
2102 {
2103     int32_t                  retval       = 0;
2104     const exr_attr_box2i_t   dw           = curpart->data_window;
2105     const exr_attr_chlist_t* channels     = curpart->channels->chlist;
2106     uint64_t                 unpackedsize = 0;
2107     uint64_t                 w;
2108     int                      hasLineSample = 0;
2109 
2110     w = (uint64_t) (((int64_t) dw.max.x) - ((int64_t) dw.min.x) + 1);
2111 
2112     if (curpart->tiles)
2113     {
2114         const exr_attr_tiledesc_t* tiledesc  = curpart->tiles->tiledesc;
2115         int64_t                    tilecount = 0;
2116 
2117         switch (EXR_GET_TILE_LEVEL_MODE ((*tiledesc)))
2118         {
2119             case EXR_TILE_ONE_LEVEL:
2120             case EXR_TILE_MIPMAP_LEVELS:
2121                 for (int32_t l = 0; l < curpart->num_tile_levels_x; ++l)
2122                     tilecount +=
2123                         ((int64_t) curpart->tile_level_tile_count_x[l] *
2124                          (int64_t) curpart->tile_level_tile_count_y[l]);
2125                 if (tilecount > (int64_t) INT_MAX) return -1;
2126                 retval = (int32_t) tilecount;
2127                 break;
2128             case EXR_TILE_RIPMAP_LEVELS:
2129                 for (int32_t lx = 0; lx < curpart->num_tile_levels_x; ++lx)
2130                 {
2131                     for (int32_t ly = 0; ly < curpart->num_tile_levels_y; ++ly)
2132                     {
2133                         tilecount +=
2134                             ((int64_t) curpart->tile_level_tile_count_x[lx] *
2135                              (int64_t) curpart->tile_level_tile_count_y[ly]);
2136                     }
2137                 }
2138                 if (tilecount > (int64_t) INT_MAX) return -1;
2139                 retval = (int32_t) tilecount;
2140                 break;
2141             case EXR_TILE_LAST_TYPE:
2142             default: return -1;
2143         }
2144 
2145         for (int c = 0; c < channels->num_channels; ++c)
2146         {
2147             uint64_t xsamp  = (uint64_t) channels->entries[c].x_sampling;
2148             uint64_t ysamp  = (uint64_t) channels->entries[c].y_sampling;
2149             uint64_t cunpsz = 0;
2150             if (channels->entries[c].pixel_type == EXR_PIXEL_HALF)
2151                 cunpsz = 2;
2152             else
2153                 cunpsz = 4;
2154             cunpsz *= (((uint64_t) tiledesc->x_size + xsamp - 1) / xsamp);
2155             if (ysamp > 1)
2156             {
2157                 hasLineSample = 1;
2158                 cunpsz *= (((uint64_t) tiledesc->y_size + ysamp - 1) / ysamp);
2159             }
2160             else
2161                 cunpsz *= (uint64_t) tiledesc->y_size;
2162             unpackedsize += cunpsz;
2163         }
2164         curpart->unpacked_size_per_chunk = unpackedsize;
2165         curpart->chan_has_line_sampling  = ((int16_t) hasLineSample);
2166     }
2167     else
2168     {
2169         uint64_t linePerChunk, h;
2170         switch (curpart->comp_type)
2171         {
2172             case EXR_COMPRESSION_NONE:
2173             case EXR_COMPRESSION_RLE:
2174             case EXR_COMPRESSION_ZIPS: linePerChunk = 1; break;
2175             case EXR_COMPRESSION_ZIP:
2176             case EXR_COMPRESSION_PXR24: linePerChunk = 16; break;
2177             case EXR_COMPRESSION_PIZ:
2178             case EXR_COMPRESSION_B44:
2179             case EXR_COMPRESSION_B44A:
2180             case EXR_COMPRESSION_DWAA: linePerChunk = 32; break;
2181             case EXR_COMPRESSION_DWAB: linePerChunk = 256; break;
2182             case EXR_COMPRESSION_LAST_TYPE:
2183             default:
2184                 /* ERROR CONDITION */
2185                 return -1;
2186         }
2187 
2188         for (int c = 0; c < channels->num_channels; ++c)
2189         {
2190             uint64_t xsamp  = (uint64_t) channels->entries[c].x_sampling;
2191             uint64_t ysamp  = (uint64_t) channels->entries[c].y_sampling;
2192             uint64_t cunpsz = 0;
2193             if (channels->entries[c].pixel_type == EXR_PIXEL_HALF)
2194                 cunpsz = 2;
2195             else
2196                 cunpsz = 4;
2197             cunpsz *= w / xsamp;
2198             cunpsz *= linePerChunk;
2199             if (ysamp > 1)
2200             {
2201                 hasLineSample = 1;
2202                 if (linePerChunk > 1) cunpsz *= linePerChunk / ysamp;
2203             }
2204             unpackedsize += cunpsz;
2205         }
2206 
2207         curpart->unpacked_size_per_chunk = unpackedsize;
2208         curpart->lines_per_chunk         = ((int16_t) linePerChunk);
2209         curpart->chan_has_line_sampling  = ((int16_t) hasLineSample);
2210 
2211         h      = (uint64_t) ((int64_t) dw.max.y - (int64_t) dw.min.y + 1);
2212         retval = (int32_t) ((h + linePerChunk - 1) / linePerChunk);
2213     }
2214     return retval;
2215 }
2216 
2217 /**************************************/
2218 
2219 static exr_result_t
update_chunk_offsets(struct _internal_exr_context * ctxt,struct _internal_exr_seq_scratch * scratch)2220 update_chunk_offsets (
2221     struct _internal_exr_context*     ctxt,
2222     struct _internal_exr_seq_scratch* scratch)
2223 {
2224     struct _internal_exr_part *curpart, *prevpart;
2225 
2226     exr_result_t rv = EXR_ERR_SUCCESS;
2227 
2228     if (!ctxt->parts) return EXR_ERR_INVALID_ARGUMENT;
2229 
2230     ctxt->parts[0]->chunk_table_offset =
2231         scratch->fileoff - (uint64_t) scratch->navail;
2232     prevpart = ctxt->parts[0];
2233     for (int p = 0; p < ctxt->num_parts; ++p)
2234     {
2235         curpart = ctxt->parts[p];
2236 
2237         rv = internal_exr_compute_tile_information (ctxt, curpart, 0);
2238         if (rv != EXR_ERR_SUCCESS) break;
2239 
2240         int32_t ccount = internal_exr_compute_chunk_offset_size (curpart);
2241         if (ccount < 0)
2242         {
2243             rv = ctxt->print_error (
2244                 ctxt,
2245                 EXR_ERR_INVALID_ATTR,
2246                 "Invalid chunk count (%d) for part '%s'",
2247                 ccount,
2248                 (curpart->name ? curpart->name->string->str : "<first>"));
2249             break;
2250         }
2251 
2252         if (curpart->chunk_count < 0)
2253             curpart->chunk_count = ccount;
2254         else if (curpart->chunk_count != ccount)
2255         {
2256             /* fatal error or just ignore it? c++ seemed to just ignore it entirely, we can at least warn */
2257             /* rv = */
2258             ctxt->print_error (
2259                 ctxt,
2260                 EXR_ERR_INVALID_ATTR,
2261                 "Invalid chunk count (%d) for part '%s', expect (%d)",
2262                 curpart->chunk_count,
2263                 (curpart->name ? curpart->name->string->str : "<first>"),
2264                 ccount);
2265             curpart->chunk_count = ccount;
2266         }
2267         if (prevpart != curpart)
2268             curpart->chunk_table_offset =
2269                 prevpart->chunk_table_offset +
2270                 sizeof (uint64_t) * (size_t) (prevpart->chunk_count);
2271         prevpart = curpart;
2272     }
2273     return rv;
2274 }
2275 
2276 /**************************************/
2277 
2278 static exr_result_t
read_magic_and_flags(struct _internal_exr_context * ctxt,uint32_t * outflags,uint64_t * initpos)2279 read_magic_and_flags (
2280     struct _internal_exr_context* ctxt, uint32_t* outflags, uint64_t* initpos)
2281 {
2282     uint32_t     magic_and_version[2];
2283     uint32_t     flags;
2284     exr_result_t rv      = EXR_ERR_UNKNOWN;
2285     uint64_t     fileoff = 0;
2286     int64_t      nread   = 0;
2287 
2288     rv = ctxt->do_read (
2289         ctxt,
2290         magic_and_version,
2291         sizeof (uint32_t) * 2,
2292         &fileoff,
2293         &nread,
2294         EXR_MUST_READ_ALL);
2295     if (rv != EXR_ERR_SUCCESS)
2296     {
2297         ctxt->report_error (
2298             ctxt, EXR_ERR_READ_IO, "Unable to read magic and version flags");
2299         return rv;
2300     }
2301 
2302     *initpos = sizeof (uint32_t) * 2;
2303 
2304     priv_to_native32 (magic_and_version, 2);
2305     if (magic_and_version[0] != 20000630)
2306     {
2307         rv = ctxt->print_error (
2308             ctxt,
2309             EXR_ERR_FILE_BAD_HEADER,
2310             "File is not an OpenEXR file: magic 0x%08X (%d) flags 0x%08X",
2311             magic_and_version[0],
2312             (int) magic_and_version[0],
2313             magic_and_version[1]);
2314         return rv;
2315     }
2316 
2317     flags = magic_and_version[1];
2318 
2319     ctxt->version = flags & EXR_FILE_VERSION_MASK;
2320     if (ctxt->version != 2)
2321     {
2322         rv = ctxt->print_error (
2323             ctxt,
2324             EXR_ERR_FILE_BAD_HEADER,
2325             "File is of an unsupported version: %d, magic 0x%08X flags 0x%08X",
2326             (int) ctxt->version,
2327             magic_and_version[0],
2328             magic_and_version[1]);
2329         return rv;
2330     }
2331 
2332     flags = flags & ~((uint32_t) EXR_FILE_VERSION_MASK);
2333     if ((flags & ~((uint32_t) EXR_VALID_FLAGS)) != 0)
2334     {
2335         rv = ctxt->print_error (
2336             ctxt,
2337             EXR_ERR_FILE_BAD_HEADER,
2338             "File has an unsupported flags: magic 0x%08X flags 0x%08X",
2339             magic_and_version[0],
2340             magic_and_version[1]);
2341         return rv;
2342     }
2343     *outflags = flags;
2344     return EXR_ERR_SUCCESS;
2345 }
2346 
2347 /**************************************/
2348 
2349 exr_result_t
internal_exr_check_magic(struct _internal_exr_context * ctxt)2350 internal_exr_check_magic (struct _internal_exr_context* ctxt)
2351 {
2352     uint32_t     flags;
2353     uint64_t     initpos;
2354     exr_result_t rv = EXR_ERR_UNKNOWN;
2355 
2356     rv = read_magic_and_flags (ctxt, &flags, &initpos);
2357     return rv;
2358 }
2359 
2360 /**************************************/
2361 
2362 exr_result_t
internal_exr_parse_header(struct _internal_exr_context * ctxt)2363 internal_exr_parse_header (struct _internal_exr_context* ctxt)
2364 {
2365     struct _internal_exr_seq_scratch scratch;
2366     struct _internal_exr_part*       curpart;
2367     uint32_t                         flags;
2368     uint64_t                         initpos;
2369     uint8_t                          next_byte;
2370     exr_result_t                     rv = EXR_ERR_UNKNOWN;
2371 
2372     rv = read_magic_and_flags (ctxt, &flags, &initpos);
2373     if (rv != EXR_ERR_SUCCESS) return rv;
2374 
2375     rv = priv_init_scratch (ctxt, &scratch, initpos);
2376     if (rv != EXR_ERR_SUCCESS)
2377     {
2378         priv_destroy_scratch (&scratch);
2379         return rv;
2380     }
2381 
2382     curpart = ctxt->parts[0];
2383     if (!curpart)
2384     {
2385         rv = ctxt->report_error (
2386             ctxt, EXR_ERR_INVALID_ARGUMENT, "Error during file initialization");
2387         priv_destroy_scratch (&scratch);
2388         return rv;
2389     }
2390 
2391     ctxt->is_singlepart_tiled = (flags & EXR_TILED_FLAG) ? 1 : 0;
2392     ctxt->max_name_length     = (flags & EXR_LONG_NAMES_FLAG)
2393                                     ? EXR_LONGNAME_MAXLEN
2394                                     : EXR_SHORTNAME_MAXLEN;
2395     ctxt->has_nonimage_data   = (flags & EXR_NON_IMAGE_FLAG) ? 1 : 0;
2396     ctxt->is_multipart        = (flags & EXR_MULTI_PART_FLAG) ? 1 : 0;
2397     if (ctxt->is_singlepart_tiled)
2398     {
2399         if (ctxt->has_nonimage_data || ctxt->is_multipart)
2400         {
2401             rv = ctxt->print_error (
2402                 ctxt,
2403                 EXR_ERR_FILE_BAD_HEADER,
2404                 "Invalid combination of version flags: single part found, but also marked as deep (%d) or multipart (%d)",
2405                 (int) ctxt->has_nonimage_data,
2406                 (int) ctxt->is_multipart);
2407             priv_destroy_scratch (&scratch);
2408             return rv;
2409         }
2410         curpart->storage_mode = EXR_STORAGE_TILED;
2411     }
2412     else
2413         curpart->storage_mode = EXR_STORAGE_SCANLINE;
2414 
2415     do
2416     {
2417         rv = scratch.sequential_read (&scratch, &next_byte, 1);
2418         if (rv != EXR_ERR_SUCCESS)
2419         {
2420             rv = ctxt->report_error (
2421                 ctxt, EXR_ERR_FILE_BAD_HEADER, "Unable to extract header byte");
2422             priv_destroy_scratch (&scratch);
2423             return rv;
2424         }
2425 
2426         if (next_byte == '\0')
2427         {
2428             rv = internal_exr_validate_read_part (ctxt, curpart);
2429             if (rv != EXR_ERR_SUCCESS)
2430             {
2431                 priv_destroy_scratch (&scratch);
2432                 return rv;
2433             }
2434 
2435             if (!ctxt->is_multipart)
2436             {
2437                 /* got a terminal mark, not multipart, so finished */
2438                 break;
2439             }
2440 
2441             rv = scratch.sequential_read (&scratch, &next_byte, 1);
2442             if (rv != EXR_ERR_SUCCESS)
2443             {
2444                 rv = ctxt->report_error (
2445                     ctxt,
2446                     EXR_ERR_FILE_BAD_HEADER,
2447                     "Unable to go to next part definition");
2448                 priv_destroy_scratch (&scratch);
2449                 return rv;
2450             }
2451 
2452             if (next_byte == '\0')
2453             {
2454                 /* got a second terminator, finished with the
2455                  * headers, can read chunk offsets next */
2456                 break;
2457             }
2458 
2459             rv = internal_exr_add_part (ctxt, &curpart, NULL);
2460         }
2461 
2462         if (rv == EXR_ERR_SUCCESS)
2463             rv = pull_attr (ctxt, curpart, next_byte, &scratch);
2464         if (rv != EXR_ERR_SUCCESS) break;
2465     } while (1);
2466 
2467     if (rv == EXR_ERR_SUCCESS) rv = update_chunk_offsets (ctxt, &scratch);
2468 
2469     priv_destroy_scratch (&scratch);
2470     return rv;
2471 }
2472