From c55983415ae3bd360deb04ede20bc482bd2c41b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Mon, 7 Mar 2011 15:13:45 +0100 Subject: [PATCH] cmsfs-fuse: fix read and write errors in text mode Description: cmsfs-fuse: fix read and write errors in text mode. Symptom: Copying a file in text mode fails with read or write errors. Problem: Miscalculation of file size in text mode and off-by-one error in record length check for fixed files. Solution: Correct the calculation of the file size by using the displacement value for the last block of a variable file and by limiting the size of the last record of a fixed file to the actual size. Additionally scan for the correct length of a fixed record in text mode. Problem-ID: 70230 --- cmsfs-fuse/cmsfs-fuse.c | 67 ++++++++++++++++++++++++++--------------------- cmsfs-fuse/dasd.c | 2 +- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/cmsfs-fuse/cmsfs-fuse.c b/cmsfs-fuse/cmsfs-fuse.c index 6c5b0b5..fd87774 100644 --- a/cmsfs-fuse/cmsfs-fuse.c +++ b/cmsfs-fuse/cmsfs-fuse.c @@ -917,6 +917,33 @@ static void set_record_extension(struct file *f, int *record, off_t addr, f->record_scan_state = RSS_DATA_BLOCK_EXT; } +/* 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, + int first) +{ + size_t filesize = f->fst->nr_records * f->fst->record_len; + struct record *rec = &f->rlist[record]; + struct record_ext *rext = rec->ext; + + /* done already includes the complete record length incl. extensions */ + done -= rec->total_len; + /* remove possible linefeeds before comparing with on-disk file size */ + if (f->linefeed && record) + done -= record; + done += rec->first_block_len; + + /* add length of all existing extensions */ + while (rext != NULL) { + done += rext->len; + rext = rext->next; + } + + if (done + first > filesize) + first = filesize - done; + return first; +} + +/* check for file end by record number */ static int end_of_file(struct file *f, int record) { if (record == f->fst->nr_records) @@ -936,6 +963,8 @@ static void walk_fixed_data_block(struct file *f, off_t addr, int *record, if (first) { BUG(first > left); + + first = crop_file_end(f, *record, *total, first); set_record_extension(f, record, addr, first, block); left -= first; if (addr != NULL_BLOCK) @@ -1572,28 +1601,6 @@ static ssize_t get_file_size_fixed(struct fst_entry *fst) return fst->nr_records * fst->record_len; } -static ssize_t get_file_size_variable_slow(struct fst_entry *fst) -{ - struct record *rec; - ssize_t total = 0; - int rc = 0; - struct file *f = create_file_object(fst, &rc); - - if (f == NULL) - return rc; - - rec = get_record(f, f->fst->nr_records - 1); - total = rec->file_start + rec->total_len; - - /* - * Note: need to add header bytes since the record information does - * not contain them but get_file_size_logical will remove them... - */ - total += f->fst->nr_records * VAR_RECORD_HEADER_SIZE; - destroy_file_object(f); - return total; -} - static ssize_t get_file_size_variable(struct fst_entry *fst) { struct var_ptr vptr; @@ -1608,11 +1615,11 @@ static ssize_t get_file_size_variable(struct fst_entry *fst) return rc; if (vptr.next == 0) { /* - * Last block is a null block. Cannot scan that block, - * need to scan the whole file instead... + * Last block is a null block. No more records can + * follow, so the displacement value points to EOF. */ - total = get_file_size_variable_slow(fst); - goto skip; + total = vptr.disp; + goto skip_scan; } ptr = ABS(vptr.next); if (vptr.disp != VAR_RECORD_SPANNED) { @@ -1638,7 +1645,6 @@ skip_scan: */ if (fst->nr_blocks) total += (fst->nr_blocks - 1) * cmsfs.blksize; -skip: return total; } @@ -3896,7 +3902,8 @@ static int do_write(struct file *f, const char *buf, size_t size, off_t offset) return rc; f->ptr_dirty = 0; } else - if (f->fst->levels > 0) { + if (f->fst->levels > 0 && + f->fst->record_format == RECORD_LEN_VARIABLE) { rc = update_last_block_vptr(f, ABS(f->fst->fop), f->fst->levels, &vptr); if (rc < 0) @@ -3962,7 +3969,7 @@ static int cmsfs_write(const char *path, const char *buf, size_t size, return do_write(f, buf, size, offset); /* remove already comitted bytes */ - offset -= f->wcache_used; + offset -= f->wcache_commited; /* write offset must be at the end of the file */ if (offset + f->null_records + f->pad_bytes != f->session_size) @@ -3981,7 +3988,7 @@ static int cmsfs_write(const char *path, const char *buf, size_t size, return -EINVAL; } else { if (f->fst->record_format == RECORD_LEN_FIXED && - f->wcache_commited + scan_len >= f->fst->record_len) { + f->wcache_commited + scan_len > f->fst->record_len) { purge_wcache(f); return -EINVAL; } diff --git a/cmsfs-fuse/dasd.c b/cmsfs-fuse/dasd.c index d79d34d..1b9af9a 100644 --- a/cmsfs-fuse/dasd.c +++ b/cmsfs-fuse/dasd.c @@ -196,7 +196,7 @@ int get_device_info(struct cmsfs *cmsfs) */ fd = open(cmsfs->device, O_RDWR); if (fd < 0) { - if (errno == EROFS) { + if (errno == EROFS || errno == EACCES) { cmsfs->readonly = 1; fd = open(cmsfs->device, O_RDONLY); if (fd < 0) -- 1.7.4