?? inode.c
字號:
//
// inode.c
//
// Disk filesystem inode routines
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os/krnl.h>
static void split_levels(struct inode *inode, unsigned int iblock, unsigned int offsets[DFS_MAX_DEPTH])
{
unsigned int shift;
int d;
shift = inode->desc->depth * inode->fs->log_blkptrs_per_block;
offsets[0] = iblock >> shift;
for (d = 1; d <= inode->desc->depth; d++)
{
iblock &= ((1 << shift) - 1);
shift -= inode->fs->log_blkptrs_per_block;
offsets[d] = iblock >> shift;
}
}
void mark_inode_dirty(struct inode *inode)
{
mark_buffer_updated(inode->fs->cache, inode->buf);
}
blkno_t get_inode_block(struct inode *inode, unsigned int iblock)
{
int d;
blkno_t block;
struct buf *buf;
unsigned int offsets[DFS_MAX_DEPTH];
split_levels(inode, iblock, offsets);
block = inode->desc->blockdir[offsets[0]];
for (d = 1; d <= inode->desc->depth; d++)
{
buf = get_buffer(inode->fs->cache, block);
if (!buf) return NOBLOCK;
block = ((blkno_t *) buf->data)[offsets[d]];
release_buffer(inode->fs->cache, buf);
if (!block) return NOBLOCK;
}
return block;
}
blkno_t set_inode_block(struct inode *inode, unsigned int iblock, blkno_t block)
{
int d;
struct buf *buf;
blkno_t dirblock;
blkno_t goal;
unsigned int offsets[DFS_MAX_DEPTH];
goal = inode->ino / inode->fs->super->inodes_per_group * inode->fs->super->blocks_per_group;
if (inode->desc->depth == 0)
{
// Allocate new block in same group as inode if requested
if (block == NOBLOCK)
{
block = new_block(inode->fs, goal);
if (block == NOBLOCK) return NOBLOCK;
inode->desc->blocks++;
}
// Update top block directory in inode descriptor
inode->desc->blockdir[iblock] = block;
mark_inode_dirty(inode);
}
else
{
buf = NULL;
// Get block directory block for first level
split_levels(inode, iblock, offsets);
dirblock = inode->desc->blockdir[offsets[0]];
// Allocate block and update inode
if (dirblock == 0)
{
dirblock = new_block(inode->fs, goal);
if (dirblock == NOBLOCK) return NOBLOCK;
inode->desc->blockdir[offsets[0]] = dirblock;
mark_inode_dirty(inode);
buf = alloc_buffer(inode->fs->cache, dirblock);
if (!buf) return NOBLOCK;
memset(buf->data, 0, inode->fs->blocksize);
}
// Traverse and allocate internal pages in block directory
for (d = 1; d < inode->desc->depth; d++)
{
// Get directory page for next level
if (!buf) buf = get_buffer(inode->fs->cache, dirblock);
if (!buf) return NOBLOCK;
goal = dirblock + 1;
dirblock = ((blkno_t *) buf->data)[offsets[d]];
// Allocate directory page block if missing
if (dirblock == 0)
{
dirblock = new_block(inode->fs, goal);
if (dirblock == NOBLOCK) return NOBLOCK;
((blkno_t *) buf->data)[offsets[d]] = dirblock;
mark_buffer_updated(inode->fs->cache, buf);
release_buffer(inode->fs->cache, buf);
buf = alloc_buffer(inode->fs->cache, dirblock);
if (!buf) return NOBLOCK;
memset(buf->data, 0, inode->fs->blocksize);
}
else
{
release_buffer(inode->fs->cache, buf);
buf = NULL;
}
}
// Get leaf block directory page
if (!buf) buf = get_buffer(inode->fs->cache, dirblock);
if (!buf) return NOBLOCK;
// Allocate new block near previous block or leaf directory page if requested
if (block == -1)
{
block = new_block(inode->fs, offsets[d] == 0 ? dirblock + 1 : ((blkno_t *) buf->data)[offsets[d] - 1] + 1);
if (block == NOBLOCK) return NOBLOCK;
inode->desc->blocks++;
mark_inode_dirty(inode);
}
// Update leaf with new block
((blkno_t *) buf->data)[offsets[d]] = block;
mark_buffer_updated(inode->fs->cache, buf);
release_buffer(inode->fs->cache, buf);
}
return block;
}
struct inode *alloc_inode(struct inode *parent, int flags)
{
ino_t ino;
struct inode *inode;
unsigned int group;
unsigned int block;
ino = new_inode(parent->fs, parent->ino, flags);
if (ino == -1) return NULL;
inode = (struct inode *) kmalloc(sizeof(struct inode));
if (!inode) return NULL;
inode->fs = parent->fs;
inode->ino = ino;
group = ino / inode->fs->super->inodes_per_group;
block = inode->fs->groups[group].desc->inode_table_block + (ino % inode->fs->super->inodes_per_group) / inode->fs->inodes_per_block;
inode->buf = get_buffer(inode->fs->cache, block);
if (!inode->buf)
{
kfree(inode);
return NULL;
}
inode->desc = (struct inodedesc *) (inode->buf->data) + (ino % inode->fs->inodes_per_block);
memset(inode->desc, 0, sizeof(struct inodedesc));
inode->desc->flags = flags;
inode->desc->ctime = inode->desc->mtime = time(NULL);
mark_inode_dirty(inode);
return inode;
}
int unlink_inode(struct inode *inode)
{
int rc;
inode->desc->linkcount--;
mark_inode_dirty(inode);
if (inode->desc->linkcount > 0) return 0;
rc = truncate_inode(inode, 0);
if (rc < 0) return rc;
memset(inode->desc, 0, sizeof(struct inodedesc));
free_inode(inode->fs, inode->ino);
return 0;
}
struct inode *get_inode(struct filsys *fs, ino_t ino)
{
struct inode *inode;
unsigned int group;
unsigned int block;
inode = (struct inode *) kmalloc(sizeof(struct inode));
if (!inode) return NULL;
inode->fs = fs;
inode->ino = ino;
group = ino / fs->super->inodes_per_group;
block = fs->groups[group].desc->inode_table_block + (ino % fs->super->inodes_per_group) / fs->inodes_per_block;
inode->buf = get_buffer(fs->cache, block);
if (!inode->buf)
{
kfree(inode);
return NULL;
}
inode->desc = (struct inodedesc *) (inode->buf->data) + (ino % fs->inodes_per_block);
return inode;
}
void release_inode(struct inode *inode)
{
if (inode->buf) release_buffer(inode->fs->cache, inode->buf);
kfree(inode);
}
blkno_t expand_inode(struct inode *inode)
{
unsigned int maxblocks;
unsigned int dirblock;
unsigned int i;
struct buf *buf;
// Increase depth of block directory tree if tree is full
maxblocks = DFS_TOPBLOCKDIR_SIZE * (1 << (inode->desc->depth * inode->fs->log_blkptrs_per_block));
if (inode->desc->blocks == maxblocks)
{
// Allocate block for new directory page
dirblock = new_block(inode->fs, inode->desc->blockdir[0] - 1);
if (dirblock == NOBLOCK) return NOBLOCK;
// Move top directory entries to new directory page
buf = alloc_buffer(inode->fs->cache, dirblock);
if (!buf) return NOBLOCK;
memset(buf->data, 0, inode->fs->blocksize);
for (i = 0; i < DFS_TOPBLOCKDIR_SIZE; i++)
{
((blkno_t *) buf->data)[i] = inode->desc->blockdir[i];
inode->desc->blockdir[i] = 0;
}
// Set top block dir to point to new directory page and increase depth
inode->desc->blockdir[0] = dirblock;
inode->desc->depth++;
mark_buffer_updated(inode->fs->cache, buf);
mark_inode_dirty(inode);
release_buffer(inode->fs->cache, buf);
}
// Allocate new block and add to inode block directory
return set_inode_block(inode, inode->desc->blocks, NOBLOCK);
}
static void remove_blocks(struct filsys *fs, blkno_t *blocks, int count)
{
int i;
if (count > 0)
{
for (i = 0; i < count; i++) invalidate_buffer(fs->cache, blocks[i]);
free_blocks(fs, blocks, count);
memset(blocks, 0, sizeof(blkno_t) * count);
}
}
int truncate_inode(struct inode *inode, unsigned int blocks)
{
int d;
unsigned int iblock;
unsigned int blocksleft;
unsigned int count;
blkno_t blk;
unsigned int offsets[DFS_MAX_DEPTH];
struct buf *buf[DFS_MAX_DEPTH];
// Check arguments
if (blocks > inode->desc->blocks) return -EINVAL;
// Check for no-op case
if (blocks == inode->desc->blocks) return 0;
if (inode->desc->blocks == 0) return 0;
// If depth 0 we just have to free blocks from top directory
if (inode->desc->depth == 0)
{
remove_blocks(inode->fs, inode->desc->blockdir + blocks, inode->desc->blocks - blocks);
inode->desc->blocks = blocks;
mark_inode_dirty(inode);
return 0;
}
// Clear buffers
for (d = 0; d < DFS_MAX_DEPTH; d++) buf[d] = NULL;
// Remove blocks from the end of the file until we reach the requested size
iblock = inode->desc->blocks - 1;
blocksleft = inode->desc->blocks - blocks;
while (blocksleft > 0)
{
// Traverse block directory tree until we reach the last block
split_levels(inode, iblock, offsets);
blk = inode->desc->blockdir[offsets[0]];
for (d = 1; d <= inode->desc->depth; d++)
{
if (!buf[d] || buf[d]->blkno != blk)
{
if (buf[d]) release_buffer(inode->fs->cache, buf[d]);
buf[d] = get_buffer(inode->fs->cache, blk);
if (!buf[d]) return -EIO;
}
blk = ((blkno_t *) buf[d]->data)[offsets[d]];
}
d = inode->desc->depth;
if (blocksleft > offsets[d])
{
// Remove all blocks in leaf directory page and free it
count = offsets[d] + 1;
remove_blocks(inode->fs, (blkno_t *) buf[d]->data, count);
free_blocks(inode->fs, &(buf[d]->blkno), 1);
mark_buffer_invalid(inode->fs->cache, buf[d]);
iblock -= count;
blocksleft -= count;
offsets[d] = 0;
// Remove internal directory pages
while (--d > 0)
{
((blkno_t *) buf[d]->data)[offsets[d]] = 0;
if (offsets[d] == 0)
{
free_blocks(inode->fs, &(buf[d]->blkno), 1);
mark_buffer_invalid(inode->fs->cache, buf[d]);
}
else
{
mark_buffer_updated(inode->fs->cache, buf[d]);
break;
}
}
// Update top directory page in inode
if (d == 0 && offsets[1] == 0)
{
inode->desc->blockdir[offsets[0]] = 0;
mark_inode_dirty(inode);
}
}
else
{
// Remove range of blocks in leaf directory page
remove_blocks(inode->fs, (blkno_t *) buf[d]->data + offsets[d] + 1 - blocksleft, blocksleft);
mark_buffer_updated(inode->fs->cache, buf[d]);
iblock -= blocksleft;
blocksleft = 0;
}
}
// Release buffers
for (d = 0; d < DFS_MAX_DEPTH; d++)
{
if (buf[d]) release_buffer(inode->fs->cache, buf[d]);
}
// Update inode
inode->desc->blocks = blocks;
if (blocks == 0) inode->desc->depth = 0;
mark_inode_dirty(inode);
return 0;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -