home *** CD-ROM | disk | FTP | other *** search
- /* iso9660.c:
- *
- * Support for the ISO-9660 filing system.
- *
- * ----------------------------------------------------------------------
- * This code is (C) Copyright 1993 by Frank Munkert.
- * All rights reserved.
- * This software may be freely distributed and redistributed for
- * non-commercial purposes, provided this notice is included.
- * ----------------------------------------------------------------------
- * History:
- *
- * 24-Sep-93 fmu Two further bugs in Seek_Position fixed.
- * 16-Sep-93 fmu Fixed bug in Seek_Position.
- * 16-Sep-93 fmu Bugfix: Top level object recognition in CDROM_Info
- * had to be changed for Rock Ridge disks.
- */
-
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <clib/exec_protos.h>
-
- #include "cdrom.h"
- #include "iso9660.h"
- #include "rock.h"
-
- int iso_errno;
-
- /* A "volume" is the basic handle that we'll use for CDROM access.
- * A volume contains all necessary information for finding files on
- * a CDROM.
- */
-
- VOLUME *Open_Volume (CDROM *p_cdrom, int p_use_rock_ridge)
- {
- VOLUME *res;
- long loc = 16;
-
- res = AllocMem (sizeof (VOLUME), MEMF_PUBLIC);
- if (!res) {
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
-
- res->cd = p_cdrom;
-
- for (;;) {
- if (!Read_Sector (res->cd, loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- FreeMem (res, sizeof (VOLUME));
- return NULL;
- }
-
- if (res->cd->buffer[0] == 1) {
- memcpy (&res->pvd, res->cd->buffer, sizeof (prim_vol_desc));
- break;
- }
-
- if (res->cd->buffer[0] == 255 || loc > 1000) {
- iso_errno = ISOERR_NO_PVD;
- FreeMem (res, sizeof (VOLUME));
- return NULL;
- }
-
- loc++;
- }
-
- res->use_rock_ridge = p_use_rock_ridge && Uses_Rock_Ridge_Protocol (res);
-
- res->valid = 1; /* may be modified by application program */
- res->locks = 0; /* may be modified by application program */
-
- return res;
- }
-
- /* Close a volume; deallocate all associated resources.
- */
-
- void Close_Volume (VOLUME *p_volume)
- {
- FreeMem (p_volume, sizeof (VOLUME));
- }
-
- /* Get the "CDROM object" for the root directory of the volume.
- */
-
- CDROM_OBJ *Open_Top_Level_Directory (VOLUME *p_volume)
- {
- CDROM_OBJ *obj;
-
- obj = AllocMem (sizeof (CDROM_OBJ), MEMF_PUBLIC);
- if (!obj) {
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
-
- obj->directory_f = TRUE;
- obj->volume = p_volume;
- obj->path_table_pos = 1;
- if (obj->volume->use_rock_ridge)
- obj->parent = p_volume->pvd.root.extent_loc_m;
- else
- obj->parent = 1;
- obj->pos = 0;
-
- obj->dir_record = AllocMem (p_volume->pvd.root.length, MEMF_PUBLIC);
- if (!obj->dir_record) {
- iso_errno = ISOERR_NO_MEMORY;
- FreeMem (obj, sizeof (CDROM_OBJ));
- return NULL;
- }
- memcpy (obj->dir_record, &p_volume->pvd.root, p_volume->pvd.root.length);
-
- return obj;
- }
-
- /* Read a record from the path table.
- */
-
- path_table_record *Get_Path_Table_Record (VOLUME *p_volume, unsigned long p_dir,
- unsigned long *p_offset)
- {
- static unsigned char result[256];
- unsigned long loc = p_volume->pvd.m_table;
- unsigned long size = 0;
- int pos = 0;
- int cnt = 1;
- int len;
-
- if (p_dir) {
- if (!Read_Sector (p_volume->cd, loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- return NULL;
- }
-
- while (cnt < p_dir) {
- if (size >= p_volume->pvd.path_size_m) {
- iso_errno = ISOERR_NO_SUCH_RECORD;
- return NULL;
- }
-
- len = 8 + p_volume->cd->buffer[pos];
- if (len & 1)
- len++;
- pos += len;
- size += len;
-
- if (pos >= 2048) {
- if (!Read_Sector (p_volume->cd, ++loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- return NULL;
- }
- pos -= 2048;
- }
-
- cnt++;
- }
- } else {
- loc += (*p_offset >> 11);
- if (!Read_Sector (p_volume->cd, loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- return NULL;
- }
- pos = *p_offset & 2047;
- size = *p_offset;
- }
-
- len = 8 + p_volume->cd->buffer[pos];
- if (len & 1)
- len++;
-
- if (pos + len <= 2048)
- memcpy (result, p_volume->cd->buffer + pos, len);
- else {
- memcpy (result, p_volume->cd->buffer + pos, 2048 - pos);
- if (!Read_Sector (p_volume->cd, ++loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- return NULL;
- }
- memcpy (result + (2048 - pos), p_volume->cd->buffer, len - (2048 - pos));
- }
-
- if (p_offset)
- *p_offset = size + len;
-
- return (path_table_record *) result;
- }
-
- /* Find parent directory.
- */
-
- unsigned long Get_Parent_Directory_ISO (VOLUME *p_volume, unsigned long p_dir)
- {
- path_table_record *ptr;
-
- ptr = Get_Path_Table_Record (p_volume, p_dir, NULL);
- if (!ptr)
- return 1;
-
- return ptr->parent;
- }
-
- /* Strncasecmp works like 'strncmp' but ignores case differences.
- */
-
- int Strncasecmp (char *p_str1, char *p_str2, int p_length)
- {
- int i;
- int len = 0;
-
- while (len < p_length && *p_str1 && *p_str2) {
- if (i = tolower (*p_str1++) - tolower (*p_str2++))
- return i;
- len++;
- }
- return (len == p_length) ? 0 : tolower (*p_str1) - tolower (*p_str2);
- }
-
- /* Test on equality of directory names (ignoring case).
- */
-
- int Directory_Names_Equal (char *p_iso_name, int p_length, char *p_name)
- {
- return Strncasecmp (p_iso_name, p_name, p_length) == 0 &&
- p_name[p_length] == 0;
- }
-
- /* Compare the name of the directory entry p_iso_name (with length p_length)
- * with the C string p_name, and return 1 iff both strings are equal.
- * NOTE: p_iso_name may be a file name (with version number) or a directory
- * name (without version number).
- */
-
- int Names_Equal (char *p_iso_name, int p_length, char *p_name)
- {
- if (!strchr (p_name, ';')) {
- /* compare without version number: */
- int pos = p_length-1;
-
- for (; pos>=0; pos--)
- if (p_iso_name[pos] == ';')
- break;
-
- if (pos>=0)
- p_length = pos;
-
- }
- return (Strncasecmp (p_iso_name, p_name, p_length) == 0 &&
- p_name[p_length] == 0);
- }
-
- /* Get a record from a directory.
- */
-
- directory_record *Get_Directory_Record (VOLUME *p_volume,
- unsigned long p_location,
- unsigned long p_offset)
- {
- static unsigned char result[256];
- int len;
- int loc;
-
- loc = p_location + (p_offset >> 11);
- if (!Read_Sector (p_volume->cd, loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- return NULL;
- }
-
- len = p_volume->cd->buffer[p_offset & 2047];
- if (len)
- memcpy (result, p_volume->cd->buffer + (p_offset & 2047), len);
- else
- result[0] = 0; /* mark as last record */
-
- return (directory_record *) result;
- }
-
- /* Find directory in path table.
- */
-
- unsigned long Find_Directory_ISO (VOLUME *p_volume, unsigned long p_parent,
- char *p_name)
- {
- path_table_record *ptr;
- unsigned long rec = p_parent + 1;
- unsigned long offset;
- directory_record *dir;
-
- ptr = Get_Path_Table_Record (p_volume, rec, &offset);
- for (;;) {
- if (!ptr)
- return 0;
-
- if (ptr->parent == p_parent &&
- Directory_Names_Equal (ptr->id, ptr->id_length, p_name))
- return rec;
-
- if (ptr->parent > p_parent) {
- iso_errno = ISOERR_NOT_FOUND;
- return 0;
- }
-
- rec++;
- /* get next record: */
- ptr = Get_Path_Table_Record (p_volume, 0, &offset);
- }
- }
-
- /* Find an entry (directory or file) in a directory.
- */
-
- directory_record *Find_Directory_Entry (VOLUME *p_volume, unsigned long p_location,
- char *p_name)
- {
- unsigned long length;
- directory_record *dir;
- unsigned long offset = 0;
- int rr_len;
- char rr_buf[256];
-
- if (!(dir = Get_Directory_Record (p_volume, p_location, 0)))
- return NULL;
-
- length = dir->data_length_m;
-
- for (;;) {
- offset += dir->length;
- for (;;) {
- if (offset >= length) {
- iso_errno = ISOERR_NOT_FOUND;
- return NULL;
- }
- dir = Get_Directory_Record (p_volume, p_location, offset);
- if (!dir)
- return NULL;
-
- if (dir->length == 0)
- /* goto next logical sector: */
- offset = (offset & 0xfffff800) + 2048;
- else
- break;
- }
-
- if (p_volume->use_rock_ridge) {
- rr_len = Get_RR_File_Name (p_volume, dir, rr_buf, sizeof (rr_buf));
- if (rr_len != -1) {
- if (Strncasecmp (rr_buf, p_name, rr_len) == 0 &&
- p_name[rr_len] == 0)
- return dir;
- else
- continue;
- }
- }
-
- if (Names_Equal (dir->file_id, dir->file_id_length, p_name))
- return dir;
- }
- }
-
- /* Open a "CDROM object" on an ISO-9660 disk.
- */
-
- CDROM_OBJ *Open_Object_ISO (CDROM_OBJ *p_result,
- CDROM_OBJ *p_current_dir, char *p_name)
- {
- CDROM_OBJ *obj = p_result;
- unsigned long path_table_pos = p_current_dir->path_table_pos;
- unsigned long child_pos;
- char *cp = p_name;
- char name[256];
- char *np;
- path_table_record *ptr;
- directory_record *dir;
-
- if (*cp == ':') {
- path_table_pos = 1;
- cp++;
- } else while (*cp == '/') {
- path_table_pos = Get_Parent_Directory_ISO (p_current_dir->volume, path_table_pos);
- cp++;
- }
-
- while (*cp) {
- for (np = name; *cp && *cp != '/'; )
- *np++ = *cp++;
- *np = 0;
-
- if (name[0] == 0) {
- iso_errno = ISOERR_ILLEGAL_NAME;
- return NULL;
- }
-
- child_pos = Find_Directory_ISO (p_current_dir->volume, path_table_pos, name);
- if (!child_pos) {
- if (iso_errno != ISOERR_NOT_FOUND) {
- return NULL;
- }
-
- if (*cp != 0) {
- return NULL;
- }
-
- ptr = Get_Path_Table_Record (p_current_dir->volume, path_table_pos, NULL);
- if (!ptr)
- return NULL;
-
- dir = Find_Directory_Entry (p_current_dir->volume, ptr->location, name);
- if (!dir)
- return NULL;
-
- obj->directory_f = FALSE;
- obj->parent = path_table_pos;
- obj->dir_record = AllocMem (dir->length, MEMF_PUBLIC);
- if (!obj->dir_record) {
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
- memcpy (obj->dir_record, dir, dir->length);
-
- return obj;
- }
-
- path_table_pos = child_pos;
-
- if (*cp == '/')
- cp++;
- }
-
- if (!(ptr = Get_Path_Table_Record (p_current_dir->volume, path_table_pos,
- NULL))) {
- return NULL;
- }
-
- if (!(dir = Get_Directory_Record (p_current_dir->volume, ptr->location, 0))) {
- return NULL;
- }
-
- obj->directory_f = TRUE;
- obj->path_table_pos = path_table_pos;
- obj->parent = ptr->parent;
- obj->dir_record = AllocMem (dir->length, MEMF_PUBLIC);
- if (!obj->dir_record) {
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
- memcpy (obj->dir_record, dir, dir->length);
-
- return obj;
- }
-
- /* Open an object on an ISO or Rock Ridge disk.
- */
-
- CDROM_OBJ *Open_Object (CDROM_OBJ *p_current_dir, char *p_name)
- {
- CDROM_OBJ *obj, *res;
-
- if (*p_name == 0) {
- obj = Clone_Object (p_current_dir);
- if (!obj)
- iso_errno = ISOERR_NO_MEMORY;
- return obj;
- }
-
- obj = AllocMem (sizeof (CDROM_OBJ), MEMF_PUBLIC);
- if (!obj) {
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
-
- obj->pos = 0;
- obj->volume = p_current_dir->volume;
-
- if (p_current_dir->volume->use_rock_ridge)
- res = Open_Object_RR (obj, p_current_dir, p_name);
- else
- res = Open_Object_ISO (obj, p_current_dir, p_name);
-
- if (!res)
- FreeMem (obj, sizeof (CDROM_OBJ));
-
- return res;
- }
-
- /* Close a "CDROM object" and deallocate all associated resources.
- */
-
- void Close_Object (CDROM_OBJ *p_object)
- {
- FreeMem (p_object->dir_record, p_object->dir_record->length);
- FreeMem (p_object, sizeof (CDROM_OBJ));
- }
-
- /* Read bytes from a file.
- */
-
- int Read_From_File (CDROM_OBJ *p_file, char *p_buffer, int p_buffer_length)
- {
- unsigned long loc;
- int remain_block, remain_file, remain;
- int len;
- CDROM *cd;
- int pos;
- int buf_pos = 0;
- int todo;
- unsigned long last_loc;
-
- if (p_file->pos == p_file->dir_record->data_length_m)
- /* at end of file: */
- return 0;
-
- cd = p_file->volume->cd;
- loc = p_file->dir_record->extent_loc_m + (p_file->pos >> 11);
- last_loc = p_file->dir_record->extent_loc_m +
- ((p_file->dir_record->data_length_m-1) >> 11);
- todo = p_buffer_length;
-
- while (todo) {
- if (!Read_Sector_With_Lookahead (cd, loc, last_loc)) {
- iso_errno = ISOERR_SCSI_ERROR;
- return -1;
- }
-
- remain_block = 2048 - (pos = (p_file->pos & 2047));
- remain_file = p_file->dir_record->data_length_m - p_file->pos;
- remain = (remain_block < remain_file) ? remain_block : remain_file;
- len = (todo < remain) ? todo : remain;
- memcpy (p_buffer + buf_pos, cd->buffer + pos, len);
- buf_pos += len;
- p_file->pos += len;
- todo -= len;
-
- if (p_file->pos >= p_file->dir_record->data_length_m)
- break;
-
- loc++;
- }
-
- return buf_pos;
- }
-
- /* Return information on a "CDROM object."
- */
-
- int CDROM_Info (CDROM_OBJ *p_obj, CDROM_INFO *p_info)
- {
- memcpy (&p_info->dir_record, p_obj->dir_record, p_obj->dir_record->length);
-
- if (p_obj->volume->use_rock_ridge) {
- p_info->name_length = Get_RR_File_Name (p_obj->volume, &p_info->dir_record,
- p_info->name, sizeof (p_info->name));
- if (p_info->name_length != -1)
- return 1;
- }
-
- if (p_obj->directory_f) {
- /* dir_record contains only the short directory name (00 byte).
- we have to find the full directory name from the path table. */
-
- path_table_record *ptr;
-
- if (p_obj->dir_record->extent_loc_m ==
- p_obj->volume->pvd.root.extent_loc_m) {
- /* top level object: */
- p_info->name[0] = ':';
- p_info->name_length = 1;
- } else {
- /* other: */
- if (!(ptr = Get_Path_Table_Record (p_obj->volume, p_obj->path_table_pos,
- NULL))) {
- return 0;
- }
- p_info->name_length = ptr->id_length;
- memcpy (p_info->name, ptr->id, ptr->id_length);
- }
- } else {
- p_info->name_length = p_info->dir_record.file_id_length;
- memcpy (p_info->name, p_info->dir_record.file_id, p_info->name_length);
- }
- return 1;
- }
-
- /* Browse all entries in a directory.
- */
-
- int Examine_Next (CDROM_OBJ *p_dir, CDROM_INFO *p_info, unsigned long *p_offset)
- {
- unsigned long offset;
- directory_record *rec;
-
- if (!p_dir->directory_f) {
- iso_errno = ISOERR_BAD_ARGUMENTS;
- return 0;
- }
-
- if (*p_offset == 0) {
- /* skip first two directory entries: */
-
- rec = Get_Directory_Record (p_dir->volume,
- p_dir->dir_record->extent_loc_m,
- 0);
- if (!rec)
- return 0;
-
- offset = rec->length;
-
- rec = Get_Directory_Record (p_dir->volume,
- p_dir->dir_record->extent_loc_m,
- offset);
- if (!rec)
- return 0;
-
- *p_offset = offset + rec->length;
- }
-
- for (;;) {
- if (p_dir->dir_record->data_length_m <= *p_offset)
- return 0;
-
- rec = Get_Directory_Record (p_dir->volume,
- p_dir->dir_record->extent_loc_m,
- *p_offset);
- if (!rec)
- return 0;
-
- if (rec->length == 0)
- /* go to next logical sector: */
- *p_offset = (*p_offset & 0xfffff800) + 2048;
- else
- break;
- }
-
- memcpy (&p_info->dir_record, rec, rec->length);
- *p_offset += rec->length;
-
- if (p_dir->volume->use_rock_ridge) {
- p_info->name_length = Get_RR_File_Name (p_dir->volume, rec,
- p_info->name, sizeof (p_info->name));
- if (p_info->name_length != -1)
- return 1;
- }
-
- p_info->name_length = rec->file_id_length;
- memcpy (p_info->name, rec->file_id, rec->length);
-
-
- return 1;
- }
-
- /* Clone a "CDROM object."
- */
-
- CDROM_OBJ *Clone_Object (CDROM_OBJ *p_object)
- {
- CDROM_OBJ *new = AllocMem (sizeof (CDROM_OBJ), MEMF_PUBLIC);
-
- if (!new)
- return NULL;
- memcpy (new, p_object, sizeof (CDROM_OBJ));
-
- new->dir_record = AllocMem (p_object->dir_record->length, MEMF_PUBLIC);
- if (!new->dir_record) {
- FreeMem (new, sizeof (CDROM_OBJ));
- return NULL;
- }
- memcpy (new->dir_record, p_object->dir_record, p_object->dir_record->length);
-
- return new;
- }
-
- /* Find parent directory.
- */
-
- CDROM_OBJ *Find_Parent_ISO (CDROM_OBJ *p_object)
- {
- path_table_record *ptr;
- directory_record *dir;
- CDROM_OBJ *obj;
-
- obj = AllocMem (sizeof (CDROM_OBJ), MEMF_PUBLIC);
- if (!obj) {
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
-
- obj->pos = 0;
- obj->volume = p_object->volume;
-
- if (!(ptr = Get_Path_Table_Record (p_object->volume, p_object->parent,
- NULL))) {
- FreeMem (obj, sizeof (CDROM_OBJ));
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
-
- if (!(dir = Get_Directory_Record (p_object->volume, ptr->location, 0))) {
- FreeMem (obj, sizeof (CDROM_OBJ));
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
-
- obj->directory_f = TRUE;
- obj->path_table_pos = p_object->parent;
- obj->parent = Get_Parent_Directory_ISO (p_object->volume, p_object->parent);
- obj->dir_record = AllocMem (dir->length, MEMF_PUBLIC);
- if (!obj->dir_record) {
- FreeMem (obj, sizeof (CDROM_OBJ));
- iso_errno = ISOERR_NO_MEMORY;
- return NULL;
- }
- memcpy (obj->dir_record, dir, dir->length);
-
- return obj;
- }
-
- /* Find parent directory on an ISO or Rock Ridge disk.
- */
-
- CDROM_OBJ *Find_Parent (CDROM_OBJ *p_object)
- {
- if (p_object->volume->use_rock_ridge)
- return Find_Parent_RR (p_object);
- else
- return Find_Parent_ISO (p_object);
- }
-
- /* Test if p_object is the root directory.
- */
-
- int Is_Top_Level_Object (CDROM_OBJ *p_object)
- {
- return p_object->directory_f &&
- p_object->dir_record->extent_loc_m ==
- p_object->volume->pvd.root.extent_loc_m;
- }
-
- /* Find a position in a file.
- */
-
- int Seek_Position (CDROM_OBJ *p_object, long p_offset, int p_mode)
- {
- unsigned long new_pos;
- unsigned long max_len = p_object->dir_record->data_length_m;
-
- if (p_object->directory_f) {
- iso_errno = ISOERR_BAD_ARGUMENTS;
- return 0;
- }
-
- switch (p_mode) {
- case SEEK_FROM_START:
- if (p_offset < 0 || p_offset > max_len) {
- iso_errno = ISOERR_OFF_BOUNDS;
- return 0;
- }
- new_pos = p_offset;
- break;
- case SEEK_FROM_CURRENT_POS:
- if ((p_offset < 0 && -p_offset > p_object->pos) ||
- (p_offset > 0 && p_object->pos + p_offset > max_len)) {
- iso_errno = ISOERR_OFF_BOUNDS;
- return 0;
- }
- new_pos = p_object->pos + p_offset;
- break;
- case SEEK_FROM_END:
- if (p_offset > 0 || -p_offset > max_len) {
- iso_errno = ISOERR_OFF_BOUNDS;
- return 0;
- }
- new_pos = max_len + p_offset;
- break;
- default:
- iso_errno = ISOERR_BAD_ARGUMENTS;
- return 0;
- }
- p_object->pos = new_pos;
- return 1;
- }
-