syscall.c 119 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 *  Linux syscalls
 * 
 *  Copyright (c) 2003 Fabrice Bellard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
bellard's avatar
bellard committed
23
#include <string.h>
24
25
26
27
28
#include <elf.h>
#include <endian.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
bellard's avatar
bellard committed
29
#include <time.h>
30
31
32
33
34
35
36
37
38
39
40
41
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <sys/swap.h>
#include <signal.h>
#include <sched.h>
#include <sys/socket.h>
#include <sys/uio.h>
bellard's avatar
bellard committed
42
#include <sys/poll.h>
bellard's avatar
bellard committed
43
#include <sys/times.h>
44
#include <sys/shm.h>
bellard's avatar
bellard committed
45
#include <sys/statfs.h>
46
#include <utime.h>
bellard's avatar
bellard committed
47
#include <sys/sysinfo.h>
bellard's avatar
bellard committed
48
//#include <sys/user.h>
49
#include <netinet/ip.h>
bellard's avatar
bellard committed
50
#include <netinet/tcp.h>
51
52
53
54

#define termios host_termios
#define winsize host_winsize
#define termio host_termio
bellard's avatar
bellard committed
55
56
57
#define sgttyb host_sgttyb /* same as target */
#define tchars host_tchars /* same as target */
#define ltchars host_ltchars /* same as target */
58
59
60
61
62
63
64

#include <linux/termios.h>
#include <linux/unistd.h>
#include <linux/utsname.h>
#include <linux/cdrom.h>
#include <linux/hdreg.h>
#include <linux/soundcard.h>
bellard's avatar
bellard committed
65
#include <linux/dirent.h>
66
#include <linux/kd.h>
67

bellard's avatar
bellard committed
68
#include "qemu.h"
69

bellard's avatar
bellard committed
70
//#define DEBUG
71

pbrook's avatar
pbrook committed
72
73
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \
    || defined(TARGET_M68K)
74
75
76
77
/* 16 bit uid wrappers emulation */
#define USE_UID16
#endif

bellard's avatar
bellard committed
78
79
80
81
//#include <linux/msdos_fs.h>
#define	VFAT_IOCTL_READDIR_BOTH		_IOR('r', 1, struct dirent [2])
#define	VFAT_IOCTL_READDIR_SHORT	_IOR('r', 2, struct dirent [2])

82
83
84
85
86
87
88

#undef _syscall0
#undef _syscall1
#undef _syscall2
#undef _syscall3
#undef _syscall4
#undef _syscall5
bellard's avatar
bellard committed
89
#undef _syscall6
90

bellard's avatar
bellard committed
91
92
93
94
95
#define _syscall0(type,name)		\
type name (void)			\
{					\
	return syscall(__NR_##name);	\
}
96

bellard's avatar
bellard committed
97
98
99
100
#define _syscall1(type,name,type1,arg1)		\
type name (type1 arg1)				\
{						\
	return syscall(__NR_##name, arg1);	\
101
102
}

bellard's avatar
bellard committed
103
104
105
106
#define _syscall2(type,name,type1,arg1,type2,arg2)	\
type name (type1 arg1,type2 arg2)			\
{							\
	return syscall(__NR_##name, arg1, arg2);	\
107
108
}

bellard's avatar
bellard committed
109
110
111
112
#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)	\
type name (type1 arg1,type2 arg2,type3 arg3)			\
{								\
	return syscall(__NR_##name, arg1, arg2, arg3);		\
113
114
}

bellard's avatar
bellard committed
115
116
117
118
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)	\
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4)				\
{										\
	return syscall(__NR_##name, arg1, arg2, arg3, arg4);			\
119
120
}

bellard's avatar
bellard committed
121
122
123
124
125
#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,	\
		  type5,arg5)							\
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5)		\
{										\
	return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5);		\
126
127
}

bellard's avatar
bellard committed
128
129
130
131
132
133

#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,	\
		  type5,arg5,type6,arg6)					\
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6)	\
{										\
	return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6);	\
134
}
bellard's avatar
bellard committed
135

136

137
#define __NR_sys_uname __NR_uname
bellard's avatar
bellard committed
138
139
#define __NR_sys_getcwd1 __NR_getcwd
#define __NR_sys_getdents __NR_getdents
bellard's avatar
bellard committed
140
#define __NR_sys_getdents64 __NR_getdents64
bellard's avatar
bellard committed
141
#define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo
142

