| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Al Viro | 53f9fc9 | 2005-10-21 03:22:24 -0400 | [diff] [blame] | 42 | void * mykmalloc(size_t s, gfp_t gfp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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",\ | 
| Harvey Harrison | 9a4a668 | 2008-03-03 11:42:17 -0800 | [diff] [blame] | 84 | __FILE__,__LINE__,__func__,(m),(a));}while(0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 146 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 |  | 
|  | 148 | SOLD("wakeing socket"); | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 149 | fdt = files_fdtable(current->files); | 
| Josef Sipek | 1250ca4 | 2006-12-08 02:37:41 -0800 | [diff] [blame] | 150 | sock = SOCKET_I(fdt->fd[fd]->f_path.dentry->d_inode); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 162 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 |  | 
|  | 164 | SOLD("queuing primsg"); | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 165 | fdt = files_fdtable(current->files); | 
|  | 166 | sock = (struct sol_socket_struct *)fdt->fd[fd]->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 178 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 |  | 
|  | 180 | SOLD("queuing primsg at end"); | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 181 | fdt = files_fdtable(current->files); | 
|  | 182 | sock = (struct sol_socket_struct *)fdt->fd[fd]->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 353 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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); | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 361 |  | 
|  | 362 | fdt = files_fdtable(current->files); | 
|  | 363 | filp = fdt->fd[fd]; | 
| Josef Sipek | 1250ca4 | 2006-12-08 02:37:41 -0800 | [diff] [blame] | 364 | ino = filp->f_path.dentry->d_inode; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 632 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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)); | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 645 | fdt = files_fdtable(current->files); | 
|  | 646 | filp = fdt->fd[fd]; | 
| Josef Sipek | 1250ca4 | 2006-12-08 02:37:41 -0800 | [diff] [blame] | 647 | ino = filp->f_path.dentry->d_inode; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 858 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 859 |  | 
|  | 860 | SOLD("entry"); | 
|  | 861 | lock_kernel(); | 
| Eric Dumazet | 9cfe015 | 2008-02-06 01:37:16 -0800 | [diff] [blame] | 862 | if (fd >= sysctl_nr_open) | 
|  | 863 | goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 864 |  | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 865 | fdt = files_fdtable(current->files); | 
|  | 866 | filp = fdt->fd[fd]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 867 | if(!filp) goto out; | 
|  | 868 |  | 
| Josef Sipek | 1250ca4 | 2006-12-08 02:37:41 -0800 | [diff] [blame] | 869 | ino = filp->f_path.dentry->d_inode; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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; | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 927 | struct fdtable *fdt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 928 |  | 
|  | 929 | SOLD("entry"); | 
|  | 930 | lock_kernel(); | 
| Eric Dumazet | 9cfe015 | 2008-02-06 01:37:16 -0800 | [diff] [blame] | 931 | if (fd >= sysctl_nr_open) | 
|  | 932 | goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 933 |  | 
| Dipankar Sarma | 6e72ad2 | 2005-09-09 13:04:12 -0700 | [diff] [blame] | 934 | fdt = files_fdtable(current->files); | 
|  | 935 | filp = fdt->fd[fd]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 936 | if(!filp) goto out; | 
|  | 937 |  | 
| Josef Sipek | 1250ca4 | 2006-12-08 02:37:41 -0800 | [diff] [blame] | 938 | ino = filp->f_path.dentry->d_inode; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | } |