Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/fs/ext2/inode.c 3 * 4 * Copyright 2018, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/kernel.h> 9 #include <fiwix/fs.h> 10 #include <fiwix/filesystems.h> 11 #include <fiwix/fs_ext2.h> 12 #include <fiwix/fs_pipe.h> 13 #include <fiwix/statfs.h> 14 #include <fiwix/sleep.h> 15 #include <fiwix/stat.h> 16 #include <fiwix/sched.h> 17 #include <fiwix/buffer.h> 18 #include <fiwix/mm.h> 19 #include <fiwix/process.h> 20 #include <fiwix/errno.h> 21 #include <fiwix/stdio.h> 22 #include <fiwix/string.h> 23 24 #define BLOCKS_PER_IND_BLOCK(sb) (EXT2_BLOCK_SIZE(sb) / sizeof(unsigned int)) 25 #define BLOCKS_PER_DIND_BLOCK(sb) (BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb)) 26 #define BLOCKS_PER_TIND_BLOCK(sb) (BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb)) 27 28 #define EXT2_INODES_PER_BLOCK(sb) (EXT2_BLOCK_SIZE(sb) / sizeof(struct ext2_inode)) 29 30 static void free_indblock(struct inode *i, int block, int offset) 31 { 32 int n; 33 struct buffer *buf; 34 __blk_t *indblock; 35 36 if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { 37 printk("WARNING: %s(): error reading block %d.\n", __FUNCTION__, block); 38 return; 39 } 40 indblock = (__blk_t *)buf->data; 41 for(n = offset; n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { 42 if(indblock[n]) { 43 ext2_bfree(i->sb, indblock[n]); 44 indblock[n] = 0; 45 i->i_blocks -= i->sb->s_blocksize / 512; 46 } 47 } 48 bwrite(buf); 49 } 50 51 static int get_group_desc(struct superblock *sb, __blk_t block_group, struct ext2_group_desc *gd) 52 { 53 __blk_t group_desc_block; 54 int group_desc; 55 struct buffer *buf; 56 57 group_desc_block = block_group / EXT2_DESC_PER_BLOCK(sb); 58 group_desc = block_group % EXT2_DESC_PER_BLOCK(sb); 59 if(!(buf = bread(sb->dev, SUPERBLOCK + sb->u.ext2.sb.s_first_data_block + group_desc_block, sb->s_blocksize))) { 60 return -EIO; 61 } 62 memcpy_b(gd, (void *)(buf->data + (group_desc * sizeof(struct ext2_group_desc))), sizeof(struct ext2_group_desc)); 63 brelse(buf); 64 return 0; 65 } 66 67 int ext2_read_inode(struct inode *i) 68 { 69 __blk_t block_group, block; 70 unsigned int offset; 71 struct superblock *sb; 72 struct ext2_inode *ii; 73 struct ext2_group_desc gd; 74 struct buffer *buf; 75 76 if(!(sb = get_superblock(i->dev))) { 77 printk("WARNING: %s(): get_superblock() has returned NULL.\n"); 78 return -EINVAL; 79 } 80 block_group = ((i->inode - 1) / EXT2_INODES_PER_GROUP(sb)); 81 if(get_group_desc(sb, block_group, &gd)) { 82 return -EIO; 83 } 84 block = (((i->inode - 1) % EXT2_INODES_PER_GROUP(sb)) / EXT2_INODES_PER_BLOCK(sb)); 85 86 if(!(buf = bread(i->dev, gd.bg_inode_table + block, i->sb->s_blocksize))) { 87 return -EIO; 88 } 89 offset = ((((i->inode - 1) % EXT2_INODES_PER_GROUP(sb)) % EXT2_INODES_PER_BLOCK(sb)) * sizeof(struct ext2_inode)); 90 91 ii = (struct ext2_inode *)(buf->data + offset); 92 memcpy_b(&i->u.ext2.i_data, ii->i_block, sizeof(ii->i_block)); 93 94 i->i_mode = ii->i_mode; 95 i->i_uid = ii->i_uid; 96 i->i_size = ii->i_size; 97 i->i_atime = ii->i_atime; 98 i->i_ctime = ii->i_ctime; 99 i->i_mtime = ii->i_mtime; 100 i->i_gid = ii->i_gid; 101 i->i_nlink = ii->i_links_count; 102 i->i_blocks = ii->i_blocks; 103 i->i_flags = ii->i_flags; 104 i->count = 1; 105 switch(i->i_mode & S_IFMT) { 106 case S_IFCHR: 107 i->fsop = &def_chr_fsop; 108 i->rdev = ii->i_block[0]; 109 break; 110 case S_IFBLK: 111 i->fsop = &def_blk_fsop; 112 i->rdev = ii->i_block[0]; 113 break; 114 case S_IFIFO: 115 i->fsop = &pipefs_fsop; 116 /* it's a union so we need to clear pipefs_i */ 117 memset_b(&i->u.pipefs, NULL, sizeof(struct pipefs_inode)); 118 break; 119 case S_IFDIR: 120 i->fsop = &ext2_dir_fsop; 121 break; 122 case S_IFREG: 123 i->fsop = &ext2_file_fsop; 124 break; 125 case S_IFLNK: 126 i->fsop = &ext2_symlink_fsop; 127 break; 128 case S_IFSOCK: 129 i->fsop = NULL; 130 break; 131 default: 132 printk("WARNING: %s(): invalid inode (%d) mode %08o.\n", __FUNCTION__, i->inode, i->i_mode); 133 brelse(buf); 134 return -ENOENT; 135 } 136 brelse(buf); 137 return 0; 138 } 139 140 int ext2_write_inode(struct inode *i) 141 { 142 __blk_t block_group, block; 143 short int offset; 144 struct superblock *sb; 145 struct ext2_inode *ii; 146 struct ext2_group_desc gd; 147 struct buffer *buf; 148 149 if(!(sb = get_superblock(i->dev))) { 150 printk("WARNING: %s(): get_superblock() has returned NULL.\n"); 151 return -EINVAL; 152 } 153 block_group = ((i->inode - 1) / EXT2_INODES_PER_GROUP(sb)); 154 if(get_group_desc(sb, block_group, &gd)) { 155 return -EIO; 156 } 157 block = (((i->inode - 1) % EXT2_INODES_PER_GROUP(sb)) / EXT2_INODES_PER_BLOCK(sb)); 158 159 if(!(buf = bread(i->dev, gd.bg_inode_table + block, i->sb->s_blocksize))) { 160 return -EIO; 161 } 162 offset = ((((i->inode - 1) % EXT2_INODES_PER_GROUP(sb)) % EXT2_INODES_PER_BLOCK(sb)) * sizeof(struct ext2_inode)); 163 ii = (struct ext2_inode *)(buf->data + offset); 164 memset_b(ii, 0, sizeof(struct ext2_inode)); 165 166 ii->i_mode = i->i_mode; 167 ii->i_uid = i->i_uid; 168 ii->i_size = i->i_size; 169 ii->i_atime = i->i_atime; 170 ii->i_ctime = i->i_ctime; 171 ii->i_mtime = i->i_mtime; 172 ii->i_dtime = i->u.ext2.i_dtime; 173 ii->i_gid = i->i_gid; 174 ii->i_links_count = i->i_nlink; 175 ii->i_blocks = i->i_blocks; 176 ii->i_flags = i->i_flags; 177 if(S_ISCHR(i->i_mode) || S_ISBLK(i->i_mode)) { 178 ii->i_block[0] = i->rdev; 179 } else { 180 memcpy_b(ii->i_block, &i->u.ext2.i_data, sizeof(i->u.ext2.i_data)); 181 } 182 i->dirty = 0; 183 bwrite(buf); 184 return 0; 185 } 186 187 int ext2_bmap(struct inode *i, __off_t offset, int mode) 188 { 189 unsigned char level; 190 __blk_t *indblock, *dindblock, *tindblock; 191 __blk_t block, iblock, dblock, tblock, newblock; 192 int blksize; 193 struct buffer *buf, *buf2, *buf3, *buf4; 194 195 blksize = i->sb->s_blocksize; 196 block = offset / blksize; 197 level = 0; 198 buf3 = NULL; /* makes GCC happy */ 199 200 if(block < EXT2_NDIR_BLOCKS) { 201 level = EXT2_NDIR_BLOCKS - 1; 202 } else { 203 if(block < (BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { 204 level = EXT2_IND_BLOCK; 205 } else if(block < ((BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)) + BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { 206 level = EXT2_DIND_BLOCK; 207 } else { 208 level = EXT2_TIND_BLOCK; 209 } 210 block -= EXT2_NDIR_BLOCKS; 211 } 212 213 if(level < EXT2_NDIR_BLOCKS) { 214 if(!i->u.ext2.i_data[block] && mode == FOR_WRITING) { 215 if((newblock = ext2_balloc(i->sb)) < 0) { 216 return -ENOSPC; 217 } 218 /* initialize the new block */ 219 if(!(buf = bread(i->dev, newblock, blksize))) { 220 ext2_bfree(i->sb, newblock); 221 return -EIO; 222 } 223 memset_b(buf->data, 0, blksize); 224 bwrite(buf); 225 i->u.ext2.i_data[block] = newblock; 226 i->i_blocks += blksize / 512; 227 } 228 return i->u.ext2.i_data[block]; 229 } 230 231 if(!i->u.ext2.i_data[level]) { 232 if(mode == FOR_WRITING) { 233 if((newblock = ext2_balloc(i->sb)) < 0) { 234 return -ENOSPC; 235 } 236 /* initialize the new block */ 237 if(!(buf = bread(i->dev, newblock, blksize))) { 238 ext2_bfree(i->sb, newblock); 239 return -EIO; 240 } 241 memset_b(buf->data, 0, blksize); 242 bwrite(buf); 243 i->u.ext2.i_data[level] = newblock; 244 i->i_blocks += blksize / 512; 245 } else { 246 return 0; 247 } 248 } 249 if(!(buf = bread(i->dev, i->u.ext2.i_data[level], blksize))) { 250 return -EIO; 251 } 252 indblock = (__blk_t *)buf->data; 253 dblock = block - BLOCKS_PER_IND_BLOCK(i->sb); 254 tblock = block - (BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)) - BLOCKS_PER_IND_BLOCK(i->sb); 255 256 if(level == EXT2_DIND_BLOCK) { 257 block = dblock / BLOCKS_PER_IND_BLOCK(i->sb); 258 } 259 if(level == EXT2_TIND_BLOCK) { 260 block = tblock / (BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)); 261 } 262 263 if(!indblock[block]) { 264 if(mode == FOR_WRITING) { 265 if((newblock = ext2_balloc(i->sb)) < 0) { 266 brelse(buf); 267 return -ENOSPC; 268 } 269 /* initialize the new block */ 270 if(!(buf2 = bread(i->dev, newblock, blksize))) { 271 ext2_bfree(i->sb, newblock); 272 brelse(buf); 273 return -EIO; 274 } 275 memset_b(buf2->data, 0, blksize); 276 bwrite(buf2); 277 indblock[block] = newblock; 278 i->i_blocks += blksize / 512; 279 if(level == EXT2_IND_BLOCK) { 280 bwrite(buf); 281 return newblock; 282 } 283 buf->flags |= (BUFFER_DIRTY | BUFFER_VALID); 284 } else { 285 brelse(buf); 286 return 0; 287 } 288 } 289 if(level == EXT2_IND_BLOCK) { 290 newblock = indblock[block]; 291 brelse(buf); 292 return newblock; 293 } 294 295 if(level == EXT2_TIND_BLOCK) { 296 if(!(buf3 = bread(i->dev, indblock[block], blksize))) { 297 printk("%s(): returning -EIO\n", __FUNCTION__); 298 brelse(buf); 299 return -EIO; 300 } 301 tindblock = (__blk_t *)buf3->data; 302 block = tindblock[tblock / BLOCKS_PER_IND_BLOCK(i->sb)]; 303 if(!block) { 304 if(mode == FOR_WRITING) { 305 if((newblock = ext2_balloc(i->sb)) < 0) { 306 brelse(buf); 307 brelse(buf3); 308 return -ENOSPC; 309 } 310 /* initialize the new block */ 311 if(!(buf4 = bread(i->dev, newblock, blksize))) { 312 ext2_bfree(i->sb, newblock); 313 brelse(buf); 314 brelse(buf3); 315 return -EIO; 316 } 317 memset_b(buf4->data, 0, blksize); 318 bwrite(buf4); 319 tindblock[tblock / BLOCKS_PER_IND_BLOCK(i->sb)] = newblock; 320 i->i_blocks += blksize / 512; 321 buf3->flags |= (BUFFER_DIRTY | BUFFER_VALID); 322 block = newblock; 323 } else { 324 brelse(buf); 325 brelse(buf3); 326 return 0; 327 } 328 } 329 dblock = tblock; 330 iblock = tblock / BLOCKS_PER_IND_BLOCK(i->sb); 331 if(!(buf2 = bread(i->dev, block, blksize))) { 332 printk("%s(): returning -EIO\n", __FUNCTION__); 333 brelse(buf); 334 brelse(buf3); 335 return -EIO; 336 } 337 } else { 338 iblock = block; 339 if(!(buf2 = bread(i->dev, indblock[iblock], blksize))) { 340 printk("%s(): returning -EIO\n", __FUNCTION__); 341 brelse(buf); 342 return -EIO; 343 } 344 } 345 346 dindblock = (__blk_t *)buf2->data; 347 block = dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))]; 348 if(!block && mode == FOR_WRITING) { 349 if((newblock = ext2_balloc(i->sb)) < 0) { 350 brelse(buf); 351 if(level == EXT2_TIND_BLOCK) { 352 brelse(buf3); 353 } 354 brelse(buf2); 355 return -ENOSPC; 356 } 357 /* initialize the new block */ 358 if(!(buf4 = bread(i->dev, newblock, blksize))) { 359 ext2_bfree(i->sb, newblock); 360 brelse(buf); 361 if(level == EXT2_TIND_BLOCK) { 362 brelse(buf3); 363 } 364 brelse(buf2); 365 return -EIO; 366 } 367 memset_b(buf4->data, 0, blksize); 368 bwrite(buf4); 369 dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))] = newblock; 370 i->i_blocks += blksize / 512; 371 buf2->flags |= (BUFFER_DIRTY | BUFFER_VALID); 372 block = newblock; 373 } 374 brelse(buf); 375 if(level == EXT2_TIND_BLOCK) { 376 brelse(buf3); 377 } 378 brelse(buf2); 379 return block; 380 } 381 382 int ext2_truncate(struct inode *i, __off_t length) 383 { 384 __blk_t block, dblock, *indblock; 385 struct buffer *buf; 386 int blksize, n; 387 388 blksize = i->sb->s_blocksize; 389 block = length / blksize; 390 391 if(!S_ISDIR(i->i_mode) && !S_ISREG(i->i_mode) && !S_ISLNK(i->i_mode)) { 392 return -EINVAL; 393 } 394 395 if(block < EXT2_NDIR_BLOCKS) { 396 for(n = block; n < EXT2_NDIR_BLOCKS; n++) { 397 if(i->u.ext2.i_data[n]) { 398 ext2_bfree(i->sb, i->u.ext2.i_data[n]); 399 i->u.ext2.i_data[n] = 0; 400 i->i_blocks -= blksize / 512; 401 } 402 } 403 block = 0; 404 } 405 406 if(!block || block < (BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { 407 if(block) { 408 block -= EXT2_NDIR_BLOCKS; 409 } 410 if(i->u.ext2.i_data[EXT2_IND_BLOCK]) { 411 free_indblock(i, i->u.ext2.i_data[EXT2_IND_BLOCK], block); 412 if(!block) { 413 ext2_bfree(i->sb, i->u.ext2.i_data[EXT2_IND_BLOCK]); 414 i->u.ext2.i_data[EXT2_IND_BLOCK] = 0; 415 i->i_blocks -= blksize / 512; 416 } 417 } 418 block = 0; 419 } 420 421 if(block) { 422 block -= EXT2_NDIR_BLOCKS; 423 block -= BLOCKS_PER_IND_BLOCK(i->sb); 424 } 425 if(i->u.ext2.i_data[EXT2_DIND_BLOCK]) { 426 if(!(buf = bread(i->dev, i->u.ext2.i_data[EXT2_DIND_BLOCK], blksize))) { 427 printk("%s(): error reading block %d.\n", __FUNCTION__, i->u.ext2.i_data[EXT2_DIND_BLOCK]); 428 return -EIO; 429 } 430 indblock = (__blk_t *)buf->data; 431 dblock = block % BLOCKS_PER_IND_BLOCK(i->sb); 432 for(n = block / BLOCKS_PER_IND_BLOCK(i->sb); n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { 433 if(indblock[n]) { 434 free_indblock(i, indblock[n], dblock); 435 if(!dblock) { 436 ext2_bfree(i->sb, indblock[n]); 437 i->i_blocks -= blksize / 512; 438 } 439 } 440 dblock = 0; 441 } 442 bwrite(buf); 443 if(!block) { 444 ext2_bfree(i->sb, i->u.ext2.i_data[EXT2_DIND_BLOCK]); 445 i->u.ext2.i_data[EXT2_DIND_BLOCK] = 0; 446 i->i_blocks -= blksize / 512; 447 } 448 } 449 450 i->i_mtime = CURRENT_TIME; 451 i->i_ctime = CURRENT_TIME; 452 i->i_size = length; 453 i->dirty = 1; 454 455 return 0; 456 }