Home | History | Annotate | Download | only in iso9660
      1 #include <dprintf.h>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <sys/dirent.h>
      5 #include <core.h>
      6 #include <cache.h>
      7 #include <disk.h>
      8 #include <fs.h>
      9 #include <stdlib.h>
     10 #include "iso9660_fs.h"
     11 #include "susp_rr.h"
     12 
     13 /* Convert to lower case string */
     14 static inline char iso_tolower(char c)
     15 {
     16     if (c >= 'A' && c <= 'Z')
     17 	c += 0x20;
     18 
     19     return c;
     20 }
     21 
     22 static struct inode *new_iso_inode(struct fs_info *fs)
     23 {
     24     return alloc_inode(fs, 0, sizeof(struct iso9660_pvt_inode));
     25 }
     26 
     27 static inline struct iso_sb_info *ISO_SB(struct fs_info *fs)
     28 {
     29     return fs->fs_info;
     30 }
     31 
     32 static size_t iso_convert_name(char *dst, const char *src, int len)
     33 {
     34     char *p = dst;
     35     char c;
     36 
     37     if (len == 1) {
     38 	switch (*src) {
     39 	case 1:
     40 	    *p++ = '.';
     41 	    /* fall through */
     42 	case 0:
     43 	    *p++ = '.';
     44 	    goto done;
     45 	default:
     46 	    /* nothing special */
     47 	    break;
     48 	}
     49     }
     50 
     51     while (len-- && (c = *src++)) {
     52 	if (c == ';')	/* Remove any filename version suffix */
     53 	    break;
     54 	*p++ = iso_tolower(c);
     55     }
     56 
     57     /* Then remove any terminal dots */
     58     while (p > dst+1 && p[-1] == '.')
     59 	p--;
     60 
     61 done:
     62     *p = '\0';
     63     return p - dst;
     64 }
     65 
     66 /*
     67  * Unlike strcmp, it does return 1 on match, or reutrn 0 if not match.
     68  */
     69 static bool iso_compare_name(const char *de_name, size_t len,
     70 			     const char *file_name)
     71 {
     72     char iso_file_name[256];
     73     char *p = iso_file_name;
     74     char c1, c2;
     75     int i;
     76 
     77     i = iso_convert_name(iso_file_name, de_name, len);
     78     (void)i;
     79     dprintf("Compare: \"%s\" to \"%s\" (len %zu)\n",
     80 	    file_name, iso_file_name, i);
     81 
     82     do {
     83 	c1 = *p++;
     84 	c2 = iso_tolower(*file_name++);
     85 
     86 	/* compare equal except for case? */
     87 	if (c1 != c2)
     88 	    return false;
     89     } while (c1);
     90 
     91     return true;
     92 }
     93 
     94 /*
     95  * Find a entry in the specified dir with name _dname_.
     96  */
     97 static const struct iso_dir_entry *
     98 iso_find_entry(const char *dname, struct inode *inode)
     99 {
    100     struct fs_info *fs = inode->fs;
    101     block_t dir_block = PVT(inode)->lba;
    102     int i = 0, offset = 0;
    103     const char *de_name;
    104     int de_name_len, de_len, rr_name_len, ret;
    105     const struct iso_dir_entry *de;
    106     const char *data = NULL;
    107     char *rr_name = NULL;
    108 
    109     dprintf("iso_find_entry: \"%s\"\n", dname);
    110 
    111     while (1) {
    112 	if (!data) {
    113 	    dprintf("Getting block %d from block %llu\n", i, dir_block);
    114 	    if (++i > inode->blocks)
    115 		return NULL;	/* End of directory */
    116 	    data = get_cache(fs->fs_dev, dir_block++);
    117 	    offset = 0;
    118 	}
    119 
    120 	de = (const struct iso_dir_entry *)(data + offset);
    121 	de_len = de->length;
    122 	offset += de_len;
    123 
    124 	/* Make sure we have a full directory entry */
    125 	if (de_len < 33 || offset > BLOCK_SIZE(fs)) {
    126 	    /*
    127 	     * Zero = end of sector, or corrupt directory entry
    128 	     *
    129 	     * ECMA-119:1987 6.8.1.1: "Each Directory Record shall end
    130 	     * in the Logical Sector in which it begins.
    131 	     */
    132 	    data = NULL;
    133 	    continue;
    134 	}
    135 
    136 	/* Try to get Rock Ridge name */
    137 	ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &rr_name_len);
    138 	if (ret > 0) {
    139 	    if (strcmp(rr_name, dname) == 0) {
    140 		dprintf("Found (by RR name).\n");
    141 		free(rr_name);
    142 		return de;
    143 	    }
    144 	    free(rr_name);
    145 	    rr_name = NULL;
    146 	    continue; /* Rock Ridge was valid and did not match */
    147 	}
    148 
    149 	/* Fall back to ISO name */
    150 	de_name_len = de->name_len;
    151 	de_name = de->name;
    152 	if (iso_compare_name(de_name, de_name_len, dname)) {
    153 	    dprintf("Found (by ISO name).\n");
    154 	    return de;
    155 	}
    156     }
    157 }
    158 
    159 static inline enum dirent_type get_inode_mode(uint8_t flags)
    160 {
    161     return (flags & 0x02) ? DT_DIR : DT_REG;
    162 }
    163 
    164 static struct inode *iso_get_inode(struct fs_info *fs,
    165 				   const struct iso_dir_entry *de)
    166 {
    167     struct inode *inode = new_iso_inode(fs);
    168     int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs);
    169 
    170     if (!inode)
    171 	return NULL;
    172 
    173     dprintf("Getting inode for: %.*s\n", de->name_len, de->name);
    174 
    175     inode->mode   = get_inode_mode(de->flags);
    176     inode->size   = de->size_le;
    177     PVT(inode)->lba = de->extent_le;
    178     inode->blocks = (inode->size + BLOCK_SIZE(fs) - 1) >> BLOCK_SHIFT(fs);
    179 
    180     /* We have a single extent for all data */
    181     inode->next_extent.pstart = (sector_t)de->extent_le << blktosec;
    182     inode->next_extent.len    = (sector_t)inode->blocks << blktosec;
    183 
    184     return inode;
    185 }
    186 
    187 static struct inode *iso_iget_root(struct fs_info *fs)
    188 {
    189     const struct iso_dir_entry *root = &ISO_SB(fs)->root;
    190 
    191     return iso_get_inode(fs, root);
    192 }
    193 
    194 static struct inode *iso_iget(const char *dname, struct inode *parent)
    195 {
    196     const struct iso_dir_entry *de;
    197 
    198     dprintf("iso_iget %p %s\n", parent, dname);
    199 
    200     de = iso_find_entry(dname, parent);
    201     if (!de)
    202 	return NULL;
    203 
    204     return iso_get_inode(parent->fs, de);
    205 }
    206 
    207 static int iso_readdir(struct file *file, struct dirent *dirent)
    208 {
    209     struct fs_info *fs = file->fs;
    210     struct inode *inode = file->inode;
    211     const struct iso_dir_entry *de;
    212     const char *data = NULL;
    213     char *rr_name = NULL;
    214     int name_len, ret;
    215 
    216     while (1) {
    217 	size_t offset = file->offset & (BLOCK_SIZE(fs) - 1);
    218 
    219 	if (!data) {
    220 	    uint32_t i = file->offset >> BLOCK_SHIFT(fs);
    221 	    if (i >= inode->blocks)
    222 		return -1;
    223 	    data = get_cache(fs->fs_dev, PVT(inode)->lba + i);
    224 	}
    225 	de = (const struct iso_dir_entry *)(data + offset);
    226 
    227 	if (de->length < 33 || offset + de->length > BLOCK_SIZE(fs)) {
    228 	    file->offset = (file->offset + BLOCK_SIZE(fs))
    229 		& ~(BLOCK_SIZE(fs) - 1); /* Start of the next block */
    230 	    data = NULL;
    231 	    continue;
    232 	}
    233 	break;
    234     }
    235 
    236     dirent->d_ino = 0;           /* Inode number is invalid to ISO fs */
    237     dirent->d_off = file->offset;
    238     dirent->d_type = get_inode_mode(de->flags);
    239 
    240     /* Try to get Rock Ridge name */
    241     ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &name_len);
    242     if (ret > 0) {
    243 	memcpy(dirent->d_name, rr_name, name_len + 1);
    244 	free(rr_name);
    245 	rr_name = NULL;
    246     } else {
    247 	name_len = iso_convert_name(dirent->d_name, de->name, de->name_len);
    248     }
    249 
    250     dirent->d_reclen = offsetof(struct dirent, d_name) + 1 + name_len;
    251 
    252     file->offset += de->length;  /* Update for next reading */
    253 
    254     return 0;
    255 }
    256 
    257 /* Load the config file, return 1 if failed, or 0 */
    258 static int iso_open_config(struct com32_filedata *filedata)
    259 {
    260     static const char *search_directories[] = {
    261 	"/boot/isolinux",
    262 	"/isolinux",
    263 	"/boot/syslinux",
    264 	"/syslinux",
    265 	"/",
    266 	NULL
    267     };
    268     static const char *filenames[] = {
    269 	"isolinux.cfg",
    270 	"syslinux.cfg",
    271 	NULL
    272     };
    273 
    274     return search_dirs(filedata, search_directories, filenames, ConfigName);
    275 }
    276 
    277 static int iso_fs_init(struct fs_info *fs)
    278 {
    279     struct iso_sb_info *sbi;
    280     char pvd[2048];		/* Primary Volume Descriptor */
    281     uint32_t pvd_lba;
    282     struct disk *disk = fs->fs_dev->disk;
    283     int blktosec;
    284 
    285     sbi = malloc(sizeof(*sbi));
    286     if (!sbi) {
    287 	malloc_error("iso_sb_info structure");
    288 	return 1;
    289     }
    290     fs->fs_info = sbi;
    291 
    292     /*
    293      * XXX: handling iso9660 in hybrid mode on top of a 4K-logical disk
    294      * will really, really hurt...
    295      */
    296     fs->sector_shift = fs->fs_dev->disk->sector_shift;
    297     fs->block_shift  = 11;	/* A CD-ROM block is always 2K */
    298     fs->sector_size  = 1 << fs->sector_shift;
    299     fs->block_size   = 1 << fs->block_shift;
    300     blktosec = fs->block_shift - fs->sector_shift;
    301 
    302     pvd_lba = iso_boot_info.pvd;
    303     if (!pvd_lba)
    304 	pvd_lba = 16;		/* Default if not otherwise defined */
    305 
    306     disk->rdwr_sectors(disk, pvd, (sector_t)pvd_lba << blktosec,
    307 		       1 << blktosec, false);
    308     memcpy(&sbi->root, pvd + ROOT_DIR_OFFSET, sizeof(sbi->root));
    309 
    310     /* Initialize the cache */
    311     cache_init(fs->fs_dev, fs->block_shift);
    312 
    313     /* Check for SP and ER in the first directory record of the root directory.
    314        Set sbi->susp_skip and enable sbi->do_rr as appropriate.
    315     */
    316     susp_rr_check_signatures(fs, 1);
    317 
    318     return fs->block_shift;
    319 }
    320 
    321 
    322 const struct fs_ops iso_fs_ops = {
    323     .fs_name       = "iso",
    324     .fs_flags      = FS_USEMEM | FS_THISIND,
    325     .fs_init       = iso_fs_init,
    326     .searchdir     = NULL,
    327     .getfssec      = generic_getfssec,
    328     .close_file    = generic_close_file,
    329     .mangle_name   = generic_mangle_name,
    330     .open_config   = iso_open_config,
    331     .iget_root     = iso_iget_root,
    332     .iget          = iso_iget,
    333     .readdir       = iso_readdir,
    334     .next_extent   = no_next_extent,
    335     .fs_uuid       = NULL,
    336 };
    337