File s390-tools-sles11sp2-cmsfs-fuse_pread_pwrite.patch of Package s390-tools
Description: cmsfs-fuse: Use pread/pwrite if mmap'ing the whole disk fails
Symptom: Trying to mount a disk fails with -ENOMEM.
Problem: On mounting a disk cmsfs-fuse tries to mmap the whole disk.
This can fail for very large disks or if the virtual memory
is limited.
Solution: Add a fall back to pread and pwrite in case the mmap system call
fails.
Problem-ID: 79223
---
cmsfs-fuse/cmsfs-fuse.c | 102 +++++++++++++++++++++++++++++++++++++-----------
1 file changed, 80 insertions(+), 22 deletions(-)
--- a/cmsfs-fuse/cmsfs-fuse.c
+++ b/cmsfs-fuse/cmsfs-fuse.c
@@ -171,6 +171,13 @@ struct file_operations {
static struct file_operations fops_fixed;
static struct file_operations fops_variable;
+struct io_operations {
+ int (*read) (void *buf, size_t size, off_t addr);
+ int (*write) (const void *buf, size_t size, off_t addr);
+};
+
+struct io_operations io_ops;
+
struct write_state {
int block_state;
/* number of free bytes in the current block */
@@ -310,28 +317,65 @@ static inline struct file *get_fobj(stru
return (struct file *) fi->fh;
}
-int _read(void *buf, size_t size, off_t addr)
+static int access_ok(size_t size, off_t addr)
{
if (((addr + (off_t) size - 1) & ~DATA_BLOCK_MASK) >
(addr & ~DATA_BLOCK_MASK))
- DIE("read: crossing blocks addr: %lx size: %ld\n",
- addr, size);
- if (addr < cmsfs.label || addr > cmsfs.size)
- return -EIO;
+ DIE("crossing disk blocks, addr: %x size: %d\n",
+ (int) addr, (int) size);
+
+ if ((unsigned long long) addr < (unsigned long long) cmsfs.label
+ || addr > cmsfs.size)
+ return 0;
+ return 1;
+}
+int read_memory(void *buf, size_t size, off_t addr)
+{
memcpy(buf, cmsfs.map + addr, size);
return 0;
}
-int _write(const void *buf, size_t size, off_t addr)
+int read_syscall(void *buf, size_t size, off_t addr)
{
- if (((addr + (off_t) size - 1) & ~DATA_BLOCK_MASK) >
- (addr & ~DATA_BLOCK_MASK))
- DIE("write: crossing blocks addr: %x size: %d\n",
- (int)addr, (int)size);
- if (addr < cmsfs.label || addr > cmsfs.size)
+ int rc;
+
+ rc = pread(cmsfs.fd, buf, size, addr);
+ if (rc < 0)
+ perror("pread failed");
+ return rc;
+}
+
+int _read(void *buf, size_t size, off_t addr)
+{
+ if (!access_ok(size, addr))
return -EIO;
+ return io_ops.read(buf, size, addr);
+}
+
+int write_syscall(const void *buf, size_t size, off_t addr)
+{
+ char *zbuf;
+ int rc;
+
+ if (buf == NULL) {
+ zbuf = malloc(size);
+ if (zbuf == NULL)
+ return -ENOMEM;
+ memset(zbuf, 0, size);
+ rc = pwrite(cmsfs.fd, zbuf, size, addr);
+ free(zbuf);
+ } else
+ rc = pwrite(cmsfs.fd, buf, size, addr);
+
+ if (rc < 0)
+ perror("pwrite failed");
+ return rc;
+}
+
+int write_memory(const void *buf, size_t size, off_t addr)
+{
if (buf == NULL)
memset(cmsfs.map + addr, 0, size);
else
@@ -339,6 +383,14 @@ int _write(const void *buf, size_t size,
return 0;
}
+int _write(const void *buf, size_t size, off_t addr)
+{
+ if (!access_ok(size, addr))
+ return -EIO;
+
+ return io_ops.write(buf, size, addr);
+}
+
int _zero(off_t addr, size_t size)
{
return _write(NULL, size, addr);
@@ -4459,29 +4511,36 @@ static int cmsfs_process_args(void *data
}
}
-static void map_device(int fd)
+static void init_io(int fd)
{
int prot;
- /* fstat on block device says st_size = 0... */
- lseek(fd, 0, SEEK_SET);
- cmsfs.size = lseek(fd, 0, SEEK_END);
- DEBUG("mmap size: %lu", cmsfs.size);
+ DEBUG("read-only: %d", cmsfs.readonly);
+ cmsfs.fd = fd;
+ cmsfs.size = (off_t) cmsfs.total_blocks * cmsfs.blksize;
+ DEBUG(" mmap size: %lu", cmsfs.size);
- /* map the whole block device for speeding-up access */
+ /* try to map the whole block device for speeding-up access */
if (cmsfs.readonly)
prot = PROT_READ;
else
prot = PROT_READ | PROT_WRITE;
+
cmsfs.map = mmap(NULL, cmsfs.size, prot, MAP_SHARED, fd, 0);
- if (cmsfs.map == MAP_FAILED)
- DIE_PERROR("mmap failed");
- DEBUG(" addr: %p read-only: %d\n", cmsfs.map, cmsfs.readonly);
+ if (cmsfs.map == MAP_FAILED) {
+ DEBUG("\nmmap failed, using pread/write for disk I/O.\n");
+ io_ops.read = &read_syscall;
+ io_ops.write = &write_syscall;
+ } else {
+ DEBUG(" addr: %p\n", cmsfs.map);
+ io_ops.read = &read_memory;
+ io_ops.write = &write_memory;
+ }
}
static void cmsfs_init(int fd)
{
- map_device(fd);
+ init_io(fd);
/* calculate blocksize dependent values */
cmsfs.data_block_mask = cmsfs.blksize - 1;
@@ -4550,7 +4609,6 @@ int main(int argc, char *argv[])
free(fsname);
cmsfs_init(fd);
- cmsfs.fd = fd;
if (cmsfs.readonly)
fuse_opt_add_arg(&args, "-oro");