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 static struct inode inodes[NR_INODE_RECORDS]; 10 static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset); 11 12 int fs_putnode(ino_t ino_nr, unsigned int count) 13 { 14 /* 15 * Find the inode specified by the request message and decrease its 16 * counter. 17 */ 18 struct inode *i_node; 19 20 if ((i_node = find_inode(ino_nr)) == NULL) { 21 printf("ISOFS: trying to free unused inode\n"); 22 return EINVAL; 23 } 24 if (count > i_node->i_count) { 25 printf("ISOFS: put_node count too high\n"); 26 return EINVAL; 27 } 28 29 i_node->i_count -= count - 1; 30 put_inode(i_node); 31 return OK; 32 } 33 34 struct inode* alloc_inode(void) 35 { 36 /* 37 * Return a free inode from the pool. 38 */ 39 static int i; 40 int end = i; 41 struct inode *i_node; 42 43 i = (i + 1) % NR_INODE_RECORDS; 44 do { 45 i_node = &inodes[i]; 46 47 if (i_node->i_count == 0) { 48 free_extent(i_node->extent); 49 50 memset(i_node, 0, sizeof(*i_node)); 51 i_node->i_count = 1; 52 53 return i_node; 54 } 55 56 i = (i + 1) % NR_INODE_RECORDS; 57 } 58 while(i != end); 59 60 panic("No free inodes in cache"); 61 } 62 63 struct inode* find_inode(ino_t i) 64 { 65 /* Get inode from cache. */ 66 int cpt; 67 struct inode *i_node; 68 69 if (i == 0) 70 return NULL; 71 72 for (cpt = 0; cpt < NR_INODE_RECORDS; cpt++) { 73 i_node = &inodes[cpt]; 74 75 if ((i_node->i_stat.st_ino == i) && (i_node->i_count > 0)) 76 return i_node; 77 } 78 79 return NULL; 80 } 81 82 struct inode* get_inode(ino_t i) 83 { 84 struct inode *i_node; 85 struct dir_extent *extent; 86 87 if (i == 0) 88 return NULL; 89 90 /* Try to get inode from cache. */ 91 i_node = find_inode(i); 92 if (i_node != NULL) { 93 dup_inode(i_node); 94 return i_node; 95 } 96 97 /* 98 * Inode wasn't in cache, try to load it. 99 * FIXME: a fake extent of one logical block is created for 100 * read_inode(). Reading a inode this way could be problematic if 101 * additional extents are stored behind the block boundary. 102 */ 103 i_node = alloc_inode(); 104 extent = alloc_extent(); 105 extent->location = i / v_pri.logical_block_size_l; 106 extent->length = 1; 107 108 if (read_inode(i_node, extent, i % v_pri.logical_block_size_l, 109 NULL) != OK) { 110 free_extent(extent); 111 put_inode(i_node); 112 return NULL; 113 } 114 115 free_extent(extent); 116 return i_node; 117 } 118 119 void put_inode(struct inode *i_node) 120 { 121 if (i_node == NULL) 122 return; 123 124 assert(i_node->i_count > 0); 125 126 i_node->i_count--; 127 } 128 129 void dup_inode(struct inode *i_node) 130 { 131 assert(i_node != NULL); 132 133 i_node->i_count++; 134 } 135 136 static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset) 137 { 138 struct iso9660_dir_record *dir_rec; 139 struct buf *bp; 140 141 /* 142 * Directory entries aren't allowed to cross a logical block boundary in 143 * ISO 9660, so we keep searching until we find something or reach the 144 * end of the extent. 145 */ 146 bp = read_extent_block(extent, *offset / v_pri.logical_block_size_l); 147 while (bp != NULL) { 148 dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset % 149 v_pri.logical_block_size_l); 150 if (dir_rec->length == 0) { 151 *offset -= *offset % v_pri.logical_block_size_l; 152 *offset += v_pri.logical_block_size_l; 153 } 154 else { 155 break; 156 } 157 158 lmfs_put_block(bp); 159 bp = read_extent_block(extent, *offset / 160 v_pri.logical_block_size_l); 161 } 162 163 return bp; 164 } 165 166 int read_inode(struct inode *i_node, struct dir_extent *extent, size_t offset, 167 size_t *new_offset) 168 { 169 struct iso9660_dir_record *dir_rec; 170 struct buf *bp; 171 172 /* Find inode. */ 173 bp = fetch_inode(extent, &offset); 174 if (bp == NULL) 175 return EOF; 176 177 dir_rec = (struct iso9660_dir_record*)(b_data(bp) + offset % 178 v_pri.logical_block_size_l); 179 180 /* Parse basic ISO 9660 specs. */ 181 if (check_dir_record(dir_rec, 182 offset % v_pri.logical_block_size_l) != OK) { 183 lmfs_put_block(bp); 184 return EINVAL; 185 } 186 187 memset(&i_node->i_stat, 0, sizeof(struct stat)); 188 189 i_node->i_stat.st_ino = get_extent_absolute_block_id(extent, 190 offset / v_pri.logical_block_size_l) * v_pri.logical_block_size_l + 191 offset % v_pri.logical_block_size_l; 192 193 read_inode_iso9660(i_node, dir_rec); 194 195 /* Parse extensions. */ 196 read_inode_susp(i_node, dir_rec, bp, 197 offset % v_pri.logical_block_size_l); 198 199 offset += dir_rec->length; 200 read_inode_extents(i_node, dir_rec, extent, &offset); 201 202 lmfs_put_block(bp); 203 if (new_offset != NULL) 204 *new_offset = offset; 205 return OK; 206 } 207 208 void read_inode_iso9660(struct inode *i, 209 const struct iso9660_dir_record *dir_rec) 210 { 211 char *cp; 212 213 /* Parse first extent. */ 214 if (dir_rec->data_length_l > 0) { 215 assert(i->extent == NULL); 216 i->extent = alloc_extent(); 217 i->extent->location = dir_rec->loc_extent_l + 218 dir_rec->ext_attr_rec_length; 219 i->extent->length = dir_rec->data_length_l / 220 v_pri.logical_block_size_l; 221 if (dir_rec->data_length_l % v_pri.logical_block_size_l) 222 i->extent->length++; 223 224 i->i_stat.st_size = dir_rec->data_length_l; 225 } 226 227 /* Parse timestamps (record date). */ 228 i->i_stat.st_atime = i->i_stat.st_mtime = i->i_stat.st_ctime = 229 i->i_stat.st_birthtime = date7_to_time_t(dir_rec->rec_date); 230 231 if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) { 232 i->i_stat.st_mode = S_IFDIR; 233 i->i_stat.st_ino = 234 i->extent->location * v_pri.logical_block_size_l; 235 } 236 else 237 i->i_stat.st_mode = S_IFREG; 238 i->i_stat.st_mode |= 0555; 239 240 /* Parse file name. */ 241 if (dir_rec->file_id[0] == 0) 242 strcpy(i->i_name, "."); 243 else if (dir_rec->file_id[0] == 1) 244 strcpy(i->i_name, ".."); 245 else { 246 memcpy(i->i_name, dir_rec->file_id, dir_rec->length_file_id); 247 248 /* Truncate/ignore file version suffix. */ 249 cp = strchr(i->i_name, ';'); 250 if (cp != NULL) 251 *cp = '\0'; 252 /* Truncate dot if file has no extension. */ 253 if (strchr(i->i_name, '.') + 1 == cp) 254 *(cp-1) = '\0'; 255 } 256 257 /* Initialize stat. */ 258 i->i_stat.st_dev = fs_dev; 259 i->i_stat.st_blksize = v_pri.logical_block_size_l; 260 i->i_stat.st_blocks = 261 dir_rec->data_length_l / v_pri.logical_block_size_l; 262 i->i_stat.st_nlink = 1; 263 } 264 265 void read_inode_extents(struct inode *i, 266 const struct iso9660_dir_record *dir_rec, 267 struct dir_extent *extent, size_t *offset) 268 { 269 struct buf *bp; 270 struct iso9660_dir_record *extent_rec; 271 struct dir_extent *cur_extent = i->extent; 272 int done = FALSE; 273 274 /* 275 * No need to search extents if file is empty or has final directory 276 * record flag set. 277 */ 278 if (cur_extent == NULL || 279 ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0)) 280 return; 281 282 while (!done) { 283 bp = fetch_inode(extent, offset); 284 if (bp == NULL) 285 return; 286 287 bp = read_extent_block(extent, 288 *offset / v_pri.logical_block_size_l); 289 extent_rec = (struct iso9660_dir_record*)(b_data(bp) + 290 *offset % v_pri.logical_block_size_l); 291 292 if (check_dir_record(dir_rec, 293 *offset % v_pri.logical_block_size_l) != OK) { 294 lmfs_put_block(bp); 295 return; 296 } 297 298 /* Extent entries should share the same name. */ 299 if ((dir_rec->length_file_id == extent_rec->length_file_id) && 300 (memcmp(dir_rec->file_id, extent_rec->file_id, 301 dir_rec->length_file_id) == 0)) { 302 /* Add the extent at the end of the linked list. */ 303 assert(cur_extent->next == NULL); 304 cur_extent->next = alloc_extent(); 305 cur_extent->next->location = dir_rec->loc_extent_l + 306 dir_rec->ext_attr_rec_length; 307 cur_extent->next->length = dir_rec->data_length_l / 308 v_pri.logical_block_size_l; 309 if (dir_rec->data_length_l % v_pri.logical_block_size_l) 310 cur_extent->next->length++; 311 312 i->i_stat.st_size += dir_rec->data_length_l; 313 i->i_stat.st_blocks += cur_extent->next->length; 314 315 cur_extent = cur_extent->next; 316 *offset += extent_rec->length; 317 } 318 else 319 done = TRUE; 320 321 /* Check if not last extent bit is not set. */ 322 if ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0) 323 done = TRUE; 324 325 lmfs_put_block(bp); 326 } 327 } 328 329 void read_inode_susp(struct inode *i, const struct iso9660_dir_record *dir_rec, 330 struct buf *bp, size_t offset) 331 { 332 int susp_offset, susp_size; 333 struct rrii_dir_record rrii_data; 334 335 susp_offset = 33 + dir_rec->length_file_id; 336 /* Get rid of padding byte. */ 337 if(dir_rec->length_file_id % 2 == 0) { 338 susp_offset++; 339 } 340 341 if(dir_rec->length - susp_offset >= 4) { 342 susp_size = dir_rec->length - susp_offset; 343 344 /* Initialize record with known, sane data. */ 345 memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 346 memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 347 memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7); 348 memcpy(rrii_data.birthtime, dir_rec->rec_date, 349 ISO9660_SIZE_DATE7); 350 351 rrii_data.d_mode = i->i_stat.st_mode; 352 rrii_data.uid = 0; 353 rrii_data.gid = 0; 354 rrii_data.rdev = NO_DEV; 355 rrii_data.file_id_rrip[0] = '\0'; 356 rrii_data.slink_rrip[0] = '\0'; 357 358 parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset, 359 susp_size); 360 361 /* Copy back data from rrii_dir_record structure. */ 362 i->i_stat.st_atime = date7_to_time_t(rrii_data.atime); 363 i->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime); 364 i->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime); 365 i->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime); 366 367 i->i_stat.st_mode = rrii_data.d_mode; 368 i->i_stat.st_uid = rrii_data.uid; 369 i->i_stat.st_gid = rrii_data.gid; 370 i->i_stat.st_rdev = rrii_data.rdev; 371 372 if (rrii_data.file_id_rrip[0] != '\0') 373 strlcpy(i->i_name, rrii_data.file_id_rrip, 374 sizeof(i->i_name)); 375 if (rrii_data.slink_rrip[0] != '\0') 376 strlcpy(i->s_link, rrii_data.slink_rrip, 377 sizeof(i->s_link)); 378 } 379 } 380 381 int check_dir_record(const struct iso9660_dir_record *d, size_t offset) 382 { 383 /* Run some consistency check on a directory entry. */ 384 if ((d->length < 33) || (d->length_file_id < 1)) 385 return EINVAL; 386 if (d->length_file_id + 32 > d->length) 387 return EINVAL; 388 if (offset + d->length > v_pri.logical_block_size_l) 389 return EINVAL; 390 391 return OK; 392 } 393 394 int check_inodes(void) 395 { 396 /* Check whether there are no more inodes in use. Called on unmount. */ 397 int i; 398 399 for (i = 0; i < NR_INODE_RECORDS; i++) 400 if (inodes[i].i_count > 0) 401 return FALSE; 402 403 return TRUE; 404 } 405