uniso.c 15.5 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1
/* uniso.c - Unpack ISO9660 File System from a stream
Natanael Copa's avatar
Natanael Copa committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

Copyright (C) 2011 Timo Teräs <timo.teras@iki.fi>
All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
Natanael Copa's avatar
Natanael Copa committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

/* Compile with:
 * gcc -std=gnu99 -D_GNU_SOURCE -D_BSD_SOURCE -O2 uniso.c -o uniso */

/*
 * TODO:
 * - fix unaligned 16-bit accesses from iso headers (ARM / MIPS)
 * - help, options, verbose logs
 */

/* needed for SPLICE_F_MOVE */
#define _GNU_SOURCE

#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <wchar.h>
#include <unistd.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
48
#include <sys/types.h>
Natanael Copa's avatar
Natanael Copa committed
49

Natanael Copa's avatar
Natanael Copa committed
50 51 52
#if defined(__linux__)
# include <malloc.h>
# include <endian.h>
53
# include <sys/vfs.h>
Natanael Copa's avatar
Natanael Copa committed
54 55 56 57 58 59 60 61 62 63
#elif defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# define be16toh(x) OSSwapBigToHostInt16(x)
#endif

#ifndef EMEDIUMTYPE
#define EMEDIUMTYPE EILSEQ
#endif


Natanael Copa's avatar
Natanael Copa committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
/**/

#ifndef container_of
#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
#endif

#define LIST_POISON1 (void *) 0xdeadbeef
#define LIST_POISON2 (void *) 0xabbaabba

struct list_head {
	struct list_head *next, *prev;
};

