1 2 /* 3 * This file contains all the function that handle the dir records 4 * (inodes) for the ISO9660 filesystem. 5 */ 6 7 #include "inc.h" 8 9 #include "uthash.h" 10 11 struct inode_cache { 12 ino_t key; 13 struct inode *value; 14 UT_hash_handle hh; 15 } ; 16 17 struct inode_cache *icache = NULL; 18 19 void read_inode_iso9660(struct inode_dir_entry *i, 20 const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, 21 size_t offset, int name_only); 22 23 #ifdef ISO9660_OPTION_MODE3 24 static void read_inode_extents(struct inode_dir_entry *i, 25 const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, 26 size_t *offset); 27 #endif 28 29 #ifdef ISO9660_OPTION_ROCKRIDGE 30 void read_inode_susp(struct inode_dir_entry *i, 31 const struct iso9660_dir_record *dir_rec, struct buf *bp, size_t offset, 32 int name_only); 33 #endif 34 35 static int check_dir_record(const struct iso9660_dir_record *d, size_t offset); 36 37 int fs_putnode(ino_t ino_nr, unsigned int count) 38 { 39 /* 40 * Find the inode specified by the request message and decrease its 41 * counter. 42 */ 43 struct inode *i_node; 44 45 if ((i_node = get_inode(ino_nr)) == NULL) { 46 puts("ISOFS: trying to free unused inode"); 47 return EINVAL; 48 } 49 if (count > i_node->i_count) { 50 puts("ISOFS: put_node count too high"); 51 return EINVAL; 52 } 53 54 i_node->i_count -= count - 1; 55 put_inode(i_node); 56 return OK; 57 } 58 59 60 struct inode* get_inode(ino_t ino_nr) { 61 /* Return an already opened inode from cache. */ 62 struct inode *i_node = inode_cache_get(ino_nr); 63 64 if (i_node == NULL) 65 return NULL; 66 67 if (i_node->i_count == 0) 68 return NULL; 69 70 return i_node; 71 } 72 73 struct inode* open_inode(ino_t ino_nr) { 74 /* Return an inode from cache. */ 75 struct inode *i_node = inode_cache_get(ino_nr); 76 if (i_node == NULL) 77 return NULL; 78 79 i_node->i_count++; 80 81 return i_node; 82 } 83 84 void put_inode(struct inode *i_node) { 85 if (i_node == NULL) 86 return; 87 88 assert(i_node->i_count > 0); 89 i_node->i_count--; 90 91 if(i_node->i_count == 0) 92 i_node->i_mountpoint = FALSE; 93 } 94 95 void dup_inode(struct inode *i_node) { 96 assert(i_node != NULL); 97 assert(i_node->i_count > 0); 98 99 i_node->i_count++; 100 } 101 102 int read_directory(struct inode *dir) { 103 #define MAX_ENTRIES 256 /* avoid using lots of stack.. */ 104 /* Read all entries in a directory. */ 105 size_t pos = 0, saved_pos, cur_entry, num_entries, cpt; 106 struct inode_dir_entry entries[MAX_ENTRIES + 1]; 107 int status; 108 109 if (dir->dir_contents) 110 return OK; 111 112 if (!S_ISDIR(dir->i_stat.st_mode)) 113 return ENOTDIR; 114 115 /* 116 * We do not know how many inode entries we will find, but we want to 117 * allocate an array of the right size for dir->dir_contents. First 118 * find out how many entries there are, and store up to MAX_ENTRIES of 119 * them into a temporary array on the stack. If there are more than 120 * MAX_ENTRIES entries, we have to do a second pass on the part of the 121 * directory that we did not manage to fit in the temporary array. 122 * 123 * The entire service needs massive structural improvement (and in 124 * particular, no dynamic memory allocation like this), but for now 125 * this is the simplest way to be fast for small directories while at 126 * the same time supporting seriously large directories. 127 */ 128 cur_entry = 0; 129 num_entries = 0; 130 131 while ((status = read_inode(&entries[cur_entry], &dir->extent, 132 &pos)) == OK) { 133 /* Dump the entry if it's not to be exported to userland. */ 134 if (entries[cur_entry].i_node->skip) { 135 free_inode_dir_entry(&entries[cur_entry]); 136 continue; 137 } 138 139 if (cur_entry < MAX_ENTRIES) { 140 cur_entry++; 141 142 /* 143 * As long as more entries fit in the temporary array, 144 * update the saved position of the next entry. Once 145 * we hit the first entry that does not fit (if any), 146 * the updating stops and we will have the correct 147 * saved position. 148 */ 149 saved_pos = pos; 150 } else { 151 /* 152 * No room in the temporary array. Free the entry 153 * again. This is costly but only for those rare 154 * directories that have more than MAX_ENTRIES entries. 155 */ 156 free_inode_dir_entry(&entries[cur_entry]); 157 } 158 159 num_entries++; 160 } 161 162 /* 163 * Allocate a dynamic array of the correct size, and populate it with 164 * all the entries in the temporary array. For large directories, the 165 * temporary array will have partial results, in which case we have to 166 * do a second pass on the rest below. 167 */ 168 dir->dir_contents = 169 alloc_mem(sizeof(struct inode_dir_entry) * num_entries); 170 171 memcpy(dir->dir_contents, entries, 172 sizeof(struct inode_dir_entry) * cur_entry); 173 174 /* 175 * The second pass. This pass starts from the saved position and reads 176 * only the entries that did not fit in the temporary array. This time 177 * we can read straight into the actual destination array. We expect 178 * to find the same entries as during the first pass. 179 */ 180 while (cur_entry < num_entries) { 181 if (read_inode(&dir->dir_contents[cur_entry], &dir->extent, 182 &saved_pos) != OK) 183 panic("unexpected EOF or error rereading directory"); 184 185 if (dir->dir_contents[cur_entry].i_node->skip) { 186 free_inode_dir_entry(&entries[cur_entry]); 187 continue; 188 } 189 190 cur_entry++; 191 } 192 193 dir->dir_size = num_entries; 194 195 /* The name pointer has to point to the new memory location. */ 196 for (cpt = 0; cpt < num_entries; cpt++) { 197 if (dir->dir_contents[cpt].r_name == NULL) 198 dir->dir_contents[cpt].name = 199 dir->dir_contents[cpt].i_name; 200 else 201 dir->dir_contents[cpt].name = 202 dir->dir_contents[cpt].r_name; 203 } 204 205 return (status == EOF) ? OK : status; 206 } 207 208 int check_inodes(void) { 209 /* Check whether there are no more inodes in use. Called on unmount. */ 210 int i; 211 212 /* XXX: actually check for inodes in use. */ 213 return TRUE; 214 } 215 216 int read_inode(struct inode_dir_entry *dir_entry, struct dir_extent *extent, 217 size_t *offset) 218 { 219 struct iso9660_dir_record *dir_rec; 220 struct buf *bp; 221 struct inode *i_node; 222 ino_t ino_nr; 223 int name_only = FALSE; 224 225 /* Find inode. */ 226 bp = read_extent_block(extent, *offset); 227 if (bp == NULL) { 228 return EOF; 229 } 230 231 /* Check if we are crossing a sector boundary. */ 232 dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset % 233 v_pri.logical_block_size_l); 234 235 if (dir_rec->length == 0) { 236 *offset = ((*offset / v_pri.logical_block_size_l) + 1) * 237 v_pri.logical_block_size_l; 238 239 lmfs_put_block(bp); 240 bp = read_extent_block(extent, *offset); 241 if (bp == NULL) { 242 return EOF; 243 } 244 245 dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset % 246 v_pri.logical_block_size_l); 247 } 248 249 /* Parse basic ISO 9660 specs. */ 250 if (check_dir_record(dir_rec, *offset % v_pri.logical_block_size_l) 251 != OK) { 252 lmfs_put_block(bp); 253 return EINVAL; 254 } 255 256 /* Get inode */ 257 if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) { 258 ino_nr = dir_rec->loc_extent_l; 259 } 260 else { 261 ino_nr = get_extent_absolute_block_id(extent, *offset) 262 * v_pri.logical_block_size_l + 263 *offset % v_pri.logical_block_size_l; 264 } 265 266 memset(dir_entry, 0, sizeof(*dir_entry)); 267 268 i_node = inode_cache_get(ino_nr); 269 if (i_node) { 270 /* Inode was already loaded, parse file names only. */ 271 dir_entry->i_node = i_node; 272 i_node->i_refcount++; 273 274 memset(&dir_entry->i_name[0], 0, sizeof(dir_entry->i_name)); 275 276 name_only = TRUE; 277 } 278 else { 279 /* Inode wasn't in memory, parse it. */ 280 i_node = alloc_mem(sizeof(struct inode)); 281 dir_entry->i_node = i_node; 282 i_node->i_refcount = 1; 283 i_node->i_stat.st_ino = ino_nr; 284 inode_cache_add(ino_nr, i_node); 285 } 286 287 dir_entry->i_node = i_node; 288 read_inode_iso9660(dir_entry, dir_rec, extent, *offset, name_only); 289 290 /* Parse extensions. */ 291 #ifdef ISO9660_OPTION_ROCKRIDGE 292 read_inode_susp(dir_entry, dir_rec, bp, 293 *offset % v_pri.logical_block_size_l, name_only); 294 #endif 295 296 *offset += dir_rec->length; 297 if (dir_rec->length % 2) 298 (*offset)++; 299 300 #ifdef ISO9660_OPTION_MODE3 301 read_inode_extents(dir_entry, dir_rec, extent, offset); 302 #endif 303 304 lmfs_put_block(bp); 305 306 return OK; 307 } 308 309 struct inode* inode_cache_get(ino_t ino_nr) { 310 struct inode_cache *i_node; 311 HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), i_node); 312 313 if (i_node) 314 return i_node->value; 315 else 316 return NULL; 317 } 318 319 void inode_cache_add(ino_t ino_nr, struct inode *i_node) { 320 struct inode_cache *c_check; 321 struct inode_cache *c_entry; 322 323 HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), c_check); 324 325 if (c_check == NULL) { 326 c_entry = alloc_mem(sizeof(struct inode_cache)); 327 c_entry->key = ino_nr; 328 c_entry->value = i_node; 329 330 HASH_ADD(hh, icache, key, sizeof(ino_t), c_entry); 331 } 332 else 333 panic("Trying to insert inode into cache twice"); 334 } 335 336 void read_inode_iso9660(struct inode_dir_entry *i, 337 const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, 338 size_t offset, int name_only) 339 { 340 char *cp; 341 342 /* Parse file name. */ 343 if (dir_rec->file_id[0] == 0) 344 strcpy(i->i_name, "."); 345 else if (dir_rec->file_id[0] == 1) 346 strcpy(i->i_name, ".."); 347 else { 348 memcpy(i->i_name, dir_rec->file_id, dir_rec->length_file_id); 349 350 /* Truncate/ignore file version suffix. */ 351 cp = strchr(i->i_name, ';'); 352 if (cp != NULL) { 353 *cp = '\0'; 354 /* Truncate dot if file has no extension. */ 355 if (strchr(i->i_name, '.') + 1 == cp) 356 *(cp-1) = '\0'; 357 } 358 } 359 360 if (name_only == TRUE) 361 return; 362 363 /* Parse first extent. */ 364 if (dir_rec->data_length_l > 0) { 365 i->i_node->extent.location = dir_rec->loc_extent_l + 366 dir_rec->ext_attr_rec_length; 367 i->i_node->extent.length = dir_rec->data_length_l / 368 v_pri.logical_block_size_l; 369 370 if (dir_rec->data_length_l % v_pri.logical_block_size_l) 371 i->i_node->extent.length++; 372 373 i->i_node->i_stat.st_size = dir_rec->data_length_l; 374 } 375 376 /* Parse timestamps (record date). */ 377 i->i_node->i_stat.st_atime = i->i_node->i_stat.st_mtime = 378 i->i_node->i_stat.st_ctime = i->i_node->i_stat.st_birthtime = 379 date7_to_time_t(dir_rec->rec_date); 380 381 if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) 382 i->i_node->i_stat.st_mode = S_IFDIR; 383 else 384 i->i_node->i_stat.st_mode = S_IFREG; 385 386 i->i_node->i_stat.st_mode |= 0555; 387 388 /* Initialize stat. */ 389 i->i_node->i_stat.st_dev = fs_dev; 390 i->i_node->i_stat.st_blksize = v_pri.logical_block_size_l; 391 i->i_node->i_stat.st_blocks = dir_rec->data_length_l / 512; 392 i->i_node->i_stat.st_nlink = 1; 393 } 394 395 #ifdef ISO9660_OPTION_ROCKRIDGE 396 397 void read_inode_susp(struct inode_dir_entry *i, 398 const struct iso9660_dir_record *dir_rec, struct buf *bp, size_t offset, 399 int name_only) 400 { 401 int susp_offset, susp_size, name_length; 402 struct rrii_dir_record rrii_data; 403 404 susp_offset = 33 + dir_rec->length_file_id; 405 /* Get rid of padding byte. */ 406 if(dir_rec->length_file_id % 2 == 0) { 407 susp_offset++; 408 } 409 410 if(dir_rec->length - susp_offset < 4) 411 return; 412 413 susp_size = dir_rec->length - susp_offset; 414 415 /* Initialize record with known, sane data. */ 416 memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 417 memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 418 memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 419 memcpy(rrii_data.birthtime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 420 421 rrii_data.d_mode = i->i_node->i_stat.st_mode; 422 rrii_data.uid = SYS_UID; 423 rrii_data.gid = SYS_GID; 424 rrii_data.rdev = NO_DEV; 425 rrii_data.file_id_rrip[0] = '\0'; 426 rrii_data.slink_rrip[0] = '\0'; 427 rrii_data.reparented_inode = NULL; 428 429 parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset, susp_size); 430 431 /* Copy back data from rrii_dir_record structure. */ 432 if (rrii_data.file_id_rrip[0] != '\0') { 433 name_length = strlen(rrii_data.file_id_rrip); 434 i->r_name = alloc_mem(name_length + 1); 435 memcpy(i->r_name, rrii_data.file_id_rrip, name_length); 436 } 437 438 if (rrii_data.slink_rrip[0] != '\0') { 439 name_length = strlen(rrii_data.slink_rrip); 440 i->i_node->s_name = alloc_mem(name_length + 1); 441 memcpy(i->i_node->s_name, rrii_data.slink_rrip, name_length); 442 } 443 444 if (rrii_data.reparented_inode) { 445 /* Recycle the inode already parsed. */ 446 i->i_node = rrii_data.reparented_inode; 447 return; 448 } 449 450 /* XXX: not the correct way to ignore reparented directory holder... */ 451 if (strcmp(rrii_data.file_id_rrip, ".rr_moved") == 0) 452 i->i_node->skip = 1; 453 454 if (name_only == TRUE) 455 return; 456 457 /* Write back all Rock Ridge properties. */ 458 i->i_node->i_stat.st_atime = date7_to_time_t(rrii_data.atime); 459 i->i_node->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime); 460 i->i_node->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime); 461 i->i_node->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime); 462 463 i->i_node->i_stat.st_mode = rrii_data.d_mode; 464 i->i_node->i_stat.st_uid = rrii_data.uid; 465 i->i_node->i_stat.st_gid = rrii_data.gid; 466 i->i_node->i_stat.st_rdev = rrii_data.rdev; 467 } 468 469 #endif 470 471 #ifdef ISO9660_OPTION_MODE3 472 473 void read_inode_extents(struct inode *i, 474 const struct iso9660_dir_record *dir_rec, 475 struct dir_extent *extent, size_t *offset) 476 { 477 panic("read_inode_extents() isn't implemented yet!"); 478 } 479 480 #endif 481 482 int check_dir_record(const struct iso9660_dir_record *d, size_t offset) { 483 /* Run some consistency check on a directory entry. */ 484 if ((d->length < 33) || (d->length_file_id < 1)) 485 return EINVAL; 486 if (d->length_file_id + 32 > d->length) 487 return EINVAL; 488 if (offset + d->length > v_pri.logical_block_size_l) 489 return EINVAL; 490 491 return OK; 492 } 493