21

I'm getting -EINVAL for some reason, and it's not clear to me why. Here's where I open and attempt to mmap the file:

if ((fd = open(argv[1], O_RDWR)) < 0)
{
    fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno));
    return 1;
}

struct stat statbuf;
if (fstat(fd, &statbuf))
{
    fprintf(stderr, "stat filed: %s\n", strerror(errno));
    return 1;
}

char* fbase = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (fbase == MAP_FAILED)
{
    fprintf(stderr, "mmap failed: %s\n", strerror(errno));
    return 1;
}

EDIT: I should add, the error is occurring in the mmap.

jxh
  • 69,070
  • 8
  • 110
  • 193
alexgolec
  • 26,898
  • 33
  • 107
  • 159

3 Answers3

60

Turns out changing the MAP_SHARED to MAP_PRIVATE allows this to succeed.

This reason this was failing is subtle: My code is running inside a VirtualBox VM, and the file I was attempting to mmap was in a shared directory on my host machine. The VirtualBox virtual filesystem apparently doesn't implement mmap with the MAP_SHARED option across the boundary of the hypervisor.

If you'll read jxh's helpful comments on both my question and on his answer, it turns out that this code was working for him because he was likely attempting to mmap a host filesystem file into the host memory.

My observation that switching from MAP_SHARED to MAP_PRIVATE is also consistent with this: since privately mapped memory is invisible to other processes, the virtual filesystem driver will probably have no objection to mapping the memory.

The solution was to move the file I wanted to map into the guest's hard drive and perform manipulation from there.

alexgolec
  • 26,898
  • 33
  • 107
  • 159
  • 4
    This is why I love stackoverflow. – Claudiu May 27 '14 at 22:27
  • Please don't think switching from `MAP_SHARED` to `MAP_PRIVATE` is an actual solution. This isn't even a workaround. Programs that use `MAP_SHARED` to modify the output file in-place will silently fail when using `MAP_PRIVATE`. It may look like it's working, but it is probably not. – Chris Warth Jan 04 '17 at 00:34
16

Your statbuf.st_size is 0. mmap() will fail if the length parameter is 0.

There are 3 listed reasons for EINVAL error mmap():

void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);

...

  • We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary).
  • (since Linux 2.6.12) length was 0.
  • flags contained neither MAP_PRIVATE or MAP_SHARED, or contained both of these values.
jxh
  • 69,070
  • 8
  • 110
  • 193
  • 1
    I feel like a moron, but: it was zero, I've replaced the empty file, and it turns out the issue persists. – alexgolec Aug 24 '13 at 16:18
  • While the length was zero previously, I ensured that the file was nonempty, and the issue is still present. This question is a mess, I'll open a new one with more detail. – alexgolec Aug 24 '13 at 16:26
  • I must be losing my mind. I tried it inside a clean file and it worked, then I tried pasting the exact same code into the file it came from that it failed. – alexgolec Aug 24 '13 at 16:42
  • It turns out that setting `MAP_PRIVATE` instead of `MAP_SHARED` was enough to make it work. – alexgolec Aug 24 '13 at 17:12
  • That doesn't explain anything, because you said the code worked as it was in a clean file. – jxh Aug 24 '13 at 18:11
  • Aha! I finally figured it out! See my updated answer for details, but the long and the short of it that I was attempting to `mmap` a file that lived on my host machine from a VM. – alexgolec Aug 25 '13 at 02:10
0

edit grub to add iomem=relaxed and reboot, make sure cat /proc/cmdline shows entry for iomem=relaxed after boot, re-run your program and check

[root@fedora ~]# cat /proc/cmdline 
BOOT_IMAGE=(hd0,gpt2)/vmlinuz-5.18.19-200.fc36.x86_64 root=/dev/mapper/fedora_fedora-root ro rd.lvm.lv=fedora_fedora/root iomem=relaxed rhgb quiet