patch-2.4.4 linux/fs/affs/namei.c
Next file: linux/fs/affs/super.c
Previous file: linux/fs/affs/inode.c
Back to the patch index
Back to the overall index
- Lines: 807
- Date:
Wed Apr 25 14:57:29 2001
- Orig file:
v2.4.3/linux/fs/affs/namei.c
- Orig date:
Tue Sep 5 14:07:29 2000
diff -u --recursive --new-file v2.4.3/linux/fs/affs/namei.c linux/fs/affs/namei.c
@@ -8,7 +8,6 @@
* (C) 1991 Linus Torvalds - minix filesystem
*/
-#define DEBUG 0
#include <linux/sched.h>
#include <linux/affs_fs.h>
#include <linux/kernel.h>
@@ -21,283 +20,268 @@
#include <linux/errno.h>
+typedef int (*toupper_t)(int);
+
extern struct inode_operations affs_symlink_inode_operations;
+static int affs_toupper(int ch);
+static int affs_hash_dentry(struct dentry *, struct qstr *);
+static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int affs_intl_toupper(int ch);
+static int affs_intl_hash_dentry(struct dentry *, struct qstr *);
+static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+
+struct dentry_operations affs_dentry_operations = {
+ d_hash: affs_hash_dentry,
+ d_compare: affs_compare_dentry,
+};
+
+struct dentry_operations affs_intl_dentry_operations = {
+ d_hash: affs_intl_hash_dentry,
+ d_compare: affs_intl_compare_dentry,
+};
+
+
/* Simple toupper() for DOS\1 */
-static unsigned int
-affs_toupper(unsigned int ch)
+static int
+affs_toupper(int ch)
{
return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
}
/* International toupper() for DOS\3 ("international") */
-static unsigned int
-affs_intl_toupper(unsigned int ch)
+static int
+affs_intl_toupper(int ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
&& ch <= 0xFE && ch != 0xF7) ?
ch - ('a' - 'A') : ch;
}
-static int affs_hash_dentry(struct dentry *, struct qstr *);
-static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-struct dentry_operations affs_dentry_operations = {
- d_hash: affs_hash_dentry,
- d_compare: affs_compare_dentry,
-};
+static inline toupper_t
+affs_get_toupper(struct super_block *sb)
+{
+ return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
+}
/*
* Note: the dentry argument is the parent dentry.
*/
-static int
-affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+static inline int
+__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
{
- unsigned int (*toupper)(unsigned int) = affs_toupper;
- unsigned long hash;
- int i;
+ const char *name = qstr->name;
+ unsigned long hash;
+ int i;
- if ((i = affs_check_name(qstr->name,qstr->len)))
+ i = affs_check_name(qstr->name,qstr->len);
+ if (i)
return i;
- /* Check whether to use the international 'toupper' routine */
- if (AFFS_I2FSTYPE(dentry->d_inode))
- toupper = affs_intl_toupper;
hash = init_name_hash();
- for (i = 0; i < qstr->len && i < 30; i++)
- hash = partial_name_hash(toupper(qstr->name[i]), hash);
+ i = MIN(qstr->len, 30);
+ for (; i > 0; name++, i--)
+ hash = partial_name_hash(toupper(*name), hash);
qstr->hash = end_name_hash(hash);
return 0;
}
static int
-affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ return __affs_hash_dentry(dentry, qstr, affs_toupper);
+}
+static int
+affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
+}
+
+static inline int
+__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
{
- unsigned int (*toupper)(unsigned int) = affs_toupper;
- int alen = a->len;
- int blen = b->len;
- int i;
+ const u8 *aname = a->name;
+ const u8 *bname = b->name;
+ int len;
/* 'a' is the qstr of an already existing dentry, so the name
* must be valid. 'b' must be validated first.
*/
-
+
if (affs_check_name(b->name,b->len))
return 1;
/* If the names are longer than the allowed 30 chars,
* the excess is ignored, so their length may differ.
*/
- if (alen > 30)
- alen = 30;
- if (blen > 30)
- blen = 30;
- if (alen != blen)
+ len = a->len;
+ if (len >= 30) {
+ if (b->len < 30)
+ return 1;
+ len = 30;
+ } else if (len != b->len)
return 1;
- /* Check whether to use the international 'toupper' routine */
- if (AFFS_I2FSTYPE(dentry->d_inode))
- toupper = affs_intl_toupper;
-
- for (i = 0; i < alen; i++)
- if (toupper(a->name[i]) != toupper(b->name[i]))
+ for (; len > 0; len--)
+ if (toupper(*aname++) != toupper(*bname++))
return 1;
-
+
return 0;
}
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ return __affs_compare_dentry(dentry, a, b, affs_toupper);
+}
+static int
+affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
+}
+
/*
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
*/
-static int
-affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
+static inline int
+affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
{
- unsigned int (*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
- int i;
-
- if (!compare)
- return 0;
+ const u8 *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
- if (len > 30)
+ if (len >= 30) {
+ if (*name2 < 30)
+ return 0;
len = 30;
- if (dlen > 30)
- dlen = 30;
-
- /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
- if (!len && dlen == 1 && compare[0] == '.')
- return 1;
- if (dlen != len)
+ } else if (len != *name2)
return 0;
- for (i = 0; i < len; i++)
- if (toupper(name[i]) != toupper(compare[i]))
+
+ for (name2++; len > 0; len--)
+ if (toupper(*name++) != toupper(*name2++))
return 0;
return 1;
}
int
-affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
+affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
{
- unsigned int i, x;
-
- if (len > 30)
- len = 30;
+ toupper_t toupper = affs_get_toupper(sb);
+ int hash;
- x = len;
- for (i = 0; i < len; i++)
- if (intl)
- x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
- else
- x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+ hash = len = MIN(len, 30);
+ for (; len > 0; len--)
+ hash = (hash * 13 + toupper(*name++)) & 0x7ff;
- return x % hashsize;
+ return hash % AFFS_SB->s_hashsize;
}
static struct buffer_head *
-affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
+affs_find_entry(struct inode *dir, struct dentry *dentry)
{
- struct buffer_head *bh;
- int intl = AFFS_I2FSTYPE(dir);
- s32 key;
- const char *name = dentry->d_name.name;
- int namelen = dentry->d_name.len;
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ toupper_t toupper = affs_get_toupper(sb);
+ u32 key;
- pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
+ pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
- bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+ bh = affs_bread(sb, dir->i_ino);
if (!bh)
- return NULL;
+ return ERR_PTR(-EIO);
- if (namelen == 1 && name[0] == '.') {
- *ino = dir->i_ino;
- return bh;
- }
- if (namelen == 2 && name[0] == '.' && name[1] == '.') {
- *ino = affs_parent_ino(dir);
- return bh;
- }
-
- key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
+ key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
for (;;) {
- unsigned char *cname;
- int cnamelen;
-
affs_brelse(bh);
- bh = NULL;
if (key == 0)
- break;
- bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
+ return NULL;
+ bh = affs_bread(sb, key);
if (!bh)
- break;
- cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
- if (affs_match(name,namelen,cname,cnamelen,intl))
- break;
- key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
+ return ERR_PTR(-EIO);
+ if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
+ return bh;
+ key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
}
- *ino = key;
- return bh;
}
struct dentry *
affs_lookup(struct inode *dir, struct dentry *dentry)
{
- unsigned long ino;
- struct buffer_head *bh;
- struct inode *inode;
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct inode *inode = NULL;
pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
- inode = NULL;
- bh = affs_find_entry(dir,dentry,&ino);
+ down(&AFFS_DIR->i_hash_lock);
+ bh = affs_find_entry(dir, dentry);
+ up(&AFFS_DIR->i_hash_lock);
+ if (IS_ERR(bh))
+ return ERR_PTR(PTR_ERR(bh));
if (bh) {
- if (FILE_END(bh->b_data,dir)->original)
- ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
+ u32 ino = bh->b_blocknr;
+
+ /* store the real header ino in d_fsdata for faster lookups */
+ dentry->d_fsdata = (void *)(long)ino;
+ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+ case ST_LINKDIR:
+ case ST_LINKFILE:
+ ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
+ }
affs_brelse(bh);
- inode = iget(dir->i_sb,ino);
+ inode = iget(sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
}
- dentry->d_op = &affs_dentry_operations;
- d_add(dentry,inode);
+ dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
+ d_add(dentry, inode);
return NULL;
}
int
affs_unlink(struct inode *dir, struct dentry *dentry)
{
- int retval;
- struct buffer_head *bh;
- unsigned long ino;
- struct inode *inode;
-
- pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
- (int)dentry->d_name.len,dentry->d_name.name);
+ pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
+ (int)dentry->d_name.len, dentry->d_name.name);
- retval = -ENOENT;
- if (!(bh = affs_find_entry(dir,dentry,&ino)))
- goto unlink_done;
-
- inode = dentry->d_inode;
-
- if ((retval = affs_remove_header(bh,inode)) < 0)
- goto unlink_done;
-
- inode->i_nlink = retval;
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_version = ++event;
- mark_inode_dirty(inode);
- mark_inode_dirty(dir);
- retval = 0;
+ if (!dentry->d_inode)
+ return -ENOENT;
-unlink_done:
- affs_brelse(bh);
- return retval;
+ return affs_remove_header(dentry);
}
int
affs_create(struct inode *dir, struct dentry *dentry, int mode)
{
+ struct super_block *sb = dir->i_sb;
struct inode *inode;
int error;
-
+
pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
dentry->d_name.name,mode);
- error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- goto out;
+ return -ENOSPC;
- pr_debug("AFFS: ino=%lu\n",inode->i_ino);
- if (dir->i_sb->u.affs_sb.s_flags & SF_OFS) {
- inode->i_op = &affs_file_inode_operations;
- inode->i_fop = &affs_file_operations_ofs;
- } else {
- inode->i_op = &affs_file_inode_operations;
- inode->i_fop = &affs_file_operations;
- inode->i_mapping->a_ops = &affs_aops;
- inode->u.affs_i.mmu_private = inode->i_size;
- }
- error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
- if (error)
- goto out_iput;
inode->i_mode = mode;
- inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
- d_instantiate(dentry,inode);
+ mode_to_prot(inode);
mark_inode_dirty(inode);
- dir->i_version = ++event;
- mark_inode_dirty(dir);
-out:
- return error;
-out_iput:
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- goto out;
+ inode->i_op = &affs_file_inode_operations;
+ inode->i_fop = &affs_file_operations;
+ inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+ error = affs_add_entry(dir, inode, dentry, ST_FILE);
+ if (error) {
+ inode->i_nlink = 0;
+ iput(inode);
+ return error;
+ }
+ return 0;
}
int
@@ -305,121 +289,78 @@
{
struct inode *inode;
int error;
-
+
pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,mode);
- error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- goto out;
+ return -ENOSPC;
+
+ inode->i_mode = S_IFDIR | mode;
+ mode_to_prot(inode);
inode->i_op = &affs_dir_inode_operations;
inode->i_fop = &affs_dir_operations;
- error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
- if (error)
- goto out_iput;
- inode->i_mode = S_IFDIR | S_ISVTX | mode;
- inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
- d_instantiate(dentry,inode);
- mark_inode_dirty(inode);
- dir->i_version = ++event;
- mark_inode_dirty(dir);
-out:
- return error;
-out_iput:
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- goto out;
-}
-
-static int
-empty_dir(struct buffer_head *bh, int hashsize)
-{
- while (--hashsize >= 0) {
- if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
- return 0;
+ error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
+ if (error) {
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
}
- return 1;
+ return 0;
}
int
affs_rmdir(struct inode *dir, struct dentry *dentry)
{
- struct inode *inode = dentry->d_inode;
- int retval;
- unsigned long ino;
- struct buffer_head *bh;
-
- pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
- (int)dentry->d_name.len,dentry->d_name.name);
-
- retval = -ENOENT;
- if (!(bh = affs_find_entry(dir,dentry,&ino)))
- goto rmdir_done;
+ pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
+ (int)dentry->d_name.len, dentry->d_name.name);
- /*
- * Make sure the directory is empty and the dentry isn't busy.
- */
- retval = -ENOTEMPTY;
- if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
- goto rmdir_done;
- retval = -EBUSY;
- if (!d_unhashed(dentry))
- goto rmdir_done;
-
- if ((retval = affs_remove_header(bh,inode)) < 0)
- goto rmdir_done;
-
- inode->i_nlink = retval;
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- retval = 0;
- dir->i_version = ++event;
- mark_inode_dirty(dir);
- mark_inode_dirty(inode);
+ if (!dentry->d_inode)
+ return -ENOENT;
-rmdir_done:
- affs_brelse(bh);
- return retval;
+ return affs_remove_header(dentry);
}
int
affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
+ struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
struct inode *inode;
char *p;
- unsigned long tmp;
int i, maxlen, error;
char c, lc;
pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,symname);
-
- maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+
+ maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- goto out;
+ return -ENOSPC;
inode->i_op = &affs_symlink_inode_operations;
inode->i_data.a_ops = &affs_symlink_aops;
inode->i_mode = S_IFLNK | 0777;
- inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ mode_to_prot(inode);
+
error = -EIO;
- bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ bh = affs_bread(sb, inode->i_ino);
if (!bh)
- goto out_iput;
+ goto err;
i = 0;
- p = ((struct slink_front *)bh->b_data)->symname;
+ p = (char *)AFFS_HEAD(bh)->table;
lc = '/';
if (*symname == '/') {
while (*symname == '/')
symname++;
- while (inode->i_sb->u.affs_sb.s_volume[i]) /* Cannot overflow */
- *p++ = inode->i_sb->u.affs_sb.s_volume[i++];
+ while (AFFS_SB->s_volume[i]) /* Cannot overflow */
+ *p++ = AFFS_SB->s_volume[i++];
}
while (i < maxlen && (c = *symname++)) {
if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
@@ -444,159 +385,90 @@
affs_brelse(bh);
mark_inode_dirty(inode);
- /* N.B. This test shouldn't be necessary ... dentry must be negative */
- error = -EEXIST;
- bh = affs_find_entry(dir,dentry,&tmp);
- if (bh)
- goto out_release;
- /* N.B. Shouldn't we add the entry before dirtying the buffer? */
- error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+ error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
if (error)
- goto out_release;
- d_instantiate(dentry,inode);
- dir->i_version = ++event;
- mark_inode_dirty(dir);
+ goto err;
-out:
- return error;
+ return 0;
-out_release:
- affs_brelse(bh);
-out_iput:
+err:
inode->i_nlink = 0;
mark_inode_dirty(inode);
iput(inode);
- goto out;
+ return error;
}
int
affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
- struct inode *oldinode = old_dentry->d_inode;
- struct inode *inode;
- struct buffer_head *bh;
- unsigned long i;
- int error;
-
- pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
- (int)dentry->d_name.len,dentry->d_name.name);
+ struct inode *inode = old_dentry->d_inode;
+ int error;
- /* N.B. Do we need this test? The dentry must be negative ... */
- bh = affs_find_entry(dir,dentry,&i);
- if (bh) {
- affs_brelse(bh);
- return -EEXIST;
- }
- if (oldinode->u.affs_i.i_hlink) { /* Cannot happen */
- affs_warning(dir->i_sb,"link","Impossible link to link");
- return -EINVAL;
- }
- error = -ENOSPC;
- if (!(inode = affs_new_inode(dir)))
- goto out;
+ pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
+ (int)dentry->d_name.len,dentry->d_name.name);
- inode->i_op = oldinode->i_op;
- inode->i_fop = oldinode->i_fop;
- inode->u.affs_i.i_protect = mode_to_prot(oldinode->i_mode);
- inode->u.affs_i.i_original = oldinode->i_ino;
- inode->u.affs_i.i_hlink = 1;
- inode->i_mtime = oldinode->i_mtime;
-
- if (S_ISDIR(oldinode->i_mode))
- error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
- else
- error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
- if (error)
+ error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
+ if (error) {
inode->i_nlink = 0;
- else {
- dir->i_version = ++event;
- mark_inode_dirty(dir);
- mark_inode_dirty(oldinode);
- atomic_inc(&oldinode->i_count);
- d_instantiate(dentry,oldinode);
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
}
- mark_inode_dirty(inode);
- iput(inode);
-
-out:
- return error;
+ return 0;
}
int
affs_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 *old_bh;
- struct buffer_head *new_bh;
- unsigned long old_ino;
- unsigned long new_ino;
- int retval;
-
- pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p))\n",
- old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode,
- new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
-
- if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
- goto out;
+ struct super_block *sb = old_dir->i_sb;
+ struct inode *dir;
+ struct buffer_head *bh = NULL;
+ int retval;
+
+ pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+ (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
+ (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
- new_bh = NULL;
- retval = -ENOENT;
- old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
- if (!old_bh)
- goto end_rename;
-
- new_bh = affs_find_entry(new_dir,new_dentry,&new_ino);
- if (new_bh && !new_inode) {
- affs_error(old_inode->i_sb,"affs_rename",
- "No inode for entry found (key=%lu)\n",new_ino);
- goto end_rename;
- }
- if (S_ISDIR(old_inode->i_mode)) {
- if (new_inode) {
- retval = -EBUSY;
- if (!d_unhashed(new_dentry))
- goto end_rename;
- retval = -ENOTEMPTY;
- if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
- goto end_rename;
- }
+ if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
+ goto done;
- retval = -ENOENT;
- if (affs_parent_ino(old_inode) != old_dir->i_ino)
- goto end_rename;
- }
/* Unlink destination if it already exists */
- if (new_inode) {
- if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
- goto end_rename;
- new_inode->i_nlink = retval;
- mark_inode_dirty(new_inode);
- if (new_inode->i_ino == new_ino)
- new_inode->i_nlink = 0;
+ if (new_dentry->d_inode) {
+ retval = affs_remove_header(new_dentry);
+ if (retval)
+ return retval;
}
+
+ retval = -EIO;
+ bh = affs_bread(sb, old_dentry->d_inode->i_ino);
+ if (!bh)
+ goto done;
+
/* Remove header from its parent directory. */
- if ((retval = affs_remove_hash(old_bh,old_dir)))
- goto end_rename;
+ dir = old_dir;
+ down(&AFFS_DIR->i_hash_lock);
+ retval = affs_remove_hash(dir, bh);
+ if (retval)
+ goto done_unlock;
+
+ up(&AFFS_DIR->i_hash_lock);
+
/* And insert it into the new directory with the new name. */
- affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name);
- if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
- goto end_rename;
- affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
-
- new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime
- = old_dir->i_mtime = CURRENT_TIME;
- new_dir->i_version = ++event;
- old_dir->i_version = ++event;
- retval = 0;
- mark_inode_dirty(new_dir);
- mark_inode_dirty(old_dir);
- mark_buffer_dirty(old_bh);
-
-end_rename:
- affs_brelse(old_bh);
- affs_brelse(new_bh);
-out:
+ dir = new_dir;
+ affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
+ affs_fix_checksum(sb, bh);
+ down(&AFFS_DIR->i_hash_lock);
+ retval = affs_insert_hash(new_dir, bh);
+ if (retval)
+ goto done_unlock;
+ up(&AFFS_DIR->i_hash_lock);
+
+done:
+ affs_brelse(bh);
return retval;
+
+done_unlock:
+ up(&AFFS_DIR->i_hash_lock);
+ goto done;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)