(root)/
util-linux-2.39/
libblkid/
src/
superblocks/
iso9660.c
       1  /*
       2   * Copyright (C) 1999 by Andries Brouwer
       3   * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
       4   * Copyright (C) 2001 by Andreas Dilger
       5   * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
       6   * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
       7   *
       8   * Inspired also by libvolume_id by
       9   *     Kay Sievers <kay.sievers@vrfy.org>
      10   *
      11   * This file may be redistributed under the terms of the
      12   * GNU Lesser General Public License.
      13   */
      14  #include <stdio.h>
      15  #include <stdlib.h>
      16  #include <unistd.h>
      17  #include <string.h>
      18  #include <stdint.h>
      19  #include <ctype.h>
      20  
      21  #include "superblocks.h"
      22  #include "cctype.h"
      23  #include "iso9660.h"
      24  
      25  struct iso9660_date {
      26  	unsigned char year[4];
      27  	unsigned char month[2];
      28  	unsigned char day[2];
      29  	unsigned char hour[2];
      30  	unsigned char minute[2];
      31  	unsigned char second[2];
      32  	unsigned char hundredth[2];
      33  	unsigned char offset;
      34  } __attribute__ ((packed));
      35  
      36  /* PVD - Primary volume descriptor */
      37  struct iso_volume_descriptor {
      38  	unsigned char	vd_type;
      39  	unsigned char	vd_id[5];
      40  	unsigned char	vd_version;
      41  	unsigned char	flags;
      42  	unsigned char	system_id[32];
      43  	unsigned char	volume_id[32];
      44  	unsigned char	unused[8];
      45  	unsigned char	space_size[8];
      46  	unsigned char	escape_sequences[8];
      47  	unsigned char  unused2[32];
      48  	unsigned char  logical_block_size[4];
      49  	unsigned char  unused3[58];
      50  	unsigned char  volume_set_id[128];
      51  	unsigned char  publisher_id[128];
      52  	unsigned char  data_preparer_id[128];
      53  	unsigned char  application_id[128];
      54  	unsigned char  unused4[111];
      55  	struct iso9660_date created;
      56  	struct iso9660_date modified;
      57  } __attribute__((packed));
      58  
      59  /* Boot Record */
      60  struct boot_record {
      61  	unsigned char	vd_type;
      62  	unsigned char	vd_id[5];
      63  	unsigned char	vd_version;
      64  	unsigned char	boot_system_id[32];
      65  	unsigned char	boot_id[32];
      66  	unsigned char	unused[1];
      67  } __attribute__((packed));
      68  
      69  #define ISO_SUPERBLOCK_OFFSET		0x8000
      70  #define ISO_SECTOR_SIZE			0x800
      71  #define ISO_VD_BOOT_RECORD		0x0
      72  #define ISO_VD_PRIMARY			0x1
      73  #define ISO_VD_SUPPLEMENTARY		0x2
      74  #define ISO_VD_END			0xff
      75  #define ISO_VD_MAX			16
      76  /* maximal string field size used anywhere in ISO; update if necessary */
      77  #define ISO_MAX_FIELDSIZ  sizeof_member(struct iso_volume_descriptor, volume_set_id)
      78  
      79  struct high_sierra_volume_descriptor {
      80  	unsigned char	foo[8];
      81  	unsigned char	type;
      82  	unsigned char	id[5];
      83  	unsigned char	version;
      84  	unsigned char	unused1;
      85  	unsigned char	system_id[32];
      86  	unsigned char   volume_id[32];
      87  } __attribute__((packed));
      88  
      89  /* old High Sierra format */
      90  static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag)
      91  {
      92  	struct high_sierra_volume_descriptor *iso;
      93  
      94  	iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor);
      95  	if (!iso)
      96  		return errno ? -errno : 1;
      97  
      98  	blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE);
      99  	blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE);
     100  	blkid_probe_set_version(pr, "High Sierra");
     101  	blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id));
     102  	return 0;
     103  }
     104  
     105  static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date)
     106  {
     107  	unsigned char buffer[16];
     108  	unsigned int i, zeros = 0;
     109  
     110  	buffer[0] = date->year[0];
     111  	buffer[1] = date->year[1];
     112  	buffer[2] = date->year[2];
     113  	buffer[3] = date->year[3];
     114  	buffer[4] = date->month[0];
     115  	buffer[5] = date->month[1];
     116  	buffer[6] = date->day[0];
     117  	buffer[7] = date->day[1];
     118  	buffer[8] = date->hour[0];
     119  	buffer[9] = date->hour[1];
     120  	buffer[10] = date->minute[0];
     121  	buffer[11] = date->minute[1];
     122  	buffer[12] = date->second[0];
     123  	buffer[13] = date->second[1];
     124  	buffer[14] = date->hundredth[0];
     125  	buffer[15] = date->hundredth[1];
     126  
     127  	/* count the number of zeros ('0') in the date buffer */
     128  	for (i = 0, zeros = 0; i < sizeof(buffer); i++)
     129  		if (buffer[i] == '0')
     130  			zeros++;
     131  
     132  	/* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */
     133  	if (zeros == sizeof(buffer) && date->offset == 0)
     134  		return 0;
     135  
     136  	/* generate an UUID using this date and return success */
     137  	blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer),
     138  		"%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
     139  		buffer[0], buffer[1], buffer[2], buffer[3],
     140  		buffer[4], buffer[5],
     141  		buffer[6], buffer[7],
     142  		buffer[8], buffer[9],
     143  		buffer[10], buffer[11],
     144  		buffer[12], buffer[13],
     145  		buffer[14], buffer[15]);
     146  
     147  	return 1;
     148  }
     149  
     150  static int is_str_empty(const unsigned char *str, size_t len)
     151  {
     152  	size_t i;
     153  
     154  	if (!str || !*str)
     155  		return 1;
     156  
     157  	for (i = 0; i < len; i++)
     158  		if (!isspace(str[i]))
     159  			return 0;
     160  	return 1;
     161  }
     162  
     163  static int is_utf16be_str_empty(unsigned char *utf16, size_t len)
     164  {
     165  	size_t i;
     166  
     167  	for (i = 0; i < len; i += 2) {
     168  		if (utf16[i] != 0x0 || !isspace(utf16[i + 1]))
     169  			return 0;
     170  	}
     171  	return 1;
     172  }
     173  
     174  /* if @utf16 is prefix of @ascii (ignoring non-representable characters and upper-case conversion)
     175   * then reconstruct prefix from @utf16 and @ascii, append suffix from @ascii, fill it into @out
     176   * and returns length of bytes written into @out; otherwise returns zero */
     177  static size_t merge_utf16be_ascii(unsigned char *out, size_t out_len, const unsigned char *utf16, const unsigned char *ascii, size_t len)
     178  {
     179  	size_t o, a, u;
     180  
     181  	for (o = 0, a = 0, u = 0; u + 1 < len && a < len && o + 1 < out_len; o += 2, a++, u += 2) {
     182  		/* Surrogate pair with code point above U+FFFF */
     183  		if (utf16[u] >= 0xD8 && utf16[u] <= 0xDB && u + 3 < len &&
     184  		    utf16[u + 2] >= 0xDC && utf16[u + 2] <= 0xDF) {
     185  			out[o++] = utf16[u++];
     186  			out[o++] = utf16[u++];
     187  		}
     188  		/* Value '_' is replacement for non-representable character */
     189  		if (ascii[a] == '_') {
     190  			out[o] = utf16[u];
     191  			out[o + 1] = utf16[u + 1];
     192  		} else if (utf16[u] == 0x00 && utf16[u + 1] == '_') {
     193  			out[o] = 0x00;
     194  			out[o + 1] = ascii[a];
     195  		} else if (utf16[u] == 0x00 && c_toupper(ascii[a]) == c_toupper(utf16[u + 1])) {
     196  			out[o] = 0x00;
     197  			out[o + 1] = c_isupper(ascii[a]) ? utf16[u + 1] : ascii[a];
     198  		} else {
     199  			return 0;
     200  		}
     201  	}
     202  
     203  	for (; a < len && o + 1 < out_len; o += 2, a++) {
     204  		out[o] = 0x00;
     205  		out[o + 1] = ascii[a];
     206  	}
     207  
     208  	return o;
     209  }
     210  
     211  /* iso9660 [+ Microsoft Joliet Extension] */
     212  static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
     213  {
     214  	struct boot_record *boot = NULL;
     215  	struct iso_volume_descriptor *pvd = NULL;
     216  	struct iso_volume_descriptor *joliet = NULL;
     217  	/* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */
     218  	unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2];
     219  	size_t len;
     220  	int is_unicode_empty;
     221  	int is_ascii_empty;
     222  	int i;
     223  	uint64_t off;
     224  
     225  	if (blkid_probe_get_hint(pr, mag->hoff, &off) < 0)
     226  		off = 0;
     227  
     228  	if (off % ISO_SECTOR_SIZE)
     229  		return 1;
     230  
     231  	if (strcmp(mag->magic, "CDROM") == 0)
     232  		return probe_iso9660_hsfs(pr, mag);
     233  
     234  	for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || !joliet); i++, off += ISO_SECTOR_SIZE) {
     235  		unsigned char *desc =
     236  			blkid_probe_get_buffer(pr,
     237  					off,
     238  					max(sizeof(struct boot_record),
     239  					    sizeof(struct iso_volume_descriptor)));
     240  
     241  		if (desc == NULL || desc[0] == ISO_VD_END)
     242  			break;
     243  		else if (!boot && desc[0] == ISO_VD_BOOT_RECORD)
     244  			boot = (struct boot_record *)desc;
     245  		else if (!pvd && desc[0] == ISO_VD_PRIMARY)
     246  			pvd = (struct iso_volume_descriptor *)desc;
     247  		else if (!joliet && desc[0] == ISO_VD_SUPPLEMENTARY) {
     248  			joliet = (struct iso_volume_descriptor *)desc;
     249  			if (memcmp(joliet->escape_sequences, "%/@", 3) != 0 &&
     250  			    memcmp(joliet->escape_sequences, "%/C", 3) != 0 &&
     251  			    memcmp(joliet->escape_sequences, "%/E", 3) != 0)
     252  				joliet = NULL;
     253  		}
     254  	}
     255  
     256  	if (!pvd)
     257  		return errno ? -errno : 1;
     258  
     259  	uint16_t logical_block_size = isonum_723(pvd->logical_block_size, false);
     260  	uint32_t space_size = isonum_733(pvd->space_size, false);
     261  
     262  	blkid_probe_set_fsblocksize(pr, logical_block_size);
     263  	blkid_probe_set_block_size(pr, logical_block_size);
     264  	blkid_probe_set_fssize(pr, (uint64_t) space_size * logical_block_size);
     265  
     266  	if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->system_id, pvd->system_id, sizeof(pvd->system_id))) != 0)
     267  		blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", buf, len, UL_ENCODE_UTF16BE);
     268  	else if (joliet)
     269  		blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", joliet->system_id, sizeof(joliet->system_id), UL_ENCODE_UTF16BE);
     270  	else
     271  		blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id));
     272  
     273  	if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0)
     274  		blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE);
     275  	else if (joliet)
     276  		blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE);
     277  	else
     278  		blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->volume_set_id, sizeof(pvd->volume_set_id));
     279  
     280  	is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_');
     281  	is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_'));
     282  	if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0)
     283  		blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE);
     284  	else if (!is_unicode_empty)
     285  		blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE);
     286  	else if (!is_ascii_empty)
     287  		blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->publisher_id, sizeof(pvd->publisher_id));
     288  
     289  	is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_');
     290  	is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_'));
     291  	if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0)
     292  		blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE);
     293  	else if (!is_unicode_empty)
     294  		blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE);
     295  	else if (!is_ascii_empty)
     296  		blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->data_preparer_id, sizeof(pvd->data_preparer_id));
     297  
     298  	is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_');
     299  	is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_'));
     300  	if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0)
     301  		blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE);
     302  	else if (!is_unicode_empty)
     303  		blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE);
     304  	else if (!is_ascii_empty)
     305  		blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->application_id, sizeof(pvd->application_id));
     306  
     307  	/* create an UUID using the modified/created date */
     308  	if (! probe_iso9660_set_uuid(pr, &pvd->modified))
     309  		probe_iso9660_set_uuid(pr, &pvd->created);
     310  
     311  	if (boot)
     312  		blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID",
     313  					boot->boot_system_id,
     314  					sizeof(boot->boot_system_id));
     315  
     316  	if (joliet)
     317  		blkid_probe_set_version(pr, "Joliet Extension");
     318  
     319  	/* Label in Joliet is UNICODE (UTF16BE) but can contain only 16 characters. Label in PVD is
     320  	 * subset of ASCII but can contain up to the 32 characters. Non-representable characters are
     321  	 * stored as replacement character '_'. Label in Joliet is in most cases trimmed but UNICODE
     322  	 * version of label in PVD. Based on these facts try to reconstruct original label if label
     323  	 * in Joliet is prefix of the label in PVD (ignoring non-representable characters).
     324  	 */
     325  	if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_id, pvd->volume_id, sizeof(pvd->volume_id))) != 0)
     326  		blkid_probe_set_utf8label(pr, buf, len, UL_ENCODE_UTF16BE);
     327  	else if (joliet)
     328  		blkid_probe_set_utf8label(pr, joliet->volume_id, sizeof(joliet->volume_id), UL_ENCODE_UTF16BE);
     329  	else
     330  		blkid_probe_set_label(pr, pvd->volume_id, sizeof(pvd->volume_id));
     331  
     332  	return 0;
     333  }
     334  
     335  const struct blkid_idinfo iso9660_idinfo =
     336  {
     337  	.name		= "iso9660",
     338  	.usage		= BLKID_USAGE_FILESYSTEM,
     339  	.probefunc	= probe_iso9660,
     340  	.flags		= BLKID_IDINFO_TOLERANT,
     341  	.magics		=
     342  	{
     343  		{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
     344  		{ .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" },
     345  		{ NULL }
     346  	}
     347  };
     348