143
#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
bellard's avatar
bellard committed
144
145
146
#define __NR__llseek __NR_lseek
#endif

bellard's avatar
bellard committed
147
#ifdef __NR_gettid
148
_syscall0(int, gettid)
bellard's avatar
bellard committed
149
150
151
152
153
#else
static int gettid(void) {
    return -ENOSYS;
}
#endif
154
_syscall1(int,sys_uname,struct new_utsname *,buf)
bellard's avatar
bellard committed
155
156
_syscall2(int,sys_getcwd1,char *,buf,size_t,size)
_syscall3(int, sys_getdents, uint, fd, struct dirent *, dirp, uint, count);
bellard's avatar
bellard committed
157
_syscall3(int, sys_getdents64, uint, fd, struct dirent64 *, dirp, uint, count);
158
159
_syscall5(int, _llseek,  uint,  fd, ulong, hi, ulong, lo,
          loff_t *, res, uint, wh);
bellard's avatar
bellard committed
160
_syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo)
161
162
163
#ifdef __NR_exit_group
_syscall1(int,exit_group,int,error_code)
#endif
bellard's avatar
bellard committed
164
165

extern int personality(int);
bellard's avatar
bellard committed
166
167
168
extern int flock(int, int);
extern int setfsuid(int);
extern int setfsgid(int);
bellard's avatar
bellard committed
169
170
171
172
extern int setresuid(uid_t, uid_t, uid_t);
extern int getresuid(uid_t *, uid_t *, uid_t *);
extern int setresgid(gid_t, gid_t, gid_t);
extern int getresgid(gid_t *, gid_t *, gid_t *);
173
extern int setgroups(int, gid_t *);
174
175
176
177
178
179
180
181
182
183
184
185
186
187

static inline long get_errno(long ret)
{
    if (ret == -1)
        return -errno;
    else
        return ret;
}

static inline int is_error(long ret)
{
    return (unsigned long)ret >= (unsigned long)(-4096);
}

188
189
static target_ulong target_brk;
static target_ulong target_original_brk;
190

191
void target_set_brk(target_ulong new_brk)
192
{
193
    target_original_brk = target_brk = new_brk;
194
195
}

196
long do_brk(target_ulong new_brk)
197
{
198
    target_ulong brk_page;
199
200
201
202
    long mapped_addr;
    int	new_alloc_size;

    if (!new_brk)
203
        return target_brk;
204
205
206
    if (new_brk < target_original_brk)
        return -ENOMEM;
    
207
    brk_page = HOST_PAGE_ALIGN(target_brk);
208
209
210
211

    /* If the new brk is less than this, set it and we're done... */
    if (new_brk < brk_page) {
	target_brk = new_brk;
212
    	return target_brk;
213
214
215
    }

    /* We need to allocate more memory after the brk... */
bellard's avatar
bellard committed
216
    new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
217
    mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, 
bellard's avatar
bellard committed
218
219
                                        PROT_READ|PROT_WRITE,
                                        MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0));
220
221
222
223
    if (is_error(mapped_addr)) {
	return mapped_addr;
    } else {
	target_brk = new_brk;
224
    	return target_brk;
225
226
227
228
229
230
    }
}

static inline fd_set *target_to_host_fds(fd_set *fds, 
                                         target_long *target_fds, int n)
{
bellard's avatar
bellard committed
231
#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
    return (fd_set *)target_fds;
#else
    int i, b;
    if (target_fds) {
        FD_ZERO(fds);
        for(i = 0;i < n; i++) {
            b = (tswapl(target_fds[i / TARGET_LONG_BITS]) >>
                 (i & (TARGET_LONG_BITS - 1))) & 1;
            if (b)
                FD_SET(i, fds);
        }
        return fds;
    } else {
        return NULL;
    }
#endif
}

static inline void host_to_target_fds(target_long *target_fds, 
                                      fd_set *fds, int n)
{
bellard's avatar
bellard committed
253
#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
254
255
256
257
258
259
    /* nothing to do */
#else
    int i, nw, j, k;
    target_long v;

    if (target_fds) {
260
        nw = (n + TARGET_LONG_BITS - 1) / TARGET_LONG_BITS;
261
262
263
264
265
266
267
268
269
270
271
272
273
        k = 0;
        for(i = 0;i < nw; i++) {
            v = 0;
            for(j = 0; j < TARGET_LONG_BITS; j++) {
                v |= ((FD_ISSET(k, fds) != 0) << j);
                k++;
            }
            target_fds[i] = tswapl(v);
        }
    }
#endif
}

bellard's avatar
bellard committed
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#if defined(__alpha__)
#define HOST_HZ 1024
#else
#define HOST_HZ 100
#endif

static inline long host_to_target_clock_t(long ticks)
{
#if HOST_HZ == TARGET_HZ
    return ticks;
#else
    return ((int64_t)ticks * TARGET_HZ) / HOST_HZ;
#endif
}

289
static inline void host_to_target_rusage(target_ulong target_addr,
bellard's avatar
bellard committed
290
291
                                         const struct rusage *rusage)
{
292
293
294
    struct target_rusage *target_rusage;

    lock_user_struct(target_rusage, target_addr, 0);
bellard's avatar
bellard committed
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
    target_rusage->ru_utime.tv_sec = tswapl(rusage->ru_utime.tv_sec);
    target_rusage->ru_utime.tv_usec = tswapl(rusage->ru_utime.tv_usec);
    target_rusage->ru_stime.tv_sec = tswapl(rusage->ru_stime.tv_sec);
    target_rusage->ru_stime.tv_usec = tswapl(rusage->ru_stime.tv_usec);
    target_rusage->ru_maxrss = tswapl(rusage->ru_maxrss);
    target_rusage->ru_ixrss = tswapl(rusage->ru_ixrss);
    target_rusage->ru_idrss = tswapl(rusage->ru_idrss);
    target_rusage->ru_isrss = tswapl(rusage->ru_isrss);
    target_rusage->ru_minflt = tswapl(rusage->ru_minflt);
    target_rusage->ru_majflt = tswapl(rusage->ru_majflt);
    target_rusage->ru_nswap = tswapl(rusage->ru_nswap);
    target_rusage->ru_inblock = tswapl(rusage->ru_inblock);
    target_rusage->ru_oublock = tswapl(rusage->ru_oublock);
    target_rusage->ru_msgsnd = tswapl(rusage->ru_msgsnd);
    target_rusage->ru_msgrcv = tswapl(rusage->ru_msgrcv);
    target_rusage->ru_nsignals = tswapl(rusage->ru_nsignals);
    target_rusage->ru_nvcsw = tswapl(rusage->ru_nvcsw);
    target_rusage->ru_nivcsw = tswapl(rusage->ru_nivcsw);
313
    unlock_user_struct(target_rusage, target_addr, 1);
bellard's avatar
bellard committed
314
315
}

316
317
static inline void target_to_host_timeval(struct timeval *tv,
                                          target_ulong target_addr)
318
{
319
320
321
    struct target_timeval *target_tv;

    lock_user_struct(target_tv, target_addr, 1);
bellard's avatar
bellard committed
322
323
    tv->tv_sec = tswapl(target_tv->tv_sec);
    tv->tv_usec = tswapl(target_tv->tv_usec);
324
    unlock_user_struct(target_tv, target_addr, 0);
325
326
}

327
static inline void host_to_target_timeval(target_ulong target_addr,
bellard's avatar
bellard committed
328
                                          const struct timeval *tv)
329
{
330
331
332
    struct target_timeval *target_tv;

    lock_user_struct(target_tv, target_addr, 0);
bellard's avatar
bellard committed
333
334
    target_tv->tv_sec = tswapl(tv->tv_sec);
    target_tv->tv_usec = tswapl(tv->tv_usec);
335
    unlock_user_struct(target_tv, target_addr, 1);
336
337
338
339
}


static long do_select(long n, 
340
341
                      target_ulong rfd_p, target_ulong wfd_p, 
                      target_ulong efd_p, target_ulong target_tv)
