2
votes

I want to access an encrypted core storage volume in my program. My plan is to mmap the decrypting block device to be able to jump around in the file system structures with ease and without having to deal with the crypto myself.

While mapping a big file works like a charm, I am getting an EINVAL error on the mmap syscall in the following code:

#include <stddef.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>

int main(int argc, char *argv[])
{
  int fd = open("/dev/disk2", O_RDONLY); // this works fine
  if (fd < 0)
  {
    perror("Could not open file"); return(-1);
  }

  int pagesize = getpagesize(); // page size is 4096 on my system
  void* address = mmap(NULL, pagesize, PROT_READ, MAP_SHARED | MAP_FILE, fd, 0); // try to map first page
  if (address == MAP_FAILED)
  {
    perror("Could not mmap"); // Error complaining about an invalid argument
  }
}

The device has a size of 112 GB and I am compiling with clang mmap.c -O0 -o mmap on Mavericks 10.9.3 for x86_64. My system has 4 GB of RAM and > 10 GB of free hard disk space.

The man-Page mmap(2) only lists the following explanations for an EINVAL error, but these do not seem to apply:

  • MAP_FIXED was specified and the addr argument was not page aligned, or part of the desired address space resides out of the valid address space for a user process.
  • flags does not include either MAP_PRIVATE or MAP_SHARED.
  • The len argument was negative.
  • The offset argument was not page-aligned based on the page size as returned by getpagesize(3).

[...]

  • The flags parameter must specify either MAP_PRIVATE or MAP_SHARED.
  • The size parameter must not be 0.
  • The off parameter must be a multiple of pagesize, as returned by sysconf().

While I have not figured out all the nitty gritty details of the implementation, the comments on this XNU kernel source file explicitly mention being able to map a block device (as long as it's shared): https://www.opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/kern/kern_mman.c

What am I missing?

1
Did you check in the manual how errors are communicated?Kerrek SB
Yes, in case of an error the return value is MAP_FAILED and errno is set to the appropriate error constant. So checking for either should be fine. In this case I am always getting EINVAL (= Invalid argument).struct54
No, "checking either" is not fine at all. If MAP_FAILED is returned, then errno contains information about the error. Typically errno is not touched if there is no error.Kerrek SB
Well yes, I stand corrected, you are right. The proper way is to check for the sentinel. Unfortunately that doesn't change anything as I was getting MAP_FAILED as return value all along. I updated source code to reflect that. Sorry about the omission.struct54
Did you check the value of pagesize?Kerrek SB

1 Answers

1
votes

Your problem might be using MAP_FILE since this indicates a regular file rather than a block device.

If that doesn't work, trying calling fstat() after you open the file and check the file's length. When I use stat -x to get the information about the block devices on my system, the file sizes are reported as zero (ls -l reported the sizes as "1,"). A zero-length file might also prevent you from being able to create a mapping.