summaryrefslogtreecommitdiffstats
path: root/fs/affs/amigaffs.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
commit967c65a99059fd459b956c1588ce0ba227912c4e (patch)
tree8224d013ff5d255420713d05610c7efebd204d2a /fs/affs/amigaffs.c
parente20c1cc1656a66a2773bca4591a895cbc12696ff (diff)
Merge with Linux 2.1.72, part 1.
Diffstat (limited to 'fs/affs/amigaffs.c')
-rw-r--r--fs/affs/amigaffs.c320
1 files changed, 250 insertions, 70 deletions
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
index 7ddb54a62..68c61753b 100644
--- a/fs/affs/amigaffs.c
+++ b/fs/affs/amigaffs.c
@@ -8,6 +8,7 @@
* Please send bug reports to: hjw@zvw.de
*/
+#define DEBUG 0
#include <stdarg.h>
#include <linux/stat.h>
#include <linux/sched.h>
@@ -30,7 +31,7 @@ static char ErrorBuffer[256];
pointed to by FH_DATA. The length of the name is returned. */
int
-affs_get_file_name(int bsize, void *fh_data, char **name)
+affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
{
struct file_end *file_end;
@@ -42,100 +43,142 @@ affs_get_file_name(int bsize, void *fh_data, char **name)
*name = "***BAD_FILE***";
return 14;
}
- *name = (char *)&file_end->file_name[1];
+ *name = (unsigned char *)&file_end->file_name[1];
return file_end->file_name[0];
}
-/* Find the predecessor in the hash chain */
+/* Insert a header block (file) into the directory (next).
+ * This routine assumes that the caller has the superblock locked.
+ */
int
-affs_fix_hash_pred(struct inode *startino, int startoffset, s32 key, s32 newkey)
+affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
{
- struct buffer_head *bh = NULL;
- s32 nextkey;
- s32 ptype, stype;
- int retval;
+ struct buffer_head *bh;
+ s32 ino;
+ int offset;
+
+ offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,
+ FILE_END(file->b_data,inode)->file_name[0],
+ AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
+ ino = be32_to_cpu(((struct dir_front *)file->b_data)->own_key);
+
+ pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
+
+ FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);
- nextkey = startino->i_ino;
- retval = -ENOENT;
- lock_super(startino->i_sb);
while (1) {
- pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset);
- if (nextkey == 0)
+ if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))
+ return -EIO;
+ next = be32_to_cpu(((s32 *)bh->b_data)[offset]);
+ if (!next || next > ino)
break;
- if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+ offset = AFFS_I2BSIZE(inode) / 4 - 4;
+ affs_brelse(bh);
+ }
+
+ DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next);
+ ((s32 *)bh->b_data)[offset] = cpu_to_be32(ino);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+
+ return 0;
+}
+/* Remove a header block from it's hash table (directory).
+ * 'inode' may be any inode on the partition, it's only
+ * used for calculating the block size and superblock
+ * reference.
+ */
+
+int
+affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
+{
+ s32 ownkey;
+ s32 key;
+ s32 ptype;
+ s32 stype;
+ int offset;
+ int retval;
+ struct buffer_head *bh;
+
+ ownkey = be32_to_cpu(((struct dir_front *)dbh->b_data)->own_key);
+ key = be32_to_cpu(FILE_END(dbh->b_data,inode)->parent);
+ offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,
+ FILE_END(dbh->b_data,inode)->file_name[0],
+ AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
+ pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);
+ retval = -ENOENT;
+
+ lock_super(inode->i_sb);
+ while (key) {
+ if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+ retval = -EIO;
break;
- if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+ }
+ if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)
|| ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
stype != ST_LINKFILE && stype != ST_LINKDIR &&
stype != ST_ROOT && stype != ST_SOFTLINK)) {
- affs_error(startino->i_sb,"affs_fix_hash_pred",
- "Bad block in link chain (ptype=%d, stype=%d)",ptype,stype);
+ affs_error(inode->i_sb,"affs_remove_hash",
+ "Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",
+ key,ptype,stype,ownkey);
affs_brelse(bh);
+ retval = -EINVAL;
break;
}
- nextkey = htonl(((s32 *)bh->b_data)[startoffset]);
- if (nextkey == key) {
- ((s32 *)bh->b_data)[startoffset] = newkey;
- affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+ key = be32_to_cpu(((s32 *)bh->b_data)[offset]);
+ if (ownkey == key) {
+ ((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
mark_buffer_dirty(bh,1);
affs_brelse(bh);
retval = 0;
break;
}
affs_brelse(bh);
- startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
+ offset = AFFS_I2BSIZE(inode) / 4 - 4;
}
- unlock_super(startino->i_sb);
+ unlock_super(inode->i_sb);
return retval;
}
-/* Remove inode from link chain */
+/* Remove header from link chain */
int
-affs_fix_link_pred(struct inode *startino, s32 key, s32 newkey)
+affs_remove_link(struct buffer_head *dbh, struct inode *inode)
{
- struct buffer_head *bh = NULL;
- s32 nextkey;
- int offset;
- s32 etype = 0;
- s32 ptype, stype;
int retval;
-
- offset = AFFS_I2BSIZE(startino) / 4 - 10;
- nextkey = startino->i_ino;
- retval = -ENOENT;
- lock_super(startino->i_sb);
- while (1) {
- if (nextkey == 0)
- break;
- pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey);
- if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
- break;
- if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
- || ptype != T_SHORT) {
- affs_brelse(bh);
+ s32 key;
+ s32 ownkey;
+ s32 ptype;
+ s32 stype;
+ struct buffer_head *bh;
+
+ ownkey = be32_to_cpu((DIR_FRONT(dbh)->own_key));
+ key = be32_to_cpu(FILE_END(dbh->b_data,inode)->original);
+ retval = -ENOENT;
+
+ pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
+
+ lock_super(inode->i_sb);
+ while (key) {
+ if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+ retval = -EIO;
break;
}
- if (!etype) {
- if (stype != ST_FILE && stype != ST_USERDIR) {
- affs_brelse(bh);
- break;
- }
- if (stype == ST_FILE)
- etype = ST_LINKFILE;
- else
- etype = ST_LINKDIR;
- } else if (stype != etype) {
+ if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {
+ affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);
affs_brelse(bh);
- retval = -EPERM;
+ retval = -EINVAL;
break;
}
- nextkey = htonl(((s32 *)bh->b_data)[offset]);
- if (nextkey == key) {
- FILE_END(bh->b_data,startino)->link_chain = newkey;
- affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+ key = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
+ if (ownkey == key) {
+ FILE_END(bh->b_data,inode)->link_chain =
+ FILE_END(dbh->b_data,inode)->link_chain;
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
mark_buffer_dirty(bh,1);
affs_brelse(bh);
retval = 0;
@@ -143,10 +186,102 @@ affs_fix_link_pred(struct inode *startino, s32 key, s32 newkey)
}
affs_brelse(bh);
}
- unlock_super(startino->i_sb);
+ unlock_super(inode->i_sb);
+
return retval;
}
+/* Remove a filesystem object. If the object to be removed has
+ * links to it, one of the links must be changed to inherit
+ * the file or directory. As above, any inode will do.
+ * The buffer will not be freed. If the header is a link, the
+ * block will be marked as free.
+ * This function returns a negative error number in case of
+ * an error, else 0 if the inode is to be deleted or 1 if not.
+ */
+
+int
+affs_remove_header(struct buffer_head *bh, struct inode *inode)
+{
+ struct buffer_head *link_bh;
+ struct inode *dir;
+ unsigned long link_ino;
+ unsigned long orig_ino;
+ unsigned int dir_ino;
+ int error;
+
+ pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));
+
+ /* Mark directory as changed. We do this before anything else,
+ * as it must be done anyway and doesn't hurt even if an
+ * error occures later.
+ */
+ dir = iget(inode->i_sb,be32_to_cpu(FILE_END(bh->b_data,inode)->parent));
+ if (!dir)
+ return -EIO;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_version++;
+ mark_inode_dirty(dir);
+ iput(dir);
+
+ orig_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->original);
+ if (orig_ino) { /* This is just a link. Nothing much to do. */
+ pr_debug(" Removing link.\n");
+ if ((error = affs_remove_link(bh,inode)))
+ return error;
+ if ((error = affs_remove_hash(bh,inode)))
+ return error;
+ affs_free_block(inode->i_sb,be32_to_cpu(DIR_FRONT(bh)->own_key));
+ return 1;
+ }
+
+ link_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
+ if (link_ino) { /* This is the complicated case. Yuck. */
+ pr_debug(" Removing original with links to it.\n");
+ /* Unlink the object and its first link from their directories. */
+ if ((error = affs_remove_hash(bh,inode)))
+ return error;
+ if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))
+ return -EIO;
+ if ((error = affs_remove_hash(link_bh,inode))) {
+ affs_brelse(link_bh);
+ return error;
+ }
+ /* Fix link chain. */
+ if ((error = affs_remove_link(link_bh,inode))) {
+ affs_brelse(link_bh);
+ return error;
+ }
+ /* Rename link to object. */
+ memcpy(FILE_END(bh->b_data,inode)->file_name,
+ FILE_END(link_bh->b_data,inode)->file_name,32);
+ /* Insert object into dir the link was in. */
+ dir_ino = be32_to_cpu(FILE_END(link_bh->b_data,inode)->parent);
+ if ((error = affs_insert_hash(dir_ino,bh,inode))) {
+ affs_brelse(link_bh);
+ return error;
+ }
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(link_bh);
+ affs_free_block(inode->i_sb,link_ino);
+ /* Mark the link's parent dir as changed, too. */
+ if (!(dir = iget(inode->i_sb,dir_ino)))
+ return -EIO;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_version++;
+ mark_inode_dirty(dir);
+ iput(dir);
+ return 1;
+ }
+ /* Plain file/dir. This is the simplest case. */
+ pr_debug(" Removing plain file/dir.\n");
+ if ((error = affs_remove_hash(bh,inode)))
+ return error;
+ return 0;
+}
+
+
/* Checksum a block, do various consistency checks and optionally return
the blocks type number. DATA points to the block. If their pointers
are non-null, *PTYPE and *STYPE are set to the primary and secondary
@@ -162,14 +297,14 @@ affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype)
bsize /= 4;
if (ptype)
- *ptype = htonl(((s32 *)data)[0]);
+ *ptype = be32_to_cpu(((s32 *)data)[0]);
if (stype)
- *stype = htonl(((s32 *)data)[bsize - 1]);
+ *stype = be32_to_cpu(((s32 *)data)[bsize - 1]);
sum = 0;
p = data;
while (bsize--)
- sum += htonl(*p++);
+ sum += be32_to_cpu(*p++);
return sum;
}
@@ -180,9 +315,9 @@ affs_fix_checksum(int bsize, void *data, int cspos)
u32 cs;
cs = affs_checksum_block(bsize,data,NULL,NULL);
- ocs = htonl (((u32 *)data)[cspos]);
+ ocs = be32_to_cpu(((u32 *)data)[cspos]);
ocs -= cs;
- ((u32 *)data)[cspos] = htonl(ocs);
+ ((u32 *)data)[cspos] = be32_to_cpu(ocs);
}
void
@@ -191,7 +326,7 @@ secs_to_datestamp(time_t secs, struct DateStamp *ds)
u32 days;
u32 minute;
- secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
+ secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
if (secs < 0)
secs = 0;
days = secs / 86400;
@@ -199,9 +334,9 @@ secs_to_datestamp(time_t secs, struct DateStamp *ds)
minute = secs / 60;
secs -= minute * 60;
- ds->ds_Days = htonl(days);
- ds->ds_Minute = htonl(minute);
- ds->ds_Tick = htonl(secs * 50);
+ ds->ds_Days = be32_to_cpu(days);
+ ds->ds_Minute = be32_to_cpu(minute);
+ ds->ds_Tick = be32_to_cpu(secs * 50);
}
int
@@ -268,6 +403,7 @@ affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
if (!(sb->s_flags & MS_RDONLY))
printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
sb->s_flags |= MS_RDONLY;
+ sb->u.affs_sb.s_flags |= SF_READONLY; /* Don't allow to remount rw */
}
void
@@ -279,6 +415,50 @@ affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
vsprintf(ErrorBuffer,fmt,args);
va_end(args);
- printk(KERN_WARNING "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),
+ printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
function,ErrorBuffer);
}
+
+/* Check if the name is valid for a affs object. */
+
+int
+affs_check_name(const unsigned char *name, int len)
+{
+ int i;
+
+ if (len > 30)
+#ifdef AFFS_NO_TRUNCATE
+ return -ENAMETOOLONG;
+#else
+ len = 30;
+#endif
+
+ for (i = 0; i < len; i++) {
+ if (name[i] < ' ' || name[i] == ':'
+ || (name[i] > 0x7e && name[i] < 0xa0))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* This function copies name to bstr, with at most 30
+ * characters length. The bstr will be prepended by
+ * a length byte.
+ * NOTE: The name will must be already checked by
+ * affs_check_name()!
+ */
+
+int
+affs_copy_name(unsigned char *bstr, const unsigned char *name)
+{
+ int len;
+
+ for (len = 0; len < 30; len++) {
+ bstr[len + 1] = name[len];
+ if (name[len] == '\0')
+ break;
+ }
+ bstr[0] = len;
+ return len;
+}