342
343
344
{
    fd_set rfds, wfds, efds;
    fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
345
    target_long *target_rfds, *target_wfds, *target_efds;
346
347
    struct timeval tv, *tv_ptr;
    long ret;
348
    int ok;
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    if (rfd_p) {
        target_rfds = lock_user(rfd_p, sizeof(target_long) * n, 1);
        rfds_ptr = target_to_host_fds(&rfds, target_rfds, n);
    } else {
        target_rfds = NULL;
        rfds_ptr = NULL;
    }
    if (wfd_p) {
        target_wfds = lock_user(wfd_p, sizeof(target_long) * n, 1);
        wfds_ptr = target_to_host_fds(&wfds, target_wfds, n);
    } else {
        target_wfds = NULL;
        wfds_ptr = NULL;
    }
    if (efd_p) {
        target_efds = lock_user(efd_p, sizeof(target_long) * n, 1);
        efds_ptr = target_to_host_fds(&efds, target_efds, n);
    } else {
        target_efds = NULL;
        efds_ptr = NULL;
    }
371
372
            
    if (target_tv) {
bellard's avatar
bellard committed
373
        target_to_host_timeval(&tv, target_tv);
374
375
376
377
378
        tv_ptr = &tv;
    } else {
        tv_ptr = NULL;
    }
    ret = get_errno(select(n, rfds_ptr, wfds_ptr, efds_ptr, tv_ptr));
379
380
381
    ok = !is_error(ret);

    if (ok) {
382
383
384
385
386
        host_to_target_fds(target_rfds, rfds_ptr, n);
        host_to_target_fds(target_wfds, wfds_ptr, n);
        host_to_target_fds(target_efds, efds_ptr, n);

        if (target_tv) {
bellard's avatar
bellard committed
387
            host_to_target_timeval(target_tv, &tv);
388
389
        }
    }
390
391
392
393
394
395
396
    if (target_rfds)
        unlock_user(target_rfds, rfd_p, ok ? sizeof(target_long) * n : 0);
    if (target_wfds)
        unlock_user(target_wfds, wfd_p, ok ? sizeof(target_long) * n : 0);
    if (target_efds)
        unlock_user(target_efds, efd_p, ok ? sizeof(target_long) * n : 0);

397
398
399
    return ret;
}

bellard's avatar
bellard committed
400
static inline void target_to_host_sockaddr(struct sockaddr *addr,
401
                                           target_ulong target_addr,
bellard's avatar
bellard committed
402
403
                                           socklen_t len)
{
404
405
406
407
408
409
    struct target_sockaddr *target_saddr;

    target_saddr = lock_user(target_addr, len, 1);
    memcpy(addr, target_saddr, len);
    addr->sa_family = tswap16(target_saddr->sa_family);
    unlock_user(target_saddr, target_addr, 0);
bellard's avatar
bellard committed
410
411
}

412
static inline void host_to_target_sockaddr(target_ulong target_addr,
bellard's avatar
bellard committed
413
414
415
                                           struct sockaddr *addr,
                                           socklen_t len)
{
416
417
418
419
420
421
    struct target_sockaddr *target_saddr;

    target_saddr = lock_user(target_addr, len, 0);
    memcpy(target_saddr, addr, len);
    target_saddr->sa_family = tswap16(addr->sa_family);
    unlock_user(target_saddr, target_addr, len);
bellard's avatar
bellard committed
422
423
}

424
/* ??? Should this also swap msgh->name?  */
bellard's avatar
bellard committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
static inline void target_to_host_cmsg(struct msghdr *msgh,
                                       struct target_msghdr *target_msgh)
{
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
    struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
    socklen_t space = 0;

    while (cmsg && target_cmsg) {
        void *data = CMSG_DATA(cmsg);
        void *target_data = TARGET_CMSG_DATA(target_cmsg);

        int len = tswapl(target_cmsg->cmsg_len) 
                  - TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr));

        space += CMSG_SPACE(len);
        if (space > msgh->msg_controllen) {
            space -= CMSG_SPACE(len);
bellard's avatar
bellard committed
442
            gemu_log("Host cmsg overflow\n");
bellard's avatar
bellard committed
443
444
445
446
447
448
449
            break;
        }

        cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
        cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
        cmsg->cmsg_len = CMSG_LEN(len);

450
        if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
bellard's avatar
bellard committed
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
            gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
            memcpy(data, target_data, len);
        } else {
            int *fd = (int *)data;
            int *target_fd = (int *)target_data;
            int i, numfds = len / sizeof(int);

            for (i = 0; i < numfds; i++)
                fd[i] = tswap32(target_fd[i]);
        }

        cmsg = CMSG_NXTHDR(msgh, cmsg);
        target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
    }

    msgh->msg_controllen = space;
}

469
/* ??? Should this also swap msgh->name?  */
bellard's avatar
bellard committed
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
                                       struct msghdr *msgh)
{
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
    struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
    socklen_t space = 0;

