patch-pre2.0.5 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

diff -u --recursive --new-file pre2.0.4/linux/fs/affs/file.c linux/fs/affs/file.c
@@ -1,7 +1,7 @@
 /*
  *  linux/fs/affs/file.c
  *
- *  (c) 1996  Hans-Joachim Widmaier - Rewritten
+ *  (c) 1996  Hans-Joachim Widmaier - rewritten
  *
  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
  *
@@ -78,7 +78,7 @@
 	NULL,			/* ioctl - default */
 	NULL,			/* mmap */
 	NULL,			/* no special open is needed */
-	NULL,			/* release */
+	affs_release_file,	/* release */
 	file_fsync		/* brute force, but works */
 };
 
@@ -98,7 +98,7 @@
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
-	affs_truncate_ofs,	/* truncate */
+	affs_truncate,		/* truncate */
 	NULL,			/* permission */
 	NULL			/* smap */
 };
@@ -262,6 +262,145 @@
 	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 }
 
+struct buffer_head *
+affs_getblock_ofs(struct inode *inode, LONG block, LONG *blk_key)
+{
+	struct buffer_head	*bh;
+	struct buffer_head	*pbh;
+	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) - 24);
+	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;
+		}
+	}
+
+	pbh = NULL;
+	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) {
+			if (!pbh && inode->u.affs_i.i_lastblock >= 0) {
+				pbh = affs_getblock_ofs(inode,inode->u.affs_i.i_lastblock,&key);
+				if (!pbh) {
+					printk("AFFS: getblock(): cannot get last block in file\n");
+					break;
+				}
+			}
+			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;
+			}
+			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
+			unlock_super(inode->i_sb);
+			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+			if (!ebh) {
+				printk("AFFS: getblock(): cannot get block %d\n",key);
+				affs_free_block(inode->i_sb,key);
+				AFFS_BLOCK(bh->b_data,inode,j) = 0;
+				break;
+			}
+			inode->u.affs_i.i_lastblock++;
+			DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
+			DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
+			DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
+			DATA_FRONT(pbh)->data_size       = ntohl(AFFS_I2HSIZE(inode) - 24);
+			DATA_FRONT(pbh)->next_data       = ntohl(key);
+			affs_fix_checksum(AFFS_I2HSIZE(inode),pbh->b_data,5);
+			mark_buffer_dirty(pbh,0);
+			mark_buffer_dirty(ebh,0);
+			affs_brelse(pbh);
+			pbh = ebh;
+			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) {
+			if (pbh)
+				affs_brelse(pbh);
+			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;
+	*blk_key = key;
+
+	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.
  */
@@ -395,101 +534,91 @@
 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)
-{
+	off_t			 pos;
+	int			 written;
+	int			 c;
+	int			 blocksize;
 	struct buffer_head	*bh;
 	struct inode		*ino;
-	LONG	 first;
-	LONG	 block;
-	LONG	 key;
-	LONG	*keyp;
-	LONG	 ekey;
-	LONG	 ptype, stype;
-	int	 freethis;
-	int	 ext;
+	char			*p;
 
-	pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+	pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+		(unsigned long)filp->f_pos,count);
 
+	if (!inode) {
+		printk("AFFS: file_write(): inode=NULL\n");
+		return -EINVAL;
+	}
 	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",
+			printk("AFFS: could not follow link from inode %lu to %d\n",
 			       inode->i_ino,inode->u.affs_i.i_original);
-			return;
+			return -EINVAL;
 		}
 		inode = ino;
 	}
-	first = (inode->i_size + AFFS_I2BSIZE(inode) - 1) / AFFS_I2BSIZE(inode);
-	ekey  = inode->i_ino;
-	ext   = 0;
+	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;
 
-	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);
+	bh        = NULL;
+	blocksize = AFFS_I2BSIZE(inode) - 24;
+	written   = 0;
+	while (written < count) {
+		bh = affs_getblock(inode,pos / blocksize);
+		if (!bh) {
+			if (!written)
+				written = -ENOSPC;
 			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);
+		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;
 			}
 		}
-		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;
+		p = (pos % blocksize) + bh->b_data + 24;
+		memcpy_fromfs(p,buf,c);
+		update_vm_cache(inode,pos,p,c);
+
+		pos     += c;
+		buf     += c;
+		written += c;
+		DATA_FRONT(bh)->data_size = ntohl(htonl(DATA_FRONT(bh)->data_size) + c);
+		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+		mark_buffer_uptodate(bh,1);
+		mark_buffer_dirty(bh,0);
+		affs_brelse(bh);
 	}
-	inode->u.affs_i.i_max_ext = 0;		/* invalidate cache */
+	if (pos > inode->i_size)
+		inode->i_size = pos;
+	filp->f_pos = pos;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_dirt  = 1;
 	iput(ino);
+	return written;
 }
 
 void
-affs_truncate_ofs(struct inode *inode)
+affs_truncate(struct inode *inode)
 {
 	struct buffer_head	*bh;
+	struct buffer_head	*ebh;
 	struct inode		*ino;
 	LONG	 first;
 	LONG	 block;
@@ -499,8 +628,10 @@
 	LONG	 ptype, stype;
 	int	 freethis;
 	int	 blocksize;
+	int	 rem;
+	int	 ext;
 
-	pr_debug("AFFS: file_truncate_ofs(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+	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) {
@@ -512,9 +643,25 @@
 		}
 		inode = ino;
 	}
-	blocksize = AFFS_I2BSIZE(inode) - 24;
+	blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
 	first = (inode->i_size + blocksize - 1) / blocksize;
+	if (inode->u.affs_i.i_lastblock < first - 1) {
+		bh = affs_getblock(inode,first - 1);
+		if (!bh) {
+			printk("AFFS: truncate(): Cannot extend file\n");
+			inode->i_size = blocksize * (inode->u.affs_i.i_lastblock + 1);
+		} else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
+			rem = inode->i_size % blocksize;
+			DATA_FRONT(bh)->data_size = ntohl(rem ? rem : blocksize);
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+			mark_buffer_dirty(bh,0);
+		}
+		affs_brelse(bh);
+		iput(ino);
+		return;
+	}
 	ekey  = inode->i_ino;
+	ext   = 0;
 
 	while (ekey) {
 		if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
@@ -538,9 +685,8 @@
 		/* 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);
+			keyp = &AFFS_BLOCK(bh->b_data,inode,block);
+			key  = htonl(*keyp);
 			if (key) {
 				*keyp = 0;
 				affs_free_block(inode->i_sb,key);
@@ -555,6 +701,21 @@
 			((struct file_front *)bh->b_data)->block_count = htonl(first);
 			first = 0;
 			*keyp = 0;
+			if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) && first > 0) {
+				block = htonl(AFFS_BLOCK(bh->b_data,inode,first - 1));
+				if ((ebh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
+					if(!(affs_checksum_block(AFFS_I2BSIZE(inode),ebh->b_data,
+					     &ptype,NULL))) {
+						rem = inode->i_size % blocksize;
+						rem = ntohl(rem ? blocksize : rem);
+						((struct data_front *)ebh->b_data)->data_size = rem;
+						((struct data_front *)ebh->b_data)->next_data = 0;
+						affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+						mark_buffer_dirty(ebh,1);
+					}
+					affs_brelse(ebh);
+				}
+			}
 		} else {
 			first -= AFFS_I2HSIZE(inode);
 		}
@@ -568,19 +729,31 @@
 		}
 		ekey = key;
 	}
-	inode->u.affs_i.i_max_ext = 0;		/* invalidate cache */
+	inode->u.affs_i.i_lastblock = ((inode->i_size + blocksize - 1) / blocksize) - 1;
+	inode->u.affs_i.i_max_ext   = 0;
 	iput(ino);
 }
 
 static void
 affs_release_file(struct inode *inode, struct file *filp)
 {
+	struct affs_zone	*zone;
+
+	pr_debug("AFFS: release_file(ino=%lu)\n",inode->i_ino);
+
 	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--;
+		}
+		if (inode->u.affs_i.i_zone) {
+			lock_super(inode->i_sb);
+			zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+			if (zone->z_ino == inode->i_ino)
+				zone->z_ino = 0;
+			unlock_super(inode->i_sb);
 		}
 	}
 }

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