Page MenuHomeClusterLabs Projects

pass2.c
No OneTemporary

#include "stdio.h"
#include "fsck_incore.h"
#include "fsck.h"
#include "block_list.h"
#include "bio.h"
#include "fs_inode.h"
#include "fs_dir.h"
#include "util.h"
#include "log.h"
#include "inode_hash.h"
#include "inode.h"
#include "link.h"
#include "metawalk.h"
#include "eattr.h"
#define MAX_FILENAME 256
struct dir_status {
uint8_t dotdir:1;
uint8_t dotdotdir:1;
struct block_query q;
uint32_t entry_count;
};
/* Set children's parent inode in dir_info structure - ext2 does not set
* dotdot inode here, but instead in pass3 - should we? */
int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
uint64_t parentblock)
{
struct dir_info *di;
if(!find_di(sbp, childblock, &di)) {
if(di->dinode == childblock) {
if (di->treewalk_parent) {
log_err("Another directory (%"PRIu64
") already contains"
" this child - checking %"PRIu64"\n",
di->treewalk_parent, parentblock);
return 1;
}
di->treewalk_parent = parentblock;
}
} else {
log_err("Unable to find block %"PRIu64" in dir_info list\n",
childblock);
return -1;
}
return 0;
}
/* Set's the child's '..' directory inode number in dir_info structure */
int set_dotdot_dir(struct fsck_sb *sbp, uint64_t childblock,
uint64_t parentblock)
{
struct dir_info *di;
if(!find_di(sbp, childblock, &di)) {
if(di->dinode == childblock) {
/* Special case for root inode because we set
* it earlier */
if(di->dotdot_parent && sbp->sb.sb_root_di.no_addr
!= di->dinode) {
/* This should never happen */
log_crit("dotdot parent already set for"
" block %"PRIu64" -> %"PRIu64"\n",
childblock, di->dotdot_parent);
return -1;
}
di->dotdot_parent = parentblock;
}
} else {
log_err("Unable to find block %"PRIu64" in dir_info list\n",
childblock);
return -1;
}
return 0;
}
static int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
uint64_t parent, osi_buf_t **bh, void *private)
{
return 0;
}
static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
uint64_t parent, osi_buf_t **bh, void *private)
{
osi_buf_t *leaf_bh;
if(get_and_read_buf(ip->i_sbd, block, &leaf_bh, 0)){
log_warn("Unable to read EA leaf block #%"PRIu64".\n",
block);
block_set(ip->i_sbd->bl, block, meta_inval);
return 1;
}
return 0;
}
static int check_file_type(uint16_t de_type, uint8_t block_type) {
switch(block_type) {
case inode_dir:
if(de_type != GFS_FILE_DIR)
return 1;
break;
case inode_file:
if(de_type != GFS_FILE_REG)
return 1;
break;
case inode_lnk:
if(de_type != GFS_FILE_LNK)
return 1;
break;
case inode_blk:
if(de_type != GFS_FILE_BLK)
return 1;
break;
case inode_chr:
if(de_type != GFS_FILE_CHR)
return 1;
break;
case inode_fifo:
if(de_type != GFS_FILE_FIFO)
return 1;
break;
case inode_sock:
if(de_type != GFS_FILE_SOCK)
return 1;
break;
default:
log_err("Invalid block type\n");
return -1;
break;
}
return 0;
}
/* FIXME: should maybe refactor this a bit - but need to deal with
* FIXMEs internally first */
int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
struct gfs_dirent *prev_de,
osi_buf_t *bh, char *filename, int *update,
uint16_t *count, void *priv)
{
struct fsck_sb *sbp = ip->i_sbd;
struct block_query q = {0};
char tmp_name[MAX_FILENAME];
uint64_t entryblock;
struct dir_status *ds = (struct dir_status *) priv;
int error;
struct fsck_inode *entry_ip = NULL;
struct metawalk_fxns clear_eattrs = {0};
struct gfs_dirent dentry, *de;
memset(&dentry, 0, sizeof(struct gfs_dirent));
gfs_dirent_in(&dentry, (char *)dent);
de = &dentry;
clear_eattrs.check_eattr_indir = clear_eattr_indir;
clear_eattrs.check_eattr_leaf = clear_eattr_leaf;
clear_eattrs.check_eattr_entry = clear_eattr_entry;
clear_eattrs.check_eattr_extentry = clear_eattr_extentry;
entryblock = de->de_inum.no_addr;
/* Start of checks */
if (de->de_rec_len < GFS_DIRENT_SIZE(de->de_name_len)){
log_err("Dir entry with bad record or name length\n"
"\tRecord length = %u\n"
"\tName length = %u\n",
de->de_rec_len,
de->de_name_len);
block_set(sbp->bl, ip->i_num.no_addr, meta_inval);
return 1;
/* FIXME: should probably delete the entry here at the
* very least - maybe look at attempting to fix it */
}
if (de->de_hash != gfs_dir_hash(filename, de->de_name_len)){
log_err("Dir entry with bad hash or name length\n"
"\tHash found = %u\n"
"\tName found = %s\n"
"\tName length found = %u\n"
"\tHash expected = %u\n",
de->de_hash,
filename,
de->de_name_len,
gfs_dir_hash(filename, de->de_name_len));
return 1;
}
/* FIXME: This should probably go to the top of the fxn, and
* references to filename should be replaced with tmp_name */
memset(tmp_name, 0, MAX_FILENAME);
if(de->de_name_len < MAX_FILENAME){
strncpy(tmp_name, filename, de->de_name_len);
} else {
strncpy(tmp_name, filename, MAX_FILENAME - 1);
}
if(check_range(ip->i_sbd, entryblock)) {
log_err("Block # referenced by directory entry %s is out of range\n",
tmp_name);
if(query(ip->i_sbd, "Clear directory entry tp out of range block? (y/n) ")) {
log_err("Clearing %s\n", tmp_name);
if(dirent_del(ip, bh, prev_de, dent))
log_err("Error encountered while removing bad "
"directory entry. Skipping.\n");
return 1;
} else {
log_err("Directory entry to out of range block remains\n");
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
if(block_check(sbp->bl, de->de_inum.no_addr, &q)) {
stack;
return -1;
}
/* Get the status of the directory inode */
if(q.bad_block) {
/* This entry's inode has bad blocks in it */
/* FIXME: user interface */
/* FIXME: do i want to kill the inode here? */
/* Handle bad blocks */
log_err("Found a bad directory entry: %s\n", filename);
if(query(sbp, "Clear entry to inode containing bad blocks? (y/n)")) {
load_inode(sbp, de->de_inum.no_addr, &entry_ip);
check_inode_eattr(entry_ip, &clear_eattrs);
free_inode(&entry_ip);
/* FIXME: make sure all blocks referenced by
* this inode are cleared in the bitmap */
dirent_del(ip, bh, prev_de, dent);
block_set(sbp->bl, de->de_inum.no_addr, meta_inval);
return 1;
} else {
log_warn("Entry to inode containing bad blocks remains\n");
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
if(q.block_type != inode_dir && q.block_type != inode_file &&
q.block_type != inode_lnk && q.block_type != inode_blk &&
q.block_type != inode_chr && q.block_type != inode_fifo &&
q.block_type != inode_sock) {
log_err("Found directory entry '%s' in block %"
PRIu64" to something"
" not a file or directory!\n", tmp_name,
ip->i_num.no_addr);
log_debug("block #%"PRIu64" in %"PRIu64"\n",
de->de_inum.no_addr, ip->i_num.no_addr);
if(query(sbp, "Clear directory entry to non-inode block? (y/n) ")) {
/* FIXME: make sure all blocks referenced by
* this inode are cleared in the bitmap */
if(dirent_del(ip, bh, prev_de, dent))
log_err("Error encountered while removing bad "
"directory entry. Skipping.\n");
log_warn("Directory entry '%s' cleared\n", tmp_name);
return 1;
} else {
log_err("Directory entry to non-inode block remains\n");
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
error = check_file_type(de->de_type, q.block_type);
if(error < 0) {
stack;
return -1;
}
if(error > 0) {
log_warn("Type in dir entry (%s, %"PRIu64") conflicts with "
"type in dinode. (Dir entry is stale.)\n",
tmp_name, de->de_inum.no_addr);
if(query(sbp, "Clear stale directory entry? (y/n) ")) {
load_inode(sbp, de->de_inum.no_addr, &entry_ip);
check_inode_eattr(entry_ip, &clear_eattrs);
free_inode(&entry_ip);
if(dirent_del(ip, bh, prev_de, dent))
log_err("Error encountered while removing bad "
"directory entry. Skipping.\n");
return 1;
} else {
log_err("Stale directory entry remains\n");
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
if(!strcmp(".", tmp_name)) {
log_debug("Found . dentry\n");
if(ds->dotdir) {
log_err("already found '.' entry\n");
if(query(sbp, "Clear duplicate '.' entry? (y/n) ")) {
load_inode(sbp, de->de_inum.no_addr, &entry_ip);
check_inode_eattr(entry_ip, &clear_eattrs);
free_inode(&entry_ip);
dirent_del(ip, bh, prev_de, dent);
return 1;
} else {
log_err("Duplicate '.' entry remains\n");
/* FIXME: Should we continue on here
* and check the rest of the '.'
* entry? */
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
/* GFS does not rely on '.' being in a certain
* location */
/* check that '.' refers to this inode */
if(de->de_inum.no_addr != ip->i_num.no_addr) {
log_err("'.' entry's value incorrect."
" Points to %"PRIu64
" when it should point to %"
PRIu64".\n",
de->de_inum.no_addr,
ip->i_num.no_addr);
if(query(sbp, "remove '.' reference? (y/n) ")) {
load_inode(sbp, de->de_inum.no_addr, &entry_ip);
check_inode_eattr(entry_ip, &clear_eattrs);
free_inode(&entry_ip);
dirent_del(ip, bh, prev_de, dent);
return 1;
} else {
log_err("Invalid '.' reference remains\n");
/* Not setting ds->dotdir here since
* this '.' entry is invalid */
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
ds->dotdir = 1;
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
if(!strcmp("..", tmp_name)) {
log_debug("Found .. dentry\n");
if(ds->dotdotdir) {
log_err("already found '..' entry\n");
if(query(sbp, "Clear duplicate '..' entry? (y/n) ")) {
load_inode(sbp, de->de_inum.no_addr, &entry_ip);
check_inode_eattr(entry_ip, &clear_eattrs);
free_inode(&entry_ip);
dirent_del(ip, bh, prev_de, dent);
*update = 1;
return 1;
} else {
log_err("Duplicate '..' entry remains\n");
/* FIXME: Should we continue on here
* and check the rest of the '..'
* entry? */
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
if(q.block_type != inode_dir) {
log_err("Found '..' entry pointing to"
" something that's not a directory");
if(query(sbp, "Clear bad '..' directory entry? (y/n) ")) {
load_inode(sbp, de->de_inum.no_addr, &entry_ip);
check_inode_eattr(entry_ip, &clear_eattrs);
free_inode(&entry_ip);
dirent_del(ip, bh, prev_de, dent);
*update = 1;
return 1;
} else {
log_err("Bad '..' directory entry remains\n");
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
/* GFS does not rely on '..' being in a
* certain location */
/* Add the address this entry is pointing to
* to this inode's dotdot_parent in
* dir_info */
if(set_dotdot_dir(sbp, ip->i_num.no_addr,
entryblock)) {
stack;
return -1;
}
ds->dotdotdir = 1;
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
/* After this point we're only concerned with
* directories */
if(q.block_type != inode_dir) {
log_debug("Found non-dir inode dentry\n");
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
log_debug("Found plain directory dentry\n");
error = set_parent_dir(sbp, entryblock, ip->i_num.no_addr);
if(error > 0) {
log_err("Hard link to block %"PRIu64" detected.\n", filename, entryblock);
if(query(sbp, "Clear hard link to directory? (y/n) ")) {
*update = 1;
dirent_del(ip, bh, prev_de, dent);
log_warn("Directory entry %s cleared\n", filename);
return 1;
} else {
log_err("Hard link to directory remains\n");
*update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
else if (error < 0) {
stack;
return -1;
}
increment_link(sbp, de->de_inum.no_addr);
*update = 1;
(*count)++;
ds->entry_count++;
/* End of checks */
return 0;
}
struct metawalk_fxns pass2_fxns = {
.private = NULL,
.check_leaf = NULL,
.check_metalist = NULL,
.check_data = NULL,
.check_eattr_indir = check_eattr_indir,
.check_eattr_leaf = check_eattr_leaf,
.check_dentry = check_dentry,
.check_eattr_entry = NULL,
};
int build_rooti(struct fsck_sb *sbp)
{
struct fsck_inode *ip;
osi_buf_t *bh;
get_and_read_buf(sbp, GFS_SB_ADDR >> sbp->fsb2bb_shift, &bh, 0);
/* Create a new inode ondisk */
create_inode(sbp, GFS_FILE_DIR, &ip);
/* Attach it to the superblock's sb_root_di address */
sbp->sb.sb_root_di.no_addr =
sbp->sb.sb_root_di.no_formal_ino = ip->i_num.no_addr;
/* Write out sb change */
gfs_sb_out(&sbp->sb, BH_DATA(bh));
write_buf(sbp, bh, 1);
relse_buf(sbp, bh);
sbp->rooti = ip;
if(fs_dir_add(ip, &(osi_filename_t){(unsigned char *)".", 1},
&(ip->i_num), ip->i_di.di_type)){
stack;
log_err("Unable to add \".\" entry to new root inode\n");
return -1;
}
if(fs_dir_add(ip, &(osi_filename_t){(unsigned char *)"..", 2},
&ip->i_num, ip->i_di.di_type)){
stack;
log_err("Unable to add \"..\" entry to new root inode\n");
return -1;
}
block_set(sbp->bl, ip->i_num.no_addr, inode_dir);
add_to_dir_list(sbp, ip->i_num.no_addr);
/* Attach l+f to it */
if(fs_mkdir(sbp->rooti, "l+f", 00700, &(sbp->lf_dip))){
log_err("Unable to create/locate l+f directory.\n");
return -1;
}
if(sbp->lf_dip){
log_debug("Lost and Found directory inode is at "
"block #%"PRIu64".\n",
sbp->lf_dip->i_num.no_addr);
}
block_set(sbp->bl, sbp->lf_dip->i_num.no_addr, inode_dir);
add_to_dir_list(sbp, sbp->lf_dip->i_num.no_addr);
return 0;
}
/* Check root inode and verify it's in the bitmap */
int check_root_dir(struct fsck_sb *sbp)
{
uint64_t rootblock;
struct dir_status ds = {0};
struct fsck_inode *ip;
osi_buf_t b, *bh = &b;
osi_filename_t filename;
char tmp_name[256];
int update=0, error = 0;
/* Read in the root inode, look at its dentries, and start
* reading through them */
rootblock = sbp->sb.sb_root_di.no_addr;
/* FIXME: check this block's validity */
if(block_check(sbp->bl, rootblock, &ds.q)) {
log_crit("Can't get root block %"PRIu64" from block list\n",
rootblock);
/* FIXME: Need to check if the root block is out of
* the fs range and if it is, rebuild it. Still can
* error out if the root block number is valid, but
* block_check fails */
return -1;
/* if(build_rooti(sbp)) {
stack;
return -1;
}*/
}
/* if there are errors with the root inode here, we need to
* create a new root inode and get it all setup - of course,
* everything will be in l+f then, but we *need* a root inode
* before we can do any of that.
*/
if(ds.q.block_type != inode_dir) {
log_err("Block %"PRIu64" marked as root inode in"
" superblock not a directory\n", rootblock);
if(query(sbp, "Create new root inode? (y/n) ")) {
if(build_rooti(sbp)) {
stack;
return -1;
}
} else {
log_err("Cannot continue without valid root inode\n");
return -1;
}
}
rootblock = sbp->sb.sb_root_di.no_addr;
pass2_fxns.private = (void *) &ds;
if(ds.q.bad_block) {
/* First check that the directory's metatree is valid */
load_inode(sbp, rootblock, &ip);
if(check_metatree(ip, &pass2_fxns)) {
stack;
free_inode(&ip);
return -1;
}
free_inode(&ip);
}
error = check_dir(sbp, rootblock, &pass2_fxns);
if(error < 0) {
stack;
return -1;
}
if (error > 0) {
block_set(sbp->bl, rootblock, meta_inval);
}
if(get_and_read_buf(sbp, rootblock, &bh, 0)){
log_err("Unable to retrieve block #%"PRIu64"\n",
rootblock);
block_set(sbp->bl, rootblock, meta_inval);
return -1;
}
if(copyin_inode(sbp, bh, &ip)) {
stack;
relse_buf(sbp, bh);
return -1;
}
if(check_inode_eattr(ip, &pass2_fxns)) {
stack;
return -1;
}
/* FIXME: Should not have to do this here - fs_dir_add reads
* the buffer too though, and commits the change to disk, so I
* have to reread the buffer after calling it if I'm going to
* make more changes */
relse_buf(sbp, bh);
if(!ds.dotdir) {
log_err("No '.' entry found\n");
sprintf(tmp_name, ".");
filename.len = strlen(tmp_name); /* no trailing NULL */
if(!(filename.name = malloc(sizeof(char) * filename.len))) {
log_err("Unable to allocate name string\n");
stack;
return -1;
}
if(!(memset(filename.name, 0, sizeof(char) * filename.len))) {
log_err("Unable to zero name string\n");
stack;
return -1;
}
memcpy(filename.name, tmp_name, filename.len);
log_warn("Adding '.' entry\n");
if(fs_dir_add(ip, &filename, &(ip->i_num),
ip->i_di.di_type)){
log_err("Failed to link \".\" entry to directory.\n");
return -1;
}
increment_link(ip->i_sbd, ip->i_num.no_addr);
ds.entry_count++;
free(filename.name);
update = 1;
}
free_inode(&ip);
if(get_and_read_buf(sbp, rootblock, &bh, 0)){
log_err("Unable to retrieve block #%"PRIu64"\n",
rootblock);
block_set(sbp->bl, rootblock, meta_inval);
return -1;
}
if(copyin_inode(sbp, bh, &ip)) {
stack;
relse_buf(sbp, bh);
return -1;
}
if(ip->i_di.di_entries != ds.entry_count) {
log_err("Entries is %d - should be %d for %"PRIu64"\n",
ip->i_di.di_entries, ds.entry_count, ip->i_di.di_num.no_addr);
if(query(sbp, "Fix entries for %"PRIu64"? (y/n) ",
ip->i_di.di_num.no_addr)) {
ip->i_di.di_entries = ds.entry_count;
log_warn("Entries updated\n");
update = 1;
} else {
log_err("Entries for %"PRIu64" left out of sync\n",
ip->i_di.di_num.no_addr);
}
}
if(update) {
gfs_dinode_out(&ip->i_di, BH_DATA(bh));
write_buf(sbp, bh, 0);
}
free_inode(&ip);
relse_buf(sbp, bh);
return 0;
}
/* What i need to do in this pass is check that the dentries aren't
* pointing to invalid blocks...and verify the contents of each
* directory. and start filling in the directory info structure*/
/**
* pass2 - check pathnames
*
* verify root inode
* directory name length
* entries in range
*/
int pass2(struct fsck_sb *sbp, struct options *opts)
{
uint64_t i;
struct block_query q;
struct dir_status ds = {0};
struct fsck_inode *ip;
osi_buf_t b, *bh = &b;
osi_filename_t filename;
char tmp_name[256];
if(check_root_dir(sbp)) {
stack;
return -1;
}
int error = 0;
log_info("Checking directory inodes.\n");
/* Grab each directory inode, and run checks on it */
for(i = 0; i < sbp->last_fs_block; i++) {
warm_fuzzy_stuff(i);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return 0;
/* Skip the root inode - it's checked above */
if(i == sbp->sb.sb_root_di.no_addr)
continue;
if(block_check(sbp->bl, i, &q)) {
log_err("Can't get block %"PRIu64 " from block list\n",
i);
return -1;
}
if(q.block_type != inode_dir)
continue;
log_debug("Checking directory inode at block %"PRIu64"\n", i);
memset(&ds, 0, sizeof(ds));
pass2_fxns.private = (void *) &ds;
if(ds.q.bad_block) {
/* First check that the directory's metatree
* is valid */
load_inode(sbp, i, &ip);
if(check_metatree(ip, &pass2_fxns)) {
stack;
free_inode(&ip);
return -1;
}
free_inode(&ip);
}
error = check_dir(sbp, i, &pass2_fxns);
if(error < 0) {
stack;
return -1;
}
if (error > 0) {
struct dir_info *di = NULL;
error = find_di(sbp, i, &di);
if(error < 0) {
stack;
return -1;
}
if(error == 0) {
/* FIXME: factor */
if(query(sbp, "Remove directory entry for bad"
" inode %"PRIu64" in %"PRIu64
"? (y/n)", i, di->treewalk_parent)) {
error = remove_dentry_from_dir(sbp,
di->treewalk_parent,
i);
if(error < 0) {
stack;
return -1;
}
if(error > 0) {
log_warn("Unable to find dentry for %"
PRIu64" in %"PRIu64"\n",
i, di->treewalk_parent);
}
log_warn("Directory entry removed\n");
} else {
log_err("Directory entry to invalid inode remains\n");
}
}
block_set(sbp->bl, i, meta_inval);
}
if(get_and_read_buf(sbp, i, &bh, 0)){
/* This shouldn't happen since we were able to
* read it before */
log_err("Unable to retrieve block #%"PRIu64
" for directory\n",
i);
return -1;
}
if(copyin_inode(sbp, bh, &ip)) {
stack;
relse_buf(sbp, bh);
return -1;
}
/* FIXME: Should not have to do this here - fs_dir_add reads
* the buffer too though, and commits the change to disk, so I
* have to reread the buffer after calling it if I'm going to
* make more changes */
relse_buf(sbp, bh);
if(!ds.dotdir) {
log_err("No '.' entry found\n");
sprintf(tmp_name, ".");
filename.len = strlen(tmp_name); /* no trailing NULL */
if(!(filename.name = malloc(sizeof(char) * filename.len))) {
log_err("Unable to allocate name string\n");
stack;
return -1;
}
if(!memset(filename.name, 0, sizeof(char) * filename.len)) {
log_err("Unable to zero name string\n");
stack;
return -1;
}
memcpy(filename.name, tmp_name, filename.len);
if(fs_dir_add(ip, &filename, &(ip->i_num),
ip->i_di.di_type)){
log_err("Failed to link \".\" entry to directory.\n");
return -1;
}
increment_link(ip->i_sbd, ip->i_num.no_addr);
ds.entry_count++;
free(filename.name);
}
free_inode(&ip);
if(get_and_read_buf(sbp, i, &bh, 0)){
log_err("Unable to retrieve block #%"PRIu64"\n",
i);
block_set(sbp->bl, i, meta_inval);
return -1;
}
if(copyin_inode(sbp, bh, &ip)) {
stack;
relse_buf(sbp, bh);
return -1;
}
if(ip->i_di.di_entries != ds.entry_count) {
log_err("Entries is %d - should be %d for %"PRIu64"\n",
ip->i_di.di_entries, ds.entry_count,
ip->i_di.di_num.no_addr);
ip->i_di.di_entries = ds.entry_count;
gfs_dinode_out(&ip->i_di, BH_DATA(bh));
write_buf(sbp, bh, 0);
}
free_inode(&ip);
relse_buf(sbp, bh);
}
return 0;
}

File Metadata

Mime Type
text/x-c
Expires
Mon, Feb 24, 5:42 PM (54 s ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1462407
Default Alt Text
pass2.c (21 KB)

Event Timeline