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:
You don't need to close and unmap stuff on error if you're just going to exit.
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.)
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;}