patch-1.3.99 linux/fs/affs/namei.c
Next file: linux/fs/affs/symlink.c
Previous file: linux/fs/affs/inode.c
Back to the patch index
Back to the overall index
- Lines: 838
- Date:
Mon May 6 14:36:05 1996
- Orig file:
v1.3.98/linux/fs/affs/namei.c
- Orig date:
Tue Apr 23 13:57:11 1996
diff -u --recursive --new-file v1.3.98/linux/fs/affs/namei.c linux/fs/affs/namei.c
@@ -1,187 +1,736 @@
/*
* linux/fs/affs/namei.c
*
- * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ * (c) 1996 Hans-Joachim Widmaier - Heavily hacked up.
*
- * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
*
* (C) 1991 Linus Torvalds - minix filesystem
*/
#include <linux/sched.h>
#include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
+#include <linux/locks.h>
#include <asm/segment.h>
#include <linux/errno.h>
+/* Simple toupper() for DOS\1 */
-static inline int namecompare(int len, int maxlen,
- const char * name, const char * buffer)
+static inline unsigned int
+affs_toupper(unsigned int ch)
{
- if (len >= maxlen || !buffer[len]) {
- return strncmp (name, buffer, len) == 0;
- }
- return 0;
+ return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
+}
+
+/* International toupper() for DOS\3 */
+
+static inline unsigned int
+affs_intl_toupper(unsigned int ch)
+{
+ return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
+ && ch <= 0xFE && ch != 0xF7) ?
+ ch - ('a' - 'A') : ch;
}
/*
- * ok, we cannot use strncmp, as the name is not in our data space.
- * Thus we'll have to use affs_match. No big problem. Match also makes
- * some sanity tests.
- *
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
*/
-static int affs_match(int len,const char * name, char * compare, int dlen)
+
+static int
+affs_match(const char *name, int len, const char *compare, int dlen, int intl)
{
- if (!compare) return 0;
+ if (!compare)
+ return 0;
+
+ if (len > 30)
+ 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 0
- if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen);
-#endif
-
- return namecompare(len,dlen,name,compare);
+ if (dlen != len)
+ return 0;
+ if (intl) {
+ while (dlen--) {
+ if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
+ return 0;
+ name++;
+ compare++;
+ }
+ } else {
+ while (dlen--) {
+ if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
+ return 0;
+ name++;
+ compare++;
+ }
+ }
+ return 1;
}
-/* Avoid pulling in ctype stuff. */
-
-static int affs_toupper (int ch)
+int
+affs_hash_name(const char *name, int len, int intl, int hashsize)
{
- if (ch >= 'a' && ch <= 'z')
- ch -= ('a' - 'A');
- return ch;
-}
+ unsigned int i, x;
-static int affs_hash_name (const char *name, int len)
-{
- int i, x;
+ if (len > 30)
+ len = 30;
x = len;
for (i = 0; i < len; i++)
- x = (x * 13 + affs_toupper (name[i])) & 0x7ff;
- return x % 72; /* FIXME: Assumes 512 byte blocks. */
+ if (intl)
+ x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
+ else
+ x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+
+ return x % hashsize;
}
-static struct buffer_head *affs_find_entry(struct inode *dir,
- const char *name, int namelen, int *ino)
+static struct buffer_head *
+affs_find_entry(struct inode *dir, const char *name, int namelen,
+ unsigned long *ino)
{
struct buffer_head *bh;
- void *dir_data;
- int key;
+ int intl;
+ ULONG key;
- *ino = 0;
+ pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
- bh = affs_pread (dir, dir->i_ino, &dir_data);
+ intl = AFFS_I2FSTYPE(dir);
+ bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
if (!bh)
return NULL;
- if (affs_match (namelen, name, ".", 1)) {
+ if (affs_match(name,namelen,".",1,intl)) {
*ino = dir->i_ino;
return bh;
}
- if (affs_match (namelen, name, "..", 2)) {
- *ino = affs_parent_ino (dir);
+ if (affs_match(name,namelen,"..",2,intl)) {
+ *ino = affs_parent_ino(dir);
return bh;
}
- key = affs_get_key_entry (AFFS_I2BSIZE (dir), dir_data,
- affs_hash_name (name, namelen));
+
+ key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
for (;;) {
char *cname;
int cnamelen;
- brelse (bh);
- if (key <= 0)
- return NULL;
- bh = affs_pread (dir, key, &dir_data);
+ affs_brelse(bh);
+ if (key == 0) {
+ bh = NULL;
+ break;
+ }
+ bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
if (!bh)
- return NULL;
- cnamelen = affs_get_file_name (AFFS_I2BSIZE (dir),
- dir_data, &cname);
- if (affs_match (namelen, name, cname, cnamelen))
break;
- key = affs_get_fh_hash_link (AFFS_I2BSIZE (dir), dir_data);
+ cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
+ if (affs_match(name,namelen,cname,cnamelen,intl))
+ break;
+ key = htonl(FILE_END(bh->b_data,dir)->hash_chain);
}
-
+ pr_debug("%lu\n",key);
*ino = key;
-
return bh;
}
-int affs_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+int
+affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
{
- int ino;
+ int res;
+ unsigned long ino;
struct buffer_head *bh;
+ pr_debug("AFFS: lookup(%.*s)\n",len,name);
+
*result = NULL;
if (!dir)
return -ENOENT;
-#ifdef DEBUG
- printk ("lookup: %d %d\n", dir->i_ino, len);
-#endif
+ res = -ENOENT;
+ if (S_ISDIR(dir->i_mode)) {
+ if ((bh = affs_find_entry(dir,name,len,&ino))) {
+ if (FILE_END(bh->b_data,dir)->original)
+ ino = htonl(FILE_END(bh->b_data,dir)->original);
+ affs_brelse(bh);
+ if ((*result = iget(dir->i_sb,ino)))
+ res = 0;
+ else
+ res = -EACCES;
+ }
+ }
+ iput(dir);
+ return res;
+}
+
+int
+affs_unlink(struct inode *dir, const char *name, int len)
+{
+ int retval;
+ struct buffer_head *bh;
+ unsigned long ino;
+ struct inode *inode;
+
+ pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,len,name);
+
+ bh = NULL;
+ inode = NULL;
+ retval = -ENOENT;
+ if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+ goto unlink_done;
+ }
+ if (!(inode = iget(dir->i_sb,ino))) {
+ goto unlink_done;
+ }
+ if (S_ISDIR(inode->i_mode)) {
+ retval = -EPERM;
+ goto unlink_done;
+ }
+
+ if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
+ AFFS_I2HSIZE(dir)) + 6,ino,
+ FILE_END(bh->b_data,dir)->hash_chain)))
+ goto unlink_done;
+
+ if ((retval = affs_fixup(bh,inode)))
+ goto unlink_done;
+
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_version = ++event;
+ dir->i_dirt=1;
+unlink_done:
+ affs_brelse(bh);
+ iput(inode);
+ iput(dir);
+ return retval;
+}
+
+int
+affs_create(struct inode *dir, const char *name, int len, int mode, struct inode **result)
+{
+ struct inode *inode;
+ int error;
+
+ pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+
- if (!S_ISDIR(dir->i_mode)) {
+ *result = NULL;
+
+ if (!dir || !dir->i_sb) {
iput(dir);
- return -ENOENT;
+ return -EINVAL;
}
- if (!(bh = affs_find_entry(dir, name, len, &ino))) {
- iput(dir);
- return -ENOENT;
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
}
- brelse(bh);
- if (!(*result = iget(dir->i_sb, ino))) {
+ inode->i_op = &affs_file_inode_operations;
+ inode->i_mode = mode;
+ error = affs_add_entry(dir,NULL,inode,name,len,ST_FILE);
+ if (error) {
iput(dir);
- return -EACCES;
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
}
- iput (dir);
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+
+ iput(dir);
+ *result = inode;
+
return 0;
+}
-#if 0
- ino = 0;
- while(cache.lock);
- cache.lock = 1;
- if (dir->i_dev == cache.dev &&
- dir->i_ino == cache.dir &&
- len == cache.dlen &&
- affs_match(len, name, cache.filename, cache.dlen))
- {
- ino = cache.ino;
- ino_back = dir->i_ino;
- /* These two cases are special, but since they are at the start
- of the directory, we can just as easily search there */
- if (cache.dlen == 1 && cache.filename[0] == '.') ino = 0;
- if (cache.dlen == 2 && cache.filename[0] == '.' &&
- cache.filename[1] == '.') ino = 0;
- };
- cache.lock = 0;
-
- if (!ino) {
- if (!(bh = affs_find_entry(dir,name,len, &ino, &ino_back))) {
- iput(dir);
- return -ENOENT;
- }
- brelse(bh);
- };
+int
+affs_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ struct inode *inode;
+ struct buffer_head *bh;
+ unsigned long i;
+ int error;
+
+ pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
- if (!(*result = iget(dir->i_sb,ino))) {
+ if (!dir || !dir->i_sb) {
+ iput(dir);
+ return -EINVAL;
+ }
+ bh = affs_find_entry(dir,name,len,&i);
+ if (bh) {
+ affs_brelse(bh);
iput(dir);
- return -EACCES;
+ return -EEXIST;
}
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &affs_dir_inode_operations;
+ error = affs_add_entry(dir,NULL,inode,name,len,ST_USERDIR);
+ if (error) {
+ iput(dir);
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ return error;
+ }
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
- /* We need this backlink for the .. entry */
-
- if (ino_back) (*result)->u.affs_i.i_backlink = ino_back;
+ iput(dir);
+ iput(inode);
+
+ return 0;
+}
+
+static int
+empty_dir(struct buffer_head *bh, int hashsize)
+{
+ while (--hashsize >= 0) {
+ if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
+ return 0;
+ }
+ return 1;
+}
+
+int
+affs_rmdir(struct inode *dir, const char *name, int len)
+{
+ int retval;
+ unsigned long ino;
+ struct inode *inode;
+ struct buffer_head *bh;
+
+ pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,len,name);
+
+ inode = NULL;
+ retval = -ENOENT;
+ if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+ goto rmdir_done;
+ }
+ if (!(inode = iget(dir->i_sb,ino))) {
+ goto rmdir_done;
+ }
+ retval = -EPERM;
+ if (!fsuser() && current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto rmdir_done;
+ if (inode->i_dev != dir->i_dev)
+ goto rmdir_done;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto rmdir_done;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto rmdir_done;
+ }
+ if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
+ retval = -ENOTEMPTY;
+ goto rmdir_done;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto rmdir_done;
+ }
+ if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
+ AFFS_I2HSIZE(dir)) + 6,ino,
+ FILE_END(bh->b_data,dir)->hash_chain)))
+ goto rmdir_done;
+
+ if ((retval = affs_fixup(bh,inode)))
+ goto rmdir_done;
+
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_version = ++event;
+ dir->i_dirt=1;
+rmdir_done:
+ iput(dir);
+ iput(inode);
+ affs_brelse(bh);
+ return retval;
+}
+
+int
+affs_symlink(struct inode *dir, const char *name, int len, const char *symname)
+{
+ struct buffer_head *bh;
+ struct inode *inode;
+ char *p;
+ unsigned long tmp;
+ int i, maxlen;
+ char c, lc;
+
+ pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+ printk("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+ maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &affs_symlink_inode_operations;
+ inode->i_mode = S_IFLNK | 0777;
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ if (!bh) {
+ iput(dir);
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -EIO;
+ }
+ i = 0;
+ p = ((struct slink_front *)bh->b_data)->symname;
+ 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 (i < maxlen && (c = *symname++)) {
+ if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
+ *p++ = '/';
+ i++;
+ symname += 2;
+ lc = '/';
+ } else if (c == '.' && lc == '/' && *symname == '/') {
+ symname++;
+ lc = '/';
+ } else {
+ *p++ = c;
+ lc = c;
+ i++;
+ }
+ if (lc == '/')
+ while (*symname == '/')
+ symname++;
+ }
+ *p = 0;
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ inode->i_dirt = 1;
+ bh = affs_find_entry(dir,name,len,&tmp);
+ if (bh) {
+ inode->i_nlink = 0;
+ iput(inode);
+ affs_brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ i = affs_add_entry(dir,NULL,inode,name,len,ST_SOFTLINK);
+ if (i) {
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ affs_brelse(bh);
+ iput(dir);
+ return i;
+ }
iput(dir);
+ iput(inode);
+
return 0;
-#endif
+}
+
+int
+affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+{
+ 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,len,name);
+
+ bh = affs_find_entry(dir,name,len,&i);
+ if (bh) {
+ affs_brelse(bh);
+ iput(oldinode);
+ iput(dir);
+ return -EEXIST;
+ }
+ if (oldinode->u.affs_i.i_hlink) {
+ i = oldinode->u.affs_i.i_original;
+ iput(oldinode);
+ oldinode = iget(dir->i_sb,i);
+ if (!oldinode) {
+ printk("AFFS: link(): original does not exist.\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ }
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput(oldinode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = oldinode->i_op;
+ inode->i_mode = oldinode->i_mode;
+ inode->i_uid = oldinode->i_uid;
+ inode->i_gid = oldinode->i_gid;
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ inode->u.affs_i.i_original = oldinode->i_ino;
+ inode->u.affs_i.i_hlink = 1;
+
+ if (S_ISDIR(oldinode->i_mode))
+ error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKDIR);
+ else
+ error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKFILE);
+ if (error) {
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ }
+ iput(dir);
+ iput(inode);
+ iput(oldinode);
+
+ return error;
+}
+
+static int
+subdir(struct inode * new_inode, struct inode * old_inode)
+{
+ int ino;
+ int result;
+
+ new_inode->i_count++;
+ result = 0;
+ for (;;) {
+ if (new_inode == old_inode) {
+ result = 1;
+ break;
+ }
+ if (new_inode->i_dev != old_inode->i_dev)
+ break;
+ ino = new_inode->i_ino;
+ if (affs_lookup(new_inode,"..",2,&new_inode))
+ break;
+ if (new_inode->i_ino == ino)
+ break;
+ }
+ iput(new_inode);
+ return result;
+}
+
+/* I'm afraid this might not be race proof. Maybe next time. */
+
+int
+affs_rename(struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len)
+{
+ struct inode *old_inode;
+ struct inode *new_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\" to new=%lu,\"%*s\")\n",old_dir->i_ino,old_len,old_name,
+ new_dir->i_ino,new_len,new_name);
+
+ if (new_len > 30)
+ new_len = 30;
+ goto start_up;
+retry:
+ affs_brelse(old_bh);
+ affs_brelse(new_bh);
+ iput(new_inode);
+ iput(old_inode);
+ current->counter = 0;
+ schedule();
+start_up:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = NULL;
+ retval = -ENOENT;
+
+ old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
+ if (!old_bh)
+ goto end_rename;
+ old_inode = __iget(old_dir->i_sb,old_ino,0);
+ if (!old_inode)
+ goto end_rename;
+ new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino);
+ if (new_bh) {
+ new_inode = __iget(new_dir->i_sb,new_ino,0);
+ if (!new_inode) { /* What does this mean? */
+ affs_brelse(new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) { /* Won't happen */
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EISDIR;
+ if (!S_ISDIR(old_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir,old_inode))
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
+ goto end_rename;
+ retval = -EBUSY;
+ if (new_inode->i_count > 1)
+ goto end_rename;
+ }
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -ENOTDIR;
+ if (new_inode && !S_ISDIR(new_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir,old_inode))
+ goto end_rename;
+ if (affs_parent_ino(old_inode) != old_dir->i_ino)
+ goto end_rename;
+ }
+ /* Unlink destination if existant */
+ if (new_inode) {
+ if ((retval = affs_fix_hash_pred(new_dir,affs_hash_name(new_name,new_len,
+ AFFS_I2FSTYPE(new_dir),AFFS_I2HSIZE(new_dir)) + 6,
+ new_ino,
+ FILE_END(new_bh->b_data,new_dir)->hash_chain)))
+ goto retry;
+ if ((retval = affs_fixup(new_bh,new_inode)))
+ goto retry;
+ mark_buffer_dirty(new_bh,1);
+ new_dir->i_version = ++event;
+ new_dir->i_dirt = 1;
+ new_inode->i_nlink = 0;
+ new_inode->i_dirt = 1;
+ }
+ retval = affs_fix_hash_pred(old_dir,affs_hash_name(old_name,old_len,AFFS_I2FSTYPE(old_dir),
+ AFFS_I2HSIZE(old_dir)) + 6,old_ino,
+ FILE_END(old_bh->b_data,old_dir)->hash_chain);
+ if (retval)
+ goto retry;
+
+ retval = affs_add_entry(new_dir,NULL,old_inode,new_name,new_len,
+ htonl(FILE_END(old_bh->b_data,old_dir)->secondary_type));
+
+ 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;
+ new_dir->i_dirt = 1;
+ old_dir->i_dirt = 1;
+ mark_buffer_dirty(old_bh,1);
+
+end_rename:
+ affs_brelse(old_bh);
+ affs_brelse(new_bh);
+ iput(new_inode);
+ iput(old_inode);
+ iput(old_dir);
+ iput(new_dir);
+
+ return retval;
+}
+
+int
+affs_fixup(struct buffer_head *bh, struct inode *inode)
+{
+ ULONG key, link_key;
+ LONG type;
+ struct buffer_head *nbh;
+ struct inode *ofinode;
+
+ type = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+ if (type == ST_LINKFILE || type == ST_LINKDIR) {
+ key = htonl(LINK_END(bh->b_data,inode)->original);
+ LINK_END(bh->b_data,inode)->original = 0;
+ if (!key) {
+ printk("AFFS: fixup(): hard link without original: ino=%lu\n",inode->i_ino);
+ return -ENOENT;
+ }
+ if (!(ofinode = iget(inode->i_sb,key)))
+ return -ENOENT;
+ type = affs_fix_link_pred(ofinode,inode->i_ino,
+ FILE_END(bh->b_data,inode)->link_chain);
+ iput(ofinode);
+ return type;
+ } else if (type == ST_FILE || type == ST_USERDIR) {
+ if ((key = htonl(FILE_END(bh->b_data,inode)->link_chain))) {
+ /* Get first link, turn it to a file */
+ if (!(ofinode = iget(inode->i_sb,key))) {
+ printk("AFFS: fixup(): cannot read inode %u\n",key);
+ return -ENOENT;
+ }
+ if (!ofinode->u.affs_i.i_hlink) {
+ printk("AFFS: fixup(): first link to %lu (%u) is not a link?\n",
+ inode->i_ino,key);
+ iput(ofinode);
+ return -ENOENT;
+ }
+ if (!(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: fixup(): cannot read block %u\n",key);
+ iput(ofinode);
+ return -ENOENT;
+ }
+ lock_super(inode->i_sb);
+ memcpy(nbh->b_data + 8,bh->b_data + 8,AFFS_I2BSIZE(inode) - 208);
+ FILE_END(nbh->b_data,inode)->byte_size = FILE_END(bh->b_data,inode)->
+ byte_size;
+ FILE_END(nbh->b_data,inode)->extension = FILE_END(bh->b_data,inode)->
+ extension;
+ FILE_END(nbh->b_data,inode)->secondary_type = FILE_END(bh->b_data,inode)->
+ secondary_type;
+ FILE_END(nbh->b_data,inode)->original = 0;
+
+ ofinode->u.affs_i.i_original = 0;
+ ofinode->u.affs_i.i_hlink = 0;
+ ofinode->i_size = inode->i_size;
+ ofinode->i_uid = inode->i_uid;
+ ofinode->i_gid = inode->i_gid;
+ ofinode->i_dirt = 1;
+ link_key = ofinode->i_ino;
+
+ /* Let all remaining links point to the new file */
+ while (1) {
+ affs_fix_checksum(AFFS_I2BSIZE(inode),nbh->b_data,5);
+ mark_buffer_dirty(nbh,1);
+ key = htonl(FILE_END(nbh->b_data,inode)->link_chain);
+ affs_brelse(nbh);
+ iput(ofinode);
+ if (!key || !(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode))))
+ break;
+ if ((ofinode = iget(inode->i_sb,key))) {
+ if (!ofinode->u.affs_i.i_hlink)
+ printk("AFFS: fixup() inode %u in link chain is "
+ "not a link\n",key);
+ ofinode->u.affs_i.i_original = link_key;
+ ofinode->i_dirt = 1;
+ FILE_END(nbh->b_data,inode)->original = htonl(link_key);
+ } else
+ printk("AFFS: fixup(): cannot get inode %u\n",key);
+ }
+ /* Turn old inode to a link */
+ inode->u.affs_i.i_hlink = 1;
+ unlock_super(inode->i_sb);
+ }
+ return 0;
+ } else if (type == ST_SOFTLINK) {
+ return 0;
+ } else {
+ printk("AFFS: fixup(): secondary type=%d\n",type);
+ return -EBADF;
+ }
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this