    while (cmsg && target_cmsg) {
        void *data = CMSG_DATA(cmsg);
        void *target_data = TARGET_CMSG_DATA(target_cmsg);

        int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));

        space += TARGET_CMSG_SPACE(len);
        if (space > tswapl(target_msgh->msg_controllen)) {
            space -= TARGET_CMSG_SPACE(len);
bellard's avatar
bellard committed
486
            gemu_log("Target cmsg overflow\n");
bellard's avatar
bellard committed
487
488
489
490
491
492
493
            break;
        }

        target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
        target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
        target_cmsg->cmsg_len = tswapl(TARGET_CMSG_LEN(len));

494
        if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
bellard's avatar
bellard committed
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
            gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
            memcpy(target_data, data, len);
        } else {
            int *fd = (int *)data;
            int *target_fd = (int *)target_data;
            int i, numfds = len / sizeof(int);

            for (i = 0; i < numfds; i++)
                target_fd[i] = tswap32(fd[i]);
        }

        cmsg = CMSG_NXTHDR(msgh, cmsg);
        target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
    }

    msgh->msg_controllen = tswapl(space);
}

static long do_setsockopt(int sockfd, int level, int optname, 
514
                          target_ulong optval, socklen_t optlen)
bellard's avatar
bellard committed
515
{
516
517
518
519
    int val, ret;
            
    switch(level) {
    case SOL_TCP:
bellard's avatar
bellard committed
520
521
522
        /* TCP options all take an 'int' value.  */
        if (optlen < sizeof(uint32_t))
            return -EINVAL;
523
        
524
        val = tget32(optval);
525
526
527
528
        ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
        break;
    case SOL_IP:
        switch(optname) {
bellard's avatar
bellard committed
529
530
        case IP_TOS:
        case IP_TTL:
531
        case IP_HDRINCL:
bellard's avatar
bellard committed
532
533
534
535
536
537
538
539
540
541
542
543
        case IP_ROUTER_ALERT:
        case IP_RECVOPTS:
        case IP_RETOPTS:
        case IP_PKTINFO:
        case IP_MTU_DISCOVER:
        case IP_RECVERR:
        case IP_RECVTOS:
#ifdef IP_FREEBIND
        case IP_FREEBIND:
#endif
        case IP_MULTICAST_TTL:
        case IP_MULTICAST_LOOP:
544
545
            val = 0;
            if (optlen >= sizeof(uint32_t)) {
546
                val = tget32(optval);
547
            } else if (optlen >= 1) {
548
                val = tget8(optval);
549
550
551
552
553
554
555
            }
            ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
            break;
        default:
            goto unimplemented;
        }
        break;
556
    case TARGET_SOL_SOCKET:
557
558
        switch (optname) {
            /* Options with 'int' argument.  */
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
        case TARGET_SO_DEBUG:
		optname = SO_DEBUG;
		break;
        case TARGET_SO_REUSEADDR:
		optname = SO_REUSEADDR;
		break;
        case TARGET_SO_TYPE:
		optname = SO_TYPE;
		break;
        case TARGET_SO_ERROR:
		optname = SO_ERROR;
		break;
        case TARGET_SO_DONTROUTE:
		optname = SO_DONTROUTE;
		break;
        case TARGET_SO_BROADCAST:
		optname = SO_BROADCAST;
		break;
        case TARGET_SO_SNDBUF:
		optname = SO_SNDBUF;
		break;
        case TARGET_SO_RCVBUF:
		optname = SO_RCVBUF;
		break;
        case TARGET_SO_KEEPALIVE:
		optname = SO_KEEPALIVE;
		break;
        case TARGET_SO_OOBINLINE:
		optname = SO_OOBINLINE;
		break;
        case TARGET_SO_NO_CHECK:
		optname = SO_NO_CHECK;
		break;
        case TARGET_SO_PRIORITY:
		optname = SO_PRIORITY;
		break;
bellard's avatar
bellard committed
595
#ifdef SO_BSDCOMPAT
596
597
598
        case TARGET_SO_BSDCOMPAT:
		optname = SO_BSDCOMPAT;
		break;
bellard's avatar
bellard committed
599
#endif
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
        case TARGET_SO_PASSCRED:
		optname = SO_PASSCRED;
		break;
        case TARGET_SO_TIMESTAMP:
		optname = SO_TIMESTAMP;
		break;
        case TARGET_SO_RCVLOWAT:
		optname = SO_RCVLOWAT;
		break;
        case TARGET_SO_RCVTIMEO:
		optname = SO_RCVTIMEO;
		break;
        case TARGET_SO_SNDTIMEO:
		optname = SO_SNDTIMEO;
		break;
615
616
617
618
            break;
        default:
            goto unimplemented;
        }
619
620
621
622
623
	if (optlen < sizeof(uint32_t))
	return -EINVAL;

	val = tget32(optval);
	ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, sizeof(val)));
