165 lines
5.3 KiB
Diff
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
|
|
|