File s390-tools-sles11sp2-cmsfs-fuse_cont_write.patch of Package s390-tools
Description: cmsfs-fuse: Multiple write requests for fixed record format files
Symptom: Writing to a fixed record format file may fail.
Problem: If multiple write requests are performed on a fixed record format
file cmsfs-fuse looks at the record information to detect the
current write position. That can be wrong if a write request on a
fixed record format file is not issued on record boundaries.
Solution: Maintain a write pointer as long as the file is open and use this
write pointer for contiguous writes to a file.
Problem-ID: 75939
---
cmsfs-fuse/cmsfs-fuse.c | 105 ++++++++++++++++++++++++++++++------------------
1 file changed, 67 insertions(+), 38 deletions(-)
--- a/cmsfs-fuse/cmsfs-fuse.c
+++ b/cmsfs-fuse/cmsfs-fuse.c
@@ -116,6 +116,7 @@ static char CODEPAGE_LINUX[] = "ISO-8859
#define RWS_RECORD_INCOMPLETE 0x4
#define RWS_RECORD_COMPLETE 0x8
+#define BWS_BLOCK_NOT_INIT 0x0
#define BWS_BLOCK_NEW 0x1
#define BWS_BLOCK_USED 0x2
@@ -172,6 +173,15 @@ struct file_operations {
static struct file_operations fops_fixed;
static struct file_operations fops_variable;
+struct write_state {
+ int block_state;
+ /* number of free bytes in the current block */
+ int block_free;
+ int var_record_state;
+ /* remaining record bytes for a write request */
+ int var_record_len;
+};
+
/*
* File object for operations that follow open
*/
@@ -204,8 +214,8 @@ struct file {
int pad_bytes;
/* old levels value, needed to rewrite pointers */
int old_levels;
- /* record write state for variable headers */
- struct var_record_state *vrstate;
+ /* state information needed for a write request */
+ struct write_state *wstate;
/* path name for open and unlink */
char path[MAX_FNAME + 1];
/* counter for pseudo null length records */
@@ -232,12 +242,6 @@ struct file {
int unlinked;
};
-struct var_record_state {
- int rlen;
- int record_state;
- int block_state;
-};
-
struct xattr {
char name[20];
size_t size;
@@ -3388,6 +3392,12 @@ static int examine_last_block(struct fil
if (!f->fst->nr_blocks || !f->fst->nr_records)
return 0;
+ /*
+ * For subsequent writes we know how much is left on the current block.
+ * */
+ if (f->wstate->block_state != BWS_BLOCK_NOT_INIT)
+ return f->wstate->block_free;
+
last_bnr = f->fst->nr_blocks - 1;
rec = &f->rlist[f->fst->nr_records - 1];
@@ -3454,6 +3464,16 @@ static off_t disk_end(struct file *f)
if (!f->fst->nr_records)
return 0;
+ /*
+ * Only the first write on a newly opened file should set the write_ptr
+ * according to what is on the disk. Subsequent writes should use the
+ * actual write_ptr. This avoids the problem for fixed records that
+ * for a record that is not yet completely written the calculated
+ * write_ptr would be wrong.
+ */
+ if (f->wstate->block_state != BWS_BLOCK_NOT_INIT)
+ return f->write_ptr;
+
rec = &f->rlist[f->fst->nr_records - 1];
if (rec->ext == NULL) {
if (rec->disk_start == NULL_BLOCK)
@@ -3481,7 +3501,7 @@ static off_t disk_end(struct file *f)
void store_displacement(struct file *f, int disp)
{
f->blist[f->fst->nr_blocks - 1].disp = disp;
- f->vrstate->block_state = BWS_BLOCK_USED;
+ f->wstate->block_state = BWS_BLOCK_USED;
}
/*
@@ -3537,13 +3557,12 @@ static int write_prepare_block(struct fi
}
if (new_block) {
- if (f->fst->record_format == RECORD_LEN_VARIABLE)
- f->vrstate->block_state = BWS_BLOCK_NEW;
+ f->wstate->block_state = BWS_BLOCK_NEW;
f->blist[f->fst->nr_blocks].disk_addr = f->write_ptr;
if (!f->write_ptr && f->fst->record_format == RECORD_LEN_FIXED)
f->nr_null_blocks++;
-
+ f->wstate->block_free = cmsfs.blksize;
f->fst->nr_blocks++;
}
return len;
@@ -3557,23 +3576,24 @@ static int write_var_header(struct file
u8 half_vheader;
int rc;
- if (f->vrstate->record_state == RWS_HEADER_COMPLETE ||
- f->vrstate->record_state == RWS_RECORD_INCOMPLETE)
+ if (f->wstate->var_record_state == RWS_HEADER_COMPLETE ||
+ f->wstate->var_record_state == RWS_RECORD_INCOMPLETE)
return 0;
- if (f->vrstate->record_state == RWS_HEADER_STARTED) {
+ if (f->wstate->var_record_state == RWS_HEADER_STARTED) {
/* write secord header byte */
half_vheader = vheader & 0xff;
rc = _write(&half_vheader, 1, f->write_ptr);
if (rc < 0)
return rc;
f->write_ptr++;
- f->vrstate->record_state = RWS_HEADER_COMPLETE;
+ f->wstate->block_free--;
+ f->wstate->var_record_state = RWS_HEADER_COMPLETE;
return 1;
}
/* block cannot be spanned if a header starts on it */
- if (f->vrstate->block_state == BWS_BLOCK_NEW)
+ if (f->wstate->block_state == BWS_BLOCK_NEW)
store_displacement(f, f->write_ptr & DATA_BLOCK_MASK);
if (len >= 2) {
@@ -3581,8 +3601,9 @@ static int write_var_header(struct file
if (rc < 0)
return rc;
f->write_ptr += 2;
- f->vrstate->rlen = vheader;
- f->vrstate->record_state = RWS_HEADER_COMPLETE;
+ f->wstate->block_free -= 2;
+ f->wstate->var_record_len = vheader;
+ f->wstate->var_record_state = RWS_HEADER_COMPLETE;
return 2;
} else {
/* len = 1, write first header byte */
@@ -3591,8 +3612,9 @@ static int write_var_header(struct file
if (rc < 0)
return rc;
f->write_ptr++;
- f->vrstate->rlen = vheader;
- f->vrstate->record_state = RWS_HEADER_STARTED;
+ f->wstate->block_free--;
+ f->wstate->var_record_len = vheader;
+ f->wstate->var_record_state = RWS_HEADER_STARTED;
return 1;
}
}
@@ -3611,6 +3633,7 @@ static int extend_block_fixed(struct fil
if (rc < 0)
return rc;
f->write_ptr += len;
+ f->wstate->block_free -= len;
}
return len;
}
@@ -3638,15 +3661,16 @@ static int extend_block_variable(struct
if (len < rlen)
rlen = len;
/* remaining record data less than block len */
- if (f->vrstate->rlen < rlen)
- rlen = f->vrstate->rlen;
+ if (f->wstate->var_record_len < rlen)
+ rlen = f->wstate->var_record_len;
rc = _write(buf, rlen, f->write_ptr);
if (rc < 0)
return rc;
f->write_ptr += rlen;
+ f->wstate->block_free -= rlen;
- if (f->vrstate->block_state == BWS_BLOCK_NEW) {
+ if (f->wstate->block_state == BWS_BLOCK_NEW) {
/*
* If the second byte of a split header was written
* (blocksize - 1) is enough to make the block spanned.
@@ -3663,11 +3687,11 @@ static int extend_block_variable(struct
copied += rlen;
size -= rlen;
len -= rlen;
- f->vrstate->rlen -= rlen;
+ f->wstate->var_record_len -= rlen;
- BUG(f->vrstate->rlen < 0);
- if (!f->vrstate->rlen) {
- f->vrstate->record_state = RWS_RECORD_COMPLETE;
+ BUG(f->wstate->var_record_len < 0);
+ if (!f->wstate->var_record_len) {
+ f->wstate->var_record_state = RWS_RECORD_COMPLETE;
/* reset rlen for the next record */
rlen = get_record_len(f, size);
}
@@ -3678,7 +3702,7 @@ static int extend_block_variable(struct
}
/* record is not yet finished */
- f->vrstate->record_state = RWS_RECORD_INCOMPLETE;
+ f->wstate->var_record_state = RWS_RECORD_INCOMPLETE;
return copied;
}
@@ -3923,6 +3947,12 @@ static int update_last_block_vptr(struct
return update_last_block_vptr(f, ABS(vptr->next), level, vptr);
}
+static void reset_write_state(struct file *f)
+{
+ f->wstate->var_record_state = RWS_RECORD_COMPLETE;
+ f->wstate->var_record_len = 0;
+}
+
/*
* Append records at current file end. If buf is NULL write zero bytes.
*/
@@ -3950,8 +3980,7 @@ static int write_append(struct file *f,
if (f->fst->nr_blocks + nblocks > 1)
reserve_meta_blocks(f, f->fst->nr_blocks, nblocks, 1);
- if (f->fst->record_format == RECORD_LEN_VARIABLE)
- f->vrstate->record_state = RWS_RECORD_COMPLETE;
+ reset_write_state(f);
/* first use existing last block */
if (last) {
@@ -4286,10 +4315,10 @@ static struct file *create_file_object(s
else
f->ptr_per_block = cmsfs.var_ptrs_per_block;
- f->vrstate = malloc(sizeof(*f->vrstate));
- if (f->vrstate == NULL)
+ f->wstate = malloc(sizeof(*f->wstate));
+ if (f->wstate == NULL)
goto oom_fst;
- memset(f->vrstate, 0, sizeof(*f->vrstate));
+ memset(f->wstate, 0, sizeof(*f->wstate));
/*
* Prevent calloc for zero records since it returns a pointer != NULL
@@ -4300,7 +4329,7 @@ static struct file *create_file_object(s
f->rlist = calloc(f->fst->nr_records, sizeof(struct record));
if (f->rlist == NULL)
- goto oom_vrstate;
+ goto oom_wstate;
f->blist = calloc(f->fst->nr_blocks, sizeof(struct block));
if (f->blist == NULL)
@@ -4316,8 +4345,8 @@ error:
*rc = -ENOMEM;
oom_rlist:
free(f->rlist);
-oom_vrstate:
- free(f->vrstate);
+oom_wstate:
+ free(f->wstate);
oom_fst:
free(f->fst);
oom_f:
@@ -4333,7 +4362,7 @@ static void destroy_file_object(struct f
int i;
free(f->wcache);
- free(f->vrstate);
+ free(f->wstate);
for (i = 0; i < f->fst->nr_records; i++) {
rec = &f->rlist[i];