extra checking on memory allocation sizes to prevent a class of overflow attacks
[platform/upstream/flac.git] / src / flac / foreign_metadata.c
1 /* flac - Command-line FLAC encoder/decoder
2  * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #if defined _MSC_VER || defined __MINGW32__
24 #include <sys/types.h> /* for off_t */
25 #if _MSC_VER <= 1600 /* @@@ [2G limit] */
26 #define fseeko fseek
27 #define ftello ftell
28 #endif
29 #endif
30 #include <stdio.h> /* for FILE etc. */
31 #include <stdlib.h> /* for calloc() etc. */
32 #include <string.h> /* for memcmp() etc. */
33 #include "FLAC/assert.h"
34 #include "FLAC/metadata.h"
35 #include "share/alloc.h"
36 #include "foreign_metadata.h"
37
38 #ifdef min
39 #undef min
40 #endif
41 #define min(x,y) ((x)<(y)?(x):(y))
42
43
44 static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID[2] = { "aiff" , "riff" };
45
46 static FLAC__uint32 unpack32be_(const FLAC__byte *b)
47 {
48         return ((FLAC__uint32)b[0]<<24) + ((FLAC__uint32)b[1]<<16) + ((FLAC__uint32)b[2]<<8) + (FLAC__uint32)b[3];
49 }
50
51 static FLAC__uint32 unpack32le_(const FLAC__byte *b)
52 {
53         return (FLAC__uint32)b[0] + ((FLAC__uint32)b[1]<<8) + ((FLAC__uint32)b[2]<<16) + ((FLAC__uint32)b[3]<<24);
54 }
55
56 static FLAC__bool copy_data_(FILE *fin, FILE *fout, size_t size, const char **error, const char * const read_error, const char * const write_error)
57 {
58         static FLAC__byte buffer[4096];
59         size_t left;
60         for(left = size; left > 0; ) {
61                 size_t need = min(sizeof(buffer), left);
62                 if(fread(buffer, 1, need, fin) < need) {
63                         if(error) *error = read_error;
64                         return false;
65                 }
66                 if(fwrite(buffer, 1, need, fout) < need) {
67                         if(error) *error = write_error;
68                         return false;
69                 }
70                 left -= need;
71         }
72         return true;
73 }
74
75 static FLAC__bool append_block_(foreign_metadata_t *fm, off_t offset, FLAC__uint32 size, const char **error)
76 {
77         foreign_block_t *fb = safe_realloc_muladd2_(fm->blocks, sizeof(foreign_block_t), /*times (*/fm->num_blocks, /*+*/1/*)*/);
78         if(fb) {
79                 fb[fm->num_blocks].offset = offset;
80                 fb[fm->num_blocks].size = size;
81                 fm->num_blocks++;
82                 fm->blocks = fb;
83 /*fprintf(stderr,"@@@@@@  appended: block#%u offset=%d size=%u\n",fm->num_blocks-1,(int)fm->blocks[fm->num_blocks-1].offset,(unsigned)fm->blocks[fm->num_blocks-1].size);*/
84                 return true;
85         }
86         if(error) *error = "out of memory";
87         return false;
88 }
89
90 static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char **error)
91 {
92         FLAC__byte buffer[12];
93         off_t offset, eof_offset;
94         if((offset = ftello(f)) < 0) {
95                 if(error) *error = "ftello() error (001)";
96                 return false;
97         }
98         if(fread(buffer, 1, 12, f) < 12 || memcmp(buffer, "FORM", 4) || (memcmp(buffer+8, "AIFF", 4) && memcmp(buffer+8, "AIFC", 4))) {
99                 if(error) *error = "unsupported FORM layout (002)";
100                 return false;
101         }
102         if(!append_block_(fm, offset, 12, error))
103                 return false;
104         eof_offset = 8 + unpack32be_(buffer+4);
105 /*fprintf(stderr,"@@@@@@ off=%d eof=%d\n",(int)offset,(int)eof_offset);*/
106         while(!feof(f)) {
107                 FLAC__uint32 size;
108                 if((offset = ftello(f)) < 0) {
109                         if(error) *error = "ftello() error (003)";
110                         return false;
111                 }
112                 if((size = fread(buffer, 1, 8, f)) < 8) {
113                         if(size == 0 && feof(f))
114                                 break;
115                         if(error) *error = "invalid AIFF file (004)";
116                         return false;
117                 }
118                 size = unpack32be_(buffer+4);
119                 /* check if pad byte needed */
120                 if(size & 1)
121                         size++;
122                 if(!memcmp(buffer, "COMM", 4)) {
123                         if(fm->format_block) {
124                                 if(error) *error = "invalid AIFF file: multiple \"COMM\" chunks (005)";
125                                 return false;
126                         }
127                         if(fm->audio_block) {
128                                 if(error) *error = "invalid AIFF file: \"SSND\" chunk before \"COMM\" chunk (006)";
129                                 return false;
130                         }
131                         fm->format_block = fm->num_blocks;
132                 }
133                 else if(!memcmp(buffer, "SSND", 4)) {
134                         if(fm->audio_block) {
135                                 if(error) *error = "invalid AIFF file: multiple \"SSND\" chunks (007)";
136                                 return false;
137                         }
138                         if(!fm->format_block) {
139                                 if(error) *error = "invalid AIFF file: \"SSND\" chunk before \"COMM\" chunk (008)";
140                                 return false;
141                         }
142                         fm->audio_block = fm->num_blocks;
143                         /* read #offset bytes */
144                         if(fread(buffer+8, 1, 4, f) < 4) {
145                                 if(error) *error = "invalid AIFF file (009)";
146                                 return false;
147                         }
148                         fm->ssnd_offset_size = unpack32be_(buffer+8);
149                         if(fseeko(f, -4, SEEK_CUR) < 0) {
150                                 if(error) *error = "invalid AIFF file: seek error (010)";
151                                 return false;
152                         }
153                 }
154                 if(!append_block_(fm, offset, 8 + (memcmp(buffer, "SSND", 4)? size : 8 + fm->ssnd_offset_size), error))
155                         return false;
156 /*fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],buffer[2],buffer[3],(int)offset,8+(int)size);*/
157                 if(fseeko(f, size, SEEK_CUR) < 0) {
158                         if(error) *error = "invalid AIFF file: seek error (011)";
159                         return false;
160                 }
161         }
162         if(eof_offset != ftello(f)) {
163                 if(error) *error = "invalid AIFF file: unexpected EOF (012)";
164                 return false;
165         }
166         if(!fm->format_block) {
167                 if(error) *error = "invalid AIFF file: missing \"COMM\" chunk (013)";
168                 return false;
169         }
170         if(!fm->audio_block) {
171                 if(error) *error = "invalid AIFF file: missing \"SSND\" chunk (014)";
172                 return false;
173         }
174         return true;
175 }
176
177 static FLAC__bool read_from_wave_(foreign_metadata_t *fm, FILE *f, const char **error)
178 {
179         FLAC__byte buffer[12];
180         off_t offset, eof_offset;
181         if((offset = ftello(f)) < 0) {
182                 if(error) *error = "ftello() error (001)";
183                 return false;
184         }
185         if(fread(buffer, 1, 12, f) < 12 || memcmp(buffer, "RIFF", 4) || memcmp(buffer+8, "WAVE", 4)) {
186                 if(error) *error = "unsupported RIFF layout (002)";
187                 return false;
188         }
189         if(!append_block_(fm, offset, 12, error))
190                 return false;
191         eof_offset = 8 + unpack32le_(buffer+4);
192 /*fprintf(stderr,"@@@@@@ off=%d eof=%d\n",(int)offset,(int)eof_offset);*/
193         while(!feof(f)) {
194                 FLAC__uint32 size;
195                 if((offset = ftello(f)) < 0) {
196                         if(error) *error = "ftello() error (003)";
197                         return false;
198                 }
199                 if((size = fread(buffer, 1, 8, f)) < 8) {
200                         if(size == 0 && feof(f))
201                                 break;
202                         if(error) *error = "invalid WAVE file (004)";
203                         return false;
204                 }
205                 size = unpack32le_(buffer+4);
206                 /* check if pad byte needed */
207                 if(size & 1)
208                         size++;
209                 if(!memcmp(buffer, "fmt ", 4)) {
210                         if(fm->format_block) {
211                                 if(error) *error = "invalid WAVE file: multiple \"fmt \" chunks (005)";
212                                 return false;
213                         }
214                         if(fm->audio_block) {
215                                 if(error) *error = "invalid WAVE file: \"data\" chunk before \"fmt \" chunk (006)";
216                                 return false;
217                         }
218                         fm->format_block = fm->num_blocks;
219                 }
220                 else if(!memcmp(buffer, "data", 4)) {
221                         if(fm->audio_block) {
222                                 if(error) *error = "invalid WAVE file: multiple \"data\" chunks (007)";
223                                 return false;
224                         }
225                         if(!fm->format_block) {
226                                 if(error) *error = "invalid WAVE file: \"data\" chunk before \"fmt \" chunk (008)";
227                                 return false;
228                         }
229                         fm->audio_block = fm->num_blocks;
230                 }
231                 if(!append_block_(fm, offset, 8 + (memcmp(buffer, "data", 4)? size : 0), error))
232                         return false;
233 /*fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],buffer[2],buffer[3],(int)offset,8+(int)size);*/
234                 if(fseeko(f, size, SEEK_CUR) < 0) {
235                         if(error) *error = "invalid WAVE file: seek error (009)";
236                         return false;
237                 }
238         }
239         if(eof_offset != ftello(f)) {
240                 if(error) *error = "invalid WAVE file: unexpected EOF (010)";
241                 return false;
242         }
243         if(!fm->format_block) {
244                 if(error) *error = "invalid WAVE file: missing \"fmt \" chunk (011)";
245                 return false;
246         }
247         if(!fm->audio_block) {
248                 if(error) *error = "invalid WAVE file: missing \"data\" chunk (012)";
249                 return false;
250         }
251         return true;
252 }
253
254 static FLAC__bool write_to_flac_(foreign_metadata_t *fm, FILE *fin, FILE *fout, FLAC__Metadata_SimpleIterator *it, const char **error)
255 {
256         FLAC__byte buffer[4];
257         const unsigned ID_LEN = FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8;
258         size_t block_num = 0;
259         FLAC__ASSERT(sizeof(buffer) >= ID_LEN);
260         while(block_num < fm->num_blocks) {
261                 /* find next matching padding block */
262                 do {
263                         /* even on the first chunk's loop there will be a skippable STREAMINFO block, on subsequent loops we are first moving past the PADDING we just used */
264                         if(!FLAC__metadata_simple_iterator_next(it)) {
265                                 if(error) *error = "no matching PADDING block found (004)";
266                                 return false;
267                         }
268                 } while(FLAC__metadata_simple_iterator_get_block_type(it) != FLAC__METADATA_TYPE_PADDING);
269                 if(FLAC__metadata_simple_iterator_get_block_length(it) != ID_LEN+fm->blocks[block_num].size) {
270                         if(error) *error = "PADDING block with wrong size found (005)";
271                         return false;
272                 }
273 /*fprintf(stderr,"@@@@@@ flac offset = %d\n",(int)FLAC__metadata_simple_iterator_get_block_offset(it));*/
274                 /* transfer chunk into APPLICATION block */
275                 /* first set up the file pointers */
276                 if(fseeko(fin, fm->blocks[block_num].offset, SEEK_SET) < 0) {
277                         if(error) *error = "seek failed in WAVE/AIFF file (006)";
278                         return false;
279                 }
280                 if(fseeko(fout, FLAC__metadata_simple_iterator_get_block_offset(it), SEEK_SET) < 0) {
281                         if(error) *error = "seek failed in FLAC file (007)";
282                         return false;
283                 }
284                 /* update the type */
285                 buffer[0] = FLAC__METADATA_TYPE_APPLICATION;
286                 if(FLAC__metadata_simple_iterator_is_last(it))
287                         buffer[0] |= 0x80; /*MAGIC number*/
288                 if(fwrite(buffer, 1, 1, fout) < 1) {
289                         if(error) *error = "write failed in FLAC file (008)";
290                         return false;
291                 }
292                 /* length stays the same so skip over it */
293                 if(fseeko(fout, FLAC__STREAM_METADATA_LENGTH_LEN/8, SEEK_CUR) < 0) {
294                         if(error) *error = "seek failed in FLAC file (009)";
295                         return false;
296                 }
297                 /* write the APPLICATION ID */
298                 memcpy(buffer, FLAC__FOREIGN_METADATA_APPLICATION_ID[fm->type], ID_LEN);
299                 if(fwrite(buffer, 1, ID_LEN, fout) < ID_LEN) {
300                         if(error) *error = "write failed in FLAC file (010)";
301                         return false;
302                 }
303                 /* transfer the foreign metadata */
304                 if(!copy_data_(fin, fout, fm->blocks[block_num].size, error, "read failed in WAVE/AIFF file (011)", "write failed in FLAC file (012)"))
305                         return false;
306                 block_num++;
307         }
308         return true;
309 }
310
311 static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadata_SimpleIterator *it, const char **error)
312 {
313         FLAC__byte id[4], buffer[12];
314         off_t offset;
315         FLAC__bool type_found = false;
316
317         FLAC__ASSERT(FLAC__STREAM_METADATA_APPLICATION_ID_LEN == sizeof(id)*8);
318
319         while(FLAC__metadata_simple_iterator_next(it)) {
320                 if(FLAC__metadata_simple_iterator_get_block_type(it) != FLAC__METADATA_TYPE_APPLICATION)
321                         continue;
322                 if(!FLAC__metadata_simple_iterator_get_application_id(it, id)) {
323                         if(error) *error = "FLAC__metadata_simple_iterator_get_application_id() error (003)";
324                         return false;
325                 }
326                 if(memcmp(id, FLAC__FOREIGN_METADATA_APPLICATION_ID[fm->type], sizeof(id)))
327                         continue;
328                 offset = FLAC__metadata_simple_iterator_get_block_offset(it);
329                 /* skip over header and app ID */
330                 offset += (FLAC__STREAM_METADATA_IS_LAST_LEN + FLAC__STREAM_METADATA_TYPE_LEN + FLAC__STREAM_METADATA_LENGTH_LEN) / 8;
331                 offset += sizeof(id);
332                 /* look for format or audio blocks */
333                 if(fseek(f, offset, SEEK_SET) < 0) {
334                         if(error) *error = "seek error (004)";
335                         return false;
336                 }
337                 if(fread(buffer, 1, 4, f) != 4) {
338                         if(error) *error = "read error (005)";
339                         return false;
340                 }
341                 if(fm->num_blocks == 0) {
342                         if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && 0 == memcmp(buffer, "RIFF", 4))
343                                 type_found = true;
344                         else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF && 0 == memcmp(buffer, "FORM", 4))
345                                 type_found = true;
346                         else {
347                                 if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (005)";
348                                 return false;
349                         }
350                 }
351                 else if(!type_found) {
352                         FLAC__ASSERT(0);
353                         /* double protection: */
354                         if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (006)";
355                         return false;
356                 }
357                 else if(fm->type == FOREIGN_BLOCK_TYPE__RIFF) {
358                         if(!memcmp(buffer, "fmt ", 4)) {
359                                 if(fm->format_block) {
360                                         if(error) *error = "invalid WAVE metadata: multiple \"fmt \" chunks (007)";
361                                         return false;
362                                 }
363                                 if(fm->audio_block) {
364                                         if(error) *error = "invalid WAVE metadata: \"data\" chunk before \"fmt \" chunk (008)";
365                                         return false;
366                                 }
367                                 fm->format_block = fm->num_blocks;
368                         }
369                         else if(!memcmp(buffer, "data", 4)) {
370                                 if(fm->audio_block) {
371                                         if(error) *error = "invalid WAVE metadata: multiple \"data\" chunks (009)";
372                                         return false;
373                                 }
374                                 if(!fm->format_block) {
375                                         if(error) *error = "invalid WAVE metadata: \"data\" chunk before \"fmt \" chunk (010)";
376                                         return false;
377                                 }
378                                 fm->audio_block = fm->num_blocks;
379                         }
380                 }
381                 else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF) {
382                         if(!memcmp(buffer, "COMM", 4)) {
383                                 if(fm->format_block) {
384                                         if(error) *error = "invalid AIFF metadata: multiple \"COMM\" chunks (011)";
385                                         return false;
386                                 }
387                                 if(fm->audio_block) {
388                                         if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (012)";
389                                         return false;
390                                 }
391                                 fm->format_block = fm->num_blocks;
392                         }
393                         else if(!memcmp(buffer, "SSND", 4)) {
394                                 if(fm->audio_block) {
395                                         if(error) *error = "invalid AIFF metadata: multiple \"SSND\" chunks (013)";
396                                         return false;
397                                 }
398                                 if(!fm->format_block) {
399                                         if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (014)";
400                                         return false;
401                                 }
402                                 fm->audio_block = fm->num_blocks;
403                                 /* read SSND offset size */
404                                 if(fread(buffer+4, 1, 8, f) != 8) {
405                                         if(error) *error = "read error (015)";
406                                         return false;
407                                 }
408                                 fm->ssnd_offset_size = unpack32be_(buffer+8);
409                         }
410                 }
411                 else {
412                         FLAC__ASSERT(0);
413                         /* double protection: */
414                         if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (016)";
415                         return false;
416                 }
417                 if(!append_block_(fm, offset, FLAC__metadata_simple_iterator_get_block_length(it)-sizeof(id), error))
418                         return false;
419         }
420         if(!type_found) {
421                 if(error) *error = "no foreign metadata found (017)";
422                 return false;
423         }
424         if(!fm->format_block) {
425                 if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"fmt \" chunk (018)" : "invalid AIFF file: missing \"COMM\" chunk (018)";
426                 return false;
427         }
428         if(!fm->audio_block) {
429                 if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"data\" chunk (019)" : "invalid AIFF file: missing \"SSND\" chunk (019)";
430                 return false;
431         }
432         return true;
433 }
434
435 static FLAC__bool write_to_iff_(foreign_metadata_t *fm, FILE *fin, FILE *fout, off_t offset1, off_t offset2, off_t offset3, const char **error)
436 {
437         size_t i;
438         if(fseeko(fout, offset1, SEEK_SET) < 0) {
439                 if(error) *error = "seek failed in WAVE/AIFF file (002)";
440                 return false;
441         }
442         for(i = 1; i < fm->format_block; i++) {
443                 if(fseeko(fin, fm->blocks[i].offset, SEEK_SET) < 0) {
444                         if(error) *error = "seek failed in FLAC file (003)";
445                         return false;
446                 }
447                 if(!copy_data_(fin, fout, fm->blocks[i].size, error, "read failed in WAVE/AIFF file (004)", "write failed in FLAC file (005)"))
448                         return false;
449         }
450         if(fseeko(fout, offset2, SEEK_SET) < 0) {
451                 if(error) *error = "seek failed in WAVE/AIFF file (006)";
452                 return false;
453         }
454         for(i = fm->format_block+1; i < fm->audio_block; i++) {
455                 if(fseeko(fin, fm->blocks[i].offset, SEEK_SET) < 0) {
456                         if(error) *error = "seek failed in FLAC file (007)";
457                         return false;
458                 }
459                 if(!copy_data_(fin, fout, fm->blocks[i].size, error, "read failed in WAVE/AIFF file (008)", "write failed in FLAC file (009)"))
460                         return false;
461         }
462         if(fseeko(fout, offset3, SEEK_SET) < 0) {
463                 if(error) *error = "seek failed in WAVE/AIFF file (010)";
464                 return false;
465         }
466         for(i = fm->audio_block+1; i < fm->num_blocks; i++) {
467                 if(fseeko(fin, fm->blocks[i].offset, SEEK_SET) < 0) {
468                         if(error) *error = "seek failed in FLAC file (011)";
469                         return false;
470                 }
471                 if(!copy_data_(fin, fout, fm->blocks[i].size, error, "read failed in WAVE/AIFF file (012)", "write failed in FLAC file (013)"))
472                         return false;
473         }
474         return true;
475 }
476
477 foreign_metadata_t *flac__foreign_metadata_new(foreign_block_type_t type)
478 {
479         foreign_metadata_t *x = (foreign_metadata_t*)calloc(sizeof(foreign_metadata_t), 1);
480         if(x)
481                 x->type = type;
482         return x;
483 }
484
485 void flac__foreign_metadata_delete(foreign_metadata_t *fm)
486 {
487         if(fm) {
488                 if(fm->blocks)
489                         free(fm->blocks);
490                 free(fm);
491         }
492 }
493
494 FLAC__bool flac__foreign_metadata_read_from_aiff(foreign_metadata_t *fm, const char *filename, const char **error)
495 {
496         FLAC__bool ok;
497         FILE *f = fopen(filename, "rb");
498         if(!f) {
499                 if(error) *error = "can't open AIFF file for reading (000)";
500                 return false;
501         }
502         ok = read_from_aiff_(fm, f, error);
503         fclose(f);
504         return ok;
505 }
506
507 FLAC__bool flac__foreign_metadata_read_from_wave(foreign_metadata_t *fm, const char *filename, const char **error)
508 {
509         FLAC__bool ok;
510         FILE *f = fopen(filename, "rb");
511         if(!f) {
512                 if(error) *error = "can't open WAVE file for reading (000)";
513                 return false;
514         }
515         ok = read_from_wave_(fm, f, error);
516         fclose(f);
517         return ok;
518 }
519
520 FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error)
521 {
522         FLAC__bool ok;
523         FILE *fin, *fout;
524         FLAC__Metadata_SimpleIterator *it = FLAC__metadata_simple_iterator_new();
525         if(!it) {
526                 if(error) *error = "out of memory (000)";
527                 return false;
528         }
529         if(!FLAC__metadata_simple_iterator_init(it, outfilename, /*read_only=*/true, /*preserve_file_stats=*/false)) {
530                 if(error) *error = "can't initialize iterator (001)";
531                 FLAC__metadata_simple_iterator_delete(it);
532                 return false;
533         }
534         if(0 == (fin = fopen(infilename, "rb"))) {
535                 if(error) *error = "can't open WAVE/AIFF file for reading (002)";
536                 FLAC__metadata_simple_iterator_delete(it);
537                 return false;
538         }
539         if(0 == (fout = fopen(outfilename, "r+b"))) {
540                 if(error) *error = "can't open FLAC file for updating (003)";
541                 FLAC__metadata_simple_iterator_delete(it);
542                 fclose(fin);
543                 return false;
544         }
545         ok = write_to_flac_(fm, fin, fout, it, error);
546         FLAC__metadata_simple_iterator_delete(it);
547         fclose(fin);
548         fclose(fout);
549         return ok;
550 }
551
552 FLAC__bool flac__foreign_metadata_read_from_flac(foreign_metadata_t *fm, const char *filename, const char **error)
553 {
554         FLAC__bool ok;
555         FILE *f;
556         FLAC__Metadata_SimpleIterator *it = FLAC__metadata_simple_iterator_new();
557         if(!it) {
558                 if(error) *error = "out of memory (000)";
559                 return false;
560         }
561         if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/false)) {
562                 if(error) *error = "can't initialize iterator (001)";
563                 FLAC__metadata_simple_iterator_delete(it);
564                 return false;
565         }
566         if(0 == (f = fopen(filename, "rb"))) {
567                 if(error) *error = "can't open FLAC file for reading (002)";
568                 FLAC__metadata_simple_iterator_delete(it);
569                 return false;
570         }
571         ok = read_from_flac_(fm, f, it, error);
572         FLAC__metadata_simple_iterator_delete(it);
573         fclose(f);
574         return ok;
575 }
576
577 FLAC__bool flac__foreign_metadata_write_to_iff(foreign_metadata_t *fm, const char *infilename, const char *outfilename, off_t offset1, off_t offset2, off_t offset3, const char **error)
578 {
579         FLAC__bool ok;
580         FILE *fin, *fout;
581         if(0 == (fin = fopen(infilename, "rb"))) {
582                 if(error) *error = "can't open FLAC file for reading (000)";
583                 return false;
584         }
585         if(0 == (fout = fopen(outfilename, "r+b"))) {
586                 if(error) *error = "can't open WAVE/AIFF file for updating (001)";
587                 fclose(fin);
588                 return false;
589         }
590         ok = write_to_iff_(fm, fin, fout, offset1, offset2, offset3, error);
591         fclose(fin);
592         fclose(fout);
593         return ok;
594 }