diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sparc64/solaris/timod.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/sparc64/solaris/timod.c')
-rw-r--r-- | arch/sparc64/solaris/timod.c | 959 |
1 files changed, 959 insertions, 0 deletions
diff --git a/arch/sparc64/solaris/timod.c b/arch/sparc64/solaris/timod.c new file mode 100644 index 000000000000..022c80f43392 --- /dev/null +++ b/arch/sparc64/solaris/timod.c | |||
@@ -0,0 +1,959 @@ | |||
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 | |||
42 | void * mykmalloc(size_t s, int gfp) | ||
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",\ | ||
84 | __FILE__,__LINE__,__FUNCTION__,(m),(a));}while(0) | ||
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; | ||
146 | |||
147 | SOLD("wakeing socket"); | ||
148 | sock = SOCKET_I(current->files->fd[fd]->f_dentry->d_inode); | ||
149 | wake_up_interruptible(&sock->wait); | ||
150 | read_lock(&sock->sk->sk_callback_lock); | ||
151 | if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) | ||
152 | __kill_fasync(sock->fasync_list, SIGIO, POLL_IN); | ||
153 | read_unlock(&sock->sk->sk_callback_lock); | ||
154 | SOLD("done"); | ||
155 | } | ||
156 | |||
157 | static void timod_queue(unsigned int fd, struct T_primsg *it) | ||
158 | { | ||
159 | struct sol_socket_struct *sock; | ||
160 | |||
161 | SOLD("queuing primsg"); | ||
162 | sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; | ||
163 | it->next = sock->pfirst; | ||
164 | sock->pfirst = it; | ||
165 | if (!sock->plast) | ||
166 | sock->plast = it; | ||
167 | timod_wake_socket(fd); | ||
168 | SOLD("done"); | ||
169 | } | ||
170 | |||
171 | static void timod_queue_end(unsigned int fd, struct T_primsg *it) | ||
172 | { | ||
173 | struct sol_socket_struct *sock; | ||
174 | |||
175 | SOLD("queuing primsg at end"); | ||
176 | sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; | ||
177 | it->next = NULL; | ||
178 | if (sock->plast) | ||
179 | sock->plast->next = it; | ||
180 | else | ||
181 | sock->pfirst = it; | ||
182 | sock->plast = it; | ||
183 | SOLD("done"); | ||
184 | } | ||
185 | |||
186 | static void timod_error(unsigned int fd, int prim, int terr, int uerr) | ||
187 | { | ||
188 | struct T_primsg *it; | ||
189 | |||
190 | SOLD("making error"); | ||
191 | it = timod_mkctl(sizeof(struct T_error_ack)); | ||
192 | if (it) { | ||
193 | struct T_error_ack *err = (struct T_error_ack *)&it->type; | ||
194 | |||
195 | SOLD("got it"); | ||
196 | err->PRIM_type = T_ERROR_ACK; | ||
197 | err->ERROR_prim = prim; | ||
198 | err->TLI_error = terr; | ||
199 | err->UNIX_error = uerr; /* FIXME: convert this */ | ||
200 | timod_queue(fd, it); | ||
201 | } | ||
202 | SOLD("done"); | ||
203 | } | ||
204 | |||
205 | static void timod_ok(unsigned int fd, int prim) | ||
206 | { | ||
207 | struct T_primsg *it; | ||
208 | struct T_ok_ack *ok; | ||
209 | |||
210 | SOLD("creating ok ack"); | ||
211 | it = timod_mkctl(sizeof(*ok)); | ||
212 | if (it) { | ||
213 | SOLD("got it"); | ||
214 | ok = (struct T_ok_ack *)&it->type; | ||
215 | ok->PRIM_type = T_OK_ACK; | ||
216 | ok->CORRECT_prim = prim; | ||
217 | timod_queue(fd, it); | ||
218 | } | ||
219 | SOLD("done"); | ||
220 | } | ||
221 | |||
222 | static int timod_optmgmt(unsigned int fd, int flag, char __user *opt_buf, int opt_len, int do_ret) | ||
223 | { | ||
224 | int error, failed; | ||
225 | int ret_space, ret_len; | ||
226 | long args[5]; | ||
227 | char *ret_pos,*ret_buf; | ||
228 | int (*sys_socketcall)(int, unsigned long *) = | ||
229 | (int (*)(int, unsigned long *))SYS(socketcall); | ||
230 | mm_segment_t old_fs = get_fs(); | ||
231 | |||
232 | SOLD("entry"); | ||
233 | SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret)); | ||
234 | if (!do_ret && (!opt_buf || opt_len <= 0)) | ||
235 | return 0; | ||
236 | SOLD("getting page"); | ||
237 | ret_pos = ret_buf = getpage(); | ||
238 | ret_space = BUF_SIZE; | ||
239 | ret_len = 0; | ||
240 | |||
241 | error = failed = 0; | ||
242 | SOLD("looping"); | ||
243 | while(opt_len >= sizeof(struct opthdr)) { | ||
244 | struct opthdr *opt; | ||
245 | int orig_opt_len; | ||
246 | SOLD("loop start"); | ||
247 | opt = (struct opthdr *)ret_pos; | ||
248 | if (ret_space < sizeof(struct opthdr)) { | ||
249 | failed = TSYSERR; | ||
250 | break; | ||
251 | } | ||
252 | SOLD("getting opthdr"); | ||
253 | if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) || | ||
254 | opt->len > opt_len) { | ||
255 | failed = TBADOPT; | ||
256 | break; | ||
257 | } | ||
258 | SOLD("got opthdr"); | ||
259 | if (flag == T_NEGOTIATE) { | ||
260 | char *buf; | ||
261 | |||
262 | SOLD("handling T_NEGOTIATE"); | ||
263 | buf = ret_pos + sizeof(struct opthdr); | ||
264 | if (ret_space < opt->len + sizeof(struct opthdr) || | ||
265 | copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) { | ||
266 | failed = TSYSERR; | ||
267 | break; | ||
268 | } | ||
269 | SOLD("got optdata"); | ||
270 | args[0] = fd; | ||
271 | args[1] = opt->level; | ||
272 | args[2] = opt->name; | ||
273 | args[3] = (long)buf; | ||
274 | args[4] = opt->len; | ||
275 | SOLD("calling SETSOCKOPT"); | ||
276 | set_fs(KERNEL_DS); | ||
277 | error = sys_socketcall(SYS_SETSOCKOPT, args); | ||
278 | set_fs(old_fs); | ||
279 | if (error) { | ||
280 | failed = TBADOPT; | ||
281 | break; | ||
282 | } | ||
283 | SOLD("SETSOCKOPT ok"); | ||
284 | } | ||
285 | orig_opt_len = opt->len; | ||
286 | opt->len = ret_space - sizeof(struct opthdr); | ||
287 | if (opt->len < 0) { | ||
288 | failed = TSYSERR; | ||
289 | break; | ||
290 | } | ||
291 | args[0] = fd; | ||
292 | args[1] = opt->level; | ||
293 | args[2] = opt->name; | ||
294 | args[3] = (long)(ret_pos+sizeof(struct opthdr)); | ||
295 | args[4] = (long)&opt->len; | ||
296 | SOLD("calling GETSOCKOPT"); | ||
297 | set_fs(KERNEL_DS); | ||
298 | error = sys_socketcall(SYS_GETSOCKOPT, args); | ||
299 | set_fs(old_fs); | ||
300 | if (error) { | ||
301 | failed = TBADOPT; | ||
302 | break; | ||
303 | } | ||
304 | SOLD("GETSOCKOPT ok"); | ||
305 | ret_space -= sizeof(struct opthdr) + opt->len; | ||
306 | ret_len += sizeof(struct opthdr) + opt->len; | ||
307 | ret_pos += sizeof(struct opthdr) + opt->len; | ||
308 | opt_len -= sizeof(struct opthdr) + orig_opt_len; | ||
309 | opt_buf += sizeof(struct opthdr) + orig_opt_len; | ||
310 | SOLD("loop end"); | ||
311 | } | ||
312 | SOLD("loop done"); | ||
313 | if (do_ret) { | ||
314 | SOLD("generating ret msg"); | ||
315 | if (failed) | ||
316 | timod_error(fd, T_OPTMGMT_REQ, failed, -error); | ||
317 | else { | ||
318 | struct T_primsg *it; | ||
319 | it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len); | ||
320 | if (it) { | ||
321 | struct T_optmgmt_ack *ack = | ||
322 | (struct T_optmgmt_ack *)&it->type; | ||
323 | SOLD("got primsg"); | ||
324 | ack->PRIM_type = T_OPTMGMT_ACK; | ||
325 | ack->OPT_length = ret_len; | ||
326 | ack->OPT_offset = sizeof(struct T_optmgmt_ack); | ||
327 | ack->MGMT_flags = (failed ? T_FAILURE : flag); | ||
328 | memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack), | ||
329 | ret_buf, ret_len); | ||
330 | timod_queue(fd, it); | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | SOLDD(("put_page %p\n", ret_buf)); | ||
335 | putpage(ret_buf); | ||
336 | SOLD("done"); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len, | ||
341 | char __user *data_buf, int data_len, int flags) | ||
342 | { | ||
343 | int ret, error, terror; | ||
344 | char *buf; | ||
345 | struct file *filp; | ||
346 | struct inode *ino; | ||
347 | struct sol_socket_struct *sock; | ||
348 | mm_segment_t old_fs = get_fs(); | ||
349 | long args[6]; | ||
350 | int (*sys_socketcall)(int, unsigned long __user *) = | ||
351 | (int (*)(int, unsigned long __user *))SYS(socketcall); | ||
352 | int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int) = | ||
353 | (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int))SYS(sendto); | ||
354 | filp = current->files->fd[fd]; | ||
355 | ino = filp->f_dentry->d_inode; | ||
356 | sock = (struct sol_socket_struct *)filp->private_data; | ||
357 | SOLD("entry"); | ||
358 | if (get_user(ret, (int __user *)A(ctl_buf))) | ||
359 | return -EFAULT; | ||
360 | switch (ret) { | ||
361 | case T_BIND_REQ: | ||
362 | { | ||
363 | struct T_bind_req req; | ||
364 | |||
365 | SOLDD(("bind %016lx(%016lx)\n", sock, filp)); | ||
366 | SOLD("T_BIND_REQ"); | ||
367 | if (sock->state != TS_UNBND) { | ||
368 | timod_error(fd, T_BIND_REQ, TOUTSTATE, 0); | ||
369 | return 0; | ||
370 | } | ||
371 | SOLD("state ok"); | ||
372 | if (copy_from_user(&req, ctl_buf, sizeof(req))) { | ||
373 | timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); | ||
374 | return 0; | ||
375 | } | ||
376 | SOLD("got ctl req"); | ||
377 | if (req.ADDR_offset && req.ADDR_length) { | ||
378 | if (req.ADDR_length > BUF_SIZE) { | ||
379 | timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); | ||
380 | return 0; | ||
381 | } | ||
382 | SOLD("req size ok"); | ||
383 | buf = getpage(); | ||
384 | if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) { | ||
385 | timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); | ||
386 | putpage(buf); | ||
387 | return 0; | ||
388 | } | ||
389 | SOLD("got ctl data"); | ||
390 | args[0] = fd; | ||
391 | args[1] = (long)buf; | ||
392 | args[2] = req.ADDR_length; | ||
393 | SOLD("calling BIND"); | ||
394 | set_fs(KERNEL_DS); | ||
395 | error = sys_socketcall(SYS_BIND, args); | ||
396 | set_fs(old_fs); | ||
397 | putpage(buf); | ||
398 | SOLD("BIND returned"); | ||
399 | } else | ||
400 | error = 0; | ||
401 | if (!error) { | ||
402 | struct T_primsg *it; | ||
403 | if (req.CONIND_number) { | ||
404 | args[0] = fd; | ||
405 | args[1] = req.CONIND_number; | ||
406 | SOLD("calling LISTEN"); | ||
407 | set_fs(KERNEL_DS); | ||
408 | error = sys_socketcall(SYS_LISTEN, args); | ||
409 | set_fs(old_fs); | ||
410 | SOLD("LISTEN done"); | ||
411 | } | ||
412 | it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr)); | ||
413 | if (it) { | ||
414 | struct T_bind_ack *ack; | ||
415 | |||
416 | ack = (struct T_bind_ack *)&it->type; | ||
417 | ack->PRIM_type = T_BIND_ACK; | ||
418 | ack->ADDR_offset = sizeof(*ack); | ||
419 | ack->ADDR_length = sizeof(struct sockaddr); | ||
420 | ack->CONIND_number = req.CONIND_number; | ||
421 | args[0] = fd; | ||
422 | args[1] = (long)(ack+sizeof(*ack)); | ||
423 | args[2] = (long)&ack->ADDR_length; | ||
424 | set_fs(KERNEL_DS); | ||
425 | sys_socketcall(SYS_GETSOCKNAME,args); | ||
426 | set_fs(old_fs); | ||
427 | sock->state = TS_IDLE; | ||
428 | timod_ok(fd, T_BIND_REQ); | ||
429 | timod_queue_end(fd, it); | ||
430 | SOLD("BIND done"); | ||
431 | return 0; | ||
432 | } | ||
433 | } | ||
434 | SOLD("some error"); | ||
435 | switch (error) { | ||
436 | case -EINVAL: | ||
437 | terror = TOUTSTATE; | ||
438 | error = 0; | ||
439 | break; | ||
440 | case -EACCES: | ||
441 | terror = TACCES; | ||
442 | error = 0; | ||
443 | break; | ||
444 | case -EADDRNOTAVAIL: | ||
445 | case -EADDRINUSE: | ||
446 | terror = TNOADDR; | ||
447 | error = 0; | ||
448 | break; | ||
449 | default: | ||
450 | terror = TSYSERR; | ||
451 | break; | ||
452 | } | ||
453 | timod_error(fd, T_BIND_REQ, terror, -error); | ||
454 | SOLD("BIND done"); | ||
455 | return 0; | ||
456 | } | ||
457 | case T_CONN_REQ: | ||
458 | { | ||
459 | struct T_conn_req req; | ||
460 | unsigned short oldflags; | ||
461 | struct T_primsg *it; | ||
462 | SOLD("T_CONN_REQ"); | ||
463 | if (sock->state != TS_UNBND && sock->state != TS_IDLE) { | ||
464 | timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); | ||
465 | return 0; | ||
466 | } | ||
467 | SOLD("state ok"); | ||
468 | if (copy_from_user(&req, ctl_buf, sizeof(req))) { | ||
469 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | ||
470 | return 0; | ||
471 | } | ||
472 | SOLD("got ctl req"); | ||
473 | if (ctl_len > BUF_SIZE) { | ||
474 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | ||
475 | return 0; | ||
476 | } | ||
477 | SOLD("req size ok"); | ||
478 | buf = getpage(); | ||
479 | if (copy_from_user(buf, ctl_buf, ctl_len)) { | ||
480 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | ||
481 | putpage(buf); | ||
482 | return 0; | ||
483 | } | ||
484 | #ifdef DEBUG_SOLARIS | ||
485 | { | ||
486 | char * ptr = buf; | ||
487 | int len = ctl_len; | ||
488 | printk("returned data (%d bytes): ",len); | ||
489 | while( len-- ) { | ||
490 | if (!(len & 7)) | ||
491 | printk(" "); | ||
492 | printk("%02x",(unsigned char)*ptr++); | ||
493 | } | ||
494 | printk("\n"); | ||
495 | } | ||
496 | #endif | ||
497 | SOLD("got ctl data"); | ||
498 | args[0] = fd; | ||
499 | args[1] = (long)buf+req.DEST_offset; | ||
500 | args[2] = req.DEST_length; | ||
501 | oldflags = filp->f_flags; | ||
502 | filp->f_flags &= ~O_NONBLOCK; | ||
503 | SOLD("calling CONNECT"); | ||
504 | set_fs(KERNEL_DS); | ||
505 | error = sys_socketcall(SYS_CONNECT, args); | ||
506 | set_fs(old_fs); | ||
507 | filp->f_flags = oldflags; | ||
508 | SOLD("CONNECT done"); | ||
509 | if (!error) { | ||
510 | struct T_conn_con *con; | ||
511 | SOLD("no error"); | ||
512 | it = timod_mkctl(ctl_len); | ||
513 | if (!it) { | ||
514 | putpage(buf); | ||
515 | return -ENOMEM; | ||
516 | } | ||
517 | con = (struct T_conn_con *)&it->type; | ||
518 | #ifdef DEBUG_SOLARIS | ||
519 | { | ||
520 | char * ptr = buf; | ||
521 | int len = ctl_len; | ||
522 | printk("returned data (%d bytes): ",len); | ||
523 | while( len-- ) { | ||
524 | if (!(len & 7)) | ||
525 | printk(" "); | ||
526 | printk("%02x",(unsigned char)*ptr++); | ||
527 | } | ||
528 | printk("\n"); | ||
529 | } | ||
530 | #endif | ||
531 | memcpy(con, buf, ctl_len); | ||
532 | SOLD("copied ctl_buf"); | ||
533 | con->PRIM_type = T_CONN_CON; | ||
534 | sock->state = TS_DATA_XFER; | ||
535 | } else { | ||
536 | struct T_discon_ind *dis; | ||
537 | SOLD("some error"); | ||
538 | it = timod_mkctl(sizeof(*dis)); | ||
539 | if (!it) { | ||
540 | putpage(buf); | ||
541 | return -ENOMEM; | ||
542 | } | ||
543 | SOLD("got primsg"); | ||
544 | dis = (struct T_discon_ind *)&it->type; | ||
545 | dis->PRIM_type = T_DISCON_IND; | ||
546 | dis->DISCON_reason = -error; /* FIXME: convert this as in iABI_errors() */ | ||
547 | dis->SEQ_number = 0; | ||
548 | } | ||
549 | putpage(buf); | ||
550 | timod_ok(fd, T_CONN_REQ); | ||
551 | it->pri = 0; | ||
552 | timod_queue_end(fd, it); | ||
553 | SOLD("CONNECT done"); | ||
554 | return 0; | ||
555 | } | ||
556 | case T_OPTMGMT_REQ: | ||
557 | { | ||
558 | struct T_optmgmt_req req; | ||
559 | SOLD("OPTMGMT_REQ"); | ||
560 | if (copy_from_user(&req, ctl_buf, sizeof(req))) | ||
561 | return -EFAULT; | ||
562 | SOLD("got req"); | ||
563 | return timod_optmgmt(fd, req.MGMT_flags, | ||
564 | req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL, | ||
565 | req.OPT_length, 1); | ||
566 | } | ||
567 | case T_UNITDATA_REQ: | ||
568 | { | ||
569 | struct T_unitdata_req req; | ||
570 | |||
571 | int err; | ||
572 | SOLD("T_UNITDATA_REQ"); | ||
573 | if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) { | ||
574 | timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); | ||
575 | return 0; | ||
576 | } | ||
577 | SOLD("state ok"); | ||
578 | if (copy_from_user(&req, ctl_buf, sizeof(req))) { | ||
579 | timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); | ||
580 | return 0; | ||
581 | } | ||
582 | SOLD("got ctl req"); | ||
583 | #ifdef DEBUG_SOLARIS | ||
584 | { | ||
585 | char * ptr = ctl_buf+req.DEST_offset; | ||
586 | int len = req.DEST_length; | ||
587 | printk("socket address (%d bytes): ",len); | ||
588 | while( len-- ) { | ||
589 | char c; | ||
590 | if (get_user(c,ptr)) | ||
591 | printk("??"); | ||
592 | else | ||
593 | printk("%02x",(unsigned char)c); | ||
594 | ptr++; | ||
595 | } | ||
596 | printk("\n"); | ||
597 | } | ||
598 | #endif | ||
599 | 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); | ||
600 | if (err == data_len) | ||
601 | return 0; | ||
602 | if(err >= 0) { | ||
603 | printk("timod: sendto failed to send all the data\n"); | ||
604 | return 0; | ||
605 | } | ||
606 | timod_error(fd, T_CONN_REQ, TSYSERR, -err); | ||
607 | return 0; | ||
608 | } | ||
609 | default: | ||
610 | printk(KERN_INFO "timod_putmsg: unsupported command %u.\n", ret); | ||
611 | break; | ||
612 | } | ||
613 | return -EINVAL; | ||
614 | } | ||
615 | |||
616 | int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, s32 __user *ctl_len, | ||
617 | char __user *data_buf, int data_maxlen, s32 __user *data_len, int *flags_p) | ||
618 | { | ||
619 | int error; | ||
620 | int oldflags; | ||
621 | struct file *filp; | ||
622 | struct inode *ino; | ||
623 | struct sol_socket_struct *sock; | ||
624 | struct T_unitdata_ind udi; | ||
625 | mm_segment_t old_fs = get_fs(); | ||
626 | long args[6]; | ||
627 | char __user *tmpbuf; | ||
628 | int tmplen; | ||
629 | int (*sys_socketcall)(int, unsigned long __user *) = | ||
630 | (int (*)(int, unsigned long __user *))SYS(socketcall); | ||
631 | int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *); | ||
632 | |||
633 | SOLD("entry"); | ||
634 | 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)); | ||
635 | filp = current->files->fd[fd]; | ||
636 | ino = filp->f_dentry->d_inode; | ||
637 | sock = (struct sol_socket_struct *)filp->private_data; | ||
638 | SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL)); | ||
639 | if ( ctl_maxlen > 0 && !sock->pfirst && SOCKET_I(ino)->type == SOCK_STREAM | ||
640 | && sock->state == TS_IDLE) { | ||
641 | SOLD("calling LISTEN"); | ||
642 | args[0] = fd; | ||
643 | args[1] = -1; | ||
644 | set_fs(KERNEL_DS); | ||
645 | sys_socketcall(SYS_LISTEN, args); | ||
646 | set_fs(old_fs); | ||
647 | SOLD("LISTEN done"); | ||
648 | } | ||
649 | if (!(filp->f_flags & O_NONBLOCK)) { | ||
650 | struct poll_wqueues wait_table; | ||
651 | poll_table *wait; | ||
652 | |||
653 | poll_initwait(&wait_table); | ||
654 | wait = &wait_table.pt; | ||
655 | for(;;) { | ||
656 | SOLD("loop"); | ||
657 | set_current_state(TASK_INTERRUPTIBLE); | ||
658 | /* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ | ||
659 | /* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ | ||
660 | /* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ | ||
661 | /* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */ | ||
662 | /* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */ | ||
663 | /* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */ | ||
664 | if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI)) | ||
665 | break; | ||
666 | SOLD("cond 1 passed"); | ||
667 | if ( | ||
668 | #if 1 | ||
669 | *flags_p != MSG_HIPRI && | ||
670 | #endif | ||
671 | ((filp->f_op->poll(filp, wait) & POLLIN) || | ||
672 | (filp->f_op->poll(filp, NULL) & POLLIN) || | ||
673 | signal_pending(current)) | ||
674 | ) { | ||
675 | break; | ||
676 | } | ||
677 | if( *flags_p == MSG_HIPRI ) { | ||
678 | SOLD("avoiding lockup"); | ||
679 | break ; | ||
680 | } | ||
681 | if(wait_table.error) { | ||
682 | SOLD("wait-table error"); | ||
683 | poll_freewait(&wait_table); | ||
684 | return wait_table.error; | ||
685 | } | ||
686 | SOLD("scheduling"); | ||
687 | schedule(); | ||
688 | } | ||
689 | SOLD("loop done"); | ||
690 | current->state = TASK_RUNNING; | ||
691 | poll_freewait(&wait_table); | ||
692 | if (signal_pending(current)) { | ||
693 | SOLD("signal pending"); | ||
694 | return -EINTR; | ||
695 | } | ||
696 | } | ||
697 | if (ctl_maxlen >= 0 && sock->pfirst) { | ||
698 | struct T_primsg *it = sock->pfirst; | ||
699 | int l = min_t(int, ctl_maxlen, it->length); | ||
700 | SCHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC); | ||
701 | SOLD("purting ctl data"); | ||
702 | if(copy_to_user(ctl_buf, | ||
703 | (char*)&it->type + sock->offset, l)) | ||
704 | return -EFAULT; | ||
705 | SOLD("pur it"); | ||
706 | if(put_user(l, ctl_len)) | ||
707 | return -EFAULT; | ||
708 | SOLD("set ctl_len"); | ||
709 | *flags_p = it->pri; | ||
710 | it->length -= l; | ||
711 | if (it->length) { | ||
712 | SOLD("more ctl"); | ||
713 | sock->offset += l; | ||
714 | return MORECTL; | ||
715 | } else { | ||
716 | SOLD("removing message"); | ||
717 | sock->pfirst = it->next; | ||
718 | if (!sock->pfirst) | ||
719 | sock->plast = NULL; | ||
720 | SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst)); | ||
721 | mykfree(it); | ||
722 | sock->offset = 0; | ||
723 | SOLD("ctl done"); | ||
724 | return 0; | ||
725 | } | ||
726 | } | ||
727 | *flags_p = 0; | ||
728 | if (ctl_maxlen >= 0) { | ||
729 | SOLD("ACCEPT perhaps?"); | ||
730 | if (SOCKET_I(ino)->type == SOCK_STREAM && sock->state == TS_IDLE) { | ||
731 | struct T_conn_ind ind; | ||
732 | char *buf = getpage(); | ||
733 | int len = BUF_SIZE; | ||
734 | |||
735 | SOLD("trying ACCEPT"); | ||
736 | if (put_user(ctl_maxlen - sizeof(ind), ctl_len)) | ||
737 | return -EFAULT; | ||
738 | args[0] = fd; | ||
739 | args[1] = (long)buf; | ||
740 | args[2] = (long)&len; | ||
741 | oldflags = filp->f_flags; | ||
742 | filp->f_flags |= O_NONBLOCK; | ||
743 | SOLD("calling ACCEPT"); | ||
744 | set_fs(KERNEL_DS); | ||
745 | error = sys_socketcall(SYS_ACCEPT, args); | ||
746 | set_fs(old_fs); | ||
747 | filp->f_flags = oldflags; | ||
748 | if (error < 0) { | ||
749 | SOLD("some error"); | ||
750 | putpage(buf); | ||
751 | return error; | ||
752 | } | ||
753 | if (error) { | ||
754 | SOLD("connect"); | ||
755 | putpage(buf); | ||
756 | if (sizeof(ind) > ctl_maxlen) { | ||
757 | SOLD("generating CONN_IND"); | ||
758 | ind.PRIM_type = T_CONN_IND; | ||
759 | ind.SRC_length = len; | ||
760 | ind.SRC_offset = sizeof(ind); | ||
761 | ind.OPT_length = ind.OPT_offset = 0; | ||
762 | ind.SEQ_number = error; | ||
763 | if(copy_to_user(ctl_buf, &ind, sizeof(ind))|| | ||
764 | put_user(sizeof(ind)+ind.SRC_length,ctl_len)) | ||
765 | return -EFAULT; | ||
766 | SOLD("CONN_IND created"); | ||
767 | } | ||
768 | if (data_maxlen >= 0) | ||
769 | put_user(0, data_len); | ||
770 | SOLD("CONN_IND done"); | ||
771 | return 0; | ||
772 | } | ||
773 | if (len>ctl_maxlen) { | ||
774 | SOLD("data don't fit"); | ||
775 | putpage(buf); | ||
776 | return -EFAULT; /* XXX - is this ok ? */ | ||
777 | } | ||
778 | if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){ | ||
779 | SOLD("can't copy data"); | ||
780 | putpage(buf); | ||
781 | return -EFAULT; | ||
782 | } | ||
783 | SOLD("ACCEPT done"); | ||
784 | putpage(buf); | ||
785 | } | ||
786 | } | ||
787 | SOLD("checking data req"); | ||
788 | if (data_maxlen <= 0) { | ||
789 | if (data_maxlen == 0) | ||
790 | put_user(0, data_len); | ||
791 | if (ctl_maxlen >= 0) | ||
792 | put_user(0, ctl_len); | ||
793 | return -EAGAIN; | ||
794 | } | ||
795 | SOLD("wants data"); | ||
796 | if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { | ||
797 | SOLD("udi fits"); | ||
798 | tmpbuf = ctl_buf + sizeof(udi); | ||
799 | tmplen = ctl_maxlen - sizeof(udi); | ||
800 | } else { | ||
801 | SOLD("udi does not fit"); | ||
802 | tmpbuf = NULL; | ||
803 | tmplen = 0; | ||
804 | } | ||
805 | if (put_user(tmplen, ctl_len)) | ||
806 | return -EFAULT; | ||
807 | SOLD("set ctl_len"); | ||
808 | oldflags = filp->f_flags; | ||
809 | filp->f_flags |= O_NONBLOCK; | ||
810 | SOLD("calling recvfrom"); | ||
811 | sys_recvfrom = (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom); | ||
812 | error = sys_recvfrom(fd, data_buf, data_maxlen, 0, (struct sockaddr __user *)tmpbuf, ctl_len); | ||
813 | filp->f_flags = oldflags; | ||
814 | if (error < 0) | ||
815 | return error; | ||
816 | SOLD("error >= 0" ) ; | ||
817 | if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { | ||
818 | SOLD("generating udi"); | ||
819 | udi.PRIM_type = T_UNITDATA_IND; | ||
820 | if (get_user(udi.SRC_length, ctl_len)) | ||
821 | return -EFAULT; | ||
822 | udi.SRC_offset = sizeof(udi); | ||
823 | udi.OPT_length = udi.OPT_offset = 0; | ||
824 | if (copy_to_user(ctl_buf, &udi, sizeof(udi)) || | ||
825 | put_user(sizeof(udi)+udi.SRC_length, ctl_len)) | ||
826 | return -EFAULT; | ||
827 | SOLD("udi done"); | ||
828 | } else { | ||
829 | if (put_user(0, ctl_len)) | ||
830 | return -EFAULT; | ||
831 | } | ||
832 | put_user(error, data_len); | ||
833 | SOLD("done"); | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) | ||
838 | { | ||
839 | struct file *filp; | ||
840 | struct inode *ino; | ||
841 | struct strbuf __user *ctlptr; | ||
842 | struct strbuf __user *datptr; | ||
843 | struct strbuf ctl, dat; | ||
844 | int __user *flgptr; | ||
845 | int flags; | ||
846 | int error = -EBADF; | ||
847 | |||
848 | SOLD("entry"); | ||
849 | lock_kernel(); | ||
850 | if(fd >= NR_OPEN) goto out; | ||
851 | |||
852 | filp = current->files->fd[fd]; | ||
853 | if(!filp) goto out; | ||
854 | |||
855 | ino = filp->f_dentry->d_inode; | ||
856 | if (!ino || !S_ISSOCK(ino->i_mode)) | ||
857 | goto out; | ||
858 | |||
859 | ctlptr = (struct strbuf __user *)A(arg1); | ||
860 | datptr = (struct strbuf __user *)A(arg2); | ||
861 | flgptr = (int __user *)A(arg3); | ||
862 | |||
863 | error = -EFAULT; | ||
864 | |||
865 | if (ctlptr) { | ||
866 | if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) || | ||
867 | put_user(-1,&ctlptr->len)) | ||
868 | goto out; | ||
869 | } else | ||
870 | ctl.maxlen = -1; | ||
871 | |||
872 | if (datptr) { | ||
873 | if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) || | ||
874 | put_user(-1,&datptr->len)) | ||
875 | goto out; | ||
876 | } else | ||
877 | dat.maxlen = -1; | ||
878 | |||
879 | if (get_user(flags,flgptr)) | ||
880 | goto out; | ||
881 | |||
882 | switch (flags) { | ||
883 | case 0: | ||
884 | case MSG_HIPRI: | ||
885 | case MSG_ANY: | ||
886 | case MSG_BAND: | ||
887 | break; | ||
888 | default: | ||
889 | error = -EINVAL; | ||
890 | goto out; | ||
891 | } | ||
892 | |||
893 | error = timod_getmsg(fd,A(ctl.buf),ctl.maxlen,&ctlptr->len, | ||
894 | A(dat.buf),dat.maxlen,&datptr->len,&flags); | ||
895 | |||
896 | if (!error && put_user(flags,flgptr)) | ||
897 | error = -EFAULT; | ||
898 | out: | ||
899 | unlock_kernel(); | ||
900 | SOLD("done"); | ||
901 | return error; | ||
902 | } | ||
903 | |||
904 | asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) | ||
905 | { | ||
906 | struct file *filp; | ||
907 | struct inode *ino; | ||
908 | struct strbuf __user *ctlptr; | ||
909 | struct strbuf __user *datptr; | ||
910 | struct strbuf ctl, dat; | ||
911 | int flags = (int) arg3; | ||
912 | int error = -EBADF; | ||
913 | |||
914 | SOLD("entry"); | ||
915 | lock_kernel(); | ||
916 | if(fd >= NR_OPEN) goto out; | ||
917 | |||
918 | filp = current->files->fd[fd]; | ||
919 | if(!filp) goto out; | ||
920 | |||
921 | ino = filp->f_dentry->d_inode; | ||
922 | if (!ino) goto out; | ||
923 | |||
924 | if (!S_ISSOCK(ino->i_mode) && | ||
925 | (imajor(ino) != 30 || iminor(ino) != 1)) | ||
926 | goto out; | ||
927 | |||
928 | ctlptr = A(arg1); | ||
929 | datptr = A(arg2); | ||
930 | |||
931 | error = -EFAULT; | ||
932 | |||
933 | if (ctlptr) { | ||
934 | if (copy_from_user(&ctl,ctlptr,sizeof(ctl))) | ||
935 | goto out; | ||
936 | if (ctl.len < 0 && flags) { | ||
937 | error = -EINVAL; | ||
938 | goto out; | ||
939 | } | ||
940 | } else { | ||
941 | ctl.len = 0; | ||
942 | ctl.buf = 0; | ||
943 | } | ||
944 | |||
945 | if (datptr) { | ||
946 | if (copy_from_user(&dat,datptr,sizeof(dat))) | ||
947 | goto out; | ||
948 | } else { | ||
949 | dat.len = 0; | ||
950 | dat.buf = 0; | ||
951 | } | ||
952 | |||
953 | error = timod_putmsg(fd,A(ctl.buf),ctl.len, | ||
954 | A(dat.buf),dat.len,flags); | ||
955 | out: | ||
956 | unlock_kernel(); | ||
957 | SOLD("done"); | ||
958 | return error; | ||
959 | } | ||