Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: timod.c,v 1.19 2002/02/08 03:57:14 davem Exp $ |
2 | * timod.c: timod emulation. | |
3 | * | |
4 | * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) | |
5 | * | |
6 | * Streams & timod emulation based on code | |
7 | * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/types.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/sched.h> | |
14 | #include <linux/smp.h> | |
15 | #include <linux/smp_lock.h> | |
16 | #include <linux/ioctl.h> | |
17 | #include <linux/fs.h> | |
18 | #include <linux/file.h> | |
19 | #include <linux/netdevice.h> | |
20 | #include <linux/poll.h> | |
21 | ||
22 | #include <net/sock.h> | |
23 | ||
24 | #include <asm/uaccess.h> | |
25 | #include <asm/termios.h> | |
26 | ||
27 | #include "conv.h" | |
28 | #include "socksys.h" | |
29 | ||
30 | asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg); | |
31 | ||
32 | static DEFINE_SPINLOCK(timod_pagelock); | |
33 | static char * page = NULL ; | |
34 | ||
35 | #ifndef DEBUG_SOLARIS_KMALLOC | |
36 | ||
37 | #define mykmalloc kmalloc | |
38 | #define mykfree kfree | |
39 | ||
40 | #else | |
41 | ||
53f9fc93 | 42 | void * mykmalloc(size_t s, gfp_t gfp) |
1da177e4 LT |
43 | { |
44 | static char * page; | |
45 | static size_t free; | |
46 | void * r; | |
47 | s = ((s + 63) & ~63); | |
48 | if( s > PAGE_SIZE ) { | |
49 | SOLD("too big size, calling real kmalloc"); | |
50 | return kmalloc(s, gfp); | |
51 | } | |
52 | if( s > free ) { | |
53 | /* we are wasting memory, but we don't care */ | |
54 | page = (char *)__get_free_page(gfp); | |
55 | free = PAGE_SIZE; | |
56 | } | |
57 | r = page; | |
58 | page += s; | |
59 | free -= s; | |
60 | return r; | |
61 | } | |
62 | ||
63 | void mykfree(void *p) | |
64 | { | |
65 | } | |
66 | ||
67 | #endif | |
68 | ||
69 | #ifndef DEBUG_SOLARIS | |
70 | ||
71 | #define BUF_SIZE PAGE_SIZE | |
72 | #define PUT_MAGIC(a,m) | |
73 | #define SCHECK_MAGIC(a,m) | |
74 | #define BUF_OFFSET 0 | |
75 | #define MKCTL_TRAILER 0 | |
76 | ||
77 | #else | |
78 | ||
79 | #define BUF_SIZE (PAGE_SIZE-2*sizeof(u64)) | |
80 | #define BUFPAGE_MAGIC 0xBADC0DEDDEADBABEL | |
81 | #define MKCTL_MAGIC 0xDEADBABEBADC0DEDL | |
82 | #define PUT_MAGIC(a,m) do{(*(u64*)(a))=(m);}while(0) | |
83 | #define SCHECK_MAGIC(a,m) do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\ | |
9a4a6682 | 84 | __FILE__,__LINE__,__func__,(m),(a));}while(0) |
1da177e4 LT |
85 | #define BUF_OFFSET sizeof(u64) |
86 | #define MKCTL_TRAILER sizeof(u64) | |
87 | ||
88 | #endif | |
89 | ||
90 | static char *getpage( void ) | |
91 | { | |
92 | char *r; | |
93 | SOLD("getting page"); | |
94 | spin_lock(&timod_pagelock); | |
95 | if (page) { | |
96 | r = page; | |
97 | page = NULL; | |
98 | spin_unlock(&timod_pagelock); | |
99 | SOLD("got cached"); | |
100 | return r + BUF_OFFSET; | |
101 | } | |
102 | spin_unlock(&timod_pagelock); | |
103 | SOLD("getting new"); | |
104 | r = (char *)__get_free_page(GFP_KERNEL); | |
105 | PUT_MAGIC(r,BUFPAGE_MAGIC); | |
106 | PUT_MAGIC(r+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC); | |
107 | return r + BUF_OFFSET; | |
108 | } | |
109 | ||
110 | static void putpage(char *p) | |
111 | { | |
112 | SOLD("putting page"); | |
113 | p = p - BUF_OFFSET; | |
114 | SCHECK_MAGIC(p,BUFPAGE_MAGIC); | |
115 | SCHECK_MAGIC(p+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC); | |
116 | spin_lock(&timod_pagelock); | |
117 | if (page) { | |
118 | spin_unlock(&timod_pagelock); | |
119 | free_page((unsigned long)p); | |
120 | SOLD("freed it"); | |
121 | } else { | |
122 | page = p; | |
123 | spin_unlock(&timod_pagelock); | |
124 | SOLD("cached it"); | |
125 | } | |
126 | } | |
127 | ||
128 | static struct T_primsg *timod_mkctl(int size) | |
129 | { | |
130 | struct T_primsg *it; | |
131 | ||
132 | SOLD("creating primsg"); | |
133 | it = (struct T_primsg *)mykmalloc(size+sizeof(*it)-sizeof(s32)+2*MKCTL_TRAILER, GFP_KERNEL); | |
134 | if (it) { | |
135 | SOLD("got it"); | |
136 | it->pri = MSG_HIPRI; | |
137 | it->length = size; | |
138 | PUT_MAGIC((char*)((u64)(((char *)&it->type)+size+7)&~7),MKCTL_MAGIC); | |
139 | } | |
140 | return it; | |
141 | } | |
142 | ||
143 | static void timod_wake_socket(unsigned int fd) | |
144 | { | |
145 | struct socket *sock; | |
6e72ad2c | 146 | struct fdtable *fdt; |
1da177e4 LT |
147 | |
148 | SOLD("wakeing socket"); | |
6e72ad2c | 149 | fdt = files_fdtable(current->files); |
1250ca4c | 150 | sock = SOCKET_I(fdt->fd[fd]->f_path.dentry->d_inode); |
1da177e4 LT |
151 | wake_up_interruptible(&sock->wait); |
152 | read_lock(&sock->sk->sk_callback_lock); | |
153 | if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) | |
154 | __kill_fasync(sock->fasync_list, SIGIO, POLL_IN); | |
155 | read_unlock(&sock->sk->sk_callback_lock); | |
156 | SOLD("done"); | |
157 | } | |
158 | ||
159 | static void timod_queue(unsigned int fd, struct T_primsg *it) | |
160 | { | |
161 | struct sol_socket_struct *sock; | |
6e72ad2c | 162 | struct fdtable *fdt; |
1da177e4 LT |
163 | |
164 | SOLD("queuing primsg"); | |
6e72ad2c DS |
165 | fdt = files_fdtable(current->files); |
166 | sock = (struct sol_socket_struct *)fdt->fd[fd]->private_data; | |
1da177e4 LT |
167 | it->next = sock->pfirst; |
168 | sock->pfirst = it; | |
169 | if (!sock->plast) | |
170 | sock->plast = it; | |
171 | timod_wake_socket(fd); | |
172 | SOLD("done"); | |
173 | } | |
174 | ||
175 | static void timod_queue_end(unsigned int fd, struct T_primsg *it) | |
176 | { | |
177 | struct sol_socket_struct *sock; | |
6e72ad2c | 178 | struct fdtable *fdt; |
1da177e4 LT |
179 | |
180 | SOLD("queuing primsg at end"); | |
6e72ad2c DS |
181 | fdt = files_fdtable(current->files); |
182 | sock = (struct sol_socket_struct *)fdt->fd[fd]->private_data; | |
1da177e4 LT |
183 | it->next = NULL; |
184 | if (sock->plast) | |
185 | sock->plast->next = it; | |
186 | else | |
187 | sock->pfirst = it; | |
188 | sock->plast = it; | |
189 | SOLD("done"); | |
190 | } | |
191 | ||
192 | static void timod_error(unsigned int fd, int prim, int terr, int uerr) | |
193 | { | |
194 | struct T_primsg *it; | |
195 | ||
196 | SOLD("making error"); | |
197 | it = timod_mkctl(sizeof(struct T_error_ack)); | |
198 | if (it) { | |
199 | struct T_error_ack *err = (struct T_error_ack *)&it->type; | |
200 | ||
201 | SOLD("got it"); | |
202 | err->PRIM_type = T_ERROR_ACK; | |
203 | err->ERROR_prim = prim; | |
204 | err->TLI_error = terr; | |
205 | err->UNIX_error = uerr; /* FIXME: convert this */ | |
206 | timod_queue(fd, it); | |
207 | } | |
208 | SOLD("done"); | |
209 | } | |
210 | ||
211 | static void timod_ok(unsigned int fd, int prim) | |
212 | { | |
213 | struct T_primsg *it; | |
214 | struct T_ok_ack *ok; | |
215 | ||
216 | SOLD("creating ok ack"); | |
217 | it = timod_mkctl(sizeof(*ok)); | |
218 | if (it) { | |
219 | SOLD("got it"); | |
220 | ok = (struct T_ok_ack *)&it->type; | |
221 | ok->PRIM_type = T_OK_ACK; | |
222 | ok->CORRECT_prim = prim; | |
223 | timod_queue(fd, it); | |
224 | } | |
225 | SOLD("done"); | |
226 | } | |
227 | ||
228 | static int timod_optmgmt(unsigned int fd, int flag, char __user *opt_buf, int opt_len, int do_ret) | |
229 | { | |
230 | int error, failed; | |
231 | int ret_space, ret_len; | |
232 | long args[5]; | |
233 | char *ret_pos,*ret_buf; | |
234 | int (*sys_socketcall)(int, unsigned long *) = | |
235 | (int (*)(int, unsigned long *))SYS(socketcall); | |
236 | mm_segment_t old_fs = get_fs(); | |
237 | ||
238 | SOLD("entry"); | |
239 | SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret)); | |
240 | if (!do_ret && (!opt_buf || opt_len <= 0)) | |
241 | return 0; | |
242 | SOLD("getting page"); | |
243 | ret_pos = ret_buf = getpage(); | |
244 | ret_space = BUF_SIZE; | |
245 | ret_len = 0; | |
246 | ||
247 | error = failed = 0; | |
248 | SOLD("looping"); | |
249 | while(opt_len >= sizeof(struct opthdr)) { | |
250 | struct opthdr *opt; | |
251 | int orig_opt_len; | |
252 | SOLD("loop start"); | |
253 | opt = (struct opthdr *)ret_pos; | |
254 | if (ret_space < sizeof(struct opthdr)) { | |
255 | failed = TSYSERR; | |
256 | break; | |
257 | } | |
258 | SOLD("getting opthdr"); | |
259 | if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) || | |
260 | opt->len > opt_len) { | |
261 | failed = TBADOPT; | |
262 | break; | |
263 | } | |
264 | SOLD("got opthdr"); | |
265 | if (flag == T_NEGOTIATE) { | |
266 | char *buf; | |
267 | ||
268 | SOLD("handling T_NEGOTIATE"); | |
269 | buf = ret_pos + sizeof(struct opthdr); | |
270 | if (ret_space < opt->len + sizeof(struct opthdr) || | |
271 | copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) { | |
272 | failed = TSYSERR; | |
273 | break; | |
274 | } | |
275 | SOLD("got optdata"); | |
276 | args[0] = fd; | |
277 | args[1] = opt->level; | |
278 | args[2] = opt->name; | |
279 | args[3] = (long)buf; | |
280 | args[4] = opt->len; | |
281 | SOLD("calling SETSOCKOPT"); | |
282 | set_fs(KERNEL_DS); | |
283 | error = sys_socketcall(SYS_SETSOCKOPT, args); | |
284 | set_fs(old_fs); | |
285 | if (error) { | |
286 | failed = TBADOPT; | |
287 | break; | |
288 | } | |
289 | SOLD("SETSOCKOPT ok"); | |
290 | } | |
291 | orig_opt_len = opt->len; | |
292 | opt->len = ret_space - sizeof(struct opthdr); | |
293 | if (opt->len < 0) { | |
294 | failed = TSYSERR; | |
295 | break; | |
296 | } | |
297 | args[0] = fd; | |
298 | args[1] = opt->level; | |
299 | args[2] = opt->name; | |
300 | args[3] = (long)(ret_pos+sizeof(struct opthdr)); | |
301 | args[4] = (long)&opt->len; | |
302 | SOLD("calling GETSOCKOPT"); | |
303 | set_fs(KERNEL_DS); | |
304 | error = sys_socketcall(SYS_GETSOCKOPT, args); | |
305 | set_fs(old_fs); | |
306 | if (error) { | |
307 | failed = TBADOPT; | |
308 | break; | |
309 | } | |
310 | SOLD("GETSOCKOPT ok"); | |
311 | ret_space -= sizeof(struct opthdr) + opt->len; | |
312 | ret_len += sizeof(struct opthdr) + opt->len; | |
313 | ret_pos += sizeof(struct opthdr) + opt->len; | |
314 | opt_len -= sizeof(struct opthdr) + orig_opt_len; | |
315 | opt_buf += sizeof(struct opthdr) + orig_opt_len; | |
316 | SOLD("loop end"); | |
317 | } | |
318 | SOLD("loop done"); | |
319 | if (do_ret) { | |
320 | SOLD("generating ret msg"); | |
321 | if (failed) | |
322 | timod_error(fd, T_OPTMGMT_REQ, failed, -error); | |
323 | else { | |
324 | struct T_primsg *it; | |
325 | it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len); | |
326 | if (it) { | |
327 | struct T_optmgmt_ack *ack = | |
328 | (struct T_optmgmt_ack *)&it->type; | |
329 | SOLD("got primsg"); | |
330 | ack->PRIM_type = T_OPTMGMT_ACK; | |
331 | ack->OPT_length = ret_len; | |
332 | ack->OPT_offset = sizeof(struct T_optmgmt_ack); | |
333 | ack->MGMT_flags = (failed ? T_FAILURE : flag); | |
334 | memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack), | |
335 | ret_buf, ret_len); | |
336 | timod_queue(fd, it); | |
337 | } | |
338 | } | |
339 | } | |
340 | SOLDD(("put_page %p\n", ret_buf)); | |
341 | putpage(ret_buf); | |
342 | SOLD("done"); | |
343 | return 0; | |
344 | } | |
345 | ||
346 | int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len, | |
347 | char __user *data_buf, int data_len, int flags) | |
348 | { | |
349 | int ret, error, terror; | |
350 | char *buf; | |
351 | struct file *filp; | |
352 | struct inode *ino; | |
6e72ad2c | 353 | struct fdtable *fdt; |
1da177e4 LT |
354 | struct sol_socket_struct *sock; |
355 | mm_segment_t old_fs = get_fs(); | |
356 | long args[6]; | |
357 | int (*sys_socketcall)(int, unsigned long __user *) = | |
358 | (int (*)(int, unsigned long __user *))SYS(socketcall); | |
359 | int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int) = | |
360 | (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int))SYS(sendto); | |
6e72ad2c DS |
361 | |
362 | fdt = files_fdtable(current->files); | |
363 | filp = fdt->fd[fd]; | |
1250ca4c | 364 | ino = filp->f_path.dentry->d_inode; |
1da177e4 LT |
365 | sock = (struct sol_socket_struct *)filp->private_data; |
366 | SOLD("entry"); | |
367 | if (get_user(ret, (int __user *)A(ctl_buf))) | |
368 | return -EFAULT; | |
369 | switch (ret) { | |
370 | case T_BIND_REQ: | |
371 | { | |
372 | struct T_bind_req req; | |
373 | ||
374 | SOLDD(("bind %016lx(%016lx)\n", sock, filp)); | |
375 | SOLD("T_BIND_REQ"); | |
376 | if (sock->state != TS_UNBND) { | |
377 | timod_error(fd, T_BIND_REQ, TOUTSTATE, 0); | |
378 | return 0; | |
379 | } | |
380 | SOLD("state ok"); | |
381 | if (copy_from_user(&req, ctl_buf, sizeof(req))) { | |
382 | timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); | |
383 | return 0; | |
384 | } | |
385 | SOLD("got ctl req"); | |
386 | if (req.ADDR_offset && req.ADDR_length) { | |
387 | if (req.ADDR_length > BUF_SIZE) { | |
388 | timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); | |
389 | return 0; | |
390 | } | |
391 | SOLD("req size ok"); | |
392 | buf = getpage(); | |
393 | if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) { | |
394 | timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); | |
395 | putpage(buf); | |
396 | return 0; | |
397 | } | |
398 | SOLD("got ctl data"); | |
399 | args[0] = fd; | |
400 | args[1] = (long)buf; | |
401 | args[2] = req.ADDR_length; | |
402 | SOLD("calling BIND"); | |
403 | set_fs(KERNEL_DS); | |
404 | error = sys_socketcall(SYS_BIND, args); | |
405 | set_fs(old_fs); | |
406 | putpage(buf); | |
407 | SOLD("BIND returned"); | |
408 | } else | |
409 | error = 0; | |
410 | if (!error) { | |
411 | struct T_primsg *it; | |
412 | if (req.CONIND_number) { | |
413 | args[0] = fd; | |
414 | args[1] = req.CONIND_number; | |
415 | SOLD("calling LISTEN"); | |
416 | set_fs(KERNEL_DS); | |
417 | error = sys_socketcall(SYS_LISTEN, args); | |
418 | set_fs(old_fs); | |
419 | SOLD("LISTEN done"); | |
420 | } | |
421 | it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr)); | |
422 | if (it) { | |
423 | struct T_bind_ack *ack; | |
424 | ||
425 | ack = (struct T_bind_ack *)&it->type; | |
426 | ack->PRIM_type = T_BIND_ACK; | |
427 | ack->ADDR_offset = sizeof(*ack); | |
428 | ack->ADDR_length = sizeof(struct sockaddr); | |
429 | ack->CONIND_number = req.CONIND_number; | |
430 | args[0] = fd; | |
431 | args[1] = (long)(ack+sizeof(*ack)); | |
432 | args[2] = (long)&ack->ADDR_length; | |
433 | set_fs(KERNEL_DS); | |
434 | sys_socketcall(SYS_GETSOCKNAME,args); | |
435 | set_fs(old_fs); | |
436 | sock->state = TS_IDLE; | |
437 | timod_ok(fd, T_BIND_REQ); | |
438 | timod_queue_end(fd, it); | |
439 | SOLD("BIND done"); | |
440 | return 0; | |
441 | } | |
442 | } | |
443 | SOLD("some error"); | |
444 | switch (error) { | |
445 | case -EINVAL: | |
446 | terror = TOUTSTATE; | |
447 | error = 0; | |
448 | break; | |
449 | case -EACCES: | |
450 | terror = TACCES; | |
451 | error = 0; | |
452 | break; | |
453 | case -EADDRNOTAVAIL: | |
454 | case -EADDRINUSE: | |
455 | terror = TNOADDR; | |
456 | error = 0; | |
457 | break; | |
458 | default: | |
459 | terror = TSYSERR; | |
460 | break; | |
461 | } | |
462 | timod_error(fd, T_BIND_REQ, terror, -error); | |
463 | SOLD("BIND done"); | |
464 | return 0; | |
465 | } | |
466 | case T_CONN_REQ: | |
467 | { | |
468 | struct T_conn_req req; | |
469 | unsigned short oldflags; | |
470 | struct T_primsg *it; | |
471 | SOLD("T_CONN_REQ"); | |
472 | if (sock->state != TS_UNBND && sock->state != TS_IDLE) { | |
473 | timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); | |
474 | return 0; | |
475 | } | |
476 | SOLD("state ok"); | |
477 | if (copy_from_user(&req, ctl_buf, sizeof(req))) { | |
478 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | |
479 | return 0; | |
480 | } | |
481 | SOLD("got ctl req"); | |
482 | if (ctl_len > BUF_SIZE) { | |
483 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | |
484 | return 0; | |
485 | } | |
486 | SOLD("req size ok"); | |
487 | buf = getpage(); | |
488 | if (copy_from_user(buf, ctl_buf, ctl_len)) { | |
489 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | |
490 | putpage(buf); | |
491 | return 0; | |
492 | } | |
493 | #ifdef DEBUG_SOLARIS | |
494 | { | |
495 | char * ptr = buf; | |
496 | int len = ctl_len; | |
497 | printk("returned data (%d bytes): ",len); | |
498 | while( len-- ) { | |
499 | if (!(len & 7)) | |
500 | printk(" "); | |
501 | printk("%02x",(unsigned char)*ptr++); | |
502 | } | |
503 | printk("\n"); | |
504 | } | |
505 | #endif | |
506 | SOLD("got ctl data"); | |
507 | args[0] = fd; | |
508 | args[1] = (long)buf+req.DEST_offset; | |
509 | args[2] = req.DEST_length; | |
510 | oldflags = filp->f_flags; | |
511 | filp->f_flags &= ~O_NONBLOCK; | |
512 | SOLD("calling CONNECT"); | |
513 | set_fs(KERNEL_DS); | |
514 | error = sys_socketcall(SYS_CONNECT, args); | |
515 | set_fs(old_fs); | |
516 | filp->f_flags = oldflags; | |
517 | SOLD("CONNECT done"); | |
518 | if (!error) { | |
519 | struct T_conn_con *con; | |
520 | SOLD("no error"); | |
521 | it = timod_mkctl(ctl_len); | |
522 | if (!it) { | |
523 | putpage(buf); | |
524 | return -ENOMEM; | |
525 | } | |
526 | con = (struct T_conn_con *)&it->type; | |
527 | #ifdef DEBUG_SOLARIS | |
528 | { | |
529 | char * ptr = buf; | |
530 | int len = ctl_len; | |
531 | printk("returned data (%d bytes): ",len); | |
532 | while( len-- ) { | |
533 | if (!(len & 7)) | |
534 | printk(" "); | |
535 | printk("%02x",(unsigned char)*ptr++); | |
536 | } | |
537 | printk("\n"); | |
538 | } | |
539 | #endif | |
540 | memcpy(con, buf, ctl_len); | |
541 | SOLD("copied ctl_buf"); | |
542 | con->PRIM_type = T_CONN_CON; | |
543 | sock->state = TS_DATA_XFER; | |
544 | } else { | |
545 | struct T_discon_ind *dis; | |
546 | SOLD("some error"); | |
547 | it = timod_mkctl(sizeof(*dis)); | |
548 | if (!it) { | |
549 | putpage(buf); | |
550 | return -ENOMEM; | |
551 | } | |
552 | SOLD("got primsg"); | |
553 | dis = (struct T_discon_ind *)&it->type; | |
554 | dis->PRIM_type = T_DISCON_IND; | |
555 | dis->DISCON_reason = -error; /* FIXME: convert this as in iABI_errors() */ | |
556 | dis->SEQ_number = 0; | |
557 | } | |
558 | putpage(buf); | |
559 | timod_ok(fd, T_CONN_REQ); | |
560 | it->pri = 0; | |
561 | timod_queue_end(fd, it); | |
562 | SOLD("CONNECT done"); | |
563 | return 0; | |
564 | } | |
565 | case T_OPTMGMT_REQ: | |
566 | { | |
567 | struct T_optmgmt_req req; | |
568 | SOLD("OPTMGMT_REQ"); | |
569 | if (copy_from_user(&req, ctl_buf, sizeof(req))) | |
570 | return -EFAULT; | |
571 | SOLD("got req"); | |
572 | return timod_optmgmt(fd, req.MGMT_flags, | |
573 | req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL, | |
574 | req.OPT_length, 1); | |
575 | } | |
576 | case T_UNITDATA_REQ: | |
577 | { | |
578 | struct T_unitdata_req req; | |
579 | ||
580 | int err; | |
581 | SOLD("T_UNITDATA_REQ"); | |
582 | if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) { | |
583 | timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); | |
584 | return 0; | |
585 | } | |
586 | SOLD("state ok"); | |
587 | if (copy_from_user(&req, ctl_buf, sizeof(req))) { | |
588 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | |
589 | return 0; | |
590 | } | |
591 | SOLD("got ctl req"); | |
592 | #ifdef DEBUG_SOLARIS | |
593 | { | |
594 | char * ptr = ctl_buf+req.DEST_offset; | |
595 | int len = req.DEST_length; | |
596 | printk("socket address (%d bytes): ",len); | |
597 | while( len-- ) { | |
598 | char c; | |
599 | if (get_user(c,ptr)) | |
600 | printk("??"); | |
601 | else | |
602 | printk("%02x",(unsigned char)c); | |
603 | ptr++; | |
604 | } | |
605 | printk("\n"); | |
606 | } | |
607 | #endif | |
608 | err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr __user *)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length); | |
609 | if (err == data_len) | |
610 | return 0; | |
611 | if(err >= 0) { | |
612 | printk("timod: sendto failed to send all the data\n"); | |
613 | return 0; | |
614 | } | |
615 | timod_error(fd, T_CONN_REQ, TSYSERR, -err); | |
616 | return 0; | |
617 | } | |
618 | default: | |
619 | printk(KERN_INFO "timod_putmsg: unsupported command %u.\n", ret); | |
620 | break; | |
621 | } | |
622 | return -EINVAL; | |
623 | } | |
624 | ||
625 | int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, s32 __user *ctl_len, | |
626 | char __user *data_buf, int data_maxlen, s32 __user *data_len, int *flags_p) | |
627 | { | |
628 | int error; | |
629 | int oldflags; | |
630 | struct file *filp; | |
631 | struct inode *ino; | |
6e72ad2c | 632 | struct fdtable *fdt; |
1da177e4 LT |
633 | struct sol_socket_struct *sock; |
634 | struct T_unitdata_ind udi; | |
635 | mm_segment_t old_fs = get_fs(); | |
636 | long args[6]; | |
637 | char __user *tmpbuf; | |
638 | int tmplen; | |
639 | int (*sys_socketcall)(int, unsigned long __user *) = | |
640 | (int (*)(int, unsigned long __user *))SYS(socketcall); | |
641 | int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *); | |
642 | ||
643 | SOLD("entry"); | |
644 | SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p)); | |
6e72ad2c DS |
645 | fdt = files_fdtable(current->files); |
646 | filp = fdt->fd[fd]; | |
1250ca4c | 647 | ino = filp->f_path.dentry->d_inode; |
1da177e4 LT |
648 | sock = (struct sol_socket_struct *)filp->private_data; |
649 | SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL)); | |
650 | if ( ctl_maxlen > 0 && !sock->pfirst && SOCKET_I(ino)->type == SOCK_STREAM | |
651 | && sock->state == TS_IDLE) { | |
652 | SOLD("calling LISTEN"); | |
653 | args[0] = fd; | |
654 | args[1] = -1; | |
655 | set_fs(KERNEL_DS); | |
656 | sys_socketcall(SYS_LISTEN, args); | |
657 | set_fs(old_fs); | |
658 | SOLD("LISTEN done"); | |
659 | } | |
660 | if (!(filp->f_flags & O_NONBLOCK)) { | |
661 | struct poll_wqueues wait_table; | |
662 | poll_table *wait; | |
663 | ||
664 | poll_initwait(&wait_table); | |
665 | wait = &wait_table.pt; | |
666 | for(;;) { | |
667 | SOLD("loop"); | |
668 | set_current_state(TASK_INTERRUPTIBLE); | |
669 | /* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ | |
670 | /* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ | |
671 | /* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ | |
672 | /* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */ | |
673 | /* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */ | |
674 | /* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */ | |
675 | if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI)) | |
676 | break; | |
677 | SOLD("cond 1 passed"); | |
678 | if ( | |
679 | #if 1 | |
680 | *flags_p != MSG_HIPRI && | |
681 | #endif | |
682 | ((filp->f_op->poll(filp, wait) & POLLIN) || | |
683 | (filp->f_op->poll(filp, NULL) & POLLIN) || | |
684 | signal_pending(current)) | |
685 | ) { | |
686 | break; | |
687 | } | |
688 | if( *flags_p == MSG_HIPRI ) { | |
689 | SOLD("avoiding lockup"); | |
690 | break ; | |
691 | } | |
692 | if(wait_table.error) { | |
693 | SOLD("wait-table error"); | |
694 | poll_freewait(&wait_table); | |
695 | return wait_table.error; | |
696 | } | |
697 | SOLD("scheduling"); | |
698 | schedule(); | |
699 | } | |
700 | SOLD("loop done"); | |
701 | current->state = TASK_RUNNING; | |
702 | poll_freewait(&wait_table); | |
703 | if (signal_pending(current)) { | |
704 | SOLD("signal pending"); | |
705 | return -EINTR; | |
706 | } | |
707 | } | |
708 | if (ctl_maxlen >= 0 && sock->pfirst) { | |
709 | struct T_primsg *it = sock->pfirst; | |
710 | int l = min_t(int, ctl_maxlen, it->length); | |
711 | SCHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC); | |
712 | SOLD("purting ctl data"); | |
713 | if(copy_to_user(ctl_buf, | |
714 | (char*)&it->type + sock->offset, l)) | |
715 | return -EFAULT; | |
716 | SOLD("pur it"); | |
717 | if(put_user(l, ctl_len)) | |
718 | return -EFAULT; | |
719 | SOLD("set ctl_len"); | |
720 | *flags_p = it->pri; | |
721 | it->length -= l; | |
722 | if (it->length) { | |
723 | SOLD("more ctl"); | |
724 | sock->offset += l; | |
725 | return MORECTL; | |
726 | } else { | |
727 | SOLD("removing message"); | |
728 | sock->pfirst = it->next; | |
729 | if (!sock->pfirst) | |
730 | sock->plast = NULL; | |
731 | SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst)); | |
732 | mykfree(it); | |
733 | sock->offset = 0; | |
734 | SOLD("ctl done"); | |
735 | return 0; | |
736 | } | |
737 | } | |
738 | *flags_p = 0; | |
739 | if (ctl_maxlen >= 0) { | |
740 | SOLD("ACCEPT perhaps?"); | |
741 | if (SOCKET_I(ino)->type == SOCK_STREAM && sock->state == TS_IDLE) { | |
742 | struct T_conn_ind ind; | |
743 | char *buf = getpage(); | |
744 | int len = BUF_SIZE; | |
745 | ||
746 | SOLD("trying ACCEPT"); | |
747 | if (put_user(ctl_maxlen - sizeof(ind), ctl_len)) | |
748 | return -EFAULT; | |
749 | args[0] = fd; | |
750 | args[1] = (long)buf; | |
751 | args[2] = (long)&len; | |
752 | oldflags = filp->f_flags; | |
753 | filp->f_flags |= O_NONBLOCK; | |
754 | SOLD("calling ACCEPT"); | |
755 | set_fs(KERNEL_DS); | |
756 | error = sys_socketcall(SYS_ACCEPT, args); | |
757 | set_fs(old_fs); | |
758 | filp->f_flags = oldflags; | |
759 | if (error < 0) { | |
760 | SOLD("some error"); | |
761 | putpage(buf); | |
762 | return error; | |
763 | } | |
764 | if (error) { | |
765 | SOLD("connect"); | |
766 | putpage(buf); | |
767 | if (sizeof(ind) > ctl_maxlen) { | |
768 | SOLD("generating CONN_IND"); | |
769 | ind.PRIM_type = T_CONN_IND; | |
770 | ind.SRC_length = len; | |
771 | ind.SRC_offset = sizeof(ind); | |
772 | ind.OPT_length = ind.OPT_offset = 0; | |
773 | ind.SEQ_number = error; | |
774 | if(copy_to_user(ctl_buf, &ind, sizeof(ind))|| | |
775 | put_user(sizeof(ind)+ind.SRC_length,ctl_len)) | |
776 | return -EFAULT; | |
777 | SOLD("CONN_IND created"); | |
778 | } | |
779 | if (data_maxlen >= 0) | |
780 | put_user(0, data_len); | |
781 | SOLD("CONN_IND done"); | |
782 | return 0; | |
783 | } | |
784 | if (len>ctl_maxlen) { | |
785 | SOLD("data don't fit"); | |
786 | putpage(buf); | |
787 | return -EFAULT; /* XXX - is this ok ? */ | |
788 | } | |
789 | if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){ | |
790 | SOLD("can't copy data"); | |
791 | putpage(buf); | |
792 | return -EFAULT; | |
793 | } | |
794 | SOLD("ACCEPT done"); | |
795 | putpage(buf); | |
796 | } | |
797 | } | |
798 | SOLD("checking data req"); | |
799 | if (data_maxlen <= 0) { | |
800 | if (data_maxlen == 0) | |
801 | put_user(0, data_len); | |
802 | if (ctl_maxlen >= 0) | |
803 | put_user(0, ctl_len); | |
804 | return -EAGAIN; | |
805 | } | |
806 | SOLD("wants data"); | |
807 | if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { | |
808 | SOLD("udi fits"); | |
809 | tmpbuf = ctl_buf + sizeof(udi); | |
810 | tmplen = ctl_maxlen - sizeof(udi); | |
811 | } else { | |
812 | SOLD("udi does not fit"); | |
813 | tmpbuf = NULL; | |
814 | tmplen = 0; | |
815 | } | |
816 | if (put_user(tmplen, ctl_len)) | |
817 | return -EFAULT; | |
818 | SOLD("set ctl_len"); | |
819 | oldflags = filp->f_flags; | |
820 | filp->f_flags |= O_NONBLOCK; | |
821 | SOLD("calling recvfrom"); | |
822 | sys_recvfrom = (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom); | |
823 | error = sys_recvfrom(fd, data_buf, data_maxlen, 0, (struct sockaddr __user *)tmpbuf, ctl_len); | |
824 | filp->f_flags = oldflags; | |
825 | if (error < 0) | |
826 | return error; | |
827 | SOLD("error >= 0" ) ; | |
828 | if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { | |
829 | SOLD("generating udi"); | |
830 | udi.PRIM_type = T_UNITDATA_IND; | |
831 | if (get_user(udi.SRC_length, ctl_len)) | |
832 | return -EFAULT; | |
833 | udi.SRC_offset = sizeof(udi); | |
834 | udi.OPT_length = udi.OPT_offset = 0; | |
835 | if (copy_to_user(ctl_buf, &udi, sizeof(udi)) || | |
836 | put_user(sizeof(udi)+udi.SRC_length, ctl_len)) | |
837 | return -EFAULT; | |
838 | SOLD("udi done"); | |
839 | } else { | |
840 | if (put_user(0, ctl_len)) | |
841 | return -EFAULT; | |
842 | } | |
843 | put_user(error, data_len); | |
844 | SOLD("done"); | |
845 | return 0; | |
846 | } | |
847 | ||
848 | asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) | |
849 | { | |
850 | struct file *filp; | |
851 | struct inode *ino; | |
852 | struct strbuf __user *ctlptr; | |
853 | struct strbuf __user *datptr; | |
854 | struct strbuf ctl, dat; | |
855 | int __user *flgptr; | |
856 | int flags; | |
857 | int error = -EBADF; | |
6e72ad2c | 858 | struct fdtable *fdt; |
1da177e4 LT |
859 | |
860 | SOLD("entry"); | |
861 | lock_kernel(); | |
9cfe015a ED |
862 | if (fd >= sysctl_nr_open) |
863 | goto out; | |
1da177e4 | 864 | |
6e72ad2c DS |
865 | fdt = files_fdtable(current->files); |
866 | filp = fdt->fd[fd]; | |
1da177e4 LT |
867 | if(!filp) goto out; |
868 | ||
1250ca4c | 869 | ino = filp->f_path.dentry->d_inode; |
1da177e4 LT |
870 | if (!ino || !S_ISSOCK(ino->i_mode)) |
871 | goto out; | |
872 | ||
873 | ctlptr = (struct strbuf __user *)A(arg1); | |
874 | datptr = (struct strbuf __user *)A(arg2); | |
875 | flgptr = (int __user *)A(arg3); | |
876 | ||
877 | error = -EFAULT; | |
878 | ||
879 | if (ctlptr) { | |
880 | if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) || | |
881 | put_user(-1,&ctlptr->len)) | |
882 | goto out; | |
883 | } else | |
884 | ctl.maxlen = -1; | |
885 | ||
886 | if (datptr) { | |
887 | if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) || | |
888 | put_user(-1,&datptr->len)) | |
889 | goto out; | |
890 | } else | |
891 | dat.maxlen = -1; | |
892 | ||
893 | if (get_user(flags,flgptr)) | |
894 | goto out; | |
895 | ||
896 | switch (flags) { | |
897 | case 0: | |
898 | case MSG_HIPRI: | |
899 | case MSG_ANY: | |
900 | case MSG_BAND: | |
901 | break; | |
902 | default: | |
903 | error = -EINVAL; | |
904 | goto out; | |
905 | } | |
906 | ||
907 | error = timod_getmsg(fd,A(ctl.buf),ctl.maxlen,&ctlptr->len, | |
908 | A(dat.buf),dat.maxlen,&datptr->len,&flags); | |
909 | ||
910 | if (!error && put_user(flags,flgptr)) | |
911 | error = -EFAULT; | |
912 | out: | |
913 | unlock_kernel(); | |
914 | SOLD("done"); | |
915 | return error; | |
916 | } | |
917 | ||
918 | asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) | |
919 | { | |
920 | struct file *filp; | |
921 | struct inode *ino; | |
922 | struct strbuf __user *ctlptr; | |
923 | struct strbuf __user *datptr; | |
924 | struct strbuf ctl, dat; | |
925 | int flags = (int) arg3; | |
926 | int error = -EBADF; | |
6e72ad2c | 927 | struct fdtable *fdt; |
1da177e4 LT |
928 | |
929 | SOLD("entry"); | |
930 | lock_kernel(); | |
9cfe015a ED |
931 | if (fd >= sysctl_nr_open) |
932 | goto out; | |
1da177e4 | 933 | |
6e72ad2c DS |
934 | fdt = files_fdtable(current->files); |
935 | filp = fdt->fd[fd]; | |
1da177e4 LT |
936 | if(!filp) goto out; |
937 | ||
1250ca4c | 938 | ino = filp->f_path.dentry->d_inode; |
1da177e4 LT |
939 | if (!ino) goto out; |
940 | ||
941 | if (!S_ISSOCK(ino->i_mode) && | |
942 | (imajor(ino) != 30 || iminor(ino) != 1)) | |
943 | goto out; | |
944 | ||
945 | ctlptr = A(arg1); | |
946 | datptr = A(arg2); | |
947 | ||
948 | error = -EFAULT; | |
949 | ||
950 | if (ctlptr) { | |
951 | if (copy_from_user(&ctl,ctlptr,sizeof(ctl))) | |
952 | goto out; | |
953 | if (ctl.len < 0 && flags) { | |
954 | error = -EINVAL; | |
955 | goto out; | |
956 | } | |
957 | } else { | |
958 | ctl.len = 0; | |
959 | ctl.buf = 0; | |
960 | } | |
961 | ||
962 | if (datptr) { | |
963 | if (copy_from_user(&dat,datptr,sizeof(dat))) | |
964 | goto out; | |
965 | } else { | |
966 | dat.len = 0; | |
967 | dat.buf = 0; | |
968 | } | |
969 | ||
970 | error = timod_putmsg(fd,A(ctl.buf),ctl.len, | |
971 | A(dat.buf),dat.len,flags); | |
972 | out: | |
973 | unlock_kernel(); | |
974 | SOLD("done"); | |
975 | return error; | |
976 | } |