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