s390utils/0063-cmsfs-fuse-fix-read-an...

165 lines
5.3 KiB
Diff

From c55983415ae3bd360deb04ede20bc482bd2c41b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
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