624
        break;
bellard's avatar
bellard committed
625
    default:
626
627
628
    unimplemented:
        gemu_log("Unsupported setsockopt level=%d optname=%d \n", level, optname);
        ret = -ENOSYS;
bellard's avatar
bellard committed
629
    }
630
    return ret;
bellard's avatar
bellard committed
631
632
633
}

static long do_getsockopt(int sockfd, int level, int optname, 
634
                          target_ulong optval, target_ulong optlen)
bellard's avatar
bellard committed
635
{
636
637
638
    int len, lv, val, ret;

    switch(level) {
639
640
    case TARGET_SOL_SOCKET:
    	level = SOL_SOCKET;
641
	switch (optname) {
642
643
644
645
646
	case TARGET_SO_LINGER:
	case TARGET_SO_RCVTIMEO:
	case TARGET_SO_SNDTIMEO:
	case TARGET_SO_PEERCRED:
	case TARGET_SO_PEERNAME:
647
648
649
	    /* These don't just return a single integer */
	    goto unimplemented;
        default:
bellard's avatar
bellard committed
650
651
652
653
654
655
            goto int_case;
        }
        break;
    case SOL_TCP:
        /* TCP options all take an 'int' value.  */
    int_case:
656
        len = tget32(optlen);
bellard's avatar
bellard committed
657
658
659
660
661
662
663
664
665
        if (len < 0)
            return -EINVAL;
        lv = sizeof(int);
        ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
        if (ret < 0)
            return ret;
        val = tswap32(val);
        if (len > lv)
            len = lv;
666
667
668
669
670
        if (len == 4)
            tput32(optval, val);
        else
            tput8(optval, val);
        tput32(optlen, len);
bellard's avatar
bellard committed
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
        break;
    case SOL_IP:
        switch(optname) {
        case IP_TOS:
        case IP_TTL:
        case IP_HDRINCL:
        case IP_ROUTER_ALERT:
        case IP_RECVOPTS:
        case IP_RETOPTS:
        case IP_PKTINFO:
        case IP_MTU_DISCOVER:
        case IP_RECVERR:
        case IP_RECVTOS:
#ifdef IP_FREEBIND
        case IP_FREEBIND:
#endif
        case IP_MULTICAST_TTL:
        case IP_MULTICAST_LOOP:
689
            len = tget32(optlen);
690
691
692
693
694
695
            if (len < 0)
                return -EINVAL;
            lv = sizeof(int);
            ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
            if (ret < 0)
                return ret;
bellard's avatar
bellard committed
696
697
            if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) {
                len = 1;
698
699
                tput32(optlen, len);
                tput8(optval, val);
bellard's avatar
bellard committed
700
701
702
            } else {
                if (len > sizeof(int))
                    len = sizeof(int);
703
704
                tput32(optlen, len);
                tput32(optval, val);
bellard's avatar
bellard committed
705
            }
706
            break;
bellard's avatar
bellard committed
707
708
        default:
            goto unimplemented;
709
710
711
712
713
714
715
716
717
718
        }
        break;
    default:
    unimplemented:
        gemu_log("getsockopt level=%d optname=%d not yet supported\n",
                 level, optname);
        ret = -ENOSYS;
        break;
    }
    return ret;
bellard's avatar
bellard committed
719
720
}

721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
static void lock_iovec(struct iovec *vec, target_ulong target_addr,
                       int count, int copy)
{
    struct target_iovec *target_vec;
    target_ulong base;
    int i;

    target_vec = lock_user(target_addr, count * sizeof(struct target_iovec), 1);
    for(i = 0;i < count; i++) {
        base = tswapl(target_vec[i].iov_base);
        vec[i].iov_len = tswapl(target_vec[i].iov_len);
        vec[i].iov_base = lock_user(base, vec[i].iov_len, copy);
    }
    unlock_user (target_vec, target_addr, 0);
}

static void unlock_iovec(struct iovec *vec, target_ulong target_addr,
                         int count, int copy)
{
    struct target_iovec *target_vec;
    target_ulong base;
    int i;

    target_vec = lock_user(target_addr, count * sizeof(struct target_iovec), 1);
    for(i = 0;i < count; i++) {
        base = tswapl(target_vec[i].iov_base);
        unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
    }
    unlock_user (target_vec, target_addr, 0);
}

