modified: AppImageAssistant.AppDir/testappimage
[appimagekit/gsi.git] / isofs.c
blobfd0fcaf52d98a5b9aed5f517654259cbb6794ae2
1 /***************************************************************************
2 * Copyright (c) 2005, 2006 by Dmitry Morozhnikov <dmiceman@mail.ru > *
3 * Copyright (c) 2005-10 Simon Peter *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 // for struct tm->tm_gmtoff
22 #define _BSD_SOURCE
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <glib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <assert.h>
32 #include <zlib.h>
33 #include <dirent.h>
34 #include <pthread.h>
35 #include <sys/statfs.h>
36 #include <iconv.h>
38 #include "isofs.h"
40 static isofs_context context;
41 static GHashTable *lookup_table;
42 static GHashTable *negative_lookup_table;
43 static char *negative_value = "does not exist";
44 static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
46 static int isofs_check_rr(struct iso_directory_record *root_record);
47 static int isofs_read_raw_block(int block, char *buf);
49 extern char* iocharset;
51 // locally implement g_strv_length, this is missing in glib2 for rhel3/rhel4
52 // -- Chandan Dutta Chowdhury 2007-07-06
53 guint local_g_strv_length (gchar **str_array) {
54 guint i = 0;
55 g_return_val_if_fail (str_array != NULL, 0);
56 while (str_array[i])
57 ++i;
58 return i;
61 int isofs_real_preinit( char* imagefile, int fd) {
63 memset(& context, 0, sizeof(isofs_context));
65 context.imagefile = imagefile;
66 context.fd = fd;
68 // trying to read all volume descriptors
69 struct iso_volume_descriptor *vd =
70 (struct iso_volume_descriptor *) malloc(sizeof(struct iso_volume_descriptor));
71 if(!vd) {
72 // perror("Can`t malloc: ");
73 exit(ENOMEM);
75 int vd_num = 0;
77 // defaults for iso
78 context.block_size = 2048;
79 context.data_size = 2048;
80 context.block_offset = 0;
81 context.file_offset = 0;
83 enum {
84 IDOFF_ISO_2048 = 2048 * 16,
85 IDOFF_MODE1_2352 = 2352 * 16 + 16,
86 IDOFF_MODE2_2352_RAW = 2352 * 16,
87 IDOFF_MODE2_2352 = 2352 * 16 + 24,
88 IDOFF_MODE2_2336 = 2336 * 16 + 16,
89 IDOFF_NRG = 2048 * 16 + 307200,
91 int iso_offsets[] = {IDOFF_ISO_2048, IDOFF_MODE2_2336, IDOFF_MODE2_2352_RAW, IDOFF_NRG};
92 // try to find CD001 identifier
93 int i;
94 for(i = 0; i < 4; i++) {
95 if(lseek(fd, iso_offsets[i], SEEK_SET) == -1) {
96 // perror("can`t lseek() to next possible data start position; is it really supported file?");
97 perror("Unsupported file");
98 exit(EIO);
100 ssize_t size = read(fd, vd, sizeof(struct iso_volume_descriptor));
101 if(size != sizeof(struct iso_volume_descriptor)) {
102 // fprintf(stderr, "only %d bytes read from position %d, %d required; is it really supported file?\n",
103 // size, iso_offsets[i], sizeof(struct iso_volume_descriptor));
104 perror("Unsupported file");
105 exit(EIO);
107 char *vd_id = (char *) vd->id;
108 if(strncmp("CD001", vd_id, 5) == 0) {
109 // found CD001!
110 // fill context with information about block size and block offsets
111 context.id_offset = iso_offsets[i];
112 switch(iso_offsets[i]) {
113 case IDOFF_ISO_2048:
114 // normal iso file
115 // use defaults
116 break;
117 case IDOFF_MODE2_2352_RAW:
118 context.block_size = 2352;
119 break;
120 case IDOFF_MODE2_2336:
121 context.block_size = 2336;
122 context.block_offset = 16;
123 break;
124 case IDOFF_NRG:
125 context.file_offset = 307200;
126 break;
127 default:
128 break;
130 break;
131 } else if(strncmp("CD001", vd_id + 16, 5) == 0) {
132 context.id_offset = iso_offsets[i] + 16;
133 context.block_size = 2352;
134 context.block_offset = 16;
135 break;
136 } else if(strncmp("CD001", vd_id + 24, 5) == 0) {
137 context.id_offset = iso_offsets[i] + 24;
138 context.block_size = 2352;
139 context.block_offset = 24;
140 break;
144 /* printf("CD001 found at %d, bs %d, boff %d, ds %d\n",
145 context.id_offset, context.block_size, context.block_offset, context.data_size);*/
146 while(1) {
147 if(lseek(fd, context.block_size * (16 + vd_num) +
148 context.block_offset + context.file_offset, SEEK_SET) == -1) {
149 // perror("can`t lseek() to next volume descriptor");
150 exit(EIO);
152 ssize_t size = read(fd, vd, sizeof(struct iso_volume_descriptor));
153 if(size != sizeof(struct iso_volume_descriptor)) {
154 // fprintf(stderr, "only %d bytes read from volume descriptor %d, %d required\n",
155 // size, vd_num, sizeof(struct iso_volume_descriptor));
156 exit(EIO);
159 int vd_type = isonum_711((unsigned char *)vd->type);
160 // printf("init: found volume descriptor type %d, vd_num %d\n", vd_type, vd_num);
162 if(strncmp("CD001", vd->id, 5) != 0) {
163 if(vd_num > 16) {
164 // no more trying
165 // fprintf(stderr, "init: wrong standard identifier in volume descriptor %d, exiting..\n", vd_num);
166 exit(EIO);
167 } else {
168 // try to continue
169 // fprintf(stderr, "init: wrong standard identifier in volume descriptor %d, skipping..\n", vd_num);
171 } else {
172 switch(vd_type) {
173 case ISO_VD_PRIMARY:
174 // check if this is only primary descriptor found
175 if(context.pd.type[0]) {
176 // fprintf(stderr, "init: primary volume descriptor already found, skipping..\n");
177 } else {
178 memcpy(& context.pd, vd, sizeof(struct iso_volume_descriptor));
179 context.root = (struct iso_directory_record *)& context.pd.root_directory_record;
180 context.data_size = isonum_723(context.pd.logical_block_size);
182 if(!context.block_size) {
183 // fprintf(stderr, "init: wrong block data size %d, using default 2048\n", context.data_size);
184 context.data_size = 2048;
187 if(context.block_size != 2048) {
188 // report unusual data block size
189 // later
190 // printf("Data block size: %d\n", context.block_size);
193 if(isofs_check_rr(context.root)) {
194 context.pd_have_rr = 1;
197 break;
199 case ISO_VD_SUPPLEMENTARY:
201 struct iso_supplementary_descriptor *sd = (struct iso_supplementary_descriptor *) vd;
203 if(!context.pd.type[0]) {
204 // fprintf(stderr, "init: supplementary volume descriptor found, but no primary descriptor!\n");
205 exit(EIO);
206 } else {
207 int joliet_level = 0;
209 if(sd->escape[0] == 0x25 && sd->escape[1] == 0x2f) {
210 switch(sd->escape[2]) {
211 case 0x40:
212 joliet_level = 1;
213 break;
214 case 0x43:
215 joliet_level = 2;
216 break;
217 case 0x45:
218 joliet_level = 3;
219 break;
223 int have_rr =
224 isofs_check_rr((struct iso_directory_record *) sd->root_directory_record);
226 // switch to SVD only if it contain RRIP or if PVD have no RRIP
227 // in other words, prefer VD with RRIP
228 if((joliet_level && have_rr) ||
229 (have_rr && !context.pd_have_rr) ||
230 (joliet_level && !context.pd_have_rr)) {
232 context.joliet_level = joliet_level;
233 memcpy(& context.sd, vd, sizeof(struct iso_volume_descriptor));
234 context.supplementary = 1;
236 context.root = (struct iso_directory_record *) context.sd.root_directory_record;
238 // printf("init: switching to supplementary descriptor %d, joliet_level %d, have_rr %d\n",
239 // vd_num, context.joliet_level, have_rr);
240 } else {
241 context.joliet_level = 0;
242 // printf("init: found supplementary descriptor %d, flags %d\n",
243 // vd_num, isonum_711(sd->flags));
247 break;
249 case 0:
250 // boot record, not intresting..
251 break;
253 case ISO_VD_END:
254 free(vd);
255 goto out;
256 break;
258 default:
259 // fprintf(stderr, "init: unsupported volume descriptor type %d, vd_num %d\n",
260 // vd_type, vd_num);
261 break;
265 vd_num += 1;
267 out:
269 if(!context.pd.type[0]) {
270 // fprintf(stderr, "init: primary volume descriptor not found! exiting..\n");
271 exit(EIO);
274 context.susp = 0;
275 context.susp_skip = 0;
277 lookup_table = g_hash_table_new(g_str_hash, g_str_equal);
278 negative_lookup_table = g_hash_table_new(g_str_hash, g_str_equal);
280 isofs_inode *inode = (isofs_inode *) malloc(sizeof(isofs_inode));
281 if(!inode) {
282 // perror("Can`t malloc: ");
283 exit(ENOMEM);
285 memset(inode, 0, sizeof(isofs_inode));
286 inode->record = context.root;
287 context.last_ino++; // set to 1
288 inode->st_ino = context.last_ino;
289 context.last_ino++;
291 g_hash_table_insert(lookup_table, "/", inode);
293 return 0;
296 static char* dstr(char* str, const char* src, int len) {
297 int i;
298 strncpy(str, src, len);
299 str[len] = '\0';
300 for(i = len - 1; i >= 0; --i) {
301 if(str[i] == '\0' || str[i] == ' ' || str[i] == '\t' || str[i] == '\r' || str[i] == '\n') {
302 str[i] = '\0';
303 } else {
304 return str;
307 return str;
310 void* isofs_real_init() {
311 /* if(context.file_offset == 307200) {
312 printf("NRG image found\n");
313 } else if(context.block_size == 2048) {
314 printf("ISO9660 image found\n");
315 } else if(context.block_size == 2352 && context.block_offset == 0) {
316 printf("MODE2 RAW BIN image found\n");
317 } else if(context.block_size == 2352 && context.block_offset == 16) {
318 printf("MODE1 BIN image found (or CCD MODE1 image, or MDF image)\n");
319 } else if(context.block_size == 2352 && context.block_offset == 24) {
320 printf("MODE2 BIN image found (or CCD MODE2 image)\n");
321 } else if(context.block_size == 2336 && context.block_offset == 16) {
322 printf("MODE2/2336 BIN image found\n");
323 } else {
324 printf("UNKNOWN image found; probably will not work\n");
327 if(context.block_size != 2048) {
328 // report unusual data block size
329 printf("Data block size: %d\n", context.block_size);
332 char buf[129];
334 printf("System Identifier : %s\n", dstr(buf, context.pd.system_id, 32));
335 printf("Volume Identifier : %.32s\n", dstr(buf, context.pd.volume_id, 32));
336 printf("Volume Set Identifier : %.128s\n", dstr(buf, context.pd.volume_set_id, 128));
337 printf("Publisher Identifier : %.128s\n", dstr(buf, context.pd.publisher_id, 128));
338 printf("Data Preparer Identifier : %.128s\n", dstr(buf, context.pd.preparer_id, 128));
339 printf("Application Identifier : %.128s\n", dstr(buf, context.pd.application_id, 128));
340 printf("Copyright File Identifier : %.37s\n", dstr(buf, context.pd.copyright_file_id, 37));
341 printf("Abstract File Identifier : %.37s\n", dstr(buf, context.pd.abstract_file_id, 37));
342 printf("Bibliographic File Identifier : %.37s\n", dstr(buf, context.pd.bibliographic_file_id, 37));
343 printf("Volume Creation Date and Time : %.17s\n", dstr(buf, context.pd.creation_date, 17));
344 printf("Volume Modification Date and Time : %.17s\n", dstr(buf, context.pd.modification_date, 17));
345 printf("Volume Expiration Date and Time : %.17s\n", dstr(buf, context.pd.expiration_date, 17));
346 printf("Volume Effective Date and Time : %.17s\n", dstr(buf, context.pd.effective_date, 17));
348 return (void*) &context;
351 static int isofs_check_rr(struct iso_directory_record *root_record) {
352 int extent = isonum_733(root_record->extent);
353 char *buf = (char *) malloc(context.data_size); // can we use "standard" 2048 there?
354 if(!buf) {
355 // perror("Can`t malloc: ");
356 return -ENOMEM;
359 int rc = isofs_read_raw_block(extent, buf);
360 if(rc < 0) {
361 free(buf);
362 return 0;
365 struct iso_directory_record *record = (struct iso_directory_record *) buf;
366 size_t record_length = isonum_711((unsigned char*) record->length);
367 size_t name_len = isonum_711(record->name_len);
368 size_t pad_len = ((name_len & 1) ? 0 : 1); // padding byte if name_len is even
369 size_t sa_len = record_length - name_len - sizeof(struct iso_directory_record) - pad_len;
370 if(record_length < sizeof(struct iso_directory_record)) {
371 // fprintf(stderr, "check_rr: directory record length too small: %d\n", record_length);
372 free(buf);
373 return -EIO;
375 if(name_len != 1) {
376 // fprintf(stderr, "check_rr: file name length too big for . record: %d\n", name_len);
377 free(buf);
378 return -EIO;
380 if(sa_len < 0) {
381 // probably something wrong with name_len
382 // fprintf(stderr, "check_rr: wrong name_len in directory entry: %d, record_length %d\n",
383 // name_len, record_length);
384 free(buf);
385 return -EIO;
389 if(sa_len >= 7) {
390 struct rock_ridge *sue = (struct rock_ridge *) (((char *) record) +
391 sizeof(struct iso_directory_record) +
392 name_len + pad_len);
394 int sue_sig = SIG(sue->signature[0], sue->signature[1]);
395 int sue_len = sue->len;
396 int sue_version = sue->version;
398 if(sue_sig == SIG('S', 'P')) {
399 if(sue_len != 7 || sue_version != 1 || sue->u.SP.magic[0] != 0xbe || sue->u.SP.magic[1] != 0xef) {
400 // incorrect SP entry
401 free(buf);
402 return 0;
403 } else {
404 // got it!
405 free(buf);
406 return 1;
408 } else {
409 // not SP record
410 free(buf);
411 return 0;
413 } else {
414 // no space for SP record
415 free(buf);
416 return 0;
419 // should not happen
420 free(buf);
421 return 0;
424 static isofs_inode *isofs_lookup(const char *path) {
425 if(path[0] == '\0') {
426 return NULL;
428 isofs_inode *inode = g_hash_table_lookup(lookup_table, path);
429 if(inode) {
430 return inode;
432 if(g_hash_table_lookup(negative_lookup_table, path)) {
433 return NULL;
435 // printf("start search for %s\n", path);
436 gchar **parts = g_strsplit(path, "/", -1);
437 guint parts_len = local_g_strv_length(parts);
438 int partno = 1;
439 gchar *rpath = g_strdup("/");
440 gchar *rpath1 = "";
441 gchar *part = parts[partno];
442 while(part && partno < parts_len) {
443 rpath1 = g_strconcat(rpath1, "/", part, NULL);
444 // printf("looking for %s in %s...\n", rpath1, rpath);
445 inode = g_hash_table_lookup(lookup_table, rpath1);
446 if(!inode) {
447 // printf("trying to load %s...\n", rpath);
448 int rc = isofs_real_readdir(rpath, NULL, NULL);
449 if(rc) {
450 // fprintf(stderr, "lookup: error %d from readdir: %s\n", rc, strerror(-rc));
451 g_strfreev(parts);
452 g_free(rpath);
453 return NULL;
456 partno++;
457 part = parts[partno];
458 g_free(rpath);
459 rpath = rpath1;
461 g_strfreev(parts);
462 g_free(rpath);
463 inode = g_hash_table_lookup(lookup_table, path);
464 if(!inode) {
465 g_hash_table_insert(negative_lookup_table, g_strdup(path), negative_value);
467 return inode;
470 static int isofs_read_raw_block(int block, char *buf) {
471 off_t off = block * context.block_size + context.block_offset + context.file_offset;
472 if(pthread_mutex_lock(& fd_mutex)) {
473 int err = errno;
474 // perror("isofs_read_raw_block: can`l lock fd_mutex");
475 return -err;
477 if(lseek(context.fd, off, SEEK_SET) == -1) {
478 // perror("isofs_read_raw_block: can`t lseek()");
479 pthread_mutex_unlock(& fd_mutex);
480 return -EIO;
482 size_t len = read(context.fd, buf, context.data_size);
483 if(len != context.data_size) {
484 // fprintf(stderr, "isofs_read_raw_block: can`t read full block, read only %d bytes from offset %d, %d required; errno %d, message %s\n",
485 // len, (int) off, context.data_size, errno, strerror(errno));
486 // fprintf(stderr, "isofs_read_raw_block: huh? reading zeros beyond file end? someone want to save a penny?\n");
487 // memset(buf + len, 0, context.data_size - len);
488 // pthread_mutex_unlock(& fd_mutex);
489 // return -EIO;
491 pthread_mutex_unlock(& fd_mutex);
492 // printf("block %d, offset %d, read %d\n", block, (int) off, len);
493 return len;
496 static time_t isofs_date(char *stamp, int stamp_len) {
497 struct tm tm;
498 memset(& tm, 0, sizeof(struct tm));
500 if(stamp_len == 7) { // ISO9660:9.1.5
501 tm.tm_year = stamp[0];
502 tm.tm_mon = stamp[1] - 1;
503 tm.tm_mday = stamp[2];
504 tm.tm_hour = stamp[3];
505 tm.tm_min = stamp[4];
506 tm.tm_sec = stamp[5];
507 tm.tm_isdst = -1;
508 tm.tm_gmtoff = stamp[6] * 15 * 60;
509 } else if(stamp_len == 17) { // ISO9660:8.4.26.1
510 // fprintf(stderr, "isofs_date: 17 byte date isn`t supported for the moment, sorry\n");
511 return 0;
512 } else {
513 // fprintf(stderr, "isofs_date: unsupported date format, stamp_len %d\n", stamp_len);
514 return 0;
517 if(tm.tm_gmtoff) {
518 // fprintf(stderr, "direntry2stat: non-zero timezone offset: %d\n", tm.tm_gmtoff);
520 time_t time = mktime(& tm);
522 return time;
525 static int isofs_direntry2stat(struct stat *st, isofs_inode *inode) {
526 struct iso_directory_record *record = inode->record;
528 // fill st_ino from block number where file start
529 // since there is no files begin in the same block
530 // st->st_ino = isonum_733(record->extent);
531 // but some iso images save space by sharing content between several files
532 // so it is better to save it unique
533 st->st_ino = inode->st_ino;
535 if(inode->ZF) { // compressed file, report real (uncompressed) size
536 st->st_size = inode->real_size;
537 } else { // no zisofs compression
538 st->st_size = isonum_733(record->size);
541 st->st_blocks = st->st_size / context.data_size; // should not be to meaningful even for zisofs compression
542 st->st_blksize = context.data_size;
543 st->st_nlink = 1; // always, even if rrip PX entry found
545 if(inode->PX) { // rrip PX entry found and in effect
546 st->st_mode = inode->st.st_mode;
547 st->st_uid = inode->st.st_uid;
548 st->st_gid = inode->st.st_gid;
549 } else {
550 /// TODO use hidden flag?
551 if(ISO_FLAGS_DIR(record->flags)) {
552 st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH |
553 S_IXUSR | S_IXGRP | S_IXOTH; // dir, readabale and browsable by everyone
554 } else {
555 st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; // regular, read by everyone
559 if(inode->TF) { // rrip TF entry found and in effect
560 st->st_atime = inode->st.st_atime;
561 st->st_ctime = inode->st.st_ctime;
562 st->st_mtime = inode->st.st_mtime;
563 } else {
564 if(!inode->ctime) {
565 inode->ctime = isofs_date(record->date, 7);
568 st->st_atime = inode->ctime;
569 st->st_ctime = inode->ctime;
570 st->st_mtime = inode->ctime;
573 return 0;
576 static char *isofs_fix_entry(char *entry, size_t len) {
577 if(!context.joliet_level) { // iso9660 names
578 char *sep2 = index(entry, ';'); // find SEPARATOR2
579 if(sep2) { // remove remaining part
580 *sep2 = '\0';
582 char *sep1 = rindex(entry, '.'); // find SEPARATOR1
583 if(sep1 && sep1[1] == '\0') { // check if SEPARATOR1 is a last symbol in string
584 *sep1 = '\0'; // remove it
587 // this part is borrowed from linux kernel code
588 // convert remaining ';' and '/' characters to dots
589 // and lowercase characters in range A-Z
590 char *p = entry;
591 while(*p) {
592 if(*p == ';' || *p == '/') {
593 *p = '.';
594 } else if(*p >= 'A' && *p <= 'Z') {
595 *p |= 0x20;
598 p++;
601 return entry;
602 } else {
603 // initialize iconv descriptor
604 iconv_t cd = iconv_open(iocharset, "UCS-2BE");
605 if(cd < 0) {
606 // perror("iconv");
607 return NULL;
610 char *inbuf = entry;
611 size_t inbytesleft = len;
613 char *outbuf = (char *) malloc(NAME_MAX); // this should be sufficient for our purposes
614 if(!outbuf) {
615 // perror("Can`t malloc: ");
616 return NULL;
618 char *outentry = outbuf;
619 size_t outbytesleft = NAME_MAX;
621 int rc = iconv(cd, & inbuf, & inbytesleft, & outbuf, & outbytesleft);
622 size_t outlen = NAME_MAX - outbytesleft;
623 outentry[outlen] = '\0';
624 if(rc == -1) {
625 // incorrect multibyte char or other iconv error -- return as much as possible anyway
626 // fprintf(stderr, "iconv on '%s': %s\n", outentry, strerror(errno));
627 if(outlen == 0) {
628 iconv_close(cd);
629 free(entry);
630 free(outentry);
631 return NULL;
633 // try to recover
636 // printf("outlen %d, outbytesleft %d, rc %d, outbuf %s\n", outlen, outbytesleft, rc, outentry);
638 // borrowed from linux kernel isofs joliet code
639 if(outlen > 2 && outentry[outlen - 2] == ';' && outentry[outlen - 1] == '1') {
640 outentry[outlen - 2] = '\0';
642 if(outlen >= 2 && outentry[outlen - 1] == '.') {
643 outentry[outlen - 1] = '\0';
646 free(entry);
647 iconv_close(cd);
649 return outentry;
653 static void isofs_free_inode(isofs_inode *inode) {
654 if(inode->SL && inode->sl) {
655 free(inode->sl);
657 if(inode->NM && inode->nm) {
658 free(inode->nm);
660 if(inode->zf_blockptr) {
661 free(inode->zf_blockptr);
663 if(inode->record) {
664 free(inode->record);
666 free(inode);
669 // borrowed from zisofs-tools
670 static const unsigned char zisofs_magic[8] = {
671 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
674 static int isofs_parse_zisofs_header(isofs_inode *inode) {
675 char *buf = (char *) malloc(context.block_size);
676 if(!buf) {
677 // perror("Can`t malloc: ");
678 return -ENOMEM;
681 int block_num = isonum_733(inode->record->extent);
682 int len = isofs_read_raw_block(block_num, buf);
683 if(len < 0) {
684 free(buf);
685 return len;
687 if(len < inode->zf_header_size) {
688 // fprintf(stderr, "isofs_parse_zisofs_header: too small block len %d\n", len);
689 free(buf);
690 return -EIO;
693 zf_file_header *zf_header = (zf_file_header *) buf;
695 if(memcmp(zf_header->magic, zisofs_magic, sizeof(zisofs_magic))) {
696 // invalid compressed file header
697 free(buf);
698 return 1;
701 size_t block_size = 1 << inode->zf_block_shift;
703 inode->zf_nblocks = ((inode->real_size + inode->zf_header_size - 1) / block_size) + 1;
705 size_t block_table_size = (inode->zf_nblocks + 1) * 4;
706 if(!inode->zf_blockptr) {
707 inode->zf_blockptr = (int *) malloc(block_table_size);
708 if(!inode->zf_blockptr) {
709 // perror("Can`t malloc: ");
710 return -ENOMEM;
714 // copy offset table into memory buffer, maintaining iso9660 block boundaries
716 int block_table_left = block_table_size;
717 int block_table_total = 0;
718 int block_table_shift = inode->zf_header_size;
720 while(block_table_left) {
721 size_t block_table_chunk =
722 (block_table_left < context.data_size - block_table_shift ?
723 block_table_left : context.data_size - block_table_shift);
725 /* printf("isofs_parse_sa: block_table_size: %d, block_table_left: %d, block_table_total %d, block_table_shift %d, block_table_chunk %d\n",
726 block_table_size, block_table_left, block_table_total, block_table_shift, block_table_chunk);*/
728 memcpy(((char *) inode->zf_blockptr) + block_table_total, buf + block_table_shift, block_table_chunk);
730 block_table_left -= block_table_chunk;
731 block_table_total += block_table_chunk;
732 block_table_shift = 0;
734 // read next block
735 block_num += 1;
736 len = isofs_read_raw_block(block_num, buf);
737 if(len < 0) {
738 free(buf);
739 return len;
742 /* printf("isofs_parse_sa: block_num: %d, len: %d\n",
743 block_num, len);*/
746 /* printf("isofs_parse_zisofs_header: real size %d, header size %d, nblocks %d, block size %d\n",
747 inode->real_size, inode->zf_header_size,
748 inode->zf_nblocks, block_size);
749 int i;
750 for(i = 0; i <= inode->zf_nblocks; i++) {
751 printf("zf block table entry %d have value %d\n", i, inode->zf_blockptr[i]);
752 };*/
754 // all information for compressed file we have already in ZF entry
755 // so just signal what this is really compressed file
756 free(buf);
757 return 0;
760 static int isofs_parse_sa(isofs_inode *inode, char *sa, size_t sa_len) {
761 int cont_block = 0;
762 int cont_offset = 0;
763 int cont_size = 0;
765 int remaining = sa_len;
766 while(remaining > 4) { // susp 4.
767 // printf("parse_sa: sa offset %d, remaining %d\n", sa_len - remaining, remaining);
768 struct rock_ridge *sue = (struct rock_ridge *) (sa + sa_len - remaining);
769 int sue_sig = SIG(sue->signature[0], sue->signature[1]);
770 int sue_len = sue->len;
771 int sue_version = sue->version;
772 /* printf("parse_sa: signature %c%c, sue_len %d, sue_version %d\n",
773 sue->signature[0], sue->signature[1], sue_len, sue_version);*/
775 int known_sue = 1;
777 switch(sue_sig) {
778 case SIG('S', 'P'): // susp 5.3
779 if(sue_len != 7 || sue_version != 1 || sue->u.SP.magic[0] != 0xbe || sue->u.SP.magic[1] != 0xef) {
780 // incorrect SP entry
781 /* fprintf(stderr,
782 "parse_sa: incorrect SP entry: sue_len %d, sue_version %d, magic %c%c\n",
783 sue_len, sue_version, sue->u.SP.magic[0], sue->u.SP.magic[1]);*/
784 context.susp = 0;
785 return 0;
786 } else {
787 context.susp = 1;
788 context.susp_skip = sue->u.SP.skip;
790 // printf("parse_sa: SP entry, skip %d\n", sue->u.SP.skip);
791 break;
792 case SIG('C', 'E'): // susp 5.1
793 if(sue_len != 28 || sue_version != 1) {
794 // incorrect entry, skip
795 /* fprintf(stderr,
796 "parse_sa: incorrect CE entry: sue_len %d, sue_version %d\n",
797 sue_len, sue_version);*/
798 } else if(cont_block != 0) { // CE entry already found
799 /* fprintf(stderr,
800 "parse_sa: duplicate CE entry, skipping, sue_len %d, sue_version %d\n",
801 sue_len, sue_version);*/
802 } else {
803 cont_block = isonum_733(sue->u.CE.extent);
804 cont_offset = isonum_733(sue->u.CE.offset);
805 cont_size = isonum_733(sue->u.CE.size);
807 if(cont_block < 16) {
808 // continuation area can`t be there
809 /* fprintf(stderr,
810 "parse_sa: wrong continuation area extent: %d, cont_offset %d, cont_size %d\n",
811 cont_block, cont_offset, cont_size);*/
812 cont_block = 0; // do not process it
813 } else if(cont_offset + cont_size > context.data_size) {
814 // something wrong with continuation area offset and/or size
815 /* fprintf(stderr,
816 "parse_sa: wrong continuation area: extent %d, cont_offset %d, cont_size %d\n",
817 cont_block, cont_offset, cont_size);'/
818 cont_block = 0; // do not process it
819 } else {
820 /* printf("parse_sa: CE entry, extent %d, offset %d, size %d\n",
821 cont_block, cont_offset, cont_size);*/
824 break;
825 case SIG('E', 'R'): // susp 5.5
826 if(sue_version != 1) {
827 // incorrect entry, skip
828 /* fprintf(stderr,
829 "parse_sa: incorrect ER entry: sue_len %d, sue_version %d\n",
830 sue_len, sue_version);*/
831 } else {
832 int len_id = sue->u.ER.len_id;
833 int len_des = sue->u.ER.len_des;
834 int len_src = sue->u.ER.len_src;
835 int ext_ver = sue->u.ER.ext_ver;
836 if(len_id + len_des + len_src + 8 > sue_len) {
837 /* fprintf(stderr,
838 "parse_sa: incorrect ER entry: sue_len %d, sue_version %d, len_id %d, len_des %d, len_src %d, ext_ver %d\n",
839 sue_len, sue_version, len_id, len_des, len_src, ext_ver);*/
840 } else {
841 char id[256];
842 char des[256];
843 char src[256];
845 strncpy(id, sue->u.ER.data, len_id);
846 id[len_id] = '\0';
847 strncpy(des, sue->u.ER.data + len_id, len_des);
848 des[len_des] = '\0';
849 strncpy(src, sue->u.ER.data + len_id + len_des, len_src);
850 src[len_src] = '\0';
852 /* printf("parse_sa: ER entry:\n\t id: %s\n\tdes: %s\n\tsrc: %s\n\tver: %d\n",
853 id, des, src, ext_ver);*/
856 break;
857 case SIG('R', 'R'):
859 // unused
860 isonum_711((unsigned char *) sue->u.RR.flags);
861 /* printf("parse_sa: RR entry, sue_version %d, sue_len %d, flags %d\n",
862 sue_version, sue_len, rr_flags);*/
864 break;
865 case SIG('P', 'X'): // rrip 4.1.1
866 // according to rrip 4.1.1, length of PX record should be exactly 44
867 // but linux kernel and mkisofs seems to be happy with length 36,
868 // where 'serial number' are not presented
869 // (or i`m looking at outdated draft.. :-)
870 if((sue_len != 44 && sue_len != 36) || sue_version != 1) {
871 // incorrect entry, skip
872 /* fprintf(stderr,
873 "parse_sa: incorrect PX entry: sue_len %d, sue_version %d\n",
874 sue_len, sue_version);*/
875 } else {
876 mode_t mode = isonum_733(sue->u.PX.mode);
877 nlink_t nlink = isonum_733(sue->u.PX.n_links);
878 uid_t uid = isonum_733(sue->u.PX.uid);
879 gid_t gid = isonum_733(sue->u.PX.gid);
880 /* printf("parse_sa: PX entry, sue_version %d, sue_len %d, mode %d, nlinks %d, uid %d, gid %d\n",
881 sue_version, sue_len, mode, nlink, uid, gid);*/
882 inode->st.st_mode = mode;
883 inode->st.st_nlink = nlink;
884 inode->st.st_uid = uid;
885 inode->st.st_gid = gid;
886 inode->PX = 1;
887 /// TODO check if entry length == 44 and set st_ino field from 'file serial number'?
889 break;
890 case SIG('S', 'L'): // rrip 4.1.3
891 if(sue_version != 1) {
892 // incorrect entry, skip
893 /* fprintf(stderr,
894 "parse_sa: incorrect SL entry: sue_len %d, sue_version %d\n",
895 sue_len, sue_version);*/
896 } else if(inode->SL) {
897 /* fprintf(stderr,
898 "parse_sa: SL entry already in effect, sue_len %d, sue_version %d\n",
899 sue_len, sue_version);*/
900 } else {
901 int sl_flags = sue->u.SL.flags;
902 int max_components = (sue_len - 5) / sizeof(struct SL_component);
904 if(max_components < 1) {
905 /* fprintf(stderr,
906 "parse_sa: SL entry found, but no components: sue_len %d, sue_version %d\n",
907 sue_len, sue_version);*/
908 } else {
909 int c;
910 int c_offset = 0;
911 int c_errors = 0;
912 for(c = 0; c < max_components; c++) {
913 struct SL_component *comp =
914 (struct SL_component *)
915 (((char *) & sue->u.SL.link) + c_offset);
917 int sl_c_flags = comp->flags;
918 int sl_c_len = comp->len;
920 if(c_offset + 5 >= sue_len) {
921 // according to rrip, we must stop if CONTINUE flag isn`t set
922 // according to linux kernel isofs code and images produced witj mkisofs
923 // we need to continue while there is space in SL entry for next component
924 break;
926 // strict rrip code:
927 // fprintf(stderr,
928 // "parse_sa: invalid SL component: sue_len %d, sue_version %d, sl_flags %d, sl_c_flags %d, sl_c_len %d\n",
929 // sue_len, sue_version, sl_flags, sl_c_flags, sl_c_len);
930 // c_errors++;
931 // break;
933 /// TODO find _working_ rrip specification
936 int c_len = 0;
937 char c_separator = 0;
938 if(!inode->sl_len) { // nothing found previoustly
939 } else if(inode->sl_len == 1 && inode->sl[0] == '/') { // previous SL component was ROOT
940 // no need for separator after ROOT component
941 } else {
942 c_len += 1; // place for '/' separator
943 c_separator = '/';
945 if(sl_c_flags & (1 << 1)) { // CURRENT component
946 c_len += 1; // place for '.' character
947 } else if(sl_c_flags & (1 << 2)) { // PARENT component
948 c_len += 2; // place for ".." characters
949 } else {
950 c_len += sl_c_len;
953 if(c_len + inode->sl_len + 1 > PATH_MAX) {
954 /* fprintf(stderr,
955 "parse_sa: too long symlink found: sue_len %d, sue_version %d, sl_flags %d, sl_c_flags %d, sl_c_len %d, c_len %d\n",
956 sue_len, sue_version, sl_flags, sl_c_flags, sl_c_len, c_len);*/
957 c_errors++;
958 break;
961 if(!inode->sl) {
962 inode->sl = (char *) malloc(PATH_MAX);
963 if(!inode->sl) {
964 // perror("Can`t malloc: ");
965 return -ENOMEM;
969 if(c_separator) {
970 inode->sl[inode->sl_len] = c_separator;
971 inode->sl_len += 1;
974 if(sl_c_flags & (1 << 1)) { // CURRENT component
975 inode->sl[inode->sl_len] = '.';
976 inode->sl_len += 1;
977 inode->sl[inode->sl_len] = '\0';
978 /* printf("parse_sa: SL CURRENT component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n",
979 sl_c_flags, sl_c_len, inode->sl_len, inode->sl);*/
980 } else if(sl_c_flags & (1 << 2)) { // PARENT component
981 inode->sl[inode->sl_len] = '.';
982 inode->sl_len += 1;
983 inode->sl[inode->sl_len] = '.';
984 inode->sl_len += 1;
985 inode->sl[inode->sl_len] = '\0';
986 /* printf("parse_sa: SL PARENT component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n",
987 sl_c_len, inode->sl_len, inode->sl);*/
988 } else if(sl_c_flags & (1 << 3)) { // ROOT component (?! does not documented at all)
989 inode->sl[inode->sl_len] = '/';
990 inode->sl_len += 1;
991 inode->sl[inode->sl_len] = '\0';
992 /* printf("parse_sa: SL ROOT component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n",
993 sl_c_len, inode->sl_len, inode->sl);*/
994 } else {
995 strncpy(inode->sl + inode->sl_len, comp->text, sl_c_len);
996 inode->sl_len += sl_c_len;
997 inode->sl[inode->sl_len] = '\0';
998 /* printf("parse_sa: SL component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n",
999 sl_c_flags, sl_c_len, inode->sl_len, inode->sl);*/
1002 // according to rrip, we must stop if CONTINUE flag isn`t set
1003 // according to linux kernel isofs code and images produced witj mkisofs
1004 // we need to continue while there is space in SL entry for next component
1005 c_offset += (2 + sl_c_len);
1007 // strict rrip code:
1008 // if(sl_c_flags & 1) { // CONTINUE
1009 // c_offset += 2 + sl_c_len;
1010 // } else {
1011 // printf("parse_sa: SL final component, sl_c_len %d, sl_c_flags %d, sl_len %d, sl %s\n",
1012 // sl_c_len, sl_c_flags, inode->sl_len, inode->sl);
1013 // break;
1014 // };
1017 if(c_errors) {
1018 // printf("parse_sa: errors found while processing SL components, cleaning\n");
1019 if(inode->sl) {
1020 free(inode->sl);
1022 inode->sl_len = 0;
1023 inode->SL = 0;
1024 } else if(!(sl_flags & 1) && inode->sl) {
1025 /* printf("parse_sa: SL entry (final), sue_len %d, sue_version %d, sl_len %d, sl %s\n",
1026 sue_len, sue_version, inode->sl_len, inode->sl);*/
1027 inode->SL = 1;
1028 } else if(!(sl_flags & 1) && !inode->sl) {
1029 /* fprintf(stderr, "parse_sa: final SL entry found, but no SL components, cleaning\n");*/
1030 if(inode->sl) {
1031 free(inode->sl);
1033 inode->sl_len = 0;
1034 inode->SL = 0;
1035 } else if(inode->sl) {
1036 /* printf("parse_sa: SL entry, sue_len %d, sue_version %d, sl_len %d, sl %s\n",
1037 sue_len, sue_version, inode->sl_len, inode->sl);*/
1038 } else {
1039 /* fprintf(stderr, "parse_sa: empty SL entry?\n");*/
1043 break;
1044 case SIG('N', 'M'): // rrip 4.1.4
1045 if(sue_version != 1) {
1046 // incorrect entry, skip
1047 /* fprintf(stderr,
1048 "parse_sa: incorrect NM entry: sue_len %d, sue_version %d\n",
1049 sue_len, sue_version);*/
1050 } else {
1051 int nm_flags = sue->u.NM.flags;
1052 if(nm_flags & (1 << 1)) { // CURRENT bit
1053 /* printf("parse_sa: NM CURRENT entry, sue_version %d, sue_len %d, flags %d\n",
1054 sue_version, sue_len, nm_flags);*/
1055 } else if(nm_flags & (1 << 2)) { // PARENT bit
1056 /* printf("parse_sa: NM PARENT entry, sue_version %d, sue_len %d, flags %d\n",
1057 sue_version, sue_len, nm_flags);*/
1058 } else {
1059 if(sue_len - 5 + inode->nm_len > NAME_MAX - 1) {
1060 fprintf(stderr,
1061 /* "parse_sa: too long NM entry: %d\n",
1062 sue_len - 5 + inode->nm_len);
1063 } else if(inode->NM) {
1064 fprintf(stderr,
1065 "parse_sa: NM entry already in effect, sue_len %d, sue_version %d\n", */
1066 sue_len, sue_version);
1067 } else {
1068 if(!inode->nm) {
1069 inode->nm = (char *) malloc(NAME_MAX);
1070 if(!inode->nm) {
1071 // perror("Can`t malloc: ");
1072 return -ENOMEM;
1076 strncpy(inode->nm + inode->nm_len, sue->u.NM.name, sue_len - 5);
1077 inode->nm_len += sue_len - 5;
1078 inode->nm[inode->nm_len] = '\0';
1080 if(!nm_flags & 1) { // CONTINUE bit
1081 inode->NM = 1;
1082 /* printf("parse_sa: NM entry (final), flags %d, len %d, name %s\n",
1083 nm_flags, sue_len - 5, inode->nm);*/
1084 } else {
1085 /* printf("parse_sa: NM entry (part), flags %d, len %d, name %s\n",
1086 nm_flags, sue_len - 5, inode->nm);*/
1091 break;
1092 case SIG('C', 'L'): // rrip 4.1.5.1
1093 if(sue_version != 1 || sue_len != 12) {
1094 // incorrect entry, skip
1095 /* fprintf(stderr,
1096 "parse_sa: incorrect CL entry: sue_len %d, sue_version %d\n",
1097 sue_len, sue_version);*/
1098 } else {
1099 int cl_block = isonum_733(sue->u.CL.location);
1100 /* printf("parse_sa: CL entry, block %d\n",
1101 cl_block);*/
1102 inode->CL = 1;
1103 inode->cl_block = cl_block;
1105 // read block pointed by CL record and process first directory entry
1106 char *buf = (char *) malloc(context.data_size);
1107 if(!buf) {
1108 // perror("Can`t malloc: ");
1109 return -ENOMEM;
1111 int rc = isofs_read_raw_block(inode->cl_block, buf);
1112 if(rc < 0) {
1113 free(buf);
1114 return rc;
1117 struct iso_directory_record *record = (struct iso_directory_record *) buf;
1118 size_t record_length = isonum_711((unsigned char *) record->length);
1119 size_t name_len = isonum_711(record->name_len);
1120 size_t pad_len = ((name_len & 1) ? 0 : 1); // padding byte if name_len is even
1121 size_t sa_len = record_length - name_len - sizeof(struct iso_directory_record) - pad_len;
1122 /* printf("boff %d, record length %d, name_len %d, pad_len %d, sa_len %d\n",
1123 (int) boff, record_length, name_len, pad_len, sa_len);*/
1124 if(record_length < sizeof(struct iso_directory_record)) {
1125 /* fprintf(stderr, "parse_sa: CL entry: directory record length too small: %d\n", record_length);*/
1126 free(buf);
1127 return -EIO;
1129 if(name_len != 1) {
1130 /* fprintf(stderr, "parse_sa: file name length too big for . record: %d\n", name_len);*/
1131 free(buf);
1132 return -EIO;
1134 if(sa_len < 0) {
1135 // probably something wrong with name_len
1136 /* fprintf(stderr, "parse_sa: CL record: wrong name_len in directory entry: %d, record_length %d\n",
1137 name_len, record_length);*/
1138 free(buf);
1139 return -EIO;
1142 // ignoring anything from original record
1143 struct iso_directory_record *cl_record =
1144 (struct iso_directory_record *) malloc(sizeof(struct iso_directory_record));
1145 if(!cl_record) {
1146 // perror("Can`t malloc: ");
1147 return -ENOMEM;
1149 memcpy(cl_record, record, sizeof(struct iso_directory_record));
1151 // drop existing record
1152 if(inode->record) {
1153 free(inode->record);
1156 // replace record with new one
1157 inode->record = cl_record;
1159 // parse sa entries from relocated directory . record
1160 rc = isofs_parse_sa(inode,
1161 ((char *) record) +
1162 sizeof(struct iso_directory_record) +
1163 name_len + pad_len + context.susp_skip,
1164 sa_len);
1165 if(rc) {
1166 free(buf);
1167 return rc;
1170 free(buf);
1172 break;
1173 case SIG('P', 'L'): // rrip 4.1.5.2
1174 if(sue_version != 1 || sue_len != 12) {
1175 // incorrect entry, skip
1176 /* fprintf(stderr,
1177 "parse_sa: incorrect PL entry: sue_len %d, sue_version %d\n",
1178 sue_len, sue_version);*/
1179 } else {
1180 int pl_block = isonum_733(sue->u.PL.location);
1181 /* printf("parse_sa: PL entry, block %d\n",
1182 pl_block);*/
1183 // probably we don`t need process PL record with FUSE
1184 inode->PL = 1;
1185 inode->pl_block = pl_block;
1187 break;
1188 case SIG('R', 'E'): // rrip 4.1.5.3
1189 if(sue_version != 1 || sue_len != 4) {
1190 // incorrect entry, skip
1191 /* fprintf(stderr,
1192 "parse_sa: incorrect RE entry: sue_len %d, sue_version %d\n",
1193 sue_len, sue_version);*/
1194 } else {
1195 // printf("parse_sa: RE entry\n");
1196 inode->RE = 1;
1198 break;
1199 case SIG('T', 'F'): // rrip 4.1.6
1200 if(sue_version != 1) {
1201 // incorrect entry, skip
1202 /* fprintf(stderr,
1203 "parse_sa: incorrect TF entry: sue_len %d, sue_version %d\n",
1204 sue_len, sue_version);*/
1205 } else {
1206 int tf_flags = sue->u.TF.flags;
1207 int stamp_length;
1208 if(tf_flags & TF_LONG_FORM) {
1209 // iso9660:8.4.26.1 format
1210 stamp_length = 17;
1211 } else {
1212 // iso9660:9.1.5 format
1213 stamp_length = 7;
1216 time_t ctime = 0;
1217 time_t mtime = 0;
1218 time_t atime = 0;
1220 int stamp_no = 0;
1221 // ctime can be stored as CREATION time
1222 if(tf_flags & TF_CREATE) {
1223 ctime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length);
1224 stamp_no++;
1226 if(tf_flags & TF_MODIFY) {
1227 mtime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length);
1228 stamp_no++;
1230 if(tf_flags & TF_ACCESS) {
1231 atime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length);
1232 stamp_no++;
1234 // ctime should be stored in ATTRIBUTES stamp according to rrip 4.1.6
1235 if(tf_flags & TF_ATTRIBUTES) {
1236 ctime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length);
1237 stamp_no++;
1239 // other fields have no meaning for us
1241 /* printf("parse_sa: TF entry, sue_version %d, sue_len %d, ctime %d, mtime %d, atime %d\n",
1242 sue_version, sue_len, ctime, mtime, atime);*/
1243 inode->st.st_ctime = ctime;
1244 inode->st.st_mtime = mtime;
1245 inode->st.st_atime = atime;
1246 inode->TF = 1;
1248 break;
1249 case SIG('S', 'F'): // rrip 4.1.7
1250 if(sue_version != 1 || sue_len != 21) {
1251 // incorrect entry, skip
1252 /* fprintf(stderr,
1253 "parse_sa: incorrect SF entry: sue_len %d, sue_version %d\n",
1254 sue_len, sue_version);*/
1255 } else {
1256 /// TODO does anyone support SF entries? linux isofs code does not..
1257 /* fprintf(stderr,
1258 "parse_sa: SF entries (sparse files) are unsupported in this version, sorry..\n");*/
1260 break;
1261 case SIG('Z', 'F'): // non-standard linux extension (zisofs)
1262 if(sue_version != 1 || sue_len != 16) {
1263 // incorrect entry, skip
1264 /* fprintf(stderr,
1265 "parse_sa: incorrect ZF entry: sue_len %d, sue_version %d\n",
1266 sue_len, sue_version);*/
1267 } else if(SIG(sue->u.ZF.algorithm[0], sue->u.ZF.algorithm[1]) == SIG('p', 'z')) {
1268 inode->zf_algorithm[0] = sue->u.ZF.algorithm[0];
1269 inode->zf_algorithm[1] = sue->u.ZF.algorithm[1];
1270 inode->zf_header_size = ((unsigned char) sue->u.ZF.parms[0]) << 2;
1271 inode->zf_block_shift = (unsigned char) sue->u.ZF.parms[1];
1272 inode->zf_size = (inode->st.st_size ? inode->st.st_size : isonum_733(inode->record->size));
1273 inode->real_size = isonum_733(sue->u.ZF.real_size);
1274 // check if file is really compressed, ignore ZF entry otherwise
1275 int rc = isofs_parse_zisofs_header(inode);
1276 if(rc == 0) {
1277 // printf("parse_sa: ZF entry found, algorithm %02x%02x, header size %d, block shift %d, compressed size %d, real size %d\n",
1278 // inode->zf_algorithm[0], inode->zf_algorithm[1],
1279 // inode->zf_header_size, inode->zf_block_shift,
1280 // inode->zf_size, inode->real_size);
1281 inode->ZF = 1;
1282 } else if(rc > 0) {
1283 /* fprintf(stderr, "parse_sa: ZF entry found, but file is not really compressed\n");*/
1284 } else { // some error occur
1285 return rc;
1287 } else {
1288 /* fprintf(stderr,
1289 "parse_sa: unknown ZF compression algorithm %c%c, sorry..\n",
1290 sue->u.ZF.algorithm[0], sue->u.ZF.algorithm[1]);*/
1292 break;
1293 default:
1294 known_sue = 0;
1295 /* fprintf(stderr, "parse_sa: unknown entry '%c%c', sue_sig %d, sue_version %d, sue_len %d\n",
1296 sue->signature[0], sue->signature[1],
1297 sue_sig, sue_version, sue_len);*/
1298 break;
1301 if(sue_len >= 4 && known_sue) {
1302 remaining -= sue_len;
1303 } else if(known_sue) {
1304 /* fprintf(stderr, "parse_sa: inappropriate susp entry length: %d, signature %c%c\n",
1305 sue_len, sue->signature[0], sue->signature[1]);*/
1306 return -EIO;
1307 } else { // probably there are no more susp entries
1308 return 0;
1312 // process continuation area if found
1313 if(cont_block) {
1314 char *buf = (char *) malloc(context.data_size);
1315 if(!buf) {
1316 // perror("Can`t malloc: ");
1317 return -ENOMEM;
1319 int rc = isofs_read_raw_block(cont_block, buf);
1320 if(rc < 0) {
1321 free(buf);
1322 return rc;
1324 /* printf("parse_sa: deep into CE, extent %d, offset %d, size %d\n",
1325 cont_block, cont_offset, cont_size);*/
1326 rc = isofs_parse_sa(inode, buf + cont_offset, cont_size);
1327 if(rc) {
1328 free(buf);
1329 return rc;
1332 free(buf);
1335 return 0;
1338 int isofs_real_opendir(const char *path) {
1339 isofs_inode *inode = isofs_lookup(path);
1340 if(!inode) {
1341 // fprintf(stderr, "opendir: know nothing about %s\n", path);
1342 return -ENOENT;
1344 if(!ISO_FLAGS_DIR(inode->record->flags)) {
1345 // fprintf(stderr, "opendir: %s not a dir\n", path);
1346 return -ENOTDIR;
1348 return 0;
1351 int isofs_real_readdir(const char *path, void *filler_buf, isofs_dir_fill_t filler) {
1352 if(path[0] == '\0') {
1353 // fprintf(stderr, "readdir: attempt to read empty path name\n");
1354 return -EINVAL;
1356 isofs_inode *current_inode = isofs_lookup(path);
1357 if(!current_inode) {
1358 // fprintf(stderr, "readdir: know nothing about %s\n", path);
1359 return -ENOENT;
1361 struct iso_directory_record *current = current_inode->record;
1362 if(!ISO_FLAGS_DIR(current->flags)) {
1363 // fprintf(stderr, "readdir: %s not a dir\n", path);
1364 return -ENOTDIR;
1367 size_t current_size = isonum_733(current->size);
1369 int block = isonum_733(current->extent);
1370 char *buf = (char *) malloc(context.data_size);
1371 if(!buf) {
1372 // perror("Can`t malloc: ");
1373 return -ENOMEM;
1375 int rc;
1376 // printf("path %s, current_size %d, block %d\n", path, current_size, block);
1378 size_t total_size = 0;
1379 int count = 1;
1380 int block_count = 0;
1381 off_t boff = 0;
1383 while(total_size <= current_size - sizeof(struct iso_directory_record)) {
1384 rc = isofs_read_raw_block(block, buf);
1385 if(rc < 0) {
1386 return rc;
1388 if(rc != context.data_size) {
1389 // can`t be allowed
1390 // fprintf(stderr, "readdir: can`t read whole block, read only %d bytes, block %d\n", rc, block);
1391 free(buf);
1392 return -EIO;
1394 block_count++;
1396 if(boff > 0) {
1397 total_size += (context.data_size - boff);
1398 boff = 0;
1401 while(boff + sizeof(struct iso_directory_record) <= context.data_size &&
1402 total_size <= current_size - sizeof(struct iso_directory_record)) {
1404 struct iso_directory_record *record = (struct iso_directory_record *) (buf + boff);
1405 size_t record_length = isonum_711((unsigned char *) record->length);
1406 size_t name_len = isonum_711(record->name_len);
1407 size_t pad_len = ((name_len & 1) ? 0 : 1); // padding byte if name_len is even
1408 size_t sa_len = record_length - name_len - sizeof(struct iso_directory_record) - pad_len;
1410 // printf("block %d, boff %d, total_size %d, current_size %d, record length %d, name_len %d, pad_len %d, sa_len %d\n",
1411 // block, (int) boff, total_size, current_size, record_length, name_len, pad_len, sa_len);
1412 if(record_length == 0) {
1413 // possible end of directory or end of block
1414 total_size += (context.data_size - boff);
1415 boff = 0;
1416 break;
1418 if(record_length < sizeof(struct iso_directory_record)) {
1419 if(count > 2) { // check if . and .. is already read
1420 // possible end of directory
1421 // at least mkisofs does not set iso_directory_record.size correct
1422 // (this is much possible it was my fault and misunderstanding -- dmiceman)
1423 // but we need to try next block to be sure
1424 /// TODO this is not clear: what to do if next block not contain next directory?
1425 total_size += (context.data_size - boff);
1426 boff = 0;
1427 break;
1428 } else {
1429 // fprintf(stderr, "readdir: directory record length too small: %d\n", record_length);
1430 free(buf);
1431 return -EIO;
1434 if(name_len > NAME_MAX - 1) {
1435 // fprintf(stderr, "readdir: file name length too big: %d\n", name_len);
1436 free(buf);
1437 return -EIO;
1439 if(sa_len < 0) {
1440 // probably something wrong with name_len
1441 // fprintf(stderr, "readdir: wrong name_len in directory entry: %d, record_length %d\n",
1442 // name_len, record_length);
1443 free(buf);
1444 return -EIO;
1446 if(count > 2 && name_len == 1 && record->name[0] == 0) {
1447 // looks like this is normal situation to meet another directory because
1448 // there is no clear way to find directory end
1449 // fprintf(stderr, "readdir: next directory found while processing another directory! boff %d, total_size %d, current_size %d, block %d, count %d\n",
1450 // (int) boff, total_size, current_size, block, count);
1451 goto out;
1454 isofs_inode *inode = (isofs_inode *) malloc(sizeof(isofs_inode));
1455 if(!inode) {
1456 // perror("Can`t malloc: ");
1457 return -ENOMEM;
1459 memset(inode, 0, sizeof(isofs_inode));
1460 inode->st_ino = context.last_ino;
1461 context.last_ino++;
1463 struct iso_directory_record *n_rec =
1464 (struct iso_directory_record *) malloc (sizeof(struct iso_directory_record));
1465 if(!n_rec) {
1466 // perror("Can`t malloc: ");
1467 return -ENOMEM;
1469 memcpy(n_rec, record, sizeof(struct iso_directory_record));
1470 inode->record = n_rec;
1472 if(context.susp || path[1] == '\0') { // if susp is known to be present or this is a root dir ("/")
1473 /* printf("sa offset %d, sa_len %d\n",
1474 sizeof(struct iso_directory_record) + name_len + pad_len, sa_len);*/
1475 rc = isofs_parse_sa(inode,
1476 ((char *) record) + sizeof(struct iso_directory_record) + name_len + pad_len + context.susp_skip,
1477 sa_len);
1478 if(rc) {
1479 free(buf);
1480 isofs_free_inode(inode);
1481 return rc;
1485 char *entry = (char *) malloc(NAME_MAX);
1486 if(!entry) {
1487 // perror("Can`t malloc: ");
1488 return -ENOMEM;
1490 if(count == 1) { // . entry ('\0' on disk)
1491 strcpy(entry, ".");
1492 } else if(count == 2) { // .. entry ('\1' on disk)
1493 strcpy(entry, "..");
1494 } else { // regular entry
1495 if(inode->NM) { // rrip NM entry present and in effect
1496 // printf("readdir: NM entry is in effect\n");
1497 strncpy(entry, inode->nm, inode->nm_len);
1498 entry[inode->nm_len] = '\0';
1499 } else { // regular ISO9660 filename
1500 // printf("readdir: no NM entry found, name len %d\n", name_len);
1501 // because there can be '\0' characters because using of UCS-2 encoding we need to use memcpy
1502 memcpy(entry, (char *) record->name, name_len);
1503 entry[name_len] = '\0';
1505 // fixup entry -- lowercase, strip leading ';', etc..
1506 entry = isofs_fix_entry(entry, name_len);
1507 if(!entry) {
1508 // fprintf(stderr, "readdir: error during entry fixup\n");
1509 isofs_free_inode(inode);
1510 free(buf);
1511 return -EIO;
1516 // fprintf(stderr, "%d -- %s\n\n", count, entry);
1518 if(filler) {
1519 struct stat st;
1520 memset(& st, '\0', sizeof(struct stat));
1521 isofs_direntry2stat(& st, inode);
1522 rc = filler(filler_buf, entry, & st, 0);
1523 if(rc) {
1524 // printf("readdir: filler return with %d, entry %s\n", rc, entry);
1525 isofs_free_inode(inode);
1526 free(buf);
1527 free(entry);
1528 return -rc;
1532 char absolute_entry[PATH_MAX];
1533 strcpy(absolute_entry, path);
1534 if(path[1] != '\0') { // not root dir
1535 strcat(absolute_entry, "/");
1537 strcat(absolute_entry, entry);
1538 if(g_hash_table_lookup(lookup_table, absolute_entry)) {
1539 // already in lookup cache
1540 isofs_free_inode(inode);
1541 } else {
1542 g_hash_table_insert(lookup_table, g_strdup(absolute_entry), inode);
1545 free(entry);
1547 boff += record_length;
1548 total_size += record_length;
1549 count++;
1552 // read next block
1554 block++;
1556 out:
1558 free(buf);
1559 return 0;
1562 int isofs_real_getattr(const char *path, struct stat *stbuf) {
1563 isofs_inode *inode = isofs_lookup(path);
1564 if(!inode) {
1565 // this error occur too often when browsing mounted tree with konqueror --
1566 // it check existence of .directory file with stat()
1567 // fprintf(stderr, "getattr: know nothing about %s\n", path);
1568 return -ENOENT;
1569 } else {
1570 // printf("getattr: found %s, size %d\n", path, isonum_733(inode->record->size));
1572 memset(stbuf, 0, sizeof(struct stat));
1573 isofs_direntry2stat(stbuf, inode);
1574 /* if(ISO_FLAGS_DIR(inode->record->flags)) {
1575 printf("%s %i %i\n", path, (int) stbuf->st_size, stbuf->st_mode);
1576 };*/
1577 return 0;
1580 int isofs_real_readlink(const char *path, char *target, size_t size) {
1581 isofs_inode *inode = isofs_lookup(path);
1582 if(!inode) {
1583 // fprintf(stderr, "readlink: know nothing about %s\n", path);
1584 return -ENOENT;
1586 if(!inode->PX || !inode->SL || !S_ISLNK(inode->st.st_mode)) {
1587 // fprintf(stderr, "readlink: %s not a link\n", path);
1588 return -EINVAL;
1590 strncpy(target, inode->sl, size - 1);
1591 target[size - 1] = '\0';
1592 return 0;
1595 int isofs_real_open(const char *path) {
1596 isofs_inode *inode = isofs_lookup(path);
1597 if(!inode) {
1598 // fprintf(stderr, "open: know nothing about %s\n", path);
1599 return -ENOENT;
1601 if(ISO_FLAGS_DIR(inode->record->flags)) {
1602 // fprintf(stderr, "open: %s not a file\n", path);
1603 return -EINVAL;
1605 if(ISO_FLAGS_HIDDEN(inode->record->flags)) {
1606 // fprintf(stderr, "open: %s is a hidden file\n", path);
1607 // return -EPERM;
1609 return 0;
1612 static int isofs_real_read_zf(isofs_inode *inode, char *out_buf, size_t size, off_t offset) {
1613 int zf_block_size = 1 << inode->zf_block_shift;
1614 int zf_start = offset / zf_block_size;
1615 int zf_end = (offset + size) / zf_block_size;
1616 // protection against cornercase when read request is exactly equal to the uncompressed file size.
1617 // -- Ryan Thomas 2007-06-13
1618 if( (offset + size ) % zf_block_size == 0 ) {
1619 zf_end--;
1621 int shift = offset % zf_block_size;
1623 // printf("zf_start %d, zf_end %d, size %d, offset %d, shift %d\n",
1624 // zf_start, zf_end, size, (int) offset, shift);
1626 // protection against some ununderstandable errors
1627 if(zf_start >= inode->zf_nblocks) {
1628 zf_start = inode->zf_nblocks - 1;
1630 if(zf_end >= inode->zf_nblocks) {
1631 zf_end = inode->zf_nblocks - 1;
1633 if(zf_end < 0 || zf_start < 0) {
1634 return 0;
1637 unsigned char *cbuf = (unsigned char *) malloc(zf_block_size * 2);
1638 if(!cbuf) {
1639 // perror("Can`t malloc: ");
1640 return -ENOMEM;
1642 unsigned char *ubuf = (unsigned char *) malloc(zf_block_size);
1643 if(!ubuf) {
1644 // perror("Can`t malloc: ");
1645 return -ENOMEM;
1648 size_t total_size = 0;
1649 size_t size_left = size;
1651 int base_offset = isonum_733(inode->record->extent) * context.data_size;
1653 int i;
1654 for(i = zf_start; i <= zf_end; i++) {
1655 int block_offset = isonum_731((char *) (& inode->zf_blockptr[i]));
1656 int block_end = isonum_731((char *) (& inode->zf_blockptr[i + 1]));
1657 int block_size = block_end - block_offset;
1659 if(block_size == 0) { // sort of sparce file block
1660 size_t payload_size = (zf_block_size - shift < size_left ? zf_block_size - shift: size_left);
1661 memset(out_buf + total_size, 0, payload_size);
1663 total_size += payload_size;
1664 size_left -= payload_size;
1665 } else if(block_size > zf_block_size * 2) {
1666 /* fprintf(stderr, "isofs_real_read_zf: compressed block size bigger than uncompressed block size * 2, something is wrong there.. block size %d, uncompressed block size %d\n",
1667 block_size, zf_block_size);*/
1668 free(cbuf);
1669 free(ubuf);
1670 return -EIO;
1671 } else {
1672 // we do not use isofs_read_raw_block() there because it is simpler to access
1673 // compressed blocks directly
1675 int image_off = base_offset + block_offset;
1677 if(pthread_mutex_lock(& fd_mutex)) {
1678 int err = errno;
1679 // perror("isofs_real_read_zf: can`l lock fd_mutex");
1680 free(cbuf);
1681 free(ubuf);
1682 return -err;
1684 if(lseek(context.fd, image_off, SEEK_SET) == -1) {
1685 // perror("isofs_real_read_zf: can`t lseek()");
1686 pthread_mutex_unlock(& fd_mutex);
1687 free(cbuf);
1688 free(ubuf);
1689 return -EIO;
1691 size_t len = read(context.fd, cbuf, block_size);
1692 if(len != block_size) {
1693 /* fprintf(stderr, "isofs_real_read_zf: can`t read full block, errno %d, message %s\n",
1694 errno, strerror(errno));*/
1695 pthread_mutex_unlock(& fd_mutex);
1696 free(cbuf);
1697 free(ubuf);
1698 return -EIO;
1700 pthread_mutex_unlock(& fd_mutex);
1702 // compressed block is read from disk, now uncompress() it
1704 uLongf usize = zf_block_size;
1705 int rc = uncompress(ubuf, &usize, cbuf, block_size);
1706 if(rc != Z_OK) {
1707 free(cbuf);
1708 free(ubuf);
1709 // fprintf(stderr, "isofs_real_read_zf: uncompress() error %i: %s\n", rc, strerror(rc));
1710 return -rc;
1713 // ubuf contain uncompressed data, usize contain uncompressed block size
1715 size_t payload_size = (usize - shift < size_left ? usize - shift : size_left);
1716 memcpy(out_buf + total_size, ubuf + shift, payload_size);
1718 total_size += payload_size;
1719 size_left -= payload_size;
1722 // shift is only meaningful for first block
1723 shift = 0;
1726 free(cbuf);
1727 free(ubuf);
1729 // printf("total size %d\n", total_size);
1731 return total_size;
1734 int isofs_real_read(const char *path, char *out_buf, size_t size, off_t offset) {
1735 isofs_inode *inode = isofs_lookup(path);
1736 if(!inode) {
1737 // fprintf(stderr, "read: know nothing about %s\n", path);
1738 return -ENOENT;
1740 struct iso_directory_record *record = inode->record;
1741 if(ISO_FLAGS_DIR(record->flags)) {
1742 // fprintf(stderr, "read: %s not a file\n", path);
1743 return -EINVAL;
1745 if(ISO_FLAGS_HIDDEN(record->flags)) {
1746 // fprintf(stderr, "read: %s is a hidden file\n", path);
1747 // return -EPERM;
1750 if(inode->ZF) { // this file is compressed, handle it specially
1751 return isofs_real_read_zf(inode, out_buf, size, offset);
1754 size_t fsize = isonum_733(record->size);
1755 if(offset + size > fsize) {
1756 size = fsize - offset;
1758 if(size < 1) {
1759 return 0;
1762 int start_block = isonum_733(record->extent);
1763 if(start_block == 0) { // empty file
1764 return 0;
1767 int start = offset / context.data_size;
1768 int end = (offset + size) / context.data_size;
1769 int shift = offset % context.data_size;
1770 int block = start_block + start;
1771 // printf("read: path %s, size %d, offset %d, fsize %d, start %d, end %d, shift %d\n",
1772 // path, size, (int) offset, fsize, start, end, shift);
1774 char *buf = (char *) malloc(context.data_size);
1775 if(!buf) {
1776 // perror("Can't malloc: ");
1777 return -ENOMEM;
1780 int i;
1781 size_t total_size = 0;
1782 size_t size_left = size;
1783 int count = 0;
1785 for(i = start; i <= end; i++) {
1786 int len = isofs_read_raw_block(block, buf);
1787 if(len < 0) {
1788 free(buf);
1789 return len;
1791 // printf("read: block %d, len %d, size_left %d\n", block, len, size_left);
1793 if(len > size_left) {
1794 len = size_left;
1797 total_size += len;
1799 memcpy(out_buf + count * context.data_size, buf + shift, len - shift);
1801 count++;
1802 shift = 0;
1803 size_left -= len;
1804 block++;
1807 free(buf);
1808 return total_size;
1811 int isofs_real_statfs(struct statfs *stbuf) {
1812 stbuf->f_type = ISOFS_SUPER_MAGIC;
1813 stbuf->f_bsize = context.data_size; // or PAGE_CACHE_SIZE?
1814 stbuf->f_blocks = 0; // while it is possible to calculate this, i see no reasons to do so
1815 stbuf->f_bfree = 0;
1816 stbuf->f_bavail = 0;
1817 stbuf->f_files = 0;
1818 stbuf->f_ffree = 0;
1819 stbuf->f_namelen = NAME_MAX - 1; // ? not sure..
1820 return 0;