root / osdep.c @ ff21f70a
History | View | Annotate | Download (7.3 kB)
1 | ea88812f | bellard | /*
|
---|---|---|---|
2 | ea88812f | bellard | * QEMU low level functions
|
3 | 5fafdf24 | ths | *
|
4 | ea88812f | bellard | * Copyright (c) 2003 Fabrice Bellard
|
5 | 5fafdf24 | ths | *
|
6 | ea88812f | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | ea88812f | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | ea88812f | bellard | * in the Software without restriction, including without limitation the rights
|
9 | ea88812f | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | ea88812f | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | ea88812f | bellard | * furnished to do so, subject to the following conditions:
|
12 | ea88812f | bellard | *
|
13 | ea88812f | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | ea88812f | bellard | * all copies or substantial portions of the Software.
|
15 | ea88812f | bellard | *
|
16 | ea88812f | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | ea88812f | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | ea88812f | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | ea88812f | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | ea88812f | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | ea88812f | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | ea88812f | bellard | * THE SOFTWARE.
|
23 | ea88812f | bellard | */
|
24 | ea88812f | bellard | #include <stdlib.h> |
25 | ea88812f | bellard | #include <stdio.h> |
26 | ea88812f | bellard | #include <stdarg.h> |
27 | ea88812f | bellard | #include <string.h> |
28 | ea88812f | bellard | #include <errno.h> |
29 | ea88812f | bellard | #include <unistd.h> |
30 | aa26bb2d | ths | #include <fcntl.h> |
31 | f582af58 | Paolo Bonzini | |
32 | f582af58 | Paolo Bonzini | /* Needed early for CONFIG_BSD etc. */
|
33 | f582af58 | Paolo Bonzini | #include "config-host.h" |
34 | f582af58 | Paolo Bonzini | |
35 | dfe5fff3 | Juan Quintela | #ifdef CONFIG_SOLARIS
|
36 | 605686cd | ths | #include <sys/types.h> |
37 | 605686cd | ths | #include <sys/statvfs.h> |
38 | 605686cd | ths | #endif
|
39 | ea88812f | bellard | |
40 | 6e4255f6 | bellard | #ifdef _WIN32
|
41 | 6e4255f6 | bellard | #include <windows.h> |
42 | 71e72a19 | Juan Quintela | #elif defined(CONFIG_BSD)
|
43 | 194884dd | bellard | #include <stdlib.h> |
44 | 194884dd | bellard | #else
|
45 | 49b470eb | bellard | #include <malloc.h> |
46 | 194884dd | bellard | #endif
|
47 | 49b470eb | bellard | |
48 | 511d2b14 | blueswir1 | #include "qemu-common.h" |
49 | 511d2b14 | blueswir1 | #include "sysemu.h" |
50 | 03ff3ca3 | aliguori | #include "qemu_socket.h" |
51 | 03ff3ca3 | aliguori | |
52 | d741429a | Blue Swirl | #if !defined(_POSIX_C_SOURCE) || defined(_WIN32) || defined(__sun__)
|
53 | d644f8be | malc | static void *oom_check(void *ptr) |
54 | d644f8be | malc | { |
55 | d644f8be | malc | if (ptr == NULL) { |
56 | d2d5adcb | Stefan Weil | #if defined(_WIN32)
|
57 | d2d5adcb | Stefan Weil | fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError());
|
58 | d2d5adcb | Stefan Weil | #else
|
59 | d2d5adcb | Stefan Weil | fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno));
|
60 | d2d5adcb | Stefan Weil | #endif
|
61 | d644f8be | malc | abort(); |
62 | d644f8be | malc | } |
63 | d644f8be | malc | return ptr;
|
64 | d644f8be | malc | } |
65 | d644f8be | malc | #endif
|
66 | d644f8be | malc | |
67 | 6e4255f6 | bellard | #if defined(_WIN32)
|
68 | 33f00271 | balrog | void *qemu_memalign(size_t alignment, size_t size)
|
69 | 33f00271 | balrog | { |
70 | d644f8be | malc | if (!size) {
|
71 | d644f8be | malc | abort(); |
72 | d644f8be | malc | } |
73 | d644f8be | malc | return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); |
74 | 33f00271 | balrog | } |
75 | 6e4255f6 | bellard | |
76 | 6e4255f6 | bellard | void *qemu_vmalloc(size_t size)
|
77 | 6e4255f6 | bellard | { |
78 | 6e4255f6 | bellard | /* FIXME: this is not exactly optimal solution since VirtualAlloc
|
79 | 6e4255f6 | bellard | has 64Kb granularity, but at least it guarantees us that the
|
80 | 6e4255f6 | bellard | memory is page aligned. */
|
81 | d644f8be | malc | if (!size) {
|
82 | d644f8be | malc | abort(); |
83 | d644f8be | malc | } |
84 | d644f8be | malc | return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); |
85 | 6e4255f6 | bellard | } |
86 | 6e4255f6 | bellard | |
87 | 6e4255f6 | bellard | void qemu_vfree(void *ptr) |
88 | 6e4255f6 | bellard | { |
89 | 6e4255f6 | bellard | VirtualFree(ptr, 0, MEM_RELEASE);
|
90 | 6e4255f6 | bellard | } |
91 | 6e4255f6 | bellard | |
92 | 6cb7ee85 | pbrook | #else
|
93 | 6cb7ee85 | pbrook | |
94 | 33f00271 | balrog | void *qemu_memalign(size_t alignment, size_t size)
|
95 | 33f00271 | balrog | { |
96 | d741429a | Blue Swirl | #if defined(_POSIX_C_SOURCE) && !defined(__sun__)
|
97 | 33f00271 | balrog | int ret;
|
98 | 33f00271 | balrog | void *ptr;
|
99 | 33f00271 | balrog | ret = posix_memalign(&ptr, alignment, size); |
100 | d2d5adcb | Stefan Weil | if (ret != 0) { |
101 | d2d5adcb | Stefan Weil | fprintf(stderr, "Failed to allocate %zu B: %s\n",
|
102 | d2d5adcb | Stefan Weil | size, strerror(ret)); |
103 | d644f8be | malc | abort(); |
104 | d2d5adcb | Stefan Weil | } |
105 | 33f00271 | balrog | return ptr;
|
106 | 71e72a19 | Juan Quintela | #elif defined(CONFIG_BSD)
|
107 | d644f8be | malc | return oom_check(valloc(size));
|
108 | 33f00271 | balrog | #else
|
109 | d644f8be | malc | return oom_check(memalign(alignment, size));
|
110 | 33f00271 | balrog | #endif
|
111 | 33f00271 | balrog | } |
112 | 33f00271 | balrog | |
113 | 49b470eb | bellard | /* alloc shared memory pages */
|
114 | 49b470eb | bellard | void *qemu_vmalloc(size_t size)
|
115 | 49b470eb | bellard | { |
116 | 48253bd8 | malc | return qemu_memalign(getpagesize(), size);
|
117 | 49b470eb | bellard | } |
118 | 49b470eb | bellard | |
119 | 49b470eb | bellard | void qemu_vfree(void *ptr) |
120 | 49b470eb | bellard | { |
121 | 49b470eb | bellard | free(ptr); |
122 | 49b470eb | bellard | } |
123 | 49b470eb | bellard | |
124 | 49b470eb | bellard | #endif
|
125 | 49b470eb | bellard | |
126 | aa26bb2d | ths | int qemu_create_pidfile(const char *filename) |
127 | aa26bb2d | ths | { |
128 | aa26bb2d | ths | char buffer[128]; |
129 | aa26bb2d | ths | int len;
|
130 | aa26bb2d | ths | #ifndef _WIN32
|
131 | aa26bb2d | ths | int fd;
|
132 | aa26bb2d | ths | |
133 | 40ff6d7e | Kevin Wolf | fd = qemu_open(filename, O_RDWR | O_CREAT, 0600);
|
134 | aa26bb2d | ths | if (fd == -1) |
135 | aa26bb2d | ths | return -1; |
136 | aa26bb2d | ths | |
137 | aa26bb2d | ths | if (lockf(fd, F_TLOCK, 0) == -1) |
138 | aa26bb2d | ths | return -1; |
139 | aa26bb2d | ths | |
140 | aa26bb2d | ths | len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); |
141 | aa26bb2d | ths | if (write(fd, buffer, len) != len)
|
142 | aa26bb2d | ths | return -1; |
143 | aa26bb2d | ths | #else
|
144 | aa26bb2d | ths | HANDLE file; |
145 | aa26bb2d | ths | OVERLAPPED overlap; |
146 | aa26bb2d | ths | BOOL ret; |
147 | 099fe236 | Juha Riihimรคki | memset(&overlap, 0, sizeof(overlap)); |
148 | aa26bb2d | ths | |
149 | 099fe236 | Juha Riihimรคki | file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
150 | aa26bb2d | ths | OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
151 | aa26bb2d | ths | |
152 | aa26bb2d | ths | if (file == INVALID_HANDLE_VALUE)
|
153 | aa26bb2d | ths | return -1; |
154 | aa26bb2d | ths | |
155 | aa26bb2d | ths | len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); |
156 | 5fafdf24 | ths | ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, |
157 | aa26bb2d | ths | &overlap, NULL);
|
158 | aa26bb2d | ths | if (ret == 0) |
159 | aa26bb2d | ths | return -1; |
160 | aa26bb2d | ths | #endif
|
161 | aa26bb2d | ths | return 0; |
162 | aa26bb2d | ths | } |
163 | 29b3a662 | pbrook | |
164 | 29b3a662 | pbrook | #ifdef _WIN32
|
165 | 29b3a662 | pbrook | |
166 | 29b3a662 | pbrook | /* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
|
167 | 29b3a662 | pbrook | #define _W32_FT_OFFSET (116444736000000000ULL) |
168 | 29b3a662 | pbrook | |
169 | 29b3a662 | pbrook | int qemu_gettimeofday(qemu_timeval *tp)
|
170 | 29b3a662 | pbrook | { |
171 | 29b3a662 | pbrook | union {
|
172 | 29b3a662 | pbrook | unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ |
173 | 29b3a662 | pbrook | FILETIME ft; |
174 | 29b3a662 | pbrook | } _now; |
175 | 29b3a662 | pbrook | |
176 | 29b3a662 | pbrook | if(tp)
|
177 | 29b3a662 | pbrook | { |
178 | 29b3a662 | pbrook | GetSystemTimeAsFileTime (&_now.ft); |
179 | 29b3a662 | pbrook | tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); |
180 | 29b3a662 | pbrook | tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); |
181 | 29b3a662 | pbrook | } |
182 | 29b3a662 | pbrook | /* Always return 0 as per Open Group Base Specifications Issue 6.
|
183 | 29b3a662 | pbrook | Do not set errno on error. */
|
184 | 29b3a662 | pbrook | return 0; |
185 | 29b3a662 | pbrook | } |
186 | 29b3a662 | pbrook | #endif /* _WIN32 */ |
187 | 03ff3ca3 | aliguori | |
188 | 03ff3ca3 | aliguori | |
189 | 03ff3ca3 | aliguori | #ifdef _WIN32
|
190 | 03ff3ca3 | aliguori | void socket_set_nonblock(int fd) |
191 | 03ff3ca3 | aliguori | { |
192 | 03ff3ca3 | aliguori | unsigned long opt = 1; |
193 | 03ff3ca3 | aliguori | ioctlsocket(fd, FIONBIO, &opt); |
194 | 03ff3ca3 | aliguori | } |
195 | 03ff3ca3 | aliguori | |
196 | 03ff3ca3 | aliguori | int inet_aton(const char *cp, struct in_addr *ia) |
197 | 03ff3ca3 | aliguori | { |
198 | 03ff3ca3 | aliguori | uint32_t addr = inet_addr(cp); |
199 | 03ff3ca3 | aliguori | if (addr == 0xffffffff) |
200 | 03ff3ca3 | aliguori | return 0; |
201 | 03ff3ca3 | aliguori | ia->s_addr = addr; |
202 | 03ff3ca3 | aliguori | return 1; |
203 | 03ff3ca3 | aliguori | } |
204 | 40ff6d7e | Kevin Wolf | |
205 | 40ff6d7e | Kevin Wolf | void qemu_set_cloexec(int fd) |
206 | 40ff6d7e | Kevin Wolf | { |
207 | 40ff6d7e | Kevin Wolf | } |
208 | 40ff6d7e | Kevin Wolf | |
209 | 03ff3ca3 | aliguori | #else
|
210 | 40ff6d7e | Kevin Wolf | |
211 | 03ff3ca3 | aliguori | void socket_set_nonblock(int fd) |
212 | 03ff3ca3 | aliguori | { |
213 | 03ff3ca3 | aliguori | int f;
|
214 | 03ff3ca3 | aliguori | f = fcntl(fd, F_GETFL); |
215 | 03ff3ca3 | aliguori | fcntl(fd, F_SETFL, f | O_NONBLOCK); |
216 | 03ff3ca3 | aliguori | } |
217 | 40ff6d7e | Kevin Wolf | |
218 | 40ff6d7e | Kevin Wolf | void qemu_set_cloexec(int fd) |
219 | 40ff6d7e | Kevin Wolf | { |
220 | 40ff6d7e | Kevin Wolf | int f;
|
221 | 40ff6d7e | Kevin Wolf | f = fcntl(fd, F_GETFD); |
222 | 40ff6d7e | Kevin Wolf | fcntl(fd, F_SETFD, f | FD_CLOEXEC); |
223 | 40ff6d7e | Kevin Wolf | } |
224 | 40ff6d7e | Kevin Wolf | |
225 | 40ff6d7e | Kevin Wolf | #endif
|
226 | 40ff6d7e | Kevin Wolf | |
227 | 40ff6d7e | Kevin Wolf | /*
|
228 | 40ff6d7e | Kevin Wolf | * Opens a file with FD_CLOEXEC set
|
229 | 40ff6d7e | Kevin Wolf | */
|
230 | 40ff6d7e | Kevin Wolf | int qemu_open(const char *name, int flags, ...) |
231 | 40ff6d7e | Kevin Wolf | { |
232 | 40ff6d7e | Kevin Wolf | int ret;
|
233 | 40ff6d7e | Kevin Wolf | int mode = 0; |
234 | 40ff6d7e | Kevin Wolf | |
235 | 40ff6d7e | Kevin Wolf | if (flags & O_CREAT) {
|
236 | 40ff6d7e | Kevin Wolf | va_list ap; |
237 | 40ff6d7e | Kevin Wolf | |
238 | 40ff6d7e | Kevin Wolf | va_start(ap, flags); |
239 | 40ff6d7e | Kevin Wolf | mode = va_arg(ap, int);
|
240 | 40ff6d7e | Kevin Wolf | va_end(ap); |
241 | 40ff6d7e | Kevin Wolf | } |
242 | 40ff6d7e | Kevin Wolf | |
243 | 40ff6d7e | Kevin Wolf | #ifdef O_CLOEXEC
|
244 | 40ff6d7e | Kevin Wolf | ret = open(name, flags | O_CLOEXEC, mode); |
245 | 40ff6d7e | Kevin Wolf | #else
|
246 | 40ff6d7e | Kevin Wolf | ret = open(name, flags, mode); |
247 | 40ff6d7e | Kevin Wolf | if (ret >= 0) { |
248 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(ret); |
249 | 40ff6d7e | Kevin Wolf | } |
250 | 03ff3ca3 | aliguori | #endif
|
251 | 40ff6d7e | Kevin Wolf | |
252 | 40ff6d7e | Kevin Wolf | return ret;
|
253 | 40ff6d7e | Kevin Wolf | } |
254 | 40ff6d7e | Kevin Wolf | |
255 | 7b5f699d | Kirill A. Shutemov | /*
|
256 | 7b5f699d | Kirill A. Shutemov | * A variant of write(2) which handles partial write.
|
257 | 7b5f699d | Kirill A. Shutemov | *
|
258 | 7b5f699d | Kirill A. Shutemov | * Return the number of bytes transferred.
|
259 | 7b5f699d | Kirill A. Shutemov | * Set errno if fewer than `count' bytes are written.
|
260 | 7b5f699d | Kirill A. Shutemov | */
|
261 | 7b5f699d | Kirill A. Shutemov | ssize_t qemu_write_full(int fd, const void *buf, size_t count) |
262 | 7b5f699d | Kirill A. Shutemov | { |
263 | 7b5f699d | Kirill A. Shutemov | ssize_t ret = 0;
|
264 | 7b5f699d | Kirill A. Shutemov | ssize_t total = 0;
|
265 | 7b5f699d | Kirill A. Shutemov | |
266 | 7b5f699d | Kirill A. Shutemov | while (count) {
|
267 | 7b5f699d | Kirill A. Shutemov | ret = write(fd, buf, count); |
268 | 7b5f699d | Kirill A. Shutemov | if (ret < 0) { |
269 | 7b5f699d | Kirill A. Shutemov | if (errno == EINTR)
|
270 | 7b5f699d | Kirill A. Shutemov | continue;
|
271 | 7b5f699d | Kirill A. Shutemov | break;
|
272 | 7b5f699d | Kirill A. Shutemov | } |
273 | 7b5f699d | Kirill A. Shutemov | |
274 | 7b5f699d | Kirill A. Shutemov | count -= ret; |
275 | 7b5f699d | Kirill A. Shutemov | buf += ret; |
276 | 7b5f699d | Kirill A. Shutemov | total += ret; |
277 | 7b5f699d | Kirill A. Shutemov | } |
278 | 7b5f699d | Kirill A. Shutemov | |
279 | 7b5f699d | Kirill A. Shutemov | return total;
|
280 | 7b5f699d | Kirill A. Shutemov | } |
281 | 7b5f699d | Kirill A. Shutemov | |
282 | 40ff6d7e | Kevin Wolf | #ifndef _WIN32
|
283 | 40ff6d7e | Kevin Wolf | /*
|
284 | 40ff6d7e | Kevin Wolf | * Creates a pipe with FD_CLOEXEC set on both file descriptors
|
285 | 40ff6d7e | Kevin Wolf | */
|
286 | 40ff6d7e | Kevin Wolf | int qemu_pipe(int pipefd[2]) |
287 | 40ff6d7e | Kevin Wolf | { |
288 | 40ff6d7e | Kevin Wolf | int ret;
|
289 | 40ff6d7e | Kevin Wolf | |
290 | 40ff6d7e | Kevin Wolf | #ifdef CONFIG_PIPE2
|
291 | 40ff6d7e | Kevin Wolf | ret = pipe2(pipefd, O_CLOEXEC); |
292 | 3a03bfa5 | Andre Przywara | if (ret != -1 || errno != ENOSYS) { |
293 | 3a03bfa5 | Andre Przywara | return ret;
|
294 | 3a03bfa5 | Andre Przywara | } |
295 | 3a03bfa5 | Andre Przywara | #endif
|
296 | 40ff6d7e | Kevin Wolf | ret = pipe(pipefd); |
297 | 40ff6d7e | Kevin Wolf | if (ret == 0) { |
298 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(pipefd[0]);
|
299 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(pipefd[1]);
|
300 | 40ff6d7e | Kevin Wolf | } |
301 | 40ff6d7e | Kevin Wolf | |
302 | 40ff6d7e | Kevin Wolf | return ret;
|
303 | 40ff6d7e | Kevin Wolf | } |
304 | 40ff6d7e | Kevin Wolf | #endif
|
305 | 40ff6d7e | Kevin Wolf | |
306 | 40ff6d7e | Kevin Wolf | /*
|
307 | 40ff6d7e | Kevin Wolf | * Opens a socket with FD_CLOEXEC set
|
308 | 40ff6d7e | Kevin Wolf | */
|
309 | 40ff6d7e | Kevin Wolf | int qemu_socket(int domain, int type, int protocol) |
310 | 40ff6d7e | Kevin Wolf | { |
311 | 40ff6d7e | Kevin Wolf | int ret;
|
312 | 40ff6d7e | Kevin Wolf | |
313 | 40ff6d7e | Kevin Wolf | #ifdef SOCK_CLOEXEC
|
314 | 40ff6d7e | Kevin Wolf | ret = socket(domain, type | SOCK_CLOEXEC, protocol); |
315 | 3a03bfa5 | Andre Przywara | if (ret != -1 || errno != EINVAL) { |
316 | 3a03bfa5 | Andre Przywara | return ret;
|
317 | 3a03bfa5 | Andre Przywara | } |
318 | 3a03bfa5 | Andre Przywara | #endif
|
319 | 40ff6d7e | Kevin Wolf | ret = socket(domain, type, protocol); |
320 | 40ff6d7e | Kevin Wolf | if (ret >= 0) { |
321 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(ret); |
322 | 40ff6d7e | Kevin Wolf | } |
323 | 40ff6d7e | Kevin Wolf | |
324 | 40ff6d7e | Kevin Wolf | return ret;
|
325 | 40ff6d7e | Kevin Wolf | } |
326 | 40ff6d7e | Kevin Wolf | |
327 | 40ff6d7e | Kevin Wolf | /*
|
328 | 40ff6d7e | Kevin Wolf | * Accept a connection and set FD_CLOEXEC
|
329 | 40ff6d7e | Kevin Wolf | */
|
330 | 40ff6d7e | Kevin Wolf | int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) |
331 | 40ff6d7e | Kevin Wolf | { |
332 | 40ff6d7e | Kevin Wolf | int ret;
|
333 | 40ff6d7e | Kevin Wolf | |
334 | 40ff6d7e | Kevin Wolf | #ifdef CONFIG_ACCEPT4
|
335 | 40ff6d7e | Kevin Wolf | ret = accept4(s, addr, addrlen, SOCK_CLOEXEC); |
336 | 347ed55c | Kevin Wolf | if (ret != -1 || errno != ENOSYS) { |
337 | 3a03bfa5 | Andre Przywara | return ret;
|
338 | 3a03bfa5 | Andre Przywara | } |
339 | 3a03bfa5 | Andre Przywara | #endif
|
340 | 40ff6d7e | Kevin Wolf | ret = accept(s, addr, addrlen); |
341 | 40ff6d7e | Kevin Wolf | if (ret >= 0) { |
342 | 40ff6d7e | Kevin Wolf | qemu_set_cloexec(ret); |
343 | 40ff6d7e | Kevin Wolf | } |
344 | 40ff6d7e | Kevin Wolf | |
345 | 40ff6d7e | Kevin Wolf | return ret;
|
346 | 40ff6d7e | Kevin Wolf | } |