diff options
Diffstat (limited to 'fs/ufs/namei.c')
-rw-r--r-- | fs/ufs/namei.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c new file mode 100644 index 000000000000..2958cde7d3d6 --- /dev/null +++ b/fs/ufs/namei.c @@ -0,0 +1,375 @@ +/* + * linux/fs/ufs/namei.c + * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + * + * from + * + * linux/fs/ext2/namei.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/namei.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 + */ + +#include <linux/time.h> +#include <linux/fs.h> +#include <linux/ufs_fs.h> +#include <linux/smp_lock.h> +#include <linux/buffer_head.h> +#include "swab.h" /* will go away - see comment in mknod() */ +#include "util.h" + +/* +#undef UFS_NAMEI_DEBUG +*/ +#define UFS_NAMEI_DEBUG + +#ifdef UFS_NAMEI_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +static inline void ufs_inc_count(struct inode *inode) +{ + inode->i_nlink++; + mark_inode_dirty(inode); +} + +static inline void ufs_dec_count(struct inode *inode) +{ + inode->i_nlink--; + mark_inode_dirty(inode); +} + +static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode) +{ + int err = ufs_add_link(dentry, inode); + if (!err) { + d_instantiate(dentry, inode); + return 0; + } + ufs_dec_count(inode); + iput(inode); + return err; +} + +static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode * inode = NULL; + ino_t ino; + + if (dentry->d_name.len > UFS_MAXNAMLEN) + return ERR_PTR(-ENAMETOOLONG); + + lock_kernel(); + ino = ufs_inode_by_name(dir, dentry); + if (ino) { + inode = iget(dir->i_sb, ino); + if (!inode) { + unlock_kernel(); + return ERR_PTR(-EACCES); + } + } + unlock_kernel(); + d_add(dentry, inode); + return NULL; +} + +/* + * By the time this is called, we already have created + * the directory cache entry for the new file, but it + * is so far negative - it has no inode. + * + * If the create succeeds, we fill in the inode information + * with d_instantiate(). + */ +static int ufs_create (struct inode * dir, struct dentry * dentry, int mode, + struct nameidata *nd) +{ + struct inode * inode = ufs_new_inode(dir, mode); + int err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + inode->i_op = &ufs_file_inode_operations; + inode->i_fop = &ufs_file_operations; + inode->i_mapping->a_ops = &ufs_aops; + mark_inode_dirty(inode); + lock_kernel(); + err = ufs_add_nondir(dentry, inode); + unlock_kernel(); + } + return err; +} + +static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev) +{ + struct inode *inode; + int err; + + if (!old_valid_dev(rdev)) + return -EINVAL; + inode = ufs_new_inode(dir, mode); + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + init_special_inode(inode, mode, rdev); + /* NOTE: that'll go when we get wide dev_t */ + ufs_set_inode_dev(inode->i_sb, UFS_I(inode), rdev); + mark_inode_dirty(inode); + lock_kernel(); + err = ufs_add_nondir(dentry, inode); + unlock_kernel(); + } + return err; +} + +static int ufs_symlink (struct inode * dir, struct dentry * dentry, + const char * symname) +{ + struct super_block * sb = dir->i_sb; + int err = -ENAMETOOLONG; + unsigned l = strlen(symname)+1; + struct inode * inode; + + if (l > sb->s_blocksize) + goto out; + + lock_kernel(); + inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out; + + if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) { + /* slow symlink */ + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ufs_aops; + err = page_symlink(inode, symname, l); + if (err) + goto out_fail; + } else { + /* fast symlink */ + inode->i_op = &ufs_fast_symlink_inode_operations; + memcpy((char*)&UFS_I(inode)->i_u1.i_data,symname,l); + inode->i_size = l-1; + } + mark_inode_dirty(inode); + + err = ufs_add_nondir(dentry, inode); +out: + unlock_kernel(); + return err; + +out_fail: + ufs_dec_count(inode); + iput(inode); + goto out; +} + +static int ufs_link (struct dentry * old_dentry, struct inode * dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + int error; + + lock_kernel(); + if (inode->i_nlink >= UFS_LINK_MAX) { + unlock_kernel(); + return -EMLINK; + } + + inode->i_ctime = CURRENT_TIME_SEC; + ufs_inc_count(inode); + atomic_inc(&inode->i_count); + + error = ufs_add_nondir(dentry, inode); + unlock_kernel(); + return error; +} + +static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) +{ + struct inode * inode; + int err = -EMLINK; + + if (dir->i_nlink >= UFS_LINK_MAX) + goto out; + + lock_kernel(); + ufs_inc_count(dir); + + inode = ufs_new_inode(dir, S_IFDIR|mode); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out_dir; + + inode->i_op = &ufs_dir_inode_operations; + inode->i_fop = &ufs_dir_operations; + + ufs_inc_count(inode); + + err = ufs_make_empty(inode, dir); + if (err) + goto out_fail; + + err = ufs_add_link(dentry, inode); + if (err) + goto out_fail; + unlock_kernel(); + + d_instantiate(dentry, inode); +out: + return err; + +out_fail: + ufs_dec_count(inode); + ufs_dec_count(inode); + iput (inode); +out_dir: + ufs_dec_count(dir); + unlock_kernel(); + goto out; +} + +static int ufs_unlink(struct inode * dir, struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + struct buffer_head * bh; + struct ufs_dir_entry * de; + int err = -ENOENT; + + lock_kernel(); + de = ufs_find_entry (dentry, &bh); + if (!de) + goto out; + + err = ufs_delete_entry (dir, de, bh); + if (err) + goto out; + + inode->i_ctime = dir->i_ctime; + ufs_dec_count(inode); + err = 0; +out: + unlock_kernel(); + return err; +} + +static int ufs_rmdir (struct inode * dir, struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + int err= -ENOTEMPTY; + + lock_kernel(); + if (ufs_empty_dir (inode)) { + err = ufs_unlink(dir, dentry); + if (!err) { + inode->i_size = 0; + ufs_dec_count(inode); + ufs_dec_count(dir); + } + } + unlock_kernel(); + return err; +} + +static int ufs_rename (struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry ) +{ + struct inode *old_inode = old_dentry->d_inode; + struct inode *new_inode = new_dentry->d_inode; + struct buffer_head *dir_bh = NULL; + struct ufs_dir_entry *dir_de = NULL; + struct buffer_head *old_bh; + struct ufs_dir_entry *old_de; + int err = -ENOENT; + + lock_kernel(); + old_de = ufs_find_entry (old_dentry, &old_bh); + if (!old_de) + goto out; + + if (S_ISDIR(old_inode->i_mode)) { + err = -EIO; + dir_de = ufs_dotdot(old_inode, &dir_bh); + if (!dir_de) + goto out_old; + } + + if (new_inode) { + struct buffer_head *new_bh; + struct ufs_dir_entry *new_de; + + err = -ENOTEMPTY; + if (dir_de && !ufs_empty_dir (new_inode)) + goto out_dir; + err = -ENOENT; + new_de = ufs_find_entry (new_dentry, &new_bh); + if (!new_de) + goto out_dir; + ufs_inc_count(old_inode); + ufs_set_link(new_dir, new_de, new_bh, old_inode); + new_inode->i_ctime = CURRENT_TIME_SEC; + if (dir_de) + new_inode->i_nlink--; + ufs_dec_count(new_inode); + } else { + if (dir_de) { + err = -EMLINK; + if (new_dir->i_nlink >= UFS_LINK_MAX) + goto out_dir; + } + ufs_inc_count(old_inode); + err = ufs_add_link(new_dentry, old_inode); + if (err) { + ufs_dec_count(old_inode); + goto out_dir; + } + if (dir_de) + ufs_inc_count(new_dir); + } + + ufs_delete_entry (old_dir, old_de, old_bh); + + ufs_dec_count(old_inode); + + if (dir_de) { + ufs_set_link(old_inode, dir_de, dir_bh, new_dir); + ufs_dec_count(old_dir); + } + unlock_kernel(); + return 0; + +out_dir: + if (dir_de) + brelse(dir_bh); +out_old: + brelse (old_bh); +out: + unlock_kernel(); + return err; +} + +struct inode_operations ufs_dir_inode_operations = { + .create = ufs_create, + .lookup = ufs_lookup, + .link = ufs_link, + .unlink = ufs_unlink, + .symlink = ufs_symlink, + .mkdir = ufs_mkdir, + .rmdir = ufs_rmdir, + .mknod = ufs_mknod, + .rename = ufs_rename, +}; |