752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
static long do_socket(int domain, int type, int protocol)
{
#if defined(TARGET_MIPS)
    switch(type) {
    case TARGET_SOCK_DGRAM:
        type = SOCK_DGRAM;
        break;
    case TARGET_SOCK_STREAM:
        type = SOCK_STREAM;
        break;
    case TARGET_SOCK_RAW:
        type = SOCK_RAW;
        break;
    case TARGET_SOCK_RDM:
        type = SOCK_RDM;
        break;
    case TARGET_SOCK_SEQPACKET:
        type = SOCK_SEQPACKET;
        break;
    case TARGET_SOCK_PACKET:
        type = SOCK_PACKET;
        break;
    }
#endif
    return get_errno(socket(domain, type, protocol));
}

static long do_bind(int sockfd, target_ulong target_addr,
                    socklen_t addrlen)
{
    void *addr = alloca(addrlen);
    
    target_to_host_sockaddr(addr, target_addr, addrlen);
    return get_errno(bind(sockfd, addr, addrlen));
}

static long do_connect(int sockfd, target_ulong target_addr,
                    socklen_t addrlen)
{
    void *addr = alloca(addrlen);
    
    target_to_host_sockaddr(addr, target_addr, addrlen);
    return get_errno(connect(sockfd, addr, addrlen));
}

static long do_sendrecvmsg(int fd, target_ulong target_msg,
                           int flags, int send)
{
    long ret;
    struct target_msghdr *msgp;
    struct msghdr msg;
    int count;
    struct iovec *vec;
    target_ulong target_vec;

    lock_user_struct(msgp, target_msg, 1);
    if (msgp->msg_name) {
        msg.msg_namelen = tswap32(msgp->msg_namelen);
        msg.msg_name = alloca(msg.msg_namelen);
        target_to_host_sockaddr(msg.msg_name, tswapl(msgp->msg_name),
                                msg.msg_namelen);
    } else {
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
    }
    msg.msg_controllen = 2 * tswapl(msgp->msg_controllen);
    msg.msg_control = alloca(msg.msg_controllen);
    msg.msg_flags = tswap32(msgp->msg_flags);
    
    count = tswapl(msgp->msg_iovlen);
    vec = alloca(count * sizeof(struct iovec));
    target_vec = tswapl(msgp->msg_iov);
    lock_iovec(vec, target_vec, count, send);
    msg.msg_iovlen = count;
    msg.msg_iov = vec;
    
    if (send) {
        target_to_host_cmsg(&msg, msgp);
        ret = get_errno(sendmsg(fd, &msg, flags));
    } else {
        ret = get_errno(recvmsg(fd, &msg, flags));
        if (!is_error(ret))
            host_to_target_cmsg(msgp, &msg);
    }
    unlock_iovec(vec, target_vec, count, !send);
    return ret;
}

pbrook's avatar
pbrook committed
840
841
842
843
static long do_accept(int fd, target_ulong target_addr,
                      target_ulong target_addrlen)
{
    socklen_t addrlen = tget32(target_addrlen);
ths's avatar
ths committed
844
    void *addr = alloca(addrlen);
pbrook's avatar
pbrook committed
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
    long ret;

    ret = get_errno(accept(fd, addr, &addrlen));
    if (!is_error(ret)) {
        host_to_target_sockaddr(target_addr, addr, addrlen);
        tput32(target_addrlen, addrlen);
    }
    return ret;
}

static long do_getpeername(int fd, target_ulong target_addr,
                           target_ulong target_addrlen)
{
    socklen_t addrlen = tget32(target_addrlen);
    void *addr = alloca(target_addrlen);
    long ret;

    ret = get_errno(getpeername(fd, addr, &addrlen));
    if (!is_error(ret)) {
        host_to_target_sockaddr(target_addr, addr, addrlen);
        tput32(target_addrlen, addrlen);
    }
    return ret;
}

static long do_getsockname(int fd, target_ulong target_addr,
                           target_ulong target_addrlen)
{
    socklen_t addrlen = tget32(target_addrlen);
    void *addr = alloca(target_addrlen);
    long ret;

    ret = get_errno(getsockname(fd, addr, &addrlen));
    if (!is_error(ret)) {
        host_to_target_sockaddr(target_addr, addr, addrlen);
        tput32(target_addrlen, addrlen);
    }
    return ret;
}

