/* * linux/fs/sysv/ialloc.c * * minix/bitmap.c * Copyright (C) 1991, 1992 Linus Torvalds * * ext/freelists.c * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) * * xenix/alloc.c * Copyright (C) 1992 Doug Evans * * coh/alloc.c * Copyright (C) 1993 Pascal Haible, Bruno Haible * * sysv/ialloc.c * Copyright (C) 1993 Bruno Haible * * This file contains code for allocating/freeing inodes. */ #include #include #include #include #include #include #include #include /* We don't trust the value of sb->sv_sbd2->s_tinode = *sb->sv_sb_total_free_inodes but we nevertheless keep it up to date. */ /* An inode on disk is considered free if both i_mode == 0 and i_nlink == 0. */ /* return &sb->sv_sb_fic_inodes[i] = &sbd->s_inode[i]; */ static inline sysv_ino_t * sv_sb_fic_inode (struct super_block * sb, unsigned int i) { if (sb->sv_bh1 == sb->sv_bh2) return &sb->sv_sb_fic_inodes[i]; else { /* 512 byte Xenix FS */ unsigned int offset = offsetof(struct xenix_super_block, s_inode[i]); if (offset < 512) return (sysv_ino_t*)(sb->sv_sbd1 + offset); else return (sysv_ino_t*)(sb->sv_sbd2 + offset); } } void sysv_free_inode(struct inode * inode) { struct super_block * sb; unsigned int ino; struct buffer_head * bh; struct sysv_inode * raw_inode; if (!inode) return; if (!inode->i_dev) { printk("sysv_free_inode: inode has no device\n"); return; } if (inode->i_count != 1) { printk("sysv_free_inode: inode has count=%d\n", inode->i_count); return; } if (inode->i_nlink) { printk("sysv_free_inode: inode has nlink=%d\n", inode->i_nlink); return; } if (!(sb = inode->i_sb)) { printk("sysv_free_inode: inode on nonexistent device\n"); return; } ino = inode->i_ino; if (ino <= SYSV_ROOT_INO || ino > sb->sv_ninodes) { printk("sysv_free_inode: inode 0,1,2 or nonexistent inode\n"); return; } if (!(bh = sv_bread(sb, inode->i_dev, sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits)))) { printk("sysv_free_inode: unable to read inode block on device " "%s\n", kdevname(inode->i_dev)); clear_inode(inode); return; } raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1); lock_super(sb); if (*sb->sv_sb_fic_count < sb->sv_fic_size) *sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)++) = ino; (*sb->sv_sb_total_free_inodes)++; mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); sb->s_dirt = 1; /* and needs time stamp */ memset(raw_inode, 0, sizeof(struct sysv_inode)); mark_buffer_dirty(bh, 1); unlock_super(sb); brelse(bh); clear_inode(inode); } struct inode * sysv_new_inode(const struct inode * dir) { struct inode * inode; struct super_block * sb; struct buffer_head * bh; struct sysv_inode * raw_inode; int i,j,ino,block; if (!dir || !(inode = get_empty_inode())) return NULL; sb = dir->i_sb; inode->i_sb = sb; inode->i_flags = inode->i_sb->s_flags; lock_super(sb); /* protect against task switches */ if ((*sb->sv_sb_fic_count == 0) || (*sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)-1) == 0) /* Applies only to SystemV2 FS */ ) { /* Rebuild cache of free inodes: */ /* i : index into cache slot being filled */ /* ino : inode we are trying */ /* block : firstinodezone + (ino-1)/inodes_per_block */ /* j : (ino-1)%inodes_per_block */ /* bh : buffer for block */ /* raw_inode : pointer to inode ino in the block */ for (i = 0, ino = SYSV_ROOT_INO+1, block = sb->sv_firstinodezone, j = SYSV_ROOT_INO ; i < sb->sv_fic_size && block < sb->sv_firstdatazone ; block++, j = 0) { if (!(bh = sv_bread(sb, sb->s_dev, block))) { printk("sysv_new_inode: unable to read inode table\n"); break; /* go with what we've got */ /* FIXME: Perhaps try the next block? */ } raw_inode = (struct sysv_inode *) bh->b_data + j; for (; j < sb->sv_inodes_per_block && i < sb->sv_fic_size; ino++, j++, raw_inode++) { if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0) *sv_sb_fic_inode(sb,i++) = ino; } brelse(bh); } if (i == 0) { iput(inode); unlock_super(sb); return NULL; /* no inodes available */ } *sb->sv_sb_fic_count = i; } /* Now *sb->sv_sb_fic_count > 0. */ ino = *sv_sb_fic_inode(sb,--(*sb->sv_sb_fic_count)); mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); sb->s_dirt = 1; /* and needs time stamp */ inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; inode->i_ino = ino; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; inode->i_blocks = inode->i_blksize = 0; insert_inode_hash(inode); mark_inode_dirty(inode); /* Change directory entry: */ inode->i_mode = 0; /* for sysv_write_inode() */ inode->i_size = 0; /* ditto */ sysv_write_inode(inode); /* ensure inode not allocated again */ /* FIXME: caller may call this too. */ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ (*sb->sv_sb_total_free_inodes)--; mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */ sb->s_dirt = 1; /* and needs time stamp again */ unlock_super(sb); return inode; } unsigned long sysv_count_free_inodes(struct super_block * sb) { #if 1 /* test */ struct buffer_head * bh; struct sysv_inode * raw_inode; int j,block,count; /* this causes a lot of disk traffic ... */ count = 0; lock_super(sb); /* i : index into cache slot being filled */ /* ino : inode we are trying */ /* block : firstinodezone + (ino-1)/inodes_per_block */ /* j : (ino-1)%inodes_per_block */ /* bh : buffer for block */ /* raw_inode : pointer to inode ino in the block */ for (block = sb->sv_firstinodezone, j = SYSV_ROOT_INO ; block < sb->sv_firstdatazone ; block++, j = 0) { if (!(bh = sv_bread(sb, sb->s_dev, block))) { printk("sysv_count_free_inodes: unable to read inode table\n"); break; /* go with what we've got */ /* FIXME: Perhaps try the next block? */ } raw_inode = (struct sysv_inode *) bh->b_data + j; for (; j < sb->sv_inodes_per_block ; j++, raw_inode++) if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0) count++; brelse(bh); } if (count != *sb->sv_sb_total_free_inodes) { printk("sysv_count_free_inodes: free inode count was %d, correcting to %d\n",(short)(*sb->sv_sb_total_free_inodes),count); if (!(sb->s_flags & MS_RDONLY)) { *sb->sv_sb_total_free_inodes = count; mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */ sb->s_dirt = 1; /* and needs time stamp */ } } unlock_super(sb); return count; #else return *sb->sv_sb_total_free_inodes; #endif }