system call to map memory to a file descriptor (inverse mmap)?
Asked Answered
S

4

6

I want to be able to map memory to a file descriptor so I can use some existing functions that need a file descriptor. Here's essentially what I'm looking for:

void do_operation1(int fd);

char data[DATA_MAX] = { /* embedded binary data */ };
int fd = addr_to_fd(data, DATA_MAX);

do_operation1(fd);
/* ... operate on fd ... */

What system call, or calls, can I use to accomplish this?

Succentor answered 12/5, 2010 at 19:18 Comment(0)
T
3

You should Check out shm_open().

Threnode answered 1/10, 2010 at 5:24 Comment(0)
R
7

Some implementations have fmemopen(). (Then of course you have to call fileno()).

If yours doesn't, you can build it yourself with fork() and pipe().

Romaineromains answered 12/5, 2010 at 19:23 Comment(4)
It turns out that fileno() fails on both Linux/FreeBSD (and I presume others) when the FILE* comes from fmemopen or something equivalent. Without a way to get the file descriptor, I'm not sure we have a solution.Succentor
fmemopen may also be implemented using funopen on BSD. One implementation is available at redmine.openinfosecfoundation.org/attachments/105/…Succentor
fork() is rather heavy-weight for this purpose. I’d suggest using a thread instead. Would be even better to use select() to multiplex between reading and writing from the pipe, but I guess that isn’t an option here.Minium
That won't work since fmemopen() just creates a FILE* backed by given memory buffer. No file descriptor created.Tableland
T
3

You should Check out shm_open().

Threnode answered 1/10, 2010 at 5:24 Comment(0)
T
2

Sure, just open(argv[0], ...), scan through the file to find where your binary data starts, lseek() there and done. That file won't have the length of your binary data of course.

Tableland answered 8/11, 2011 at 17:24 Comment(0)
E
1

You cannot map "some existing memory buffer" to a file descriptor. As said in a comment above, the fmemopen() function associates a memory buffer with a "FILE *" stream pointer which can be manipulated with the libc-provided streams functions. No file descriptor is allocated: the "FILE *" stream is a high-level abstraction and is NOT compatible with a file descriptor which is a low-level handle.

Instead, you may want to allocate a new shared memory buffer and map it to a file descriptor. This is widely used and known as a "memory-mapped file" in the Linux jargon.

You can use a file descriptor obtained with open() that refers to a file or a file descriptor obtained with shm_open() that refers to a shared memory object. Any file descriptor handle will do the job. You can then invoke mmap() to map the file descriptor to a shared memory buffer.

Note: mmap() will fail if the file descriptor refers to a non-regular file such as a pipe, a socket or a character device file (e.g., /dev/ttys001). Due to this, you cannot usually create a memory-mapped file for the STDIN, STDOUT or STDERR file descriptors.

You can manipulate the memory buffer in an array-like fashion and modifications to the memory-mapped file are committed to disk. The opposite is also true, with any modifications made to the file (e.g., with a write() syscall) committed to memory as well.


The following snippet opens a file of your choice and maps it into memory. It will print the first 256 characters and replace them with "*" in the original file. Compile it with cc mmap_test.c -o mmap_test on a POSIX-compliant system.

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

int main(int ac, char *av[])
{
    int pagesize, fd;
    unsigned char *data;

    if ( ac < 2 ) {
        printf("Usage: %s <filepath>\n", av[0]);
        return 1;
    }

    pagesize = getpagesize();

    if ( (fd = open(av[1], O_RDWR)) == -1 ) {
        perror("Error: cannot open file for reading");
        return 1;
    }

    if ( (data = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED ) {
        perror("Error: cannot create memory-mapped file");
        return 1;
    }

    write(1, data, 256);
    memset(data, '*', 256);

    return 0;
}
Essonite answered 16/11, 2021 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.