DISK: Add support for ISO 9660 file system detection

Any attach of an ISO 9660 file system is done Read Only since that
file system is logically non-writable.

Related to #1094
This commit is contained in:
Mark Pizzolato 2022-03-19 17:18:54 -07:00
parent 3b06ed7ec9
commit e0700d3b7c
2 changed files with 139 additions and 14 deletions

View file

@ -238,7 +238,7 @@ Host platforms which have libSDL2 available can leverage this functionality.
RAW Disk Access (including CDROM) RAW Disk Access (including CDROM)
Virtual Disk Container files, including differencing disks Virtual Disk Container files, including differencing disks
File System type detection to accurately autosize disks. File System type detection to accurately autosize disks.
Recognized file systems are: DEC ODS1, DEC ODS2, DEC RT11, DEC RSTS, DEC RSX11, Ultrix Partitions Recognized file systems are: DEC ODS1, DEC ODS2, DEC RT11, DEC RSTS, DEC RSX11, Ultrix Partitions, ISO 9660
#### Tape Extensions #### Tape Extensions
AWS format tape support AWS format tape support

View file

@ -361,7 +361,7 @@ static t_stat sim_os_disk_write (UNIT *uptr, t_offset addr, uint8 *buf, uint32 *
static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable, uint32 *is_cdrom); static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable, uint32 *is_cdrom);
static char *HostPathToVhdPath (const char *szHostPath, char *szVhdPath, size_t VhdPathSize); static char *HostPathToVhdPath (const char *szHostPath, char *szVhdPath, size_t VhdPathSize);
static char *VhdPathToHostPath (const char *szVhdPath, char *szHostPath, size_t HostPathSize); static char *VhdPathToHostPath (const char *szVhdPath, char *szHostPath, size_t HostPathSize);
static t_offset get_filesystem_size (UNIT *uptr); static t_offset get_filesystem_size (UNIT *uptr, t_bool *readonly);
struct sim_disk_fmt { struct sim_disk_fmt {
const char *name; /* name */ const char *name; /* name */
@ -566,7 +566,7 @@ if ((uptr->flags & UNIT_ATT) == 0)
return (t_offset)-1; return (t_offset)-1;
physical_size = ctx->container_size; physical_size = ctx->container_size;
sim_quiet = TRUE; sim_quiet = TRUE;
filesystem_size = get_filesystem_size (uptr); filesystem_size = get_filesystem_size (uptr, NULL);
sim_quiet = saved_quiet; sim_quiet = saved_quiet;
if ((filesystem_size == (t_offset)-1) || if ((filesystem_size == (t_offset)-1) ||
(filesystem_size < physical_size)) (filesystem_size < physical_size))
@ -1254,7 +1254,7 @@ ODSChecksum (void *Buffer, uint16 WordCount)
} }
static t_offset get_ods2_filesystem_size (UNIT *uptr, uint32 physsectsz) static t_offset get_ods2_filesystem_size (UNIT *uptr, uint32 physsectsz, t_bool *readonly)
{ {
DEVICE *dptr; DEVICE *dptr;
t_addr saved_capac; t_addr saved_capac;
@ -1335,10 +1335,12 @@ ret_val = ((t_offset)Scb.scb_l_volsize) * 512;
Return_Cleanup: Return_Cleanup:
uptr->capac = saved_capac; uptr->capac = saved_capac;
if (readonly)
*readonly = sim_disk_wrp (uptr);
return ret_val; return ret_val;
} }
static t_offset get_ods1_filesystem_size (UNIT *uptr, uint32 physsectsz) static t_offset get_ods1_filesystem_size (UNIT *uptr, uint32 physsectsz, t_bool *readonly)
{ {
DEVICE *dptr; DEVICE *dptr;
t_addr saved_capac; t_addr saved_capac;
@ -1394,6 +1396,8 @@ sim_messagef (SCPE_OK, "%s: Volume Name: %12.12s Format: %12.12s Sectors In Volu
sim_uname (uptr), Home.hm1_t_volname, Home.hm1_t_format, (uint32)(ret_val / 512)); sim_uname (uptr), Home.hm1_t_volname, Home.hm1_t_format, (uint32)(ret_val / 512));
Return_Cleanup: Return_Cleanup:
uptr->capac = saved_capac; uptr->capac = saved_capac;
if (readonly)
*readonly = sim_disk_wrp (uptr);
return ret_val; return ret_val;
} }
@ -1409,7 +1413,7 @@ typedef struct ultrix_disklabel {
#define PT_MAGIC 0x032957 /* Partition magic number */ #define PT_MAGIC 0x032957 /* Partition magic number */
#define PT_VALID 1 /* Indicates if struct is valid */ #define PT_VALID 1 /* Indicates if struct is valid */
static t_offset get_ultrix_filesystem_size (UNIT *uptr, uint32 physsectsz) static t_offset get_ultrix_filesystem_size (UNIT *uptr, uint32 physsectsz, t_bool *readonly)
{ {
DEVICE *dptr; DEVICE *dptr;
t_addr saved_capac; t_addr saved_capac;
@ -1447,6 +1451,106 @@ ret_val = ((t_offset)max_lbn) * 512;
Return_Cleanup: Return_Cleanup:
uptr->capac = saved_capac; uptr->capac = saved_capac;
if (readonly)
*readonly = sim_disk_wrp (uptr);
return ret_val;
}
/* ISO 9660 Volume Recognizer - Structure Info gathered from: https://wiki.osdev.org/ISO_9660 */
typedef struct ISO_9660_Volume_Descriptor {
uint8 Type; // Volume Descriptor type code (0, 1, 2, 3 and 255)
uint8 Identifier[5]; // Always 'CD001'.
uint8 Version; // Volume Descriptor Version (0x01).
uint8 Data[2041]; // Depends on the volume descriptor type.
} ISO_9660_Volume_Descriptor;
typedef struct ISO_9660_Primary_Volume_Descriptor {
uint8 Type; // Always 0x01 for a Primary Volume Descriptor.
uint8 Identifier[5]; // Always 'CD001'.
uint8 Version; // Always 0x01.
uint8 Unused; // Always 0x00.
uint8 SystemIdentifier[32]; // The name of the system that can act upon sectors 0x00-0x0F for the volume.
uint8 VolumeIdentifier[32]; // Identification of this volume.
uint8 UnusedField[8]; // All zeros.
uint32 VolumeSpaceSize[2]; // Number of Logical Blocks in which the volume is recorded
uint8 UnusedField2[32]; // All zeroes.
uint16 VolumeSetSize[2]; // The size of the set in this logical volume (number of disks).
uint16 VolumeSequenceNumber[2]; // The number of this disk in the Volume Set.
uint16 LogicalBlockSize[2]; // The size in bytes of a logical block. NB: This means that a logical block on a CD could be something other than 2 KiB!
uint32 PathTableSize[2]; // The size in bytes of the path table.
uint32 LocationTypeLPathTable; // LBA location of the path table. The path table pointed to contains only little-endian values.
uint32 LocationOptTypeLPathTable; // LBA location of the optional path table. The path table pointed to contains only little-endian values. Zero means that no optional path table exists.
uint32 LocationTypeMPathTable; // LBA location of the path table. The path table pointed to contains only big-endian values.
uint32 LocationOptTypeMPathTable; // LBA location of the optional path table. The path table pointed to contains only big-endian values. Zero means that no optional path table exists.
uint8 DirectoryEntryRootDirectory[34];// Note that this is not an LBA address, it is the actual Directory Record, which contains a single byte Directory Identifier (0x00), hence the fixed 34 byte size.
uint8 VolumeSetIdentifier[128]; // Identifier of the volume set of which this volume is a member.
uint8 PublisherIdentifier[128]; // The volume publisher. For extended publisher information, the first byte should be 0x5F, followed by the filename of a file in the root directory. If not specified, all bytes should be 0x20.
uint8 DataPreparerIdentifier[128];// The identifier of the person(s) who prepared the data for this volume. For extended preparation information, the first byte should be 0x5F, followed by the filename of a file in the root directory. If not specified, all bytes should be 0x20.
uint8 ApplicationIdentifier[128]; // Identifies how the data are recorded on this volume. For extended information, the first byte should be 0x5F, followed by the filename of a file in the root directory. If not specified, all bytes should be 0x20.
uint8 CopyrightFileIdentifier[37];// Filename of a file in the root directory that contains copyright information for this volume set. If not specified, all bytes should be 0x20.
uint8 AbstractFileIdentifier[37]; // Filename of a file in the root directory that contains abstract information for this volume set. If not specified, all bytes should be 0x20.
uint8 BibliographicFileIdentifier[37];// Filename of a file in the root directory that contains bibliographic information for this volume set. If not specified, all bytes should be 0x20.
uint8 VolumeCreationDateTime[17]; // The date and time of when the volume was created.
uint8 VolumeModificationDateTime[17];// The date and time of when the volume was modified.
uint8 VolumeExpirationDateTime[17];// The date and time after which this volume is considered to be obsolete. If not specified, then the volume is never considered to be obsolete.
uint8 VolumeEffectiveDateTime[17];// The date and time after which the volume may be used. If not specified, the volume may be used immediately.
uint8 FileStructureVersion; // The directory records and path table version (always 0x01).
uint8 Unused2; // Always 0x00.
uint8 ApplicationUsed[512]; // Contents not defined by ISO 9660.
uint8 Reserved[653]; // Reserved by ISO.
} ISO_9660_Primary_Volume_Descriptor;
static t_offset get_iso9660_filesystem_size (UNIT *uptr, uint32 physsectsz, t_bool *readonly)
{
DEVICE *dptr;
t_addr saved_capac;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
t_addr temp_capac = (sim_toffset_64 ? (t_addr)0xFFFFFFFFu : (t_addr)0x7FFFFFFFu); /* Make sure we can access the largest sector */
uint8 sector_buf[2048];
ISO_9660_Volume_Descriptor *Desc = (ISO_9660_Volume_Descriptor *)sector_buf;
uint8 primary_buf[2048];
ISO_9660_Primary_Volume_Descriptor *Primary = NULL;
t_lba sectfactor = sizeof (*Desc) / ctx->sector_size;
t_offset ret_val = (t_offset)-1;
t_offset cur_pos = 32768; /* Beyond the boot area of an ISO 9660 image */
t_seccnt sectsread;
int read_count = 0;
if ((dptr = find_dev_from_unit (uptr)) == NULL)
return ret_val;
saved_capac = uptr->capac;
uptr->capac = temp_capac;
while (sim_disk_rdsect(uptr, (t_lba)(sectfactor * cur_pos / sizeof (*Desc)), (uint8 *)Desc, &sectsread, sectfactor) == DKSE_OK) {
if ((sectsread != sectfactor) ||
(Desc->Version != 1) ||
(0 != memcmp (Desc->Identifier, "CD001", sizeof (Desc->Identifier))))
break;
if (Desc->Type == 1) { /* Primary Volume Descriptor */
Primary = (ISO_9660_Primary_Volume_Descriptor *)primary_buf;
*Primary = *(ISO_9660_Primary_Volume_Descriptor *)Desc;
}
cur_pos += sizeof (*Desc);
++read_count;
if ((Desc->Type == 255) ||
(read_count >= 32)) {
ret_val = ctx->container_size;
sim_messagef (SCPE_OK, "%s: '%s' Contains an ISO 9660 filesystem\n", sim_uname (uptr), uptr->filename);
if (Primary) {
char VolId[sizeof (Primary->VolumeIdentifier) + 1];
memcpy (VolId, Primary->VolumeIdentifier, sizeof (Primary->VolumeIdentifier));
VolId[sizeof (Primary->VolumeIdentifier)] = '\0';
sim_messagef (SCPE_OK, "%s: Volume Identifier: %s Containing %u %u Byte Sectors\n", sim_uname (uptr), sim_trim_endspc (VolId), (uint32)(ctx->container_size / Primary->LogicalBlockSize[1 - sim_end]), (uint32)Primary->LogicalBlockSize[1 - sim_end]);
}
break;
}
}
uptr->capac = saved_capac;
if (readonly)
*readonly = sim_disk_wrp (uptr) || (ret_val != (t_offset)-1);
return ret_val; return ret_val;
} }
@ -1856,7 +1960,7 @@ if (uar != 0) {
return SCPE_IOERR; return SCPE_IOERR;
} }
static t_offset get_rsts_filesystem_size (UNIT *uptr, uint32 physsectsz) static t_offset get_rsts_filesystem_size (UNIT *uptr, uint32 physsectsz, t_bool *readonly)
{ {
DEVICE *dptr; DEVICE *dptr;
t_addr saved_capac; t_addr saved_capac;
@ -1936,6 +2040,8 @@ for (context.dcshift = 0; context.dcshift < 8; context.dcshift++) {
} }
cleanup_done: cleanup_done:
uptr->capac = saved_capac; uptr->capac = saved_capac;
if (readonly)
*readonly = sim_disk_wrp (uptr);
return ret_val; return ret_val;
} }
@ -2018,7 +2124,7 @@ if (strncmp((char *)&home->hb_b_sysid, HB_C_VMSSYSID, strlen(HB_C_VMSSYSID)) ==
return RT11_NOPART; return RT11_NOPART;
} }
static t_offset get_rt11_filesystem_size (UNIT *uptr, uint32 physsectsz) static t_offset get_rt11_filesystem_size (UNIT *uptr, uint32 physsectsz, t_bool *readonly)
{ {
DEVICE *dptr; DEVICE *dptr;
t_addr saved_capac; t_addr saved_capac;
@ -2149,19 +2255,22 @@ if (partitions) {
sim_messagef (SCPE_OK, "%d valid partition%s, Type: %s, Sectors On Disk: %u\n", partitions, partitions == 1 ? "" : "s", parttype, (uint32)(ret_val / 512)); sim_messagef (SCPE_OK, "%d valid partition%s, Type: %s, Sectors On Disk: %u\n", partitions, partitions == 1 ? "" : "s", parttype, (uint32)(ret_val / 512));
} }
uptr->capac = saved_capac; uptr->capac = saved_capac;
if (readonly)
*readonly = sim_disk_wrp (uptr);
return ret_val; return ret_val;
} }
t_offset pseudo_filesystem_size = 0; /* Dummy file system check return used during testing */ t_offset pseudo_filesystem_size = 0; /* Dummy file system check return used during testing */
typedef t_offset (*FILESYSTEM_CHECK)(UNIT *uptr, uint32); typedef t_offset (*FILESYSTEM_CHECK)(UNIT *uptr, uint32, t_bool *);
static t_offset get_filesystem_size (UNIT *uptr) static t_offset get_filesystem_size (UNIT *uptr, t_bool *readonly)
{ {
static FILESYSTEM_CHECK checks[] = { static FILESYSTEM_CHECK checks[] = {
&get_ods2_filesystem_size, &get_ods2_filesystem_size,
&get_ods1_filesystem_size, &get_ods1_filesystem_size,
&get_ultrix_filesystem_size, &get_ultrix_filesystem_size,
&get_iso9660_filesystem_size,
&get_rsts_filesystem_size, &get_rsts_filesystem_size,
&get_rt11_filesystem_size, /* This should be the last entry &get_rt11_filesystem_size, /* This should be the last entry
in the table to reduce the in the table to reduce the
@ -2181,7 +2290,7 @@ if (pseudo_filesystem_size != 0) { /* Dummy file system size mechanism? */
} }
for (i = 0; checks[i] != NULL; i++) for (i = 0; checks[i] != NULL; i++)
if ((ret_val = checks[i] (uptr, 0)) != (t_offset)-1) if ((ret_val = checks[i] (uptr, 0, readonly)) != (t_offset)-1)
return ret_val; return ret_val;
/* /*
* The only known interleaved disk devices have either 256 byte * The only known interleaved disk devices have either 256 byte
@ -2192,10 +2301,10 @@ for (i = 0; checks[i] != NULL; i++)
for (i = 0; checks[i] != NULL; i++) { for (i = 0; checks[i] != NULL; i++) {
ctx->sector_size = 256; ctx->sector_size = 256;
if ((ret_val = checks[i] (uptr, ctx->sector_size)) != (t_offset)-1) if ((ret_val = checks[i] (uptr, ctx->sector_size, readonly)) != (t_offset)-1)
break; break;
ctx->sector_size = 128; ctx->sector_size = 128;
if ((ret_val = checks[i] (uptr, ctx->sector_size)) != (t_offset)-1) if ((ret_val = checks[i] (uptr, ctx->sector_size, readonly)) != (t_offset)-1)
break; break;
} }
if ((ret_val != (t_offset)-1) && (ctx->sector_size != saved_sector_size )) if ((ret_val != (t_offset)-1) && (ctx->sector_size != saved_sector_size ))
@ -3022,7 +3131,7 @@ if (get_disk_footer (uptr) != SCPE_OK) {
sim_disk_detach (uptr); sim_disk_detach (uptr);
return SCPE_OPENERR; return SCPE_OPENERR;
} }
filesystem_size = get_filesystem_size (uptr); filesystem_size = get_filesystem_size (uptr, NULL);
if (filesystem_size != (t_offset)-1) if (filesystem_size != (t_offset)-1)
filesystem_size += reserved_sectors * sector_size; filesystem_size += reserved_sectors * sector_size;
container_size = sim_disk_size (uptr); container_size = sim_disk_size (uptr);
@ -3165,6 +3274,22 @@ if (container_size && (container_size != (t_offset)-1)) {
} }
} }
if ((uptr->flags & UNIT_RO) == 0) {
t_bool readonly;
int32 saved_quiet = sim_quiet;
sim_quiet = 1;
get_filesystem_size (uptr, &readonly);
if (readonly) {
sim_disk_detach (uptr);
sim_switches |= SWMASK ('R');
sim_disk_attach_ex2 (uptr, cptr, sector_size, xfer_element_size, dontchangecapac,
dbit, dtype, pdp11tracksize, completion_delay, drivetypes,
reserved_sectors);
}
sim_quiet = saved_quiet;
}
if (dtype && (created || (autosized && (ctx->footer == NULL)))) if (dtype && (created || (autosized && (ctx->footer == NULL))))
store_disk_footer (uptr, dtype); store_disk_footer (uptr, dtype);