Why does mmap() fail with permission denied for the destination file of a file copy program? Why does mmap() fail with permission denied for the destination file of a file copy program? linux linux

Why does mmap() fail with permission denied for the destination file of a file copy program?


From the mmap() man page:

EACCES
A file descriptor refers to a non-regular file. Or MAP_PRIVATE was requested, but fd is not open for reading. Or MAP_SHARED was requested and PROT_WRITE is set, but fd is not open in read/write (O_RDWR) mode. Or PROT_WRITE is set, but the file is append-only.

You are opening your destination file with O_WRONLY. Use O_RDWR instead.

Also, you should use memcpy to copy the memory rather than using your own loop:

memcpy(dest, orig, info.st_size);

Your loop has an off by 1 bug.


This works for me. Note that I had to open the destination O_RDWR. I suspect the kernel attempts to map whole pages from the file into memory (reading it) because you're updating it a byte or word at a time, and that might not change the whole page.

A couple of other points:

  1. You don't need to close and unmap stuff on error if you're just going to exit.

  2. Use memcpy and don't write your own byte-copying loop. Memcpy will be a lot better optimised in general. (Though it's not always the absolute best.)

  3. You might want to read the source code to FreeBSD's "cp" utility. Take a look here and search for the use of mmap. http://svnweb.freebsd.org/base/stable/9/bin/cp/utils.c?revision=225736&view=markup


#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <sys/mman.h>#include <string.h>#include <sys/stat.h>int main(int argc, char *argv[]){        int s, d;        struct stat st;        void *sp, *dp;        s = open(argv[1], O_RDONLY);        if (s == -1) {                perror("open source");                exit(1);        }        d = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);        if (d == -1) {                perror("open destintation");                exit(1);        }        if (fstat(s, &st)) {                perror("stat source");                exit(1);        }        if (ftruncate(d, st.st_size)) {                perror("truncate destination");                exit(1);        }        sp = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, s, 0);        if (sp == MAP_FAILED) {                perror("map source");                exit(1);        }        dp = mmap(NULL, st.st_size, PROT_WRITE | PROT_READ, MAP_SHARED, d, 0);        if (dp == MAP_FAILED) {                perror("map destintation");                exit(1);        }        memcpy(dp, sp, st.st_size);        return 0;}


Original File: O_RDONLY open, MAP_PRIVATE mmap

destination file: O_WRONLY open, MAP_SHARED mmap

You need to open with O_RDWR flag for using MAP_SHARED.

Don't you actually need to do MAP_FILE | MAP_SHARED ?