📄 inode.c
字号:
if (!bh) { ext3_error(inode->i_sb, "ext3_free_branches", "Read failure, inode=%ld, block=%ld", inode->i_ino, nr); continue; } /* This zaps the entire block. Bottom up. */ BUFFER_TRACE(bh, "free child branches"); ext3_free_branches(handle, inode, bh, (u32*)bh->b_data, (u32*)bh->b_data + addr_per_block, depth); /* * We've probably journalled the indirect block several * times during the truncate. But it's no longer * needed and we now drop it from the transaction via * journal_revoke(). * * That's easy if it's exclusively part of this * transaction. But if it's part of the committing * transaction then journal_forget() will simply * brelse() it. That means that if the underlying * block is reallocated in ext3_get_block(), * unmap_underlying_metadata() will find this block * and will try to get rid of it. damn, damn. * * If this block has already been committed to the * journal, a revoke record will be written. And * revoke records must be emitted *before* clearing * this block's bit in the bitmaps. */ ext3_forget(handle, 1, inode, bh, bh->b_blocknr); /* * Everything below this this pointer has been * released. Now let this top-of-subtree go. * * We want the freeing of this indirect block to be * atomic in the journal with the updating of the * bitmap block which owns it. So make some room in * the journal. * * We zero the parent pointer *after* freeing its * pointee in the bitmaps, so if extend_transaction() * for some reason fails to put the bitmap changes and * the release into the same transaction, recovery * will merely complain about releasing a free block, * rather than leaking blocks. */ if (is_handle_aborted(handle)) return; if (try_to_extend_transaction(handle, inode)) { ext3_mark_inode_dirty(handle, inode); ext3_journal_test_restart(handle, inode); } ext3_free_blocks(handle, inode, nr, 1); if (parent_bh) { /* * The block which we have just freed is * pointed to by an indirect block: journal it */ BUFFER_TRACE(parent_bh, "get_write_access"); if (!ext3_journal_get_write_access(handle, parent_bh)){ *p = 0; BUFFER_TRACE(parent_bh, "call ext3_journal_dirty_metadata"); ext3_journal_dirty_metadata(handle, parent_bh); } } } } else { /* We have reached the bottom of the tree. */ BUFFER_TRACE(parent_bh, "free data blocks"); ext3_free_data(handle, inode, parent_bh, first, last); }}/* * ext3_truncate() * * We block out ext3_get_block() block instantiations across the entire * transaction, and VFS/VM ensures that ext3_truncate() cannot run * simultaneously on behalf of the same inode. * * As we work through the truncate and commmit bits of it to the journal there * is one core, guiding principle: the file's tree must always be consistent on * disk. We must be able to restart the truncate after a crash. * * The file's tree may be transiently inconsistent in memory (although it * probably isn't), but whenever we close off and commit a journal transaction, * the contents of (the filesystem + the journal) must be consistent and * restartable. It's pretty simple, really: bottom up, right to left (although * left-to-right works OK too). * * Note that at recovery time, journal replay occurs *before* the restart of * truncate against the orphan inode list. * * The committed inode has the new, desired i_size (which is the same as * i_disksize in this case). After a crash, ext3_orphan_cleanup() will see * that this inode's truncate did not complete and it will again call * ext3_truncate() to have another go. So there will be instantiated blocks * to the right of the truncation point in a crashed ext3 filesystem. But * that's fine - as long as they are linked from the inode, the post-crash * ext3_truncate() run will find them and release them. */void ext3_truncate(struct inode * inode){ handle_t *handle; u32 *i_data = inode->u.ext3_i.i_data; int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb); int offsets[4]; Indirect chain[4]; Indirect *partial; int nr = 0; int n; long last_block; unsigned blocksize; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; ext3_discard_prealloc(inode); handle = start_transaction(inode); if (IS_ERR(handle)) return; /* AKPM: return what? */ blocksize = inode->i_sb->s_blocksize; last_block = (inode->i_size + blocksize-1) >> EXT3_BLOCK_SIZE_BITS(inode->i_sb); ext3_block_truncate_page(handle, inode->i_mapping, inode->i_size); n = ext3_block_to_path(inode, last_block, offsets); if (n == 0) goto out_stop; /* error */ /* * OK. This truncate is going to happen. We add the inode to the * orphan list, so that if this truncate spans multiple transactions, * and we crash, we will resume the truncate when the filesystem * recovers. It also marks the inode dirty, to catch the new size. * * Implication: the file must always be in a sane, consistent * truncatable state while each transaction commits. */ if (ext3_orphan_add(handle, inode)) goto out_stop; /* * The orphan list entry will now protect us from any crash which * occurs before the truncate completes, so it is now safe to propagate * the new, shorter inode size (held for now in i_size) into the * on-disk inode. We do this via i_disksize, which is the value which * ext3 *really* writes onto the disk inode. */ inode->u.ext3_i.i_disksize = inode->i_size; /* * From here we block out all ext3_get_block() callers who want to * modify the block allocation tree. */ down_write(&inode->u.ext3_i.truncate_sem); if (n == 1) { /* direct blocks */ ext3_free_data(handle, inode, NULL, i_data+offsets[0], i_data + EXT3_NDIR_BLOCKS); goto do_indirects; } partial = ext3_find_shared(inode, n, offsets, chain, &nr); /* Kill the top of shared branch (not detached) */ if (nr) { if (partial == chain) { /* Shared branch grows from the inode */ ext3_free_branches(handle, inode, NULL, &nr, &nr+1, (chain+n-1) - partial); *partial->p = 0; /* * We mark the inode dirty prior to restart, * and prior to stop. No need for it here. */ } else { /* Shared branch grows from an indirect block */ BUFFER_TRACE(partial->bh, "get_write_access"); ext3_free_branches(handle, inode, partial->bh, partial->p, partial->p+1, (chain+n-1) - partial); } } /* Clear the ends of indirect blocks on the shared branch */ while (partial > chain) { ext3_free_branches(handle, inode, partial->bh, partial->p + 1, (u32*)partial->bh->b_data + addr_per_block, (chain+n-1) - partial); BUFFER_TRACE(partial->bh, "call brelse"); brelse (partial->bh); partial--; }do_indirects: /* Kill the remaining (whole) subtrees */ switch (offsets[0]) { default: nr = i_data[EXT3_IND_BLOCK]; if (nr) { ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 1); i_data[EXT3_IND_BLOCK] = 0; } case EXT3_IND_BLOCK: nr = i_data[EXT3_DIND_BLOCK]; if (nr) { ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 2); i_data[EXT3_DIND_BLOCK] = 0; } case EXT3_DIND_BLOCK: nr = i_data[EXT3_TIND_BLOCK]; if (nr) { ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 3); i_data[EXT3_TIND_BLOCK] = 0; } case EXT3_TIND_BLOCK: ; } up_write(&inode->u.ext3_i.truncate_sem); inode->i_mtime = inode->i_ctime = CURRENT_TIME; ext3_mark_inode_dirty(handle, inode); /* In a multi-transaction truncate, we only make the final * transaction synchronous */ if (IS_SYNC(inode)) handle->h_sync = 1;out_stop: /* * If this was a simple ftruncate(), and the file will remain alive * then we need to clear up the orphan record which we created above. * However, if this was a real unlink then we were called by * ext3_delete_inode(), and we allow that function to clean up the * orphan info for us. */ if (inode->i_nlink) ext3_orphan_del(handle, inode); ext3_journal_stop(handle, inode);}/* * ext3_get_inode_loc returns with an extra refcount against the * inode's underlying buffer_head on success. */int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc){ struct buffer_head *bh = 0; unsigned long block; unsigned long block_group; unsigned long group_desc; unsigned long desc; unsigned long offset; struct ext3_group_desc * gdp; if ((inode->i_ino != EXT3_ROOT_INO && inode->i_ino != EXT3_ACL_IDX_INO && inode->i_ino != EXT3_ACL_DATA_INO && inode->i_ino != EXT3_JOURNAL_INO && inode->i_ino < EXT3_FIRST_INO(inode->i_sb)) || inode->i_ino > le32_to_cpu( inode->i_sb->u.ext3_sb.s_es->s_inodes_count)) { ext3_error (inode->i_sb, "ext3_get_inode_loc", "bad inode number: %lu", inode->i_ino); goto bad_inode; } block_group = (inode->i_ino - 1) / EXT3_INODES_PER_GROUP(inode->i_sb); if (block_group >= inode->i_sb->u.ext3_sb.s_groups_count) { ext3_error (inode->i_sb, "ext3_get_inode_loc", "group >= groups count"); goto bad_inode; } group_desc = block_group >> EXT3_DESC_PER_BLOCK_BITS(inode->i_sb); desc = block_group & (EXT3_DESC_PER_BLOCK(inode->i_sb) - 1); bh = inode->i_sb->u.ext3_sb.s_group_desc[group_desc]; if (!bh) { ext3_error (inode->i_sb, "ext3_get_inode_loc", "Descriptor not loaded"); goto bad_inode; } gdp = (struct ext3_group_desc *) bh->b_data; /* * Figure out the offset within the block group inode table */ offset = ((inode->i_ino - 1) % EXT3_INODES_PER_GROUP(inode->i_sb)) * EXT3_INODE_SIZE(inode->i_sb); block = le32_to_cpu(gdp[desc].bg_inode_table) + (offset >> EXT3_BLOCK_SIZE_BITS(inode->i_sb)); if (!(bh = sb_bread(inode->i_sb, block))) { ext3_error (inode->i_sb, "ext3_get_inode_loc", "unable to read inode block - " "inode=%lu, block=%lu", inode->i_ino, block); goto bad_inode; } offset &= (EXT3_BLOCK_SIZE(inode->i_sb) - 1); iloc->bh = bh; iloc->raw_inode = (struct ext3_inode *) (bh->b_data + offset); iloc->block_group = block_group; return 0; bad_inode: return -EIO;}void ext3_read_inode(struct inode * inode){ struct ext3_iloc iloc; struct ext3_inode *raw_inode; struct buffer_head *bh; int block; if(ext3_get_inode_loc(inode, &iloc)) goto bad_inode; bh = iloc.bh; raw_inode = iloc.raw_inode; init_rwsem(&inode->u.ext3_i.truncate_sem); inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); if(!(test_opt (inode->i_sb, NO_UID32))) { inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16; inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16; } inode->i_nlink = le16_to_cpu(raw_inode->i_links_count); inode->i_size = le32_to_cpu(raw_inode->i_size); inode->i_atime = le32_to_cpu(raw_inode->i_atime); inode->i_ctime = le32_to_cpu(raw_inode->i_ctime); inode->i_mtime = le32_to_cpu(raw_inode->i_mtime); inode->u.ext3_i.i_dtime = le32_to_cpu(raw_inode->i_dtime); /* We now have enough fields to check if the inode was active or not. * This is needed because nfsd might try to access dead inodes * the test is that same one that e2fsck uses * NeilBrown 1999oct15 */ if (inode->i_nlink == 0) { if (inode->i_mode == 0 || !(inode->i_sb->u.ext3_sb.s_mount_state & EXT3_ORPHAN_FS)) { /* this inode is deleted */ brelse (bh); goto bad_inode; } /* The only unlinked inodes we let through here have * valid i_mode and are being read by the orphan * recovery code: that's fine, we're about to complete * the process of deleting those. */ } inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size * (for stat), not the fs block * size */ inode->i_blocks = le32_to_cpu(raw_inode->i_blocks); inode->i_version = ++event; inode->u.ext3_i.i_flags = le32_to_cpu(raw_inode->i_flags);#ifdef EXT3_FRAGMENTS inode->u.ext3_i.i_faddr = le32_to_cpu(raw_inode->i_faddr); inode->u.ext3_i.i_frag_no = raw_inode->i_frag; inode->u.ext3_i.i_frag_size = raw_inode->i_fsize;#endif inode->u.ext3_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl); if (!S_ISREG(inode->i_mode)) { inode->u.ext3_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); } else { inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; } inode->u.ext3_i.i_disksize = inode->i_size; inode->i_generation = le32_to_cpu(raw_inode->i_generation);#ifdef EXT3_PREALLOCATE inode->u.ext3_i.i_prealloc_count = 0;#endif inode->u.ext3_i.i_block_group = iloc.block_group; /* * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! */ for (block = 0; block < EXT3_N_BLOCKS; block++) inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block]; INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan); brelse (iloc.bh); if (inode->i_ino == EXT3_ACL_IDX_INO || inode->i_ino == EXT3_ACL_DATA_INO) /* Nothing to do */ ; else if (S_ISREG(inode->i_mode)) { inode->i_op = &ext3_file_inode_operations; inode->i_fop = &ext3_file_operations; inode->i_mapping->a_ops = &ext3_aops; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; } else if (S_ISLNK(inode->i_mode)) { if (!inode->i_blocks) inode->i_op = &ext3_fast_symlink_inode_operations; else { inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &ext3_aops; } } else init_special_inode(inode, inode->i_mode, le32_to_cpu(iloc.raw_inode->i_block[0])); /* inode->i_attr_flags = 0; unused */ if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) { /* inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; unused */ inode->i_flags |= S_SYNC; } if (inode->u.ext3_i.i_flags & EXT3_APPEND_FL) { /* inode->i_attr_flags |= ATTR_FLAG_APPEND; unused */ inode->i_f
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -