1 /* 2 * 32 bit compatibility code for System V IPC 3 * 4 * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 5 * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) 6 * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> 7 * Copyright (C) 2000 VA Linux Co 8 * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> 9 * Copyright (C) 2000 Hewlett-Packard Co. 10 * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> 11 * Copyright (C) 2000 Gerhard Tonn (ton@de.ibm.com) 12 * Copyright (C) 2000-2002 Andi Kleen, SuSE Labs (x86-64 port) 13 * Copyright (C) 2000 Silicon Graphics, Inc. 14 * Copyright (C) 2001 IBM 15 * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation 16 * Copyright (C) 2004 Arnd Bergmann (arnd@arndb.de) 17 * 18 * This code is collected from the versions for sparc64, mips64, s390x, ia64, 19 * ppc64 and x86_64, all of which are based on the original sparc64 version 20 * by Jakub Jelinek. 21 * 22 */ 23 #include <linux/compat.h> 24 #include <linux/errno.h> 25 #include <linux/highuid.h> 26 #include <linux/init.h> 27 #include <linux/msg.h> 28 #include <linux/shm.h> 29 #include <linux/slab.h> 30 #include <linux/syscalls.h> 31 32 #include <linux/mutex.h> 33 #include <asm/uaccess.h> 34 35 #include "util.h" 36 37 struct compat_msgbuf { 38 compat_long_t mtype; 39 char mtext[1]; 40 }; 41 42 struct compat_ipc_perm { 43 key_t key; 44 __compat_uid_t uid; 45 __compat_gid_t gid; 46 __compat_uid_t cuid; 47 __compat_gid_t cgid; 48 compat_mode_t mode; 49 unsigned short seq; 50 }; 51 52 struct compat_semid_ds { 53 struct compat_ipc_perm sem_perm; 54 compat_time_t sem_otime; 55 compat_time_t sem_ctime; 56 compat_uptr_t sem_base; 57 compat_uptr_t sem_pending; 58 compat_uptr_t sem_pending_last; 59 compat_uptr_t undo; 60 unsigned short sem_nsems; 61 }; 62 63 struct compat_msqid_ds { 64 struct compat_ipc_perm msg_perm; 65 compat_uptr_t msg_first; 66 compat_uptr_t msg_last; 67 compat_time_t msg_stime; 68 compat_time_t msg_rtime; 69 compat_time_t msg_ctime; 70 compat_ulong_t msg_lcbytes; 71 compat_ulong_t msg_lqbytes; 72 unsigned short msg_cbytes; 73 unsigned short msg_qnum; 74 unsigned short msg_qbytes; 75 compat_ipc_pid_t msg_lspid; 76 compat_ipc_pid_t msg_lrpid; 77 }; 78 79 struct compat_shmid_ds { 80 struct compat_ipc_perm shm_perm; 81 int shm_segsz; 82 compat_time_t shm_atime; 83 compat_time_t shm_dtime; 84 compat_time_t shm_ctime; 85 compat_ipc_pid_t shm_cpid; 86 compat_ipc_pid_t shm_lpid; 87 unsigned short shm_nattch; 88 unsigned short shm_unused; 89 compat_uptr_t shm_unused2; 90 compat_uptr_t shm_unused3; 91 }; 92 93 struct compat_ipc_kludge { 94 compat_uptr_t msgp; 95 compat_long_t msgtyp; 96 }; 97 98 struct compat_shminfo64 { 99 compat_ulong_t shmmax; 100 compat_ulong_t shmmin; 101 compat_ulong_t shmmni; 102 compat_ulong_t shmseg; 103 compat_ulong_t shmall; 104 compat_ulong_t __unused1; 105 compat_ulong_t __unused2; 106 compat_ulong_t __unused3; 107 compat_ulong_t __unused4; 108 }; 109 110 struct compat_shm_info { 111 compat_int_t used_ids; 112 compat_ulong_t shm_tot, shm_rss, shm_swp; 113 compat_ulong_t swap_attempts, swap_successes; 114 }; 115 116 extern int sem_ctls[]; 117 #define sc_semopm (sem_ctls[2]) 118 119 static inline int compat_ipc_parse_version(int *cmd) 120 { 121 int version = *cmd & IPC_64; 122 123 /* this is tricky: architectures that have support for the old 124 * ipc structures in 64 bit binaries need to have IPC_64 set 125 * in cmd, the others need to have it cleared */ 126 #ifndef ipc_parse_version 127 *cmd |= IPC_64; 128 #else 129 *cmd &= ~IPC_64; 130 #endif 131 return version; 132 } 133 134 static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, 135 struct compat_ipc64_perm __user *up64) 136 { 137 int err; 138 139 err = __get_user(p64->uid, &up64->uid); 140 err |= __get_user(p64->gid, &up64->gid); 141 err |= __get_user(p64->mode, &up64->mode); 142 return err; 143 } 144 145 static inline int __get_compat_ipc_perm(struct ipc64_perm *p, 146 struct compat_ipc_perm __user *up) 147 { 148 int err; 149 150 err = __get_user(p->uid, &up->uid); 151 err |= __get_user(p->gid, &up->gid); 152 err |= __get_user(p->mode, &up->mode); 153 return err; 154 } 155 156 static inline int __put_compat_ipc64_perm(struct ipc64_perm *p64, 157 struct compat_ipc64_perm __user *up64) 158 { 159 int err; 160 161 err = __put_user(p64->key, &up64->key); 162 err |= __put_user(p64->uid, &up64->uid); 163 err |= __put_user(p64->gid, &up64->gid); 164 err |= __put_user(p64->cuid, &up64->cuid); 165 err |= __put_user(p64->cgid, &up64->cgid); 166 err |= __put_user(p64->mode, &up64->mode); 167 err |= __put_user(p64->seq, &up64->seq); 168 return err; 169 } 170 171 static inline int __put_compat_ipc_perm(struct ipc64_perm *p, 172 struct compat_ipc_perm __user *up) 173 { 174 int err; 175 __compat_uid_t u; 176 __compat_gid_t g; 177 178 err = __put_user(p->key, &up->key); 179 SET_UID(u, p->uid); 180 err |= __put_user(u, &up->uid); 181 SET_GID(g, p->gid); 182 err |= __put_user(g, &up->gid); 183 SET_UID(u, p->cuid); 184 err |= __put_user(u, &up->cuid); 185 SET_GID(g, p->cgid); 186 err |= __put_user(g, &up->cgid); 187 err |= __put_user(p->mode, &up->mode); 188 err |= __put_user(p->seq, &up->seq); 189 return err; 190 } 191 192 static inline int get_compat_semid64_ds(struct semid64_ds *s64, 193 struct compat_semid64_ds __user *up64) 194 { 195 if (!access_ok (VERIFY_READ, up64, sizeof(*up64))) 196 return -EFAULT; 197 return __get_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); 198 } 199 200 static inline int get_compat_semid_ds(struct semid64_ds *s, 201 struct compat_semid_ds __user *up) 202 { 203 if (!access_ok (VERIFY_READ, up, sizeof(*up))) 204 return -EFAULT; 205 return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm); 206 } 207 208 static inline int put_compat_semid64_ds(struct semid64_ds *s64, 209 struct compat_semid64_ds __user *up64) 210 { 211 int err; 212 213 if (!access_ok (VERIFY_WRITE, up64, sizeof(*up64))) 214 return -EFAULT; 215 err = __put_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); 216 err |= __put_user(s64->sem_otime, &up64->sem_otime); 217 err |= __put_user(s64->sem_ctime, &up64->sem_ctime); 218 err |= __put_user(s64->sem_nsems, &up64->sem_nsems); 219 return err; 220 } 221 222 static inline int put_compat_semid_ds(struct semid64_ds *s, 223 struct compat_semid_ds __user *up) 224 { 225 int err; 226 227 if (!access_ok (VERIFY_WRITE, up, sizeof(*up))) 228 return -EFAULT; 229 err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm); 230 err |= __put_user(s->sem_otime, &up->sem_otime); 231 err |= __put_user(s->sem_ctime, &up->sem_ctime); 232 err |= __put_user(s->sem_nsems, &up->sem_nsems); 233 return err; 234 } 235 236 long compat_sys_semctl(int first, int second, int third, void __user *uptr) 237 { 238 union semun fourth; 239 u32 pad; 240 int err, err2; 241 struct semid64_ds s64; 242 struct semid64_ds __user *up64; 243 int version = compat_ipc_parse_version(&third); 244 245 if (!uptr) 246 return -EINVAL; 247 if (get_user(pad, (u32 __user *) uptr)) 248 return -EFAULT; 249 if ((third & (~IPC_64)) == SETVAL) 250 fourth.val = (int) pad; 251 else 252 fourth.__pad = compat_ptr(pad); 253 switch (third & (~IPC_64)) { 254 case IPC_INFO: 255 case IPC_RMID: 256 case SEM_INFO: 257 case GETVAL: 258 case GETPID: 259 case GETNCNT: 260 case GETZCNT: 261 case GETALL: 262 case SETVAL: 263 case SETALL: 264 err = sys_semctl(first, second, third, fourth); 265 break; 266 267 case IPC_STAT: 268 case SEM_STAT: 269 up64 = compat_alloc_user_space(sizeof(s64)); 270 fourth.__pad = up64; 271 err = sys_semctl(first, second, third, fourth); 272 if (err < 0) 273 break; 274 if (copy_from_user(&s64, up64, sizeof(s64))) 275 err2 = -EFAULT; 276 else if (version == IPC_64) 277 err2 = put_compat_semid64_ds(&s64, compat_ptr(pad)); 278 else 279 err2 = put_compat_semid_ds(&s64, compat_ptr(pad)); 280 if (err2) 281 err = -EFAULT; 282 break; 283 284 case IPC_SET: 285 if (version == IPC_64) { 286 err = get_compat_semid64_ds(&s64, compat_ptr(pad)); 287 } else { 288 err = get_compat_semid_ds(&s64, compat_ptr(pad)); 289 } 290 up64 = compat_alloc_user_space(sizeof(s64)); 291 if (copy_to_user(up64, &s64, sizeof(s64))) 292 err = -EFAULT; 293 if (err) 294 break; 295 296 fourth.__pad = up64; 297 err = sys_semctl(first, second, third, fourth); 298 break; 299 300 default: 301 err = -EINVAL; 302 break; 303 } 304 return err; 305 } 306 307 long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) 308 { 309 struct compat_msgbuf __user *up = uptr; 310 long type; 311 312 if (first < 0) 313 return -EINVAL; 314 if (second < 0) 315 return -EINVAL; 316 317 if (get_user(type, &up->mtype)) 318 return -EFAULT; 319 320 return do_msgsnd(first, type, up->mtext, second, third); 321 } 322 323 long compat_sys_msgrcv(int first, int second, int msgtyp, int third, 324 int version, void __user *uptr) 325 { 326 struct compat_msgbuf __user *up; 327 long type; 328 int err; 329 330 if (first < 0) 331 return -EINVAL; 332 if (second < 0) 333 return -EINVAL; 334 335 if (!version) { 336 struct compat_ipc_kludge ipck; 337 err = -EINVAL; 338 if (!uptr) 339 goto out; 340 err = -EFAULT; 341 if (copy_from_user (&ipck, uptr, sizeof(ipck))) 342 goto out; 343 uptr = compat_ptr(ipck.msgp); 344 msgtyp = ipck.msgtyp; 345 } 346 up = uptr; 347 err = do_msgrcv(first, &type, up->mtext, second, msgtyp, third); 348 if (err < 0) 349 goto out; 350 if (put_user(type, &up->mtype)) 351 err = -EFAULT; 352 out: 353 return err; 354 } 355 356 static inline int get_compat_msqid64(struct msqid64_ds *m64, 357 struct compat_msqid64_ds __user *up64) 358 { 359 int err; 360 361 if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) 362 return -EFAULT; 363 err = __get_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); 364 err |= __get_user(m64->msg_qbytes, &up64->msg_qbytes); 365 return err; 366 } 367 368 static inline int get_compat_msqid(struct msqid64_ds *m, 369 struct compat_msqid_ds __user *up) 370 { 371 int err; 372 373 if (!access_ok(VERIFY_READ, up, sizeof(*up))) 374 return -EFAULT; 375 err = __get_compat_ipc_perm(&m->msg_perm, &up->msg_perm); 376 err |= __get_user(m->msg_qbytes, &up->msg_qbytes); 377 return err; 378 } 379 380 static inline int put_compat_msqid64_ds(struct msqid64_ds *m64, 381 struct compat_msqid64_ds __user *up64) 382 { 383 int err; 384 385 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) 386 return -EFAULT; 387 err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); 388 err |= __put_user(m64->msg_stime, &up64->msg_stime); 389 err |= __put_user(m64->msg_rtime, &up64->msg_rtime); 390 err |= __put_user(m64->msg_ctime, &up64->msg_ctime); 391 err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes); 392 err |= __put_user(m64->msg_qnum, &up64->msg_qnum); 393 err |= __put_user(m64->msg_qbytes, &up64->msg_qbytes); 394 err |= __put_user(m64->msg_lspid, &up64->msg_lspid); 395 err |= __put_user(m64->msg_lrpid, &up64->msg_lrpid); 396 return err; 397 } 398 399 static inline int put_compat_msqid_ds(struct msqid64_ds *m, 400 struct compat_msqid_ds __user *up) 401 { 402 int err; 403 404 if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) 405 return -EFAULT; 406 err = __put_compat_ipc_perm(&m->msg_perm, &up->msg_perm); 407 err |= __put_user(m->msg_stime, &up->msg_stime); 408 err |= __put_user(m->msg_rtime, &up->msg_rtime); 409 err |= __put_user(m->msg_ctime, &up->msg_ctime); 410 err |= __put_user(m->msg_cbytes, &up->msg_cbytes); 411 err |= __put_user(m->msg_qnum, &up->msg_qnum); 412 err |= __put_user(m->msg_qbytes, &up->msg_qbytes); 413 err |= __put_user(m->msg_lspid, &up->msg_lspid); 414 err |= __put_user(m->msg_lrpid, &up->msg_lrpid); 415 return err; 416 } 417 418 long compat_sys_msgctl(int first, int second, void __user *uptr) 419 { 420 int err, err2; 421 struct msqid64_ds m64; 422 int version = compat_ipc_parse_version(&second); 423 void __user *p; 424 425 switch (second & (~IPC_64)) { 426 case IPC_INFO: 427 case IPC_RMID: 428 case MSG_INFO: 429 err = sys_msgctl(first, second, uptr); 430 break; 431 432 case IPC_SET: 433 if (version == IPC_64) { 434 err = get_compat_msqid64(&m64, uptr); 435 } else { 436 err = get_compat_msqid(&m64, uptr); 437 } 438 if (err) 439 break; 440 p = compat_alloc_user_space(sizeof(m64)); 441 if (copy_to_user(p, &m64, sizeof(m64))) 442 err = -EFAULT; 443 else 444 err = sys_msgctl(first, second, p); 445 break; 446 447 case IPC_STAT: 448 case MSG_STAT: 449 p = compat_alloc_user_space(sizeof(m64)); 450 err = sys_msgctl(first, second, p); 451 if (err < 0) 452 break; 453 if (copy_from_user(&m64, p, sizeof(m64))) 454 err2 = -EFAULT; 455 else if (version == IPC_64) 456 err2 = put_compat_msqid64_ds(&m64, uptr); 457 else 458 err2 = put_compat_msqid_ds(&m64, uptr); 459 if (err2) 460 err = -EFAULT; 461 break; 462 463 default: 464 err = -EINVAL; 465 break; 466 } 467 return err; 468 } 469 470 long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, 471 void __user *uptr) 472 { 473 int err; 474 unsigned long raddr; 475 compat_ulong_t __user *uaddr; 476 477 if (version == 1) 478 return -EINVAL; 479 err = do_shmat(first, uptr, second, &raddr); 480 if (err < 0) 481 return err; 482 uaddr = compat_ptr(third); 483 return put_user(raddr, uaddr); 484 } 485 486 static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, 487 struct compat_shmid64_ds __user *up64) 488 { 489 if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) 490 return -EFAULT; 491 return __get_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); 492 } 493 494 static inline int get_compat_shmid_ds(struct shmid64_ds *s, 495 struct compat_shmid_ds __user *up) 496 { 497 if (!access_ok(VERIFY_READ, up, sizeof(*up))) 498 return -EFAULT; 499 return __get_compat_ipc_perm(&s->shm_perm, &up->shm_perm); 500 } 501 502 static inline int put_compat_shmid64_ds(struct shmid64_ds *s64, 503 struct compat_shmid64_ds __user *up64) 504 { 505 int err; 506 507 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) 508 return -EFAULT; 509 err = __put_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); 510 err |= __put_user(s64->shm_atime, &up64->shm_atime); 511 err |= __put_user(s64->shm_dtime, &up64->shm_dtime); 512 err |= __put_user(s64->shm_ctime, &up64->shm_ctime); 513 err |= __put_user(s64->shm_segsz, &up64->shm_segsz); 514 err |= __put_user(s64->shm_nattch, &up64->shm_nattch); 515 err |= __put_user(s64->shm_cpid, &up64->shm_cpid); 516 err |= __put_user(s64->shm_lpid, &up64->shm_lpid); 517 return err; 518 } 519 520 static inline int put_compat_shmid_ds(struct shmid64_ds *s, 521 struct compat_shmid_ds __user *up) 522 { 523 int err; 524 525 if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) 526 return -EFAULT; 527 err = __put_compat_ipc_perm(&s->shm_perm, &up->shm_perm); 528 err |= __put_user(s->shm_atime, &up->shm_atime); 529 err |= __put_user(s->shm_dtime, &up->shm_dtime); 530 err |= __put_user(s->shm_ctime, &up->shm_ctime); 531 err |= __put_user(s->shm_segsz, &up->shm_segsz); 532 err |= __put_user(s->shm_nattch, &up->shm_nattch); 533 err |= __put_user(s->shm_cpid, &up->shm_cpid); 534 err |= __put_user(s->shm_lpid, &up->shm_lpid); 535 return err; 536 } 537 538 static inline int put_compat_shminfo64(struct shminfo64 *smi, 539 struct compat_shminfo64 __user *up64) 540 { 541 int err; 542 543 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) 544 return -EFAULT; 545 if (smi->shmmax > INT_MAX) 546 smi->shmmax = INT_MAX; 547 err = __put_user(smi->shmmax, &up64->shmmax); 548 err |= __put_user(smi->shmmin, &up64->shmmin); 549 err |= __put_user(smi->shmmni, &up64->shmmni); 550 err |= __put_user(smi->shmseg, &up64->shmseg); 551 err |= __put_user(smi->shmall, &up64->shmall); 552 return err; 553 } 554 555 static inline int put_compat_shminfo(struct shminfo64 *smi, 556 struct shminfo __user *up) 557 { 558 int err; 559 560 if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) 561 return -EFAULT; 562 if (smi->shmmax > INT_MAX) 563 smi->shmmax = INT_MAX; 564 err = __put_user(smi->shmmax, &up->shmmax); 565 err |= __put_user(smi->shmmin, &up->shmmin); 566 err |= __put_user(smi->shmmni, &up->shmmni); 567 err |= __put_user(smi->shmseg, &up->shmseg); 568 err |= __put_user(smi->shmall, &up->shmall); 569 return err; 570 } 571 572 static inline int put_compat_shm_info(struct shm_info __user *ip, 573 struct compat_shm_info __user *uip) 574 { 575 int err; 576 struct shm_info si; 577 578 if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip)) || 579 copy_from_user(&si, ip, sizeof(si))) 580 return -EFAULT; 581 err = __put_user(si.used_ids, &uip->used_ids); 582 err |= __put_user(si.shm_tot, &uip->shm_tot); 583 err |= __put_user(si.shm_rss, &uip->shm_rss); 584 err |= __put_user(si.shm_swp, &uip->shm_swp); 585 err |= __put_user(si.swap_attempts, &uip->swap_attempts); 586 err |= __put_user(si.swap_successes, &uip->swap_successes); 587 return err; 588 } 589 590 long compat_sys_shmctl(int first, int second, void __user *uptr) 591 { 592 void __user *p; 593 struct shmid64_ds s64; 594 struct shminfo64 smi; 595 int err, err2; 596 int version = compat_ipc_parse_version(&second); 597 598 switch (second & (~IPC_64)) { 599 case IPC_RMID: 600 case SHM_LOCK: 601 case SHM_UNLOCK: 602 err = sys_shmctl(first, second, uptr); 603 break; 604 605 case IPC_INFO: 606 p = compat_alloc_user_space(sizeof(smi)); 607 err = sys_shmctl(first, second, p); 608 if (err < 0) 609 break; 610 if (copy_from_user(&smi, p, sizeof(smi))) 611 err2 = -EFAULT; 612 else if (version == IPC_64) 613 err2 = put_compat_shminfo64(&smi, uptr); 614 else 615 err2 = put_compat_shminfo(&smi, uptr); 616 if (err2) 617 err = -EFAULT; 618 break; 619 620 621 case IPC_SET: 622 if (version == IPC_64) { 623 err = get_compat_shmid64_ds(&s64, uptr); 624 } else { 625 err = get_compat_shmid_ds(&s64, uptr); 626 } 627 if (err) 628 break; 629 p = compat_alloc_user_space(sizeof(s64)); 630 if (copy_to_user(p, &s64, sizeof(s64))) 631 err = -EFAULT; 632 else 633 err = sys_shmctl(first, second, p); 634 break; 635 636 case IPC_STAT: 637 case SHM_STAT: 638 p = compat_alloc_user_space(sizeof(s64)); 639 err = sys_shmctl(first, second, p); 640 if (err < 0) 641 break; 642 if (copy_from_user(&s64, p, sizeof(s64))) 643 err2 = -EFAULT; 644 else if (version == IPC_64) 645 err2 = put_compat_shmid64_ds(&s64, uptr); 646 else 647 err2 = put_compat_shmid_ds(&s64, uptr); 648 if (err2) 649 err = -EFAULT; 650 break; 651 652 case SHM_INFO: 653 p = compat_alloc_user_space(sizeof(struct shm_info)); 654 err = sys_shmctl(first, second, p); 655 if (err < 0) 656 break; 657 err2 = put_compat_shm_info(p, uptr); 658 if (err2) 659 err = -EFAULT; 660 break; 661 662 default: 663 err = -EINVAL; 664 break; 665 } 666 return err; 667 } 668 669 long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, 670 unsigned nsops, const struct compat_timespec __user *timeout) 671 { 672 struct timespec __user *ts64 = NULL; 673 if (timeout) { 674 struct timespec ts; 675 ts64 = compat_alloc_user_space(sizeof(*ts64)); 676 if (get_compat_timespec(&ts, timeout)) 677 return -EFAULT; 678 if (copy_to_user(ts64, &ts, sizeof(ts))) 679 return -EFAULT; 680 } 681 return sys_semtimedop(semid, tsems, nsops, ts64); 682 } 683