static inline void list_init(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

static inline void __list_del(struct list_head * prev, struct list_head * next)
{
	next->prev = prev;
	prev->next = next;
}

static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

static inline int list_hashed(const struct list_head *n)
{
	return n->next != n && n->next != NULL;
}

#define list_entry(ptr, type, member) container_of(ptr,type,member)

/**/

#define ISOFS_BLOCK_SIZE		2048
#define ISOFS_TMPBUF_SIZE		(32*ISOFS_BLOCK_SIZE)

#if __BYTE_ORDER == __LITTLE_ENDIAN
#define endianess	le
#else
#define endianess	be
#endif

struct isofs723 {
	u_int16_t le;
	u_int16_t be;
};

struct isofs731 {
	u_int32_t le;
};

struct isofs732 {
	u_int32_t be;
};

struct isofs733 {
	u_int32_t le;
	u_int32_t be;
};

#define ISOFS_VD_PRIMARY		1
#define ISOFS_VD_SUPPLEMENTARY		2
#define ISOFS_VD_END			255
#define ISOFS_STANDARD_ID		"CD001"

struct isofs_volume_descriptor {
	unsigned char type;
	unsigned char id[5];
	unsigned char version;
	unsigned char data[2040];
};

/* Primary or Supplementary Volume Descriptor
 * (they are about the same, Supplementary just has few more fields
 *  and the date they point to might be encoded differently) */
struct isofs_pri_sup_descriptor {
	unsigned char 	type;
	unsigned char 	id[5];
	unsigned char 	version;
	char		flags;			/* unused1 in primary */
	char		system_id[32];
	char		volume_id[32];
	char		unused2[8];
	struct isofs733	volume_space_size;
	char		escape[32];		/* unused3 in primary */
	struct isofs723	volume_set_size;
	struct isofs723	volume_sequence_number;
	struct isofs723	logical_block_size;
	struct isofs733	path_table_size;
	struct isofs731	type_l_path_table, opt_type_l_path_table;
	struct isofs732	type_m_path_table, opt_type_m_path_table;
	char		root_directory_record[34];
	char		volume_set_id[128];
	char		publisher_id[128];
	char		preparer_id[128];
	char		application_id[128];
	char		copyright_file_id[37];
	char		abstract_file_id[37];
	char		bibliographic_file_id[37];
	char		creation_date[17];
	char		modification_date[17];
	char		expiration_date[17];
	char		effective_date[17];
	char		file_structure_version;
	char		unused4[1];
	char		application_data[512];
	char		unused5[653];
};

struct isofs_directory_record {
	unsigned char	length;
	unsigned char	ext_attr_length;
	struct isofs733	extent;
	struct isofs733	size;
	char		date[7];
	char		flags;
#define ISOFS_DR_FLAG_DIRECTORY		(1<<1)
	unsigned char	file_unit_size;
	unsigned char	interleave;
	struct isofs723	volume_sequence_number;
	unsigned char	name_len;
	char		name[0];
} __attribute__((packed));

struct uniso_reader;
struct uniso_context;

typedef int (*uniso_handler_f)(struct uniso_context *, struct uniso_reader *);

struct uniso_reader {
	struct list_head parser_list;
225
	off_t offset;
Natanael Copa's avatar
Natanael Copa committed
226 227 228 229 230
	uniso_handler_f handler;
};

struct uniso_dirent {
	struct uniso_reader reader;
231
	size_t size;
Natanael Copa's avatar
Natanael Copa committed
232 233 234 235 236 237 238
	u_int32_t flags;
	char name[0];
};

struct uniso_context {
	int stream_fd, null_fd;
	int skip_method, copy_method;
239 240 241
	int joliet_level;
	int loglevel, block_size;
	off_t pos, last_queued_read, disk_free, disk_needed;
Natanael Copa's avatar
Natanael Copa committed
242 243 244 245 246
	struct list_head parser_head;
	struct uniso_reader volume_desc_reader;
	unsigned char *tmpbuf;
};

Natanael Copa's avatar
Natanael Copa committed
247
#if defined(__linux__)
248
static int do_splice(struct uniso_context *ctx, int to_fd, size_t bytes)
Natanael Copa's avatar
Natanael Copa committed
249
{
250
	int r, left;
Natanael Copa's avatar
Natanael Copa committed
251

252 253
	for (left = bytes; left; ) {
		r = splice(ctx->stream_fd, NULL, to_fd, NULL, left,
Natanael Copa's avatar
Natanael Copa committed
254
			   SPLICE_F_MOVE);
255 256 257
		if (r < 0) return -errno;
		if (r == 0) return bytes - left;
		left -= r;
Natanael Copa's avatar
Natanael Copa committed
258
		ctx->pos += r;
259
	}
Natanael Copa's avatar
Natanael Copa committed
260

261
	return bytes;
Natanael Copa's avatar
Natanael Copa committed
262
}
Natanael Copa's avatar
Natanael Copa committed
263 264 265
#else
#define do_splice(ctx,to_fd,bytes) (-1)
#endif
Natanael Copa's avatar
Natanael Copa committed
266

267
static int do_read(struct uniso_context *ctx, unsigned char *buf, size_t bytes)
Natanael Copa's avatar
Natanael Copa committed
268
{
269
	int r, left;
Natanael Copa's avatar
Natanael Copa committed
270

271 272
	for (left = bytes; left; ) {
		r = read(ctx->stream_fd, buf, left);
Natanael Copa's avatar
Natanael Copa committed
273 274 275 276
		if (r < 0) {
			perror("read");
			return -errno;
		}
277 278
		if (r == 0) return bytes - left;
		left -= r;
Natanael Copa's avatar
Natanael Copa committed
279 280
		buf += r;
		ctx->pos += r;
281
	}
Natanael Copa's avatar
Natanael Copa committed
282

283
	return bytes;
Natanael Copa's avatar
Natanael Copa committed
284 285
}

286
static int do_write(int tofd, const unsigned char *buf, size_t bytes)
Natanael Copa's avatar
Natanael Copa committed
287
{
288
	int r, left;
Natanael Copa's avatar
Natanael Copa committed
289

290 291
	for (left = bytes; left; ) {
		r = write(tofd, buf, left);
Natanael Copa's avatar
Natanael Copa committed
292 293 294 295
		if (r < 0) {
			perror("write");
			return -errno;
		}
296
		left -= r;
Natanael Copa's avatar
Natanael Copa committed
297
		buf += r;
298
	}
Natanael Copa's avatar
Natanael Copa committed
299

300
	return bytes;
Natanael Copa's avatar
Natanael Copa committed
301 302
}

303
static int do_skip(struct uniso_context *ctx, size_t bytes)
Natanael Copa's avatar
Natanael Copa committed
304
{
305
	int r, left, now;
Natanael Copa's avatar
Natanael Copa committed
306 307 308 309 310

	switch (ctx->skip_method) {
	case 0:
		if (lseek(ctx->stream_fd, bytes, SEEK_CUR) != (off_t) -1) {
			ctx->pos += bytes;
311
			return bytes;
Natanael Copa's avatar
Natanael Copa committed
312 313 314
		}
		ctx->skip_method = 1;
	case 1:
315 316
		r = do_splice(ctx, ctx->null_fd, bytes);
		if (r >= 0) return r;
Natanael Copa's avatar
Natanael Copa committed
317 318
		ctx->skip_method = 2;
	case 2:
319 320
		for (left = bytes; left; ) {
			now = left;
Natanael Copa's avatar
Natanael Copa committed
321 322 323
			if (now > ISOFS_TMPBUF_SIZE)
				now = ISOFS_TMPBUF_SIZE;
			r = do_read(ctx, ctx->tmpbuf, now);
324 325 326
			if (r < 0) return r;
			if (r == 0) return bytes - left;
			left -= now;
Natanael Copa's avatar
Natanael Copa committed
327
		}
328
		return bytes;
Natanael Copa's avatar
Natanael Copa committed
329 330 331 332
	}
	return 0;
}

333
static int do_copy(struct uniso_context *ctx, int tofd, size_t bytes)
Natanael Copa's avatar
Natanael Copa committed
334
{
335
	int r, now, left;
Natanael Copa's avatar
Natanael Copa committed
336 337 338

	switch (ctx->copy_method) {
	case 0:
339 340
		r = do_splice(ctx, tofd, bytes);
		if (r >= 0) return r;
341
		ctx->copy_method = 1;
Natanael Copa's avatar
Natanael Copa committed
342 343
	case 1:
		/* FIXME: Use mmaped IO */
344
		ctx->copy_method = 2;
Natanael Copa's avatar
Natanael Copa committed
345
	case 2:
346 347
		for (left = bytes; left; ) {
			now = left;
Natanael Copa's avatar
Natanael Copa committed
348 349 350 351
			if (now > ISOFS_TMPBUF_SIZE)
				now = ISOFS_TMPBUF_SIZE;

			r = do_read(ctx, ctx->tmpbuf, now);
352
			if (r <= 0) return r < 0 ? r : -EIO;
Natanael Copa's avatar
Natanael Copa committed
353

354
			now = r;
Natanael Copa's avatar
Natanael Copa committed
355
			r = do_write(tofd, ctx->tmpbuf, now);
356
			if (r != now) return r < 0 ? r : -EIO;
Natanael Copa's avatar
Natanael Copa committed
357

358
			left -= now;
Natanael Copa's avatar
Natanael Copa committed
359
		}
360
		return bytes;
Natanael Copa's avatar
Natanael Copa committed
361 362 363 364 365 366
	}
	return 0;
}

static int queue_reader(
		struct uniso_context *ctx, struct uniso_reader *reader,
367
		off_t offset, uniso_handler_f handler)
Natanael Copa's avatar
Natanael Copa committed
368 369 370 371 372 373
{
	struct list_head *n;
	struct uniso_reader *r;

	if (offset < ctx->pos) {
		fprintf(stderr, "ERROR: non-linear reads are not supported "
374
				"(asked %zx, we are at %zx)\n",
Natanael Copa's avatar
Natanael Copa committed
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
			offset, ctx->last_queued_read);
		return -1;
	}

	reader->offset = offset;
	reader->handler = handler;
	list_init(&reader->parser_list);

	if (offset >= ctx->last_queued_read || !list_hashed(&ctx->parser_head)) {
		ctx->last_queued_read = offset;
		list_add_tail(&reader->parser_list, &ctx->parser_head);
		return 0;
	}

	for (n = ctx->parser_head.prev; n != &ctx->parser_head; n = n->prev) {
		r = list_entry(n, struct uniso_reader, parser_list);
		if (r->offset < offset)
			break;
	}
	list_add(&reader->parser_list, n);

	return 0;
}

static int queue_dirent(struct uniso_context *ctx, void *isode, const char *name);

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
static int link_or_clone(const char *src, const char *dst, size_t bytes)
{
	int src_fd, dst_fd;
	int r;
	static char buf[ISOFS_TMPBUF_SIZE];

	if (link(src, dst) == 0)
		return 0;

	src_fd = open(src, O_RDONLY);
	if (src_fd < 0)
		return src_fd;

	dst_fd = open(dst, O_WRONLY | O_TRUNC | O_CREAT, 0777);
	if (dst_fd < 0) {
		close(src_fd);
		return dst_fd;
	}

	while (bytes) {
		size_t now = bytes;
		if (now > sizeof(buf))
			now = sizeof(buf);
		r = read(src_fd, buf, now);
425
		if (r < 0) return r;
426 427

		r = write(dst_fd, buf, now);
428
		if (r < 0) return r;
429 430 431 432 433 434 435
		bytes -= now;
	}
	close(dst_fd);
	close(src_fd);
	return 0;
}

Natanael Copa's avatar
Natanael Copa committed
436
static int uniso_read_file(struct uniso_context *ctx,
437
                           struct uniso_reader *rd)
Natanael Copa's avatar
Natanael Copa committed
438 439 440
{
	struct uniso_dirent *dir = container_of(rd, struct uniso_dirent, reader);
	int fd, rc;
441
	static off_t prev_offset = 0;
442 443
	static char prev_name[512];
	static size_t prev_size = 0;
Natanael Copa's avatar
Natanael Copa committed
444

445 446 447 448 449 450 451 452
	if (ctx->disk_free && ctx->disk_needed > ctx->disk_free) {
		if (ctx->loglevel > 0)
			fprintf(stderr, "ERROR: Disk needed %zd MiB (free %zd MiB)\n",
				(size_t)(ctx->disk_needed * ctx->block_size / (1024*1024)),
				(size_t)(ctx->disk_free * ctx->block_size / (1024*1024)));
		return -ENOSPC;
	}

Natanael Copa's avatar
Natanael Copa committed
453 454 455
	// FIXME: seems that hardlinked files get the same file extent
	// shared. need to fix extraction of such files.
	if (ctx->pos > rd->offset) {
456 457 458 459
		rc = 1;
		if (rd->offset == prev_offset)
			rc = link_or_clone(prev_name, dir->name, prev_size);
		if (ctx->loglevel > 0 && rc != 0)
Natanael Copa's avatar
Natanael Copa committed
460 461 462 463 464 465
			fprintf(stderr, "WARNING: Not extracting '%s' as "
					"it's sharing file-extents with "
					"another file\n", dir->name);
		return 0;
	}

466 467 468 469
	prev_offset = rd->offset;
	strncpy(prev_name, dir->name, sizeof(prev_name));
	prev_size = dir->size;

Natanael Copa's avatar
Natanael Copa committed
470 471 472 473 474
	fd = open(dir->name, O_WRONLY | O_TRUNC | O_CREAT, 0777);
	if (fd < 0)
		return -errno;

	if (ctx->loglevel > 1)
475
		fprintf(stderr, "%s : %zd bytes, flags 0x%08x\n",
Natanael Copa's avatar
Natanael Copa committed
476
			dir->name, dir->size, dir->flags);
477 478 479 480 481

	if (dir->size) {
		rc = -posix_fallocate(fd, 0, dir->size);
		if (rc) goto err;
	}
Natanael Copa's avatar
Natanael Copa committed
482
	rc = do_copy(ctx, fd, dir->size);
483 484
	if (rc == dir->size) fdatasync(fd);
err:
Natanael Copa's avatar
Natanael Copa committed
485
	close(fd);
486 487 488
	if (rc < 0) return rc;
	if (rc != dir->size) return -EIO;
	return 0;
Natanael Copa's avatar
Natanael Copa committed
489 490 491 492 493 494 495 496
}

static int uniso_read_directory(struct uniso_context *ctx,
				struct uniso_reader *rd)
{
	struct uniso_dirent *dir = container_of(rd, struct uniso_dirent, reader);
	struct isofs_directory_record *ide;
	char buf[512], *name;
Natanael Copa's avatar
Natanael Copa committed
497
	unsigned char *tmp;
Natanael Copa's avatar
Natanael Copa committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
	int r, i, offs;

	if (ctx->loglevel > 1)
		fprintf(stderr, "%s : DIR, flags 0x%08x\n",
			dir->name, dir->flags);
	mkdir(dir->name, 0777);

	tmp = malloc(dir->size);
	if (tmp == NULL) {
		r = -ENOMEM;
		goto ret_nomem;
	}

	r = do_read(ctx, tmp, dir->size);
	if (r < 0)
		goto ret;

	strcpy(buf, dir->name);
	strcat(buf, "/");

	for (offs = 0; offs < dir->size; ) {
		ide = (void *) &tmp[offs];

		offs += (ide->length + 1) & ~1;
		if (ide->length == 0) {
			offs &= ~(ISOFS_BLOCK_SIZE-1);
			offs += ISOFS_BLOCK_SIZE;
			continue;
		}

		/* Skip '.' and '..' entries */
		if ((ide->name_len == 0) ||
		    (ide->name_len == 1 && ide->name[0] <= 1))
			continue;

		name = &buf[strlen(dir->name) + 1];
		if (ctx->joliet_level) {
			u_int16_t *wname = (u_int16_t *) ide->name;
			for (i = 0; i < ide->name_len; i += 2, wname++) {
				r = wctomb(name, be16toh(*wname));
				if (r == -1)
					*name++ = '?';
				else
					name += r;
			}
			*name = 0;
		} else {
			memcpy(name, ide->name, ide->name_len);
			name[ide->name_len] = 0;
		}

		queue_dirent(ctx, ide, buf);
	}
	r = 0;

ret:
	free(tmp);
ret_nomem:
	free(dir);
	return r;
}

static int queue_dirent(struct uniso_context *ctx, void *isode, const char *name)
{
	struct isofs_directory_record *ide = isode;
	struct uniso_dirent *dir;

	dir = calloc(1, sizeof(*dir) + strlen(name) + 1);
	if (dir == NULL)
		return -ENOMEM;

	dir->size = ide->size.endianess;
	dir->flags = ide->flags;
	strcpy(dir->name, name);

573 574 575
	if (!(ide->flags & ISOFS_DR_FLAG_DIRECTORY))
		ctx->disk_needed += (dir->size + ctx->block_size - 1) / ctx->block_size;

Natanael Copa's avatar
Natanael Copa committed
576
	return queue_reader(ctx, &dir->reader,
577 578 579
	                    ide->extent.endianess * ISOFS_BLOCK_SIZE,
	                    (ide->flags & ISOFS_DR_FLAG_DIRECTORY) ?
	                    uniso_read_directory : uniso_read_file);
Natanael Copa's avatar
Natanael Copa committed
580 581 582 583 584
}

static int uniso_read_volume_descriptor(struct uniso_context *ctx,
					struct uniso_reader *rd)
{
Natanael Copa's avatar
Natanael Copa committed
585
	unsigned char buf[ISOFS_BLOCK_SIZE];
Natanael Copa's avatar
Natanael Copa committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
	struct isofs_volume_descriptor *vd = (void*) buf;
	struct isofs_pri_sup_descriptor *pd = (void*) buf;
	char root_dir[sizeof(pd->root_directory_record)];
	int r;

	do {
		r = do_read(ctx, buf, ISOFS_BLOCK_SIZE);
		if (r < 0)
			return r;

		if (memcmp(vd->id, ISOFS_STANDARD_ID, sizeof(vd->id)) != 0)
			return -EMEDIUMTYPE;

		switch (vd->type) {
		case ISOFS_VD_PRIMARY:
			if (ctx->joliet_level == 0)
				memcpy(root_dir, pd->root_directory_record, sizeof(root_dir));
			break;
		case ISOFS_VD_SUPPLEMENTARY:
			if (pd->escape[0] == 0x25 && pd->escape[1] == 0x2f) {
				switch (pd->escape[2]) {
				case 0x45:
					ctx->joliet_level++;
				case 0x43:
					ctx->joliet_level++;
				case 0x40:
					ctx->joliet_level++;
					memcpy(root_dir, pd->root_directory_record, sizeof(root_dir));
				}
			}
			break;
		}
	} while (vd->type != ISOFS_VD_END);

	if (ctx->joliet_level && ctx->loglevel > 1)
		fprintf(stderr, "INFO: Microsoft Joliet Level %d\n",
			ctx->joliet_level);

	return queue_dirent(ctx, root_dir, ".");
}

627 628 629 630 631 632 633 634 635 636 637 638
static void get_disk_info(struct uniso_context *ctx)
{
#if defined(__linux__)
	struct statfs sfs;

	if (statfs(".", &sfs) == 0) {
		if (sfs.f_bsize) ctx->block_size = sfs.f_bsize;
		ctx->disk_free = (off_t)sfs.f_bavail;
	}
#endif
}

Natanael Copa's avatar
Natanael Copa committed
639 640 641 642
int uniso(int fd)
{
	struct uniso_context context, *ctx = &context;
	struct uniso_reader *rd;
643
	ssize_t r, skipped;
Natanael Copa's avatar
Natanael Copa committed
644 645 646 647 648 649 650

	memset(ctx, 0, sizeof(*ctx));
	list_init(&ctx->parser_head);
	ctx->stream_fd = fd;
	ctx->null_fd = open("/dev/null", O_RDWR);
	ctx->tmpbuf = malloc(ISOFS_TMPBUF_SIZE);
	ctx->loglevel = 1;
651 652
	ctx->block_size = 4*1024;
	get_disk_info(ctx);
Natanael Copa's avatar
Natanael Copa committed
653 654

	queue_reader(ctx, &ctx->volume_desc_reader,
655 656
	             16 * ISOFS_BLOCK_SIZE,
	             uniso_read_volume_descriptor);
Natanael Copa's avatar
Natanael Copa committed
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671

	while (list_hashed(&ctx->parser_head)) {
		rd = list_entry(ctx->parser_head.next, struct uniso_reader, parser_list);
		list_del(&rd->parser_list);

		r = rd->offset - ctx->pos;
		if (r > 0) {
			if ((r = do_skip(ctx, r)) < 0)
				return r;
		}
		r = rd->handler(ctx, rd);
		if (r != 0)
			return r;
	}

672 673 674 675 676 677
	if (ctx->skip_method) {
		skipped = 0;
		do {
			r = do_skip(ctx, ISOFS_TMPBUF_SIZE);
			if (r > 0) skipped += r;
		} while (r == ISOFS_TMPBUF_SIZE);
678
		if (ctx->loglevel > 1) fprintf(stderr, "Skipped %zd bytes at the end\n", skipped);
679 680
	}

Natanael Copa's avatar
Natanael Copa committed
681 682
	free(ctx->tmpbuf);
	close(ctx->null_fd);
Natanael Copa's avatar
Natanael Copa committed
683
	return 0;
Natanael Copa's avatar
Natanael Copa committed
684 685 686 687 688 689 690
}

int main(void)
{
	uniso(STDIN_FILENO);
	return 0;
}