/*--------------------------------------------------------------*/
/*---------------------------- ux_dir.c ------------------------*/
/*--------------------------------------------------------------*/
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/locks.h>
#include "ux_fs.h"
/*
* Add "name" to the directory "dip"
*/
int
ux_diradd(struct inode *dip, const char *name, int inum)
{
struct ux_inode *uip = (struct ux_inode *)
&dip->i_private;
struct buffer_head *bh;
struct super_block *sb = dip->i_sb;
struct ux_dirent *dirent;
__u32 blk = 0;
int i, pos;
for (blk=0 ; blk < uip->i_blocks ; blk++) {
bh = sb_bread(sb, uip->i_addr[blk]);
dirent = (struct ux_dirent *)bh->b_data;
for (i=0 ; i < UX_DIRS_PER_BLOCK ; i++) {
if (dirent->d_ino != 0) {
dirent++;
continue;
} else {
dirent->d_ino = inum;
strcpy(dirent->d_name, name);
mark_buffer_dirty(bh);
mark_inode_dirty(dip);
brelse(bh);
return 0;
}
}
brelse(bh);
}
/*
* We didn't find an empty slot so need to allocate
* a new block if there's space in the inode.
*/
if (uip->i_blocks < UX_DIRECT_BLOCKS) {
pos = uip->i_blocks;
blk = ux_block_alloc(sb);
uip->i_blocks++;
uip->i_size += UX_BSIZE;
dip->i_size += UX_BSIZE;
dip->i_blocks++;
uip->i_addr[pos] = blk;
bh = sb_bread(sb, blk);
memset(bh->b_data, 0, UX_BSIZE);
mark_inode_dirty(dip);
dirent = (struct ux_dirent *)bh->b_data;
dirent->d_ino = inum;
strcpy(dirent->d_name, name);
mark_buffer_dirty(bh);
brelse(bh);
}
return 0;
}
/*
* Remove "name" from the specified directory.
*/
int
ux_dirdel(struct inode *dip, char *name)
{
struct ux_inode *uip = (struct ux_inode *)
&dip->i_private;
struct buffer_head *bh;
struct super_block *sb = dip->i_sb;
struct ux_dirent *dirent;
__u32 blk = 0;
int i;
while (blk < uip->i_blocks) {
bh = sb_bread(sb, uip->i_addr[blk]);
blk++;
dirent = (struct ux_dirent *)bh->b_data;
for (i=0 ; i < UX_DIRS_PER_BLOCK ; i++) {
if (strcmp(dirent->d_name, name) != 0) {
dirent++;
continue;
} else {
dirent->d_ino = 0;
dirent->d_name[0] = '\0';
mark_buffer_dirty(bh);
dip->i_nlink--;
mark_inode_dirty(dip);
break;
}
}
brelse(bh);
}
return 0;
}
int
ux_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
unsigned long pos;
struct inode *inode = filp->f_dentry->d_inode;
struct ux_inode *uip = (struct ux_inode *)
&inode->i_private;
struct ux_dirent *udir;
struct buffer_head *bh;
__u32 blk;
start_again:
pos = filp->f_pos;
if (pos >= inode->i_size) {
return 0;
}
blk = (pos + 1) / UX_BSIZE;
blk = uip->i_addr[blk];
bh = sb_bread(inode->i_sb, blk);
udir = (struct ux_dirent *)(bh->b_data + pos % UX_BSIZE);
/*
* Skip over 'null' directory entries.
*/
if (udir->d_ino == 0) {
filp->f_pos += sizeof(struct ux_dirent);
brelse(bh);
goto start_again;
} else {
filldir(dirent, udir->d_name,
sizeof(udir->d_name), pos,
udir->d_ino, DT_UNKNOWN);
}
filp->f_pos += sizeof(struct ux_dirent);
brelse(bh);
return 0;
}
struct file_operations ux_dir_operations = {
read: generic_read_dir,
readdir: ux_readdir,
fsync: file_fsync,
};
/*
* When we reach this point, ux_lookup() has already been called
* to create a negative entry in the dcache. Thus, we need to
* allocate a new inode on disk and associate it with the dentry.
*/
int
ux_create(struct inode *dip, struct dentry *dentry, int mode)
{
struct ux_inode *nip;
struct super_block *sb = dip->i_sb;
struct inode *inode;
ino_t inum = 0;
/*
* See if the entry exists. If not, create a new
* disk inode, and incore inode. The add the new
* entry to the directory.
*/
inum = ux_find_entry(dip, (char *)dentry->d_name.name);
if (inum) {
return -EEXIST;
}
inode = new_inode(sb);
if (!inode) {
return -ENOSPC;
}
inum = ux_ialloc(sb);
if (!inum) {
iput(inode);
return -ENOSPC;
}
ux_diradd(dip, (char *)dentry->d_name.name, inum);
/*
* Increment the parent link count and intialize the inode.
*/
dip->i_nlink++;
inode->i_uid = current->fsuid;
inode->i_gid = (dip->i_mode & S_ISGID) ?
dip->i_gid : current->fsgid;
inode->i_mtime = inode->i_atime =
inode->i_ctime = CURRENT_TIME;
inode->i_blocks = inode->i_blksize = 0;
inode->i_op = &ux_file_inops;
inode->i_fop = &ux_file_operations;
inode->i_mapping->a_ops = &ux_aops;
inode->i_mode = mode;
inode->i_nlink = 1;
inode->i_ino = inum;
insert_inode_hash(inode);
nip = (struct ux_inode *)&inode->i_private;
nip->i_mode = mode;
nip->i_nlink = 1;
nip->i_atime = nip->i_ctime = nip->i_mtime = CURRENT_TIME;
nip->i_uid = inode->i_gid;
nip->i_gid = inode->i_gid;
nip->i_size = 0;
nip->i_blocks = 0;
memset(nip->i_addr, 0, UX_DIRECT_BLOCKS);
d_instantiate(dentry, inode);
mark_inode_dirty(dip);
mark_inode_dirty(inode);
return 0;
}
/*
* Make a new directory. We already have a negative dentry
* so must create the directory and instantiate it.
*/
int
ux_mkdir(struct inode *dip, struct dentry *dentry, int mode)
{
struct ux_inode *nip;
struct buffer_head *bh;
struct super_block *sb = dip->i_sb;
struct ux_dirent *dirent;
struct inode *inode;
ino_t inum = 0;
int blk;
/*
* Make sure there isn't already an entry. If not,
* allocate one, a new inode and new incore inode.
*/
inum = ux_find_entry(dip, (char *)dentry->d_name.name);
if (inum) {
return -EEXIST;
}
inode = new_inode(sb);
if (!inode) {
return -ENOSPC;
}
inum = ux_ialloc(sb);
if (!inum) {
iput(inode);