static long do_socketpair(int domain, int type, int protocol,
                          target_ulong target_tab)
{
    int tab[2];
    long ret;

    ret = get_errno(socketpair(domain, type, protocol, tab));
    if (!is_error(ret)) {
        tput32(target_tab, tab[0]);
        tput32(target_tab + 4, tab[1]);
    }
    return ret;
}

static long do_sendto(int fd, target_ulong msg, size_t len, int flags,
                      target_ulong target_addr, socklen_t addrlen)
{
    void *addr;
    void *host_msg;
    long ret;

    host_msg = lock_user(msg, len, 1);
    if (target_addr) {
        addr = alloca(addrlen);
        target_to_host_sockaddr(addr, target_addr, addrlen);
        ret = get_errno(sendto(fd, host_msg, len, flags, addr, addrlen));
    } else {
        ret = get_errno(send(fd, host_msg, len, flags));
    }
    unlock_user(host_msg, msg, 0);
    return ret;
}

static long do_recvfrom(int fd, target_ulong msg, size_t len, int flags,
                        target_ulong target_addr, target_ulong target_addrlen)
{
    socklen_t addrlen;
    void *addr;
    void *host_msg;
    long ret;

    host_msg = lock_user(msg, len, 0);
    if (target_addr) {
        addrlen = tget32(target_addrlen);
        addr = alloca(addrlen);
        ret = get_errno(recvfrom(fd, host_msg, len, flags, addr, &addrlen));
    } else {
        addr = NULL; /* To keep compiler quiet.  */
        ret = get_errno(recv(fd, host_msg, len, flags));
    }
    if (!is_error(ret)) {
        if (target_addr) {
            host_to_target_sockaddr(target_addr, addr, addrlen);
            tput32(target_addrlen, addrlen);
        }
        unlock_user(host_msg, msg, len);
    } else {
        unlock_user(host_msg, msg, 0);
    }
    return ret;
}

947
static long do_socketcall(int num, target_ulong vptr)
948
949
{
    long ret;
950
    const int n = sizeof(target_ulong);
951
952
953

    switch(num) {
    case SOCKOP_socket:
bellard's avatar
bellard committed
954
	{
955
956
957
            int domain = tgetl(vptr);
            int type = tgetl(vptr + n);
            int protocol = tgetl(vptr + 2 * n);
958
            ret = do_socket(domain, type, protocol);
bellard's avatar
bellard committed
959
	}
960
961
        break;
    case SOCKOP_bind:
bellard's avatar
bellard committed
962
	{
963
964
965
            int sockfd = tgetl(vptr);
            target_ulong target_addr = tgetl(vptr + n);
            socklen_t addrlen = tgetl(vptr + 2 * n);
966
            ret = do_bind(sockfd, target_addr, addrlen);
bellard's avatar
bellard committed
967
        }
968
969
        break;
    case SOCKOP_connect:
bellard's avatar
bellard committed
970
        {
971
972
973
            int sockfd = tgetl(vptr);
            target_ulong target_addr = tgetl(vptr + n);
            socklen_t addrlen = tgetl(vptr + 2 * n);
974
            ret = do_connect(sockfd, target_addr, addrlen);
bellard's avatar
bellard committed
975
        }
976
977
        break;
    case SOCKOP_listen:
bellard's avatar
bellard committed
978
        {
979
980
            int sockfd = tgetl(vptr);
            int backlog = tgetl(vptr + n);
bellard's avatar
bellard committed
981
982
            ret = get_errno(listen(sockfd, backlog));
        }
983
984
985
        break;
    case SOCKOP_accept:
        {
986
987
988
            int sockfd = tgetl(vptr);
            target_ulong target_addr = tgetl(vptr + n);
            target_ulong target_addrlen = tgetl(vptr + 2 * n);
pbrook's avatar
pbrook committed
989
            ret = do_accept(sockfd, target_addr, target_addrlen);
990
991
992
993
        }
        break;
    case SOCKOP_getsockname:
        {
994
995
996
            int sockfd = tgetl(vptr);
            target_ulong target_addr = tgetl(vptr + n);
            target_ulong target_addrlen = tgetl(vptr + 2 * n);
pbrook's avatar
pbrook committed
997
            ret = do_getsockname(sockfd, target_addr, target_addrlen);
998
999
1000
        }
        break;
    case SOCKOP_getpeername:
For faster browsing, not all history is shown. View entire blame