只写映射一个以O_WRONLY方式打开的文件是否可行?

10

mmap()能够创建一个只写映射的O_WRONLY打开的文件吗?我问这个问题是因为在Linux 4.0.4 x86-64系统上,以下操作失败了(strace日志):

mkdir("test", 0700)          = 0
open("test/foo", O_WRONLY|O_CREAT, 0666) = 3
ftruncate(3, 11)                        = 0
mmap(NULL, 11, PROT_WRITE, MAP_SHARED, 3, 0) = -1 EACCES (Permission denied)

errno 等于 EACCESS

将打开标志 O_WRONLY 替换为 O_RDWR 可以成功映射。

Linux 的 mmap 手册将错误码描述为:

   EACCES A  file descriptor refers to a non-regular file.  Or a file map‐
          ping 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.
因此,第二句话记录了这种行为。
但是背后的原因是什么呢?
POSIX是否允许?
这是内核还是库的限制?(快速查看,我在Linux/mm/mmap.c中没有找到任何明显的东西)

我认为将文件映射为O_WRONLY甚至不合逻辑。毕竟,你怎么可能向只写页面写入内容呢?操作系统首先必须读取完整页的文件内容(或者如果您新创建或扩展了文件,则代表该文件的零页),然后才能将第一个字节写入其中。因此,在没有读取的情况下,就无法进行写入(至少在内存映射中是这样)。如果禁止读取,则必定会失败。 - Damon
2个回答

7

IEEE Std 1003.1, 2004 Edition (POSIX.1 2004)似乎禁止此操作。

实现可以允许除 prot 指定内容之外的访问;然而,如果支持内存保护选项,则实现不应允许在未设置 PROT_WRITE 或仅设置了 PROT_NONE 的情况下写入成功,或不应允许任何访问。该实现应至少支持以下值:PROT_NONEPROT_READPROT_WRITE 和按位包含 PROT_READPROT_WRITE 的 OR。如果不支持内存保护选项,则任何与指定保护冲突的访问结果都是未定义的。文件描述符 fildes 必须已通过读取权限打开,无论指定了哪些保护选项。如果指定了 PROT_WRITE,则应用程序必须确保已使用写入权限打开了文件描述符 fildes,除非在下面的 flags 参数中指定了 MAP_PRIVATE

(重点标注已添加)

另外,在 x86 上,无法拥有只写内存,这是页表条目的限制。页面可以标记为只读或可读可写,并且可以独立地标记为可执行或不可执行,但不能标记为只写。此外,mprotect() 的 man 页说:

PROT_EXEC 是否具有与 PROT_READ 不同的效果取决于架构和内核版本。在某些硬件架构(例如 i386)上,PROT_WRITE 暗示了 PROT_READ

考虑到以上情况,你已经打开了没有读权限的文件描述符,但是 mmap() 会通过给您 PROT_READ 权限来绕过 O_WRONLY。相反,它将以 EACCESS 的方式拒绝访问。


6

我认为x86硬件不支持只写页面,因此写访问意味着读取。但这似乎是一个更普遍的要求,不仅限于x86 - mm/mmap.cdo_mmap_pgoff()中包含了这段代码:

    case MAP_SHARED:
        if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
            return -EACCES;
        ....
        /* fall through */
    case MAP_PRIVATE:
        if (!(file->f_mode & FMODE_READ))
            return -EACCES;

我认为这解释了你所见到的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接