/*************************************************************************** isofs.c - libisofs implementation ------------------- begin : Oct 25 2002 copyright : (C) 2002 by Szombathelyi György email : gyurco@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include "isofs.h" #include "rock.h" /* internal function from the linux kernel (isofs fs) */ static time_t getisotime(int year,int month,int day,int hour, int minute,int second,int tz) { int days, i; time_t crtime; year-=1970; if (year < 0) { crtime = 0; } else { int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; days = year * 365; if (year > 2) days += (year+1) / 4; for (i = 1; i < month; i++) days += monlen[i-1]; if (((year+2) % 4) == 0 && month > 2) days++; days += day - 1; crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; /* sign extend */ if (tz & 0x80) tz |= (-1 << 8); /* * The timezone offset is unreliable on some disks, * so we make a sanity check. In no case is it ever * more than 13 hours from GMT, which is 52*15min. * The time is always stored in localtime with the * timezone offset being what get added to GMT to * get to localtime. Thus we need to subtract the offset * to get to true GMT, which is what we store the time * as internally. On the local system, the user may set * their timezone any way they wish, of course, so GMT * gets converted back to localtime on the receiving * system. * * NOTE: mkisofs in versions prior to mkisofs-1.10 had * the sign wrong on the timezone offset. This has now * been corrected there too, but if you are getting screwy * results this may be the explanation. If enough people * complain, a user configuration option could be added * to add the timezone offset in with the wrong sign * for 'compatibility' with older discs, but I cannot see how * it will matter that much. * * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann) * for pointing out the sign error. */ if (-52 <= tz && tz <= 52) crtime -= tz * 15 * 60; } return crtime; } /** * Returns the Unix from the ISO9660 9.1.5 time format */ time_t isodate_915(char * p, int hs) { return getisotime(1900+p[0],p[1],p[2],p[3],p[4],p[5],hs==0 ? p[6] : 0); } /** * Returns the Unix from the ISO9660 8.4.26.1 time format * BUG: hundredth of seconds are ignored, because Unix time_t has one second * resolution (I think it's no problem at all) */ time_t isodate_84261(char * p, int hs) { int year,month,day,hour,minute,second; year=(p[0]-'0')*1000 + (p[1]-'0')*100 + (p[2]-'0')*10 + p[3]-'0'; month=(p[4]-'0')*10 + (p[5]-'0'); day=(p[6]-'0')*10 + (p[7]-'0'); hour=(p[8]-'0')*10 + (p[9]-'0'); minute=(p[10]-'0')*10 + (p[11]-'0'); second=(p[12]-'0')*10 + (p[13]-'0'); return getisotime(year,month,day,hour,minute,second,hs==0 ? p[16] : 0); } void FreeBootTable(boot_head *boot) { boot_entry *be,*next; be=boot->defentry; while (be) { next=be->next; free(be); be=next; } boot->defentry=NULL; } int BootImageSize(readfunc* read,int media,sector_t start,long long len,void* udata) { int ret; switch(media & 0xf) { case 0: ret=len; /* No emulation */ break; case 1: ret=80*2*15; /* 1.2 MB */ break; case 2: ret=80*2*18; /* 1.44 MB */ break; case 3: ret=80*2*36; /* 2.88 MB */ break; case 4: /* FIXME!!! */ ret=len; /* Hard Disk */ break; default: ret=len; } return ret; } static boot_entry *CreateBootEntry(char *be) { boot_entry *entry; entry = (boot_entry*) malloc(sizeof(boot_entry)); if (!entry) return NULL; memset(entry, 0, sizeof(boot_entry)); memcpy(entry->data,be,0x20); return entry; } int ReadBootTable(readfunc *read,sector_t sector, boot_head *head, void *udata) { char buf[2048], *c, *be; int i,end=0; unsigned short sum; boot_entry *defcur=NULL,*deflast=NULL; struct validation_entry *ventry=NULL; struct default_entry *dentry=NULL; struct section_header *sheader=NULL; struct section_entry *sentry=NULL; struct section_entry_ext *extsentry=NULL; head->sections=NULL; head->defentry=NULL; while (1) { be = (char*) &buf; if ( read(be, sector, 1, udata) != 1 ) goto err; /* first entry needs to be a validation entry */ if (!ventry) { ventry=(struct validation_entry *) be; if ( isonum_711(ventry->type) !=1 ) goto err; sum=0; c = (char*) ventry; for (i=0;i<16;i++) { sum += isonum_721(c); c+=2; } if (sum) goto err; memcpy(&head->ventry,be,0x20); be += 0x20; } while (!end && (be < (buf+1))) { switch (isonum_711(be)) { case 0x88: defcur=CreateBootEntry(be); if (!defcur) goto err; if (deflast) deflast->next=defcur; else head->defentry=defcur; defcur->prev=deflast; deflast=defcur; break; case 0x90: case 0x91: break; default: end=1; break; } be += 0x20; } if (end) break; sector ++; } return 0; err: FreeBootTable(head); return -1; } /** * Creates the linked list of the volume descriptors */ iso_vol_desc *ReadISO9660(readfunc *read,sector_t sector,void *udata) { int i; struct iso_volume_descriptor buf; iso_vol_desc *first=NULL,*current=NULL,*prev=NULL; for (i=0;i<100;i++) { if (read( (char*) &buf, sector+i+16, 1, udata) != 1 ) { FreeISO9660(first); return NULL; } if (!memcmp(ISO_STANDARD_ID,&buf.id,5)) { switch ( isonum_711(&buf.type[0]) ) { case ISO_VD_BOOT: case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: current=(iso_vol_desc*) malloc(sizeof(iso_vol_desc)); if (!current) { FreeISO9660(first); return NULL; } current->prev=prev; current->next=NULL; if (prev) prev->next=current; memcpy(&(current->data),&buf,2048); if (!first) first=current; prev=current; break; case ISO_VD_END: return first; break; } } else if (!memcmp(HS_STANDARD_ID,(struct hs_volume_descriptor*) &buf,5)) { /* High Sierra format not supported (yet) */ } } return first; } /** * Frees the linked list of volume descriptors */ void FreeISO9660(iso_vol_desc *data) { iso_vol_desc *current; while (data) { current=data; data=current->next; free(current); } } /** * Frees the strings in 'rrentry' */ void FreeRR(rr_entry *rrentry) { if (rrentry->name) { free(rrentry->name); rrentry->name=NULL; } if (rrentry->sl) { free(rrentry->sl); rrentry->name=NULL; } } static int str_nappend(char **d,char *s,int n) { int i=0; char *c; /* i=strnlen(s,n)+1; */ while (iname_len); if (!(isonum_711(idr->name_len) & 1)) suspoffs++; susplen=isonum_711(idr->length)-suspoffs; r= & (((char*) idr)[suspoffs]); rr = (struct rock_ridge*) r; memset(rrentry,0,sizeof(rr_entry)); rrentry->len = sizeof(rr_entry); while (susplen > 0) { if (isonum_711(&rr->len) > susplen || rr->len == 0) break; if (rr->signature[0]=='N' && rr->signature[1]=='M') { if (!(rr->u.NM.flags & 0x26) && rr->len>5 && !rrentry->name) { if (str_nappend(&rrentry->name,rr->u.NM.name,isonum_711(&rr->len)-5)) { FreeRR(rrentry); return -ENOMEM; } ret++; } } else if (rr->signature[0]=='P' && rr->signature[1]=='X' && (isonum_711(&rr->len)==44 || isonum_711(&rr->len)==36)) { rrentry->mode=isonum_733(rr->u.PX.mode); rrentry->nlink=isonum_733(rr->u.PX.n_links); rrentry->uid=isonum_733(rr->u.PX.uid); rrentry->gid=isonum_733(rr->u.PX.gid); if (isonum_711(&rr->len)==44) rrentry->serno=isonum_733(rr->u.PX.serno); ret++; } else if (rr->signature[0]=='P' && rr->signature[1]=='N' && isonum_711(&rr->len)==20) { rrentry->dev_major=isonum_733(rr->u.PN.dev_high); rrentry->dev_minor=isonum_733(rr->u.PN.dev_low); ret++; } else if (rr->signature[0]=='P' && rr->signature[1]=='L' && isonum_711(&rr->len)==12) { rrentry->pl=isonum_733(rr->u.PL.location); ret++; } else if (rr->signature[0]=='C' && rr->signature[1]=='L' && isonum_711(&rr->len)==12) { rrentry->cl=isonum_733(rr->u.CL.location); ret++; } else if (rr->signature[0]=='R' && rr->signature[1]=='E' && isonum_711(&rr->len)==4) { rrentry->re=1; ret++; } else if (rr->signature[0]=='S' && rr->signature[1]=='L' && isonum_711(&rr->len)>7) { i = isonum_711(&rr->len)-5; c = (char*) rr; c += 5; while (i>0) { switch(c[0] & ~1) { case 0x2: if (str_append(&rrentry->sl,".")) { FreeRR(rrentry); return -ENOMEM; } break; case 0x4: if (str_append(&rrentry->sl,"..")) { FreeRR(rrentry); return -ENOMEM; } break; } if ( (c[0] & 0x08) == 0x08 || (c[1] && rrentry->sl && strlen(rrentry->sl)>1) ) { if (str_append(&rrentry->sl,"/")) { FreeRR(rrentry); return -ENOMEM; } } if ((unsigned char)c[1]>0) { if (str_nappend(&rrentry->sl,c+2,(unsigned char)c[1])) { FreeRR(rrentry); return -ENOMEM; } } i -= ((unsigned char)c[1] + 2); c += ((unsigned char)c[1] + 2); } ret++; } else if (rr->signature[0]=='T' && rr->signature[1]=='F' && isonum_711(&rr->len)>5) { i = isonum_711(&rr->len)-5; f = rr->u.TF.flags; c = (char*) rr; c += 5; while (i >= rrtlen(f)) { if (f & 1) { rrentry->t_creat=rrctime(f,c); f &= ~1; } else if (f & 2) { rrentry->rr_st_mtime=rrctime(f,c); f &= ~2; } else if (f & 4) { rrentry->rr_st_atime=rrctime(f,c); f &= ~4; } else if (f & 8) { rrentry->rr_st_ctime=rrctime(f,c); f &= ~8; } else if (f & 16) { rrentry->t_backup=rrctime(f,c); f &= ~16; } else if (f & 32) { rrentry->t_expire=rrctime(f,c); f &= ~32; } else if (f & 64) { rrentry->t_effect=rrctime(f,c); f &= ~64; } i -= rrtlen(f); c += rrtlen(f); } ret++; } else if (rr->signature[0]=='Z' && rr->signature[1]=='F' && isonum_711(&rr->len)==16) { /* Linux-specific extension: transparent decompression */ rrentry->z_algo[0]=rr->u.ZF.algorithm[0]; rrentry->z_algo[1]=rr->u.ZF.algorithm[1]; rrentry->z_params[0]=rr->u.ZF.parms[0]; rrentry->z_params[1]=rr->u.ZF.parms[1]; rrentry->z_size=isonum_733(rr->u.ZF.real_size); ret++; } else { /* printf("SUSP sign: %c%c\n",rr->signature[0],rr->signature[1]); */ } susplen -= isonum_711(&rr->len); r += isonum_711(&rr->len); rr = (struct rock_ridge*) r; } return ret; } /** * Iterates over the directory entries. The directory is in 'buf', * the size of the directory is 'size'. 'callback' is called for each * directory entry with the parameter 'udata'. */ int ProcessDir(readfunc *read,int extent,int size,dircallback *callback,void *udata) { int pos=0,ret=0,siz; char *buf; struct iso_directory_record *idr; if (size & 2047) { siz=((size>>11)+1)<<11; } else { siz=size; } buf=(char*) malloc(siz); if (!buf) return -ENOMEM; if (read(buf,extent,siz>>11,udata)!=siz>>11) { free(buf); return -EIO; } while (size>0) { idr=(struct iso_directory_record*) &buf[pos]; if (isonum_711(idr->length)==0) { size-=(2048 - (pos & 0x7ff)); if (size<=2) break; pos+=0x800; pos&=0xfffff800; idr=(struct iso_directory_record*) &buf[pos]; } pos+=isonum_711(idr->length); pos+=isonum_711(idr->ext_attr_length); size-=isonum_711(idr->length); size-=isonum_711(idr->ext_attr_length); if (size<0) break; if (isonum_711(idr->length) <33 || isonum_711(idr->length)<33+isonum_711(idr->name_len)) { /* Invalid directory entry */ continue; } if ((ret=callback(idr,udata))) break; } free(buf); return ret; } /** * returns the joliet level from the volume descriptor */ int JolietLevel(struct iso_volume_descriptor *ivd) { int ret=0; struct iso_supplementary_descriptor *isd; isd = (struct iso_supplementary_descriptor *) ivd; if (isonum_711(ivd->type)==ISO_VD_SUPPLEMENTARY) { if (isd->escape[0]==0x25 && isd->escape[1]==0x2f) { switch (isd->escape[2]) { case 0x40: ret=1; break; case 0x43: ret=2; break; case 0x45: ret=3; break; } } } return ret; } /********************************************************************/ #if 0 #include #include #include #include #include #include #include int level=0,joliet=0,dirs,files; iconv_t iconv_d; int fd; int readf(char *buf, int start, int len,void *udata) { int ret; if ((ret=lseek(fd, start << 11, SEEK_SET))<0) return ret; ret=read(fd, buf, len << 11); if (ret<0) return ret; return (ret >> 11); } void dumpchars(char *c,int len) { while (len>0) { printf("%c",*c); len--; c++; } } void sp(int num) { int i; for (i=0;iname_len)==1) { switch (dir->name[0]) { case 0: printf("."); break; case 1: printf(".."); break; default: printf("%c",dir->name[0]); break; } } dumpchardesc(dir->name,isonum_711(dir->name_len)); printf(" size=%d",isonum_733(dir->size)); printf(" extent=%d ",isonum_733(dir->extent)); dumpflags(isonum_711(dir->flags)); dumpiso915time((char*) &(dir->date),0); } void dumprrentry(rr_entry *rr) { printf(" NM=[%s] uid=%d gid=%d nlink=%d mode=%o ", rr->name,rr->uid,rr->gid,rr->nlink,rr->mode); if (S_ISCHR(rr->mode) || S_ISBLK(rr->mode)) printf("major=%d minor=%d ",rr->dev_major,rr->dev_minor); if (rr->mode & S_IFLNK && rr->sl) printf("slink=%s ",rr->sl); /* printf("\n"); if (rr->t_creat) printf("t_creat: %s",ctime(&rr->t_creat)); if (rr->rr_st_mtime) printf("rr_st_mtime: %s",ctime(&rr->rr_st_mtime)); if (rr->rr_st_atime) printf("rr_st_atime: %s",ctime(&rr->rr_st_atime)); if (rr->rr_st_ctime) printf("rr_st_ctime: %s",ctime(&rr->rr_st_ctime)); if (rr->t_backup) printf("t_backup: %s",ctime(&rr->t_backup)); if (rr->t_expire) printf("t_expire: %s",ctime(&rr->t_expire)); if (rr->t_effect) printf("t_effect: %s",ctime(&rr->t_effect)); */ } void dumpsusp(char *c, int len) { dumpchars(c,len); } void dumpboot(struct el_torito_boot_descriptor *ebd) { printf("version: %d\n",isonum_711(ebd->version)); printf("system id: ");dumpchars(ebd->system_id,ISODCL(8,39));printf("\n"); printf("boot catalog start: %d\n",isonum_731(ebd->boot_catalog)); } void dumpdefentry(struct default_entry *de) { printf("Default entry: \n"); printf(" bootid=%x\n",isonum_711(de->bootid)); printf(" media emulation=%d (",isonum_711(de->media)); switch(isonum_711(de->media) & 0xf) { case 0: printf("No emulation"); break; case 1: printf("1.2 Mb floppy"); break; case 2: printf("1.44 Mb floppy"); break; case 3: printf("2.88 Mb floppy"); break; case 4: printf("Hard Disk"); break; default: printf("Unknown/Invalid"); break; } printf(")\n"); printf(" loadseg=%d\n",isonum_721(de->loadseg)); printf(" systype=%d\n",isonum_711(de->systype)); printf(" start lba=%d count=%d\n",isonum_731(de->start), isonum_721(de->seccount)); } void dumpbootcat(boot_head *bh) { boot_entry *be; printf("System id: ");dumpchars(bh->ventry.id,ISODCL(28,5));printf("\n"); be=bh->defentry; while (be) { dumpdefentry(be->data); be=be->next; } } void dumpdesc(struct iso_primary_descriptor *ipd) { printf("system id: ");dumpchardesc(ipd->system_id,ISODCL(9,40));printf("\n"); printf("volume id: ");dumpchardesc(ipd->volume_id,ISODCL(41,72));printf("\n"); printf("volume space size: %d\n",isonum_733(ipd->volume_space_size)); printf("volume set size: %d\n",isonum_723(ipd->volume_set_size)); printf("volume seq num: %d\n",isonum_723(ipd->volume_set_size)); printf("logical block size: %d\n",isonum_723(ipd->logical_block_size)); printf("path table size: %d\n",isonum_733(ipd->path_table_size)); printf("location of type_l path table: %d\n",isonum_731(ipd->type_l_path_table)); printf("location of optional type_l path table: %d\n",isonum_731(ipd->opt_type_l_path_table)); printf("location of type_m path table: %d\n",isonum_732(ipd->type_m_path_table)); printf("location of optional type_m path table: %d\n",isonum_732(ipd->opt_type_m_path_table)); /* printf("Root dir record:\n");dumpdirrec((struct iso_directory_record*) &ipd->root_directory_record); */ printf("Volume set id: ");dumpchardesc(ipd->volume_set_id,ISODCL(191,318));printf("\n"); printf("Publisher id: ");dumpchardesc(ipd->publisher_id,ISODCL(319,446));printf("\n"); printf("Preparer id: ");dumpchardesc(ipd->preparer_id,ISODCL(447,574));printf("\n"); printf("Application id: ");dumpchardesc(ipd->application_id,ISODCL(575,702));printf("\n"); printf("Copyright id: ");dumpchardesc(ipd->copyright_file_id,ISODCL(703,739));printf("\n"); printf("Abstract file id: ");dumpchardesc(ipd->abstract_file_id,ISODCL(740,776));printf("\n"); printf("Bibliographic file id: ");dumpchardesc(ipd->bibliographic_file_id,ISODCL(777,813));printf("\n"); printf("Volume creation date: ");dumpiso84261time(ipd->creation_date,0);printf("\n"); printf("Volume modification date: ");dumpiso84261time(ipd->modification_date,0);printf("\n"); printf("Volume expiration date: ");dumpiso84261time(ipd->expiration_date,0);printf("\n"); printf("Volume effective date: ");dumpiso84261time(ipd->effective_date,0);printf("\n"); printf("File structure version: %d\n",isonum_711(ipd->file_structure_version)); } int mycallb(struct iso_directory_record *idr,void *udata) { rr_entry rrentry; sp(level);dumpdirrec(idr); if (level==0) printf(" (Root directory) "); printf("\n"); if (ParseRR(idr,&rrentry)>0) { sp(level);printf(" ");dumprrentry(&rrentry);printf("\n"); } FreeRR(&rrentry); if ( !(idr->flags[0] & 2) ) files++; if ( (idr->flags[0] & 2) && (level==0 || isonum_711(idr->name_len)>1) ) { level++; dirs++; ProcessDir(&readf,isonum_733(idr->extent),isonum_733(idr->size),&mycallb,udata); level--; } return 0; } /************************************************/ int main(int argc, char *argv[]) { int i=1,sector=0; iso_vol_desc *desc; boot_head boot; if (argc<2) { fprintf(stderr,"\nUsage: %s iso-file-name or device [starting sector]\n\n",argv[0]); return 0; } if (argc>=3) { sector=atoi(argv[2]); printf("Using starting sector number %d\n",sector); } fd=open(argv[1],O_RDONLY); if (fd<0) { fprintf(stderr,"open error\n"); return -1; } iconv_d=iconv_open("ISO8859-2","UTF16BE"); if (iconv_d==0) { fprintf(stderr,"iconv open error\n"); return -1; } desc=ReadISO9660(&readf,sector,NULL); if (!desc) { printf("No volume descriptors\n"); return -1; } while (desc) { printf("\n\n--------------- Volume descriptor (%d.) type %d: ---------------\n\n", i,isonum_711(desc->data.type)); switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: { struct el_torito_boot_descriptor* bootdesc; bootdesc=&(desc->data); dumpboot(bootdesc); if ( !memcmp(EL_TORITO_ID,bootdesc->system_id,ISODCL(8,39)) ) { if (ReadBootTable(&readf,isonum_731(bootdesc->boot_catalog),&boot,NULL)) { printf("Boot Catalog Error\n"); } else { dumpbootcat(&boot); FreeBootTable(&boot); } } } break; case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: joliet=0; joliet = JolietLevel(&desc->data); printf("Joliet level: %d\n",joliet); dumpdesc((struct iso_primary_descriptor*) &desc->data); printf("\n\n--------------- Directory structure: -------------------\n\n"); dirs=0;files=0; mycallb( &( ((struct iso_primary_descriptor*) &desc->data)->root_directory_record), NULL ); printf("\nnumber of directories: %d\n",dirs); printf("\nnumber of files: %d\n",files); break; } desc=desc->next; i++; } iconv_close(iconv_d); close(fd); FreeISO9660(desc); return 0; } #endif