Descripton: cmsfs-fuse: Fix file size calculation for files larger than 2 GB
Symptom:    It is not possible to create files larger than 2 GB.
Problem:    The file size calculation overflows resulting in a negative value.
Solution:   Cast to a longer type before calculating the file size.
Problem-ID: 75609
---
 cmsfs-fuse/amap.c       |   11 +++---
 cmsfs-fuse/cmsfs-fuse.c |   77 +++++++++++++++++++++++++-----------------------
 cmsfs-fuse/cmsfs-fuse.h |    6 +--
 cmsfs-fuse/edf.h        |    2 -
 4 files changed, 51 insertions(+), 45 deletions(-)

--- a/cmsfs-fuse/amap.c
+++ b/cmsfs-fuse/amap.c
@@ -49,7 +49,7 @@ static off_t get_amap_addr(int level, of
 		return cmsfs.amap;
 
 	if (level--) {
-		ptr = get_fixed_pointer(ptr + block * PTR_SIZE);
+		ptr = get_fixed_pointer(ptr + (off_t ) block * PTR_SIZE);
 		if (!ptr)
 			DIE("amap invalid ptr at addr: %lx\n",
 				ptr + block * PTR_SIZE);
@@ -69,7 +69,7 @@ static void amap_block_set(off_t addr)
 	u8 entry;
 
 	if (block > 0)
-		addr -= block * BYTES_PER_BLOCK;
+		addr -= (off_t) block * BYTES_PER_BLOCK;
 
 	addr >>= BITS_PER_DATA_BLOCK;
 	byte = addr / 8;
@@ -97,7 +97,7 @@ static void amap_block_clear(off_t addr)
 	u8 entry;
 
 	if (block > 0)
-		addr -= block * BYTES_PER_BLOCK;
+		addr -= (off_t) block * BYTES_PER_BLOCK;
 
 	addr >>= BITS_PER_DATA_BLOCK;
 	byte = addr / 8;
@@ -163,8 +163,9 @@ static off_t __get_free_block(int level,
 			bit = find_first_empty_bit(entry);
 
 			/* bit -> addr */
-			addr = (i * cmsfs.blksize * 8 + cmsfs.blksize * bit)
-				+ (block_nr * BYTES_PER_BLOCK);
+			addr = ((off_t) cmsfs.blksize * i * 8 +
+				(off_t) cmsfs.blksize * bit) +
+				(off_t) block_nr * BYTES_PER_BLOCK;
 			amap_block_set(addr);
 			return addr;
 		}
--- a/cmsfs-fuse/cmsfs-fuse.c
+++ b/cmsfs-fuse/cmsfs-fuse.c
@@ -162,7 +162,7 @@ struct file;
 
 struct file_operations {
 	int (*cache_data) (struct file *f, off_t addr, int level, int *block,
-			    unsigned int *disp, int *record, size_t *total);
+			    unsigned int *disp, int *record, off_t *total);
 	int (*write_data) (struct file *f, const char *buf, int len, size_t size,
 			   int rlen);
 	int (*delete_pointers) (struct file *f, int level, off_t addr);
@@ -197,7 +197,7 @@ struct file {
 	/* disk address of next byte to write */
 	off_t		write_ptr;
 	/* the filesize while the file is opened */
-	ssize_t		session_size;
+	off_t		session_size;
 	/* number of null blocks for fixed files */
 	int		nr_null_blocks;
 	/* number of written padding bytes for a fixed file */
@@ -312,7 +312,7 @@ int _read(void *buf, size_t size, off_t
 	    (addr & ~DATA_BLOCK_MASK))
 		DIE("read: crossing blocks addr: %lx  size: %ld\n",
 			addr, size);
-	if ((addr < cmsfs.fdir) || ((size_t) addr > cmsfs.size))
+	if (addr < cmsfs.fdir || addr > cmsfs.size)
 		return -EIO;
 
 	memcpy(buf, cmsfs.map + addr, size);
@@ -326,7 +326,7 @@ int _write(const void *buf, size_t size,
 		DIE("write: crossing blocks addr: %x  size: %d\n",
 			(int)addr, (int)size);
 
-	if ((addr < (2 * cmsfs.blksize)) || ((size_t) addr > cmsfs.size))
+	if ((addr < (2 * cmsfs.blksize)) || (addr > cmsfs.size))
 		return -EIO;
 
 	if (buf == NULL)
@@ -745,7 +745,7 @@ static int edf_name_valid(const char *na
 /*
  * Summarize the number of bytes used in the last data block.
  */
-static int walk_last_var_data_block(off_t addr, ssize_t *total)
+static int walk_last_var_data_block(off_t addr, off_t *total)
 {
 	ssize_t left = cmsfs.blksize;
 	u16 len;
@@ -837,7 +837,7 @@ static void set_record_len(struct file *
 }
 
 static void set_record(struct file *f, int *record, off_t addr, int len,
-		       size_t *total, int block)
+		       off_t *total, int block)
 {
 	struct record *r = &f->rlist[*record];
 
@@ -917,10 +917,10 @@ static void set_record_extension(struct
 }
 
 /* check for file end via byte count and return count of bytes left */
-static size_t crop_file_end(struct file *f, int record, size_t done,
+static int crop_file_end(struct file *f, int record, off_t done,
 			    int first)
 {
-	size_t filesize = f->fst->nr_records * f->fst->record_len;
+	off_t filesize = (off_t) f->fst->nr_records * (off_t) f->fst->record_len;
 	struct record *rec = &f->rlist[record];
 	struct record_ext *rext = rec->ext;
 
@@ -951,9 +951,9 @@ static int end_of_file(struct file *f, i
 }
 
 static void walk_fixed_data_block(struct file *f, off_t addr, int *record,
-				  size_t *total, int block, int disp)
+				  off_t *total, int block, int disp)
 {
-	off_t offset = block * cmsfs.blksize + disp;
+	off_t offset = (off_t ) block * cmsfs.blksize + disp;
 	int rlen = (f->fst->record_len > cmsfs.blksize) ?
 		cmsfs.blksize : f->fst->record_len;
 	int first = (offset % f->fst->record_len) ?
@@ -1024,7 +1024,7 @@ out:
 }
 
 static int walk_var_data_block(struct file *f, off_t addr, unsigned int disp,
-				int *record, size_t *total, int block, int skip)
+			       int *record, off_t *total, int block, int skip)
 {
 	ssize_t left = cmsfs.blksize - skip;
 	int last, rc;
@@ -1159,7 +1159,7 @@ static int walk_var_data_block(struct fi
 }
 
 static void cache_fixed_data_block(struct file *f, off_t addr, int *block,
-				   int *record, size_t *total, int disp)
+				   int *record, off_t *total, int disp)
 {
 	/*
 	 * Cannot distinguish null block pointers from not existing pointers,
@@ -1180,7 +1180,7 @@ static void cache_fixed_data_block(struc
  * data block respecting the sequence of the data.
  */
 static int cache_file_fixed(struct file *f, off_t addr, int level, int *block,
-			     unsigned int *disp, int *record, size_t *total)
+			     unsigned int *disp, int *record, off_t *total)
 {
 	int left = f->ptr_per_block;
 	off_t ptr;
@@ -1203,7 +1203,7 @@ static int cache_file_fixed(struct file
 }
 
 static int cache_variable_data_block(struct file *f, off_t addr, int *block,
-				      int *record, int disp, size_t *total, int skip)
+				      int *record, int disp, off_t *total, int skip)
 {
 	int rc;
 
@@ -1234,7 +1234,7 @@ static int cache_variable_data_block(str
  */
 static int cache_file_variable(struct file *f, off_t addr, int level,
 				int *block, unsigned int *disp,
-				int *record, size_t *total)
+				int *record, off_t *total)
 {
 	int nr, left = f->ptr_per_block;
 	off_t ptr;
@@ -1570,7 +1570,7 @@ static int cache_file(struct file *f)
 {
 	int block = 0, record = -1;
 	unsigned int disp = 0;
-	size_t total = 0;
+	off_t total = 0;
 
 	return f->fops->cache_data(f, ABS(f->fst->fop), f->fst->levels,
 				   &block, &disp, &record, &total);
@@ -1588,22 +1588,23 @@ static void workaround_nr_blocks(struct
 
 	if (f->fst->record_format == RECORD_LEN_VARIABLE)
 		return;
-	nr = f->fst->nr_records * f->fst->record_len / cmsfs.blksize;
-	if (f->fst->nr_records * f->fst->record_len % cmsfs.blksize)
+	nr = (off_t) f->fst->nr_records * (off_t) f->fst->record_len
+		/ cmsfs.blksize;
+	if ((off_t) f->fst->nr_records * (off_t) f->fst->record_len % cmsfs.blksize)
 		nr++;
 	f->nr_null_blocks = nr - f->fst->nr_blocks;
 	f->fst->nr_blocks = nr;
 }
 
-static ssize_t get_file_size_fixed(struct fst_entry *fst)
+static off_t get_file_size_fixed(struct fst_entry *fst)
 {
-	return fst->nr_records * fst->record_len;
+	return (off_t) fst->nr_records * (off_t) fst->record_len;
 }
 
-static ssize_t get_file_size_variable(struct fst_entry *fst)
+static off_t get_file_size_variable(struct fst_entry *fst)
 {
 	struct var_ptr vptr;
-	ssize_t total = 0;
+	off_t total = 0;
 	off_t ptr;
 	int rc;
 
@@ -1643,7 +1644,7 @@ skip_scan:
 	 * also null blocks.
 	 */
 	if (fst->nr_blocks)
-		total += (fst->nr_blocks - 1) * cmsfs.blksize;
+		total += ((off_t) fst->nr_blocks - 1) * cmsfs.blksize;
 	return total;
 }
 
@@ -1651,7 +1652,7 @@ skip_scan:
  * Return the file size as it is on the disk. Includes headers for
  * variable records.
  */
-static ssize_t get_file_size(struct fst_entry *fst)
+static off_t get_file_size(struct fst_entry *fst)
 {
 	if (fst->record_format == RECORD_LEN_FIXED)
 		return get_file_size_fixed(fst);
@@ -1660,9 +1661,9 @@ static ssize_t get_file_size(struct fst_
 	return 0;
 }
 
-static ssize_t get_file_size_logical(struct fst_entry *fst)
+static off_t get_file_size_logical(struct fst_entry *fst)
 {
-	ssize_t total;
+	off_t total;
 
 	if (fst->nr_records == 0)
 		return 0;
@@ -1674,7 +1675,7 @@ static ssize_t get_file_size_logical(str
 
 	/* subtract the record headers */
 	if (fst->record_format == RECORD_LEN_VARIABLE)
-		total -= fst->nr_records * VAR_RECORD_HEADER_SIZE;
+		total -= (off_t) fst->nr_records * VAR_RECORD_HEADER_SIZE;
 
 	if (linefeed_mode_enabled(fst))
 		total += fst->nr_records;
@@ -1707,7 +1708,7 @@ static int cmsfs_getattr(const char *pat
 		stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime;
 
 		/* size */
-		stbuf->st_size = fst.record_len * fst.nr_records;
+		stbuf->st_size = (off_t) fst.record_len * (off_t) fst.nr_records;
 		stbuf->st_blocks = max(stbuf->st_size, cmsfs.blksize);
 		stbuf->st_blocks = ((stbuf->st_blocks + cmsfs.data_block_mask) &
 			~cmsfs.data_block_mask) >> 9;
@@ -1730,7 +1731,7 @@ static int cmsfs_getattr(const char *pat
 		 * Include potential sparse blocks for variable files which
 		 * are included in nr_blocks to avoid scanning the whole file.
 		 */
-		stbuf->st_blocks = fst.nr_blocks * cmsfs.nr_blocks_512;
+		stbuf->st_blocks = (off_t) fst.nr_blocks * cmsfs.nr_blocks_512;
 	}
 	return 0;
 }
@@ -2598,16 +2599,21 @@ static int cmsfs_statfs(const char *path
 {
 	unsigned int inode_size = cmsfs.blksize + sizeof(struct fst_entry);
 	unsigned int free_blocks = cmsfs.total_blocks - cmsfs.used_blocks;
+	unsigned long long tmp;
 
 	(void) path;
 
 	buf->f_bsize = buf->f_frsize = cmsfs.blksize;
 	buf->f_blocks =	cmsfs.total_blocks;
 	buf->f_bfree =  buf->f_bavail = free_blocks;
+
 	/* number of possible inodes */
-	buf->f_files = cmsfs.total_blocks * cmsfs.blksize / inode_size;
+	tmp = (unsigned long long) cmsfs.total_blocks *
+		cmsfs.blksize / inode_size;
+	buf->f_files = (long) tmp;
 
-	buf->f_ffree = free_blocks * cmsfs.blksize / inode_size;
+	tmp = (unsigned long long) free_blocks * cmsfs.blksize / inode_size;
+	buf->f_ffree = (long) tmp;
 	buf->f_namemax = MAX_FNAME - 1;
 	return 0;
 }
@@ -3163,9 +3169,8 @@ static void update_fst(struct file *f, o
 static int cmsfs_truncate(const char *path, off_t size)
 {
 	struct fst_entry fst;
-	off_t fst_addr;
+	off_t fst_addr, len;
 	struct file *f;
-	ssize_t len;
 	int rc = 0;
 
 	if (cmsfs.readonly)
@@ -3715,7 +3720,7 @@ static void delete_record(struct file *f
 static int update_records(struct file *f, int nrecords)
 {
 	int i, rc, rnr, bnr = 0, skip = 0;
-	size_t total = 0;
+	off_t total = 0;
 
 	rnr = f->fst->nr_records - 1;
 	if (rnr >= 0) {
@@ -3812,7 +3817,7 @@ static int new_blocks(struct file *f, si
 		return 0;
 
 	if (f->fst->record_format == RECORD_LEN_VARIABLE)
-		tmp += nrecords * VAR_RECORD_HEADER_SIZE;
+		tmp += (double) nrecords * VAR_RECORD_HEADER_SIZE;
 
 	tmp -= last;
 	if (tmp <= 0)
@@ -3981,7 +3986,7 @@ out:
 
 static int do_write(struct file *f, const char *buf, size_t size, off_t offset)
 {
-	ssize_t len, copied = 0;
+	off_t len, copied = 0;
 	struct var_ptr vptr;
 	int rc;
 
--- a/cmsfs-fuse/cmsfs-fuse.h
+++ b/cmsfs-fuse/cmsfs-fuse.h
@@ -18,7 +18,7 @@
 extern struct cmsfs cmsfs;
 
 /* conversion between absolute and relative addresses */
-#define ABS(x)			((x - 1) * cmsfs.blksize)
+#define ABS(x)			((off_t) (x - 1) * cmsfs.blksize)
 #define REL(x)			((x / cmsfs.blksize) + 1)
 
 struct fcache_entry {
@@ -45,7 +45,7 @@ struct cmsfs {
 	/* start of mmap of the whole block device */
 	char		*map;
 	/* size of the disk */
-	size_t		size;
+	off_t		size;
 	/* formatted blocksize */
 	int		blksize;
 	/* number of 512 byte blocks per block */
@@ -86,7 +86,7 @@ struct cmsfs {
 	int		bits_per_data_block;
 	int		bits_per_ptr_block;
 	int		data_block_mask;
-	int		amap_bytes_per_block;
+	off_t		amap_bytes_per_block;
 
 	/* file cache */
 	struct		fcache_entry *fcache;
--- a/cmsfs-fuse/edf.h
+++ b/cmsfs-fuse/edf.h
@@ -27,7 +27,7 @@ struct fst_entry {
 	int		record_len;
 	char		res3[4];
 
-	int		fop;
+	unsigned int	fop;
 	/* number of data blocks (not incl. pointer blocks) */
 	int		nr_blocks;
 	int		nr_records;
