patch-1.3.99 linux/fs/affs/file.c
Next file: linux/fs/affs/inode.c
Previous file: linux/fs/affs/dir.c
Back to the patch index
Back to the overall index
- Lines: 651
- Date:
Mon May 6 14:36:24 1996
- Orig file:
v1.3.98/linux/fs/affs/file.c
- Orig date:
Sat Apr 27 15:19:57 1996
diff -u --recursive --new-file v1.3.98/linux/fs/affs/file.c linux/fs/affs/file.c
@@ -1,6 +1,8 @@
/*
* linux/fs/affs/file.c
*
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
*
* (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
@@ -12,7 +14,6 @@
#include <asm/segment.h>
#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/affs_fs.h>
#include <linux/fcntl.h>
@@ -20,36 +21,31 @@
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/locks.h>
-
#include <linux/dirent.h>
-
-#define NBUF 16
+#include <linux/fs.h>
+#include <linux/amigaffs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
-#include <linux/fs.h>
-#include <linux/affs_fs.h>
-
-#include "amigaffs.h"
-
-int affs_file_read(struct inode *, struct file *, char *, int);
+static int affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count);
+static int affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count);
+static int affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count);
+static void affs_release_file(struct inode *inode, struct file *filp);
-/*
- * We have mostly NULL's here: the current defaults are ok for
- * the affs filesystem.
- */
-struct file_operations affs_file_operations = {
+static struct file_operations affs_file_operations = {
NULL, /* lseek - default */
- affs_file_read, /* read */
- NULL, /* write */
+ generic_file_read, /* read */
+ affs_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
- NULL, /* release */
- NULL /* can't fsync */
+ affs_release_file, /* release */
+ file_fsync /* brute force, but works */
};
struct inode_operations affs_file_inode_operations = {
@@ -65,75 +61,229 @@
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
- NULL /* affs_bmap */, /* bmap */
- NULL, /* truncate */
- NULL /* permission */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ affs_bmap, /* bmap */
+ affs_truncate, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
};
-static int affs_smap(struct inode *inode, int block)
-{
- struct buffer_head *bh;
- int key;
- void *fh_data;
+static struct file_operations affs_file_operations_ofs = {
+ NULL, /* lseek - default */
+ affs_file_read_ofs, /* read */
+ affs_file_write_ofs, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ file_fsync /* brute force, but works */
+};
-/* FIXME */
-#define KEY_SLOTS_PER_BLOCK 72
+struct inode_operations affs_file_inode_operations_ofs = {
+ &affs_file_operations_ofs, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ affs_truncate_ofs, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+int
+affs_bmap(struct inode *inode, LONG block)
+{
+ struct buffer_head *bh;
+ LONG ext, key;
+ LONG ptype, stype;
-#ifdef DEBUG
- printk ("affs_smap: ino=%d block=%d\n", inode->i_ino, block);
-#endif
+ pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
if (block < 0) {
- printk("affs_smap: block < 0");
+ printk("affs_bmap: block < 0\n");
return 0;
}
- key = inode->i_ino;
+ /* If this is a hard link, quietly exchange the inode with the original */
+
+ key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+
+ ext = block / AFFS_I2HSIZE(inode);
+ if (ext) {
+ if (ext > inode->u.affs_i.i_max_ext)
+ ext = inode->u.affs_i.i_max_ext;
+ if (ext)
+ key = inode->u.affs_i.i_ext[ext - 1];
+ block -= ext * AFFS_I2HSIZE(inode);
+ }
+
for (;;) {
- bh = affs_pread (inode, key, &fh_data);
+ bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
if (!bh)
return 0;
- if (block < KEY_SLOTS_PER_BLOCK)
+ if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
+ (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE) {
+ affs_brelse(bh);
+ return 0;
+ }
+ if (block < AFFS_I2HSIZE(inode))
break;
- block -= KEY_SLOTS_PER_BLOCK;
- key = affs_get_extension (AFFS_I2BSIZE (inode), fh_data);
-#ifdef DEBUG
- printk ("affs_smap: reading extension block %d\n", key);
-#endif
- brelse (bh);
- }
- key = affs_get_key_entry (AFFS_I2BSIZE (inode), fh_data,
- (KEY_SLOTS_PER_BLOCK - 1) - block);
- brelse (bh);
-
-#ifdef DEBUG
- printk ("affs_smap: key=%d\n", key);
-#endif
+ block -= AFFS_I2HSIZE(inode);
+ key = htonl(FILE_END(bh->b_data,inode)->extension);
+ affs_brelse(bh);
+ if (ext < EXT_CACHE_SIZE - 1) {
+ inode->u.affs_i.i_ext[ext] = key;
+ inode->u.affs_i.i_max_ext = ++ext;
+ }
+ }
+ key = AFFS_GET_HASHENTRY(bh->b_data,(AFFS_I2HSIZE(inode) - 1) - block);
+ affs_brelse(bh);
return key;
}
-/*
- * affs_file_read() is also needed by the directory read-routine,
- * so it's not static. NOTE! reading directories directly is a bad idea,
- * but has to be supported for now for compatibility reasons with older
- * versions.
+struct buffer_head *
+affs_getblock(struct inode *inode, LONG block)
+{
+ struct buffer_head *bh;
+ struct buffer_head *ebh;
+ LONG key;
+ LONG ext;
+ LONG cnt, j, pt;
+
+ pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
+
+ if (block < 0)
+ return NULL;
+ key = inode->i_ino;
+ pt = T_SHORT;
+
+ ext = block / AFFS_I2HSIZE(inode);
+ if (ext) {
+ if (ext > inode->u.affs_i.i_max_ext)
+ ext = inode->u.affs_i.i_max_ext;
+ if (ext) {
+ key = inode->u.affs_i.i_ext[ext - 1];
+ block -= ext * AFFS_I2HSIZE(inode);
+ pt = T_LIST;
+ }
+ }
+
+ for (;;) {
+ bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ if (!bh)
+ return NULL;
+ if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cnt,&j) ||
+ cnt != pt || j != ST_FILE) {
+ printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
+ pt == T_SHORT ? "file header" : "extension block");
+ affs_brelse(bh);
+ return NULL;
+ }
+ j = htonl(((struct file_front *)bh->b_data)->block_count);
+ while (j < AFFS_I2HSIZE(inode) && j <= block) {
+ key = affs_new_data(inode);
+ if (!key)
+ break;
+ lock_super(inode->i_sb);
+ if (AFFS_BLOCK(bh->b_data,inode,j)) {
+ unlock_super(inode->i_sb);
+ printk("AFFS: getblock(): block already allocated\n");
+ affs_free_block(inode->i_sb,key);
+ j++;
+ continue;
+ }
+ unlock_super(inode->i_sb);
+ AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
+ j++;
+ }
+ if (pt == T_SHORT)
+ ((struct file_front *)bh->b_data)->first_data =
+ AFFS_BLOCK(bh->b_data,inode,0);
+ ((struct file_front *)bh->b_data)->block_count = ntohl(j);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+
+ if (block < j)
+ break;
+ if (j < AFFS_I2HSIZE(inode)) {
+ affs_brelse(bh);
+ return NULL;
+ }
+
+ block -= AFFS_I2HSIZE(inode);
+ key = htonl(FILE_END(bh->b_data,inode)->extension);
+ if (!key) {
+ key = affs_new_header(inode);
+ if (!key) {
+ affs_brelse(bh);
+ return NULL;
+ }
+ ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ if (!ebh) {
+ affs_free_block(inode->i_sb,key);
+ return NULL;
+ }
+ ((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
+ ((struct file_front *)ebh->b_data)->own_key = ntohl(key);
+ FILE_END(ebh->b_data,inode)->secondary_type = ntohl(ST_FILE);
+ FILE_END(ebh->b_data,inode)->parent = ntohl(inode->i_ino);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+ FILE_END(bh->b_data,inode)->extension = ntohl(key);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ bh = ebh;
+ }
+ affs_brelse(bh);
+ pt = T_LIST;
+ if (ext < EXT_CACHE_SIZE - 1) {
+ inode->u.affs_i.i_ext[ext] = key;
+ inode->u.affs_i.i_max_ext = ++ext;
+ }
+ }
+ key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
+ affs_brelse(bh);
+ if (!key)
+ return NULL;
+
+ return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+}
+
+/* This could be made static, regardless of what the former comment said.
+ * You cannot directly read affs directories.
*/
-int affs_file_read(struct inode * inode, struct file * filp,
- char * buf, int count)
+
+static int
+affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count)
{
char *start;
int left, offset, size, sector;
+ int blocksize;
struct buffer_head *bh;
void *data;
+ pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,(long)filp->f_pos,count);
+
if (!inode) {
printk("affs_file_read: inode = NULL\n");
return -EINVAL;
}
+ blocksize = AFFS_I2BSIZE(inode) - 24;
if (!(S_ISREG(inode->i_mode))) {
-#ifdef DEBUG
- printk("affs_file_read: mode = %07o\n",inode->i_mode);
-#endif
+ pr_debug("affs_file_read: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
if (filp->f_pos >= inode->i_size || count <= 0)
@@ -141,34 +291,296 @@
start = buf;
for (;;) {
- left = MIN (inode->i_size - filp->f_pos,
- count - (buf - start));
+ left = MIN (inode->i_size - filp->f_pos,count - (buf - start));
if (!left)
break;
- sector = affs_smap (inode, filp->f_pos >> AFFS_BLOCK_BITS);
+ sector = affs_bmap(inode,(ULONG)filp->f_pos / blocksize);
if (!sector)
break;
- offset = filp->f_pos & (AFFS_BLOCK_SIZE - 1);
- bh = affs_pread (inode, sector, &data);
+ offset = (ULONG)filp->f_pos % blocksize;
+ bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode));
if (!bh)
break;
- size = MIN (AFFS_BLOCK_SIZE - offset, left);
+ data = bh->b_data + 24;
+ size = MIN(blocksize - offset,left);
filp->f_pos += size;
- memcpy_tofs (buf, data + offset, size);
+ memcpy_tofs(buf,data + offset,size);
buf += size;
- brelse (bh);
+ affs_brelse(bh);
}
if (start == buf)
return -EIO;
return buf - start;
+}
-#if 0
- if (filp->f_pos == 0 && count > 0) {
- put_fs_byte ('X', buf++);
- filp->f_pos++;
- return 1;
+static int
+affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count)
+{
+ off_t pos;
+ int written;
+ int c;
+ int blocksize;
+ struct buffer_head *bh;
+ struct inode *ino;
+ char *p;
+
+ pr_debug("AFFS: file_write(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+ (unsigned long)filp->f_pos,count);
+
+ ino = NULL;
+ if (!inode) {
+ printk("AFFS: file_write(): inode=NULL\n");
+ return -EINVAL;
+ }
+ if (inode->u.affs_i.i_original) {
+ ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+ if (!ino) {
+ printk("AFFS: could not follow link from inode %lu to %d\n",
+ inode->i_ino,inode->u.affs_i.i_original);
+ return -EINVAL;
+ }
+ inode = ino;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("AFFS: file_write(): mode=%07o\n",inode->i_mode);
+ iput(inode);
+ return -EINVAL;
+ }
+ if (filp->f_flags & O_APPEND) {
+ pos = inode->i_size;
+ } else
+ pos = filp->f_pos;
+ written = 0;
+ blocksize = AFFS_I2BSIZE(inode);
+
+ while (written < count) {
+ bh = affs_getblock(inode,pos / blocksize);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = blocksize - (pos % blocksize);
+ if (c > count - written)
+ c = count - written;
+ if (c != blocksize && !buffer_uptodate(bh)) {
+ ll_rw_block(READ,1,&bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ affs_brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ p = (pos % blocksize) + bh->b_data;
+ memcpy_fromfs(p,buf,c);
+ update_vm_cache(inode,pos,p,c);
+ mark_buffer_uptodate(bh,1);
+ mark_buffer_dirty(bh,0);
+ affs_brelse(bh);
+ pos += c;
+ written += c;
+ buf += c;
+ }
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+ iput(ino);
+ return written;
+}
+
+static int
+affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count)
+{
+ pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+ (unsigned long)filp->f_pos,count);
+
+ return -ENOSPC;
+}
+
+void
+affs_truncate(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct inode *ino;
+ LONG first;
+ LONG block;
+ LONG key;
+ LONG *keyp;
+ LONG ekey;
+ LONG ptype, stype;
+ int freethis;
+ int ext;
+
+ pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+
+ ino = NULL;
+ if (inode->u.affs_i.i_original) {
+ ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+ if (!ino) {
+ printk("AFFS: truncate(): cannot follow link from %lu to %u\n",
+ inode->i_ino,inode->u.affs_i.i_original);
+ return;
+ }
+ inode = ino;
+ }
+ first = (inode->i_size + AFFS_I2BSIZE(inode) - 1) / AFFS_I2BSIZE(inode);
+ ekey = inode->i_ino;
+ ext = 0;
+
+ while (ekey) {
+ if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: truncate(): Can't read block %d\n",ekey);
+ break;
+ }
+ ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
+ stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+ if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
+ LINK_END(bh->b_data,inode)->original == 0) {
+ pr_debug("AFFS: truncate(): dumping link\n");
+ affs_brelse(bh);
+ break;
+ }
+ if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
+ printk("AFFS: truncate(): bad block (ptype=%d, stype=%d)\n",
+ ptype,stype);
+ affs_brelse(bh);
+ break;
+ }
+ /* Do not throw away file header */
+ freethis = first == 0 && ekey != inode->i_ino;
+ for ( block = first; block < AFFS_I2HSIZE(inode); block++) {
+ keyp = &AFFS_BLOCK(bh->b_data,inode,block);
+ key = htonl(*keyp);
+ if (key) {
+ *keyp = 0;
+ affs_free_block(inode->i_sb,key);
+ } else {
+ block = AFFS_I2HSIZE(inode);
+ break;
+ }
+ }
+ keyp = &GET_END_PTR(struct file_end,bh->b_data,AFFS_I2BSIZE(inode))->extension;
+ key = htonl(*keyp);
+ if (first <= AFFS_I2HSIZE(inode)) {
+ ((struct file_front *)bh->b_data)->block_count = htonl(first);
+ first = 0;
+ *keyp = 0;
+ } else {
+ first -= AFFS_I2HSIZE(inode);
+ }
+ if (freethis) { /* Don't bother fixing checksum */
+ affs_brelse(bh);
+ affs_free_block(inode->i_sb,ekey);
+ } else {
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ }
+ ekey = key;
+ }
+ inode->u.affs_i.i_max_ext = 0; /* invalidate cache */
+ iput(ino);
+}
+
+void
+affs_truncate_ofs(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct inode *ino;
+ LONG first;
+ LONG block;
+ LONG key;
+ LONG *keyp;
+ LONG ekey;
+ LONG ptype, stype;
+ int freethis;
+ int blocksize;
+
+ pr_debug("AFFS: file_truncate_ofs(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+
+ ino = NULL;
+ if (inode->u.affs_i.i_original) {
+ ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+ if (!ino) {
+ printk("AFFS: truncate(): cannot follow link from %lu to %u\n",
+ inode->i_ino,inode->u.affs_i.i_original);
+ return;
+ }
+ inode = ino;
+ }
+ blocksize = AFFS_I2BSIZE(inode) - 24;
+ first = (inode->i_size + blocksize - 1) / blocksize;
+ ekey = inode->i_ino;
+
+ while (ekey) {
+ if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: truncate(): Can't read block %d\n",ekey);
+ break;
+ }
+ ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
+ stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+ if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
+ LINK_END(bh->b_data,inode)->original == 0) {
+ pr_debug("AFFS: truncate(): dumping link\n");
+ affs_brelse(bh);
+ break;
+ }
+ if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
+ printk("AFFS: truncate(): bad block (ptype=%d, stype=%d)\n",
+ ptype,stype);
+ affs_brelse(bh);
+ break;
+ }
+ /* Do not throw away file header */
+ freethis = first == 0 && ekey != inode->i_ino;
+ for ( block = first; block < AFFS_I2HSIZE(inode); block++) {
+ keyp = &((struct file_front *)bh->b_data)->
+ blocks[AFFS_I2HSIZE(inode) - 1 - block];
+ key = htonl(*keyp);
+ if (key) {
+ *keyp = 0;
+ affs_free_block(inode->i_sb,key);
+ } else {
+ block = AFFS_I2HSIZE(inode);
+ break;
+ }
+ }
+ keyp = &GET_END_PTR(struct file_end,bh->b_data,AFFS_I2BSIZE(inode))->extension;
+ key = htonl(*keyp);
+ if (first <= AFFS_I2HSIZE(inode)) {
+ ((struct file_front *)bh->b_data)->block_count = htonl(first);
+ first = 0;
+ *keyp = 0;
+ } else {
+ first -= AFFS_I2HSIZE(inode);
+ }
+ if (freethis) { /* Don't bother fixing checksum */
+ affs_brelse(bh);
+ affs_free_block(inode->i_sb,ekey);
+ } else {
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ }
+ ekey = key;
+ }
+ inode->u.affs_i.i_max_ext = 0; /* invalidate cache */
+ iput(ino);
+}
+
+static void
+affs_release_file(struct inode *inode, struct file *filp)
+{
+ if (filp->f_mode & 2) { /* Free preallocated blocks */
+ while (inode->u.affs_i.i_pa_cnt) {
+ affs_free_block(inode->i_sb,
+ inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
+ inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+ inode->u.affs_i.i_pa_cnt--;
+ }
}
- else
- return 0;
-#endif
}
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