1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (c) 2010 University of Szeged
4 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 * THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "SharedMemory.h"
30
31#include "ArgumentDecoder.h"
32#include "ArgumentEncoder.h"
33#include "WebCoreArgumentCoders.h"
34#include <errno.h>
35#include <fcntl.h>
36#include <stdlib.h>
37#include <sys/mman.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <unistd.h>
41#include <wtf/Assertions.h>
42#include <wtf/CurrentTime.h>
43
44#if PLATFORM(QT)
45#include <QDir>
46#elif PLATFORM(GTK)
47#include <wtf/gobject/GOwnPtr.h>
48#endif
49
50namespace WebKit {
51
52SharedMemory::Handle::Handle()
53    : m_fileDescriptor(-1)
54    , m_size(0)
55{
56}
57
58SharedMemory::Handle::~Handle()
59{
60    if (!isNull())
61        while (close(m_fileDescriptor) == -1 && errno == EINTR) { }
62}
63
64bool SharedMemory::Handle::isNull() const
65{
66    return m_fileDescriptor == -1;
67}
68
69void SharedMemory::Handle::encode(CoreIPC::ArgumentEncoder* encoder) const
70{
71    ASSERT(!isNull());
72
73    encoder->encode(releaseToAttachment());
74}
75
76bool SharedMemory::Handle::decode(CoreIPC::ArgumentDecoder* decoder, Handle& handle)
77{
78    ASSERT_ARG(handle, !handle.m_size);
79    ASSERT_ARG(handle, handle.isNull());
80
81    CoreIPC::Attachment attachment;
82    if (!decoder->decode(attachment))
83        return false;
84
85    handle.adoptFromAttachment(attachment.releaseFileDescriptor(), attachment.size());
86    return true;
87}
88
89CoreIPC::Attachment SharedMemory::Handle::releaseToAttachment() const
90{
91    ASSERT(!isNull());
92
93    int temp = m_fileDescriptor;
94    m_fileDescriptor = -1;
95    return CoreIPC::Attachment(temp, m_size);
96}
97
98void SharedMemory::Handle::adoptFromAttachment(int fileDescriptor, size_t size)
99{
100    ASSERT(!m_size);
101    ASSERT(isNull());
102
103    m_fileDescriptor = fileDescriptor;
104    m_size = size;
105}
106
107PassRefPtr<SharedMemory> SharedMemory::create(size_t size)
108{
109#if PLATFORM(QT)
110    QString tempName = QDir::temp().filePath(QLatin1String("qwkshm.XXXXXX"));
111    QByteArray tempNameCSTR = tempName.toLocal8Bit();
112    char* tempNameC = tempNameCSTR.data();
113#elif PLATFORM(GTK)
114    GOwnPtr<gchar> tempName(g_build_filename(g_get_tmp_dir(), "WK2SharedMemoryXXXXXX", NULL));
115    gchar* tempNameC = tempName.get();
116#endif
117
118    int fileDescriptor;
119    while ((fileDescriptor = mkstemp(tempNameC)) == -1) {
120        if (errno != EINTR)
121            return 0;
122    }
123    while (fcntl(fileDescriptor, F_SETFD, FD_CLOEXEC) == -1) {
124        if (errno != EINTR) {
125            while (close(fileDescriptor) == -1 && errno == EINTR) { }
126            unlink(tempNameC);
127            return 0;
128        }
129    }
130
131    while (ftruncate(fileDescriptor, size) == -1) {
132        if (errno != EINTR) {
133            while (close(fileDescriptor) == -1 && errno == EINTR) { }
134            unlink(tempNameC);
135            return 0;
136        }
137    }
138
139    void* data = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileDescriptor, 0);
140    if (data == MAP_FAILED) {
141        while (close(fileDescriptor) == -1 && errno == EINTR) { }
142        unlink(tempNameC);
143        return 0;
144    }
145
146    unlink(tempNameC);
147
148    RefPtr<SharedMemory> instance = adoptRef(new SharedMemory());
149    instance->m_data = data;
150    instance->m_fileDescriptor = fileDescriptor;
151    instance->m_size = size;
152    return instance.release();
153}
154
155static inline int accessModeMMap(SharedMemory::Protection protection)
156{
157    switch (protection) {
158    case SharedMemory::ReadOnly:
159        return PROT_READ;
160    case SharedMemory::ReadWrite:
161        return PROT_READ | PROT_WRITE;
162    }
163
164    ASSERT_NOT_REACHED();
165    return PROT_READ | PROT_WRITE;
166}
167
168PassRefPtr<SharedMemory> SharedMemory::create(const Handle& handle, Protection protection)
169{
170    ASSERT(!handle.isNull());
171
172    void* data = mmap(0, handle.m_size, accessModeMMap(protection), MAP_SHARED, handle.m_fileDescriptor, 0);
173    if (data == MAP_FAILED)
174        return 0;
175
176    RefPtr<SharedMemory> instance = adoptRef(new SharedMemory());
177    instance->m_data = data;
178    instance->m_fileDescriptor = handle.m_fileDescriptor;
179    instance->m_size = handle.m_size;
180    handle.m_fileDescriptor = -1;
181    return instance;
182}
183
184SharedMemory::~SharedMemory()
185{
186    munmap(m_data, m_size);
187    while (close(m_fileDescriptor) == -1 && errno == EINTR) { }
188}
189
190static inline int accessModeFile(SharedMemory::Protection protection)
191{
192    switch (protection) {
193    case SharedMemory::ReadOnly:
194        return O_RDONLY;
195    case SharedMemory::ReadWrite:
196        return O_RDWR;
197    }
198
199    ASSERT_NOT_REACHED();
200    return O_RDWR;
201}
202
203bool SharedMemory::createHandle(Handle& handle, Protection protection)
204{
205    ASSERT_ARG(handle, !handle.m_size);
206    ASSERT_ARG(handle, handle.isNull());
207
208    int duplicatedHandle;
209    while ((duplicatedHandle = dup(m_fileDescriptor)) == -1) {
210        if (errno != EINTR) {
211            ASSERT_NOT_REACHED();
212            return false;
213        }
214    }
215
216    while ((fcntl(duplicatedHandle, F_SETFD, FD_CLOEXEC | accessModeFile(protection)) == -1)) {
217        if (errno != EINTR) {
218            ASSERT_NOT_REACHED();
219            while (close(duplicatedHandle) == -1 && errno == EINTR) { }
220            return false;
221        }
222    }
223    handle.m_fileDescriptor = duplicatedHandle;
224    handle.m_size = m_size;
225    return true;
226}
227
228unsigned SharedMemory::systemPageSize()
229{
230    static unsigned pageSize = 0;
231
232    if (!pageSize)
233        pageSize = getpagesize();
234
235    return pageSize;
236}
237
238} // namespace WebKit
239