1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2026 The FreeBSD Foundation 5 * 6 * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 7 * under sponsorship from the FreeBSD Foundation. 8 */ 9 10 #include <sys/systm.h> 11 #include <sys/conf.h> 12 #include <sys/fcntl.h> 13 #include <sys/file.h> 14 #include <sys/filedesc.h> 15 #include <sys/kernel.h> 16 #include <sys/limits.h> 17 #include <sys/malloc.h> 18 #include <sys/module.h> 19 #include <sys/proc.h> 20 #include <sys/stat.h> 21 #include <sys/sysent.h> 22 #include <sys/user.h> 23 #include <dev/ntsync/ntsyncvar.h> 24 25 static struct cdev *ntsync_cdev; 26 MALLOC_DEFINE(M_NTSYNC, "ntsync", "ntsync"); 27 28 static void ntsync_free_priv(struct ntsync_priv *priv); 29 30 /* 31 * Returning error from an ioctl handler prevents the generic ioctl 32 * code from copying out the result. Use direct access to ioctl(2) 33 * args to get the parameters block pointer to implement Linux 34 * semantic of both returning an error and updating the parameters 35 * block. 36 */ 37 static int 38 ntsync_ioctl_copyout(struct thread *td, const void *ptr, size_t sz) 39 { 40 void *uptr; 41 42 if (SV_PROC_ABI(td->td_proc) != SV_ABI_FREEBSD) 43 return (0); 44 uptr = (void *)(uintptr_t)td->td_sa.args[2]; 45 return (copyout(ptr, uptr, sz)); 46 } 47 48 static bool 49 ntsync_wait_any(struct ntsync_wait_state *state) 50 { 51 struct ntsync_obj *obj; 52 int i; 53 54 MPASS(state->any); 55 NTSYNC_PRIV_ASSERT(state->owner); 56 57 for (i = 0; i < state->obj_count; i++) { 58 obj = state->objs[i]; 59 if (obj->is_signaled(obj, state, i)) { 60 state->index = i; 61 obj->consume(obj, state, state->index); 62 return (true); 63 } 64 } 65 return (false); 66 } 67 68 static bool 69 ntsync_wait_all_prepare(struct ntsync_wait_state *state, bool *stop) 70 { 71 struct ntsync_obj *obj; 72 int alerti, i; 73 bool first; 74 75 MPASS(state->all); 76 MPASS(state->error == 0); 77 MPASS(!*stop); 78 NTSYNC_PRIV_ASSERT(state->owner); 79 80 alerti = state->alert_event == NULL ? 0 : 1; 81 first = true; 82 83 for (i = 0; i < state->obj_count - alerti; i++) { 84 obj = state->objs[i]; 85 if (!obj->prepare(obj, state, i, stop)) 86 return (false); 87 if (*stop) { 88 MPASS(state->error != 0); 89 return (false); 90 } 91 MPASS (state->error == 0); 92 if (first) { 93 first = false; 94 state->index = i; 95 } 96 } 97 return (true); 98 } 99 100 static void 101 ntsync_wait_all_commit(struct ntsync_wait_state *state) 102 { 103 struct ntsync_obj *obj; 104 int i, alerti; 105 106 MPASS(state->all); 107 NTSYNC_PRIV_ASSERT(state->owner); 108 alerti = state->alert_event == NULL ? 0 : 1; 109 110 for (i = 0; i < state->obj_count - alerti; i++) { 111 obj = state->objs[i]; 112 obj->commit(obj, state, i); 113 } 114 } 115 116 static void 117 ntsync_wait_link_waiters(struct ntsync_wait_state *state) 118 { 119 struct ntsync_obj *obj; 120 struct ntsync_obj_waiter *waiter; 121 int i; 122 123 NTSYNC_PRIV_ASSERT(state->owner); 124 125 for (i = 0; i < state->obj_count; i++) { 126 obj = state->objs[i]; 127 waiter = &state->waiters[i]; 128 waiter->state = state; 129 TAILQ_INSERT_TAIL(&obj->waiters, waiter, link); 130 } 131 } 132 133 static void 134 ntsync_wait_unlink_waiters(struct ntsync_wait_state *state) 135 { 136 struct ntsync_obj *obj; 137 struct ntsync_obj_waiter *waiter; 138 int i; 139 140 NTSYNC_PRIV_ASSERT(state->owner); 141 142 for (i = 0; i < state->obj_count; i++) { 143 obj = state->objs[i]; 144 waiter = &state->waiters[i]; 145 TAILQ_REMOVE(&obj->waiters, waiter, link); 146 } 147 } 148 149 static void 150 ntsync_wait_post_commit(struct ntsync_wait_state *state) 151 { 152 struct ntsync_obj *obj; 153 int alerti, i; 154 155 NTSYNC_PRIV_ASSERT(state->owner); 156 157 alerti = state->alert_event == NULL ? 0 : 1; 158 for (i = 0; i < state->obj_count - alerti; i++) { 159 obj = state->objs[i]; 160 obj->post_commit(obj, state, i); 161 } 162 } 163 164 static void 165 ntsync_wait_check_ready(struct ntsync_wait_state *state) 166 { 167 struct ntsync_obj *ae; 168 int index; 169 bool stop; 170 171 NTSYNC_PRIV_ASSERT(state->owner); 172 173 if (state->ready) 174 return; 175 176 if (state->all) { 177 stop = false; 178 if (ntsync_wait_all_prepare(state, &stop)) { 179 MPASS(!stop); 180 ntsync_wait_all_commit(state); 181 state->ready = true; 182 ntsync_wait_post_commit(state); 183 } else if (stop) { 184 /* skip */ 185 } else if (state->alert_event != NULL) { 186 ae = &state->alert_event->obj; 187 index = state->obj_count - 1; 188 if (ae->is_signaled(ae, state, index)) { 189 state->index = index; 190 ae->consume(ae, state, index); 191 ae->post_commit(ae, state, index); 192 state->ready = true; 193 } 194 } 195 } else { /* state->any */ 196 if (ntsync_wait_any(state)) 197 state->ready = true; 198 } 199 } 200 201 /* 202 * Perform the wait. Errors returned through state->error still 203 * result in the copyout of the ntsync_wait_args after the wait, while 204 * errors returned as the function result do not. 205 */ 206 static int 207 ntsync_wait_locked(struct ntsync_wait_state *state, struct thread *td) 208 { 209 int error; 210 211 NTSYNC_PRIV_ASSERT(state->owner); 212 213 for (;;) { 214 ntsync_wait_check_ready(state); 215 if (state->ready) 216 break; 217 error = msleep_sbt(state, &state->owner->lock, 218 PCATCH, "ntsync", state->sb, state->prec, 219 C_ABSOLUTE /* | C_HARDCLOCK XXXKIB */); 220 221 /* 222 * Check state->ready before checking error from 223 * msleep(). If there was a wake up that set the 224 * readiness before us receiving a signal or timeout, 225 * the objects states are modified to reflect wakeup. 226 * Due to this, ready should result in normal return. 227 */ 228 if (state->ready) { 229 error = 0; 230 break; 231 } 232 233 if (error != 0) { 234 if (error == EAGAIN) 235 error = ETIMEDOUT; 236 break; 237 } 238 } 239 return (error); 240 } 241 242 static int 243 ntsync_wait(struct ntsync_wait_state *state, struct thread *td) 244 { 245 int error; 246 247 NTSYNC_PRIV_LOCK(state->owner); 248 ntsync_wait_link_waiters(state); 249 error = ntsync_wait_locked(state, td); 250 ntsync_wait_unlink_waiters(state); 251 NTSYNC_PRIV_UNLOCK(state->owner); 252 return (error); 253 } 254 255 static void 256 ntsync_wakeup_waiters(struct ntsync_obj *obj) 257 { 258 struct ntsync_obj_waiter *w; 259 260 NTSYNC_PRIV_ASSERT(obj->owner); 261 262 TAILQ_FOREACH(w, &obj->waiters, link) { 263 ntsync_wait_check_ready(w->state); 264 if (w->state->ready) 265 wakeup(w->state); 266 } 267 } 268 269 static int 270 ntsync_create_obj(struct ntsync_obj *obj, struct fileops *fops, 271 struct ntsync_priv *priv, struct thread *td) 272 { 273 struct file *fp; 274 int error, fd; 275 276 error = falloc_noinstall(td, &fp); 277 if (error != 0) 278 return (error); 279 280 /* 281 * The priv fd cannot be closed during object creation since 282 * it is fget-ed around ioctl. 283 */ 284 obj->owner = priv; 285 286 TAILQ_INIT(&obj->waiters); 287 NTSYNC_PRIV_LOCK(priv); 288 MPASS(!priv->closed); 289 if (priv->objs_cnt == UINT_MAX) { 290 NTSYNC_PRIV_UNLOCK(priv); 291 fdrop(fp, td); 292 return (EMFILE); 293 } 294 priv->objs_cnt++; 295 NTSYNC_PRIV_UNLOCK(priv); 296 297 finit(fp, FREAD | FWRITE, DTYPE_NTSYNC, obj, fops); 298 error = finstall(td, fp, &fd, 0, NULL); 299 if (error != 0) { 300 finit(fp, FREAD | FWRITE, DTYPE_NONE, NULL, &badfileops); 301 NTSYNC_PRIV_LOCK(priv); 302 MPASS(priv->objs_cnt > 0); 303 priv->objs_cnt--; 304 NTSYNC_PRIV_UNLOCK(priv); 305 } else { 306 td->td_retval[0] = fd; 307 } 308 fdrop(fp, td); 309 return (error); 310 } 311 312 static void 313 ntsync_close_obj(struct ntsync_obj *obj, struct thread *td) 314 { 315 struct ntsync_priv *priv; 316 317 priv = obj->owner; 318 NTSYNC_PRIV_LOCK(priv); 319 MPASS(priv->objs_cnt > 0); 320 MPASS(TAILQ_EMPTY(&obj->waiters)); 321 priv->objs_cnt--; 322 NTSYNC_PRIV_UNLOCK(priv); 323 ntsync_free_priv(priv); 324 } 325 326 static bool 327 ntsync_sem_is_signaled(struct ntsync_obj *obj, struct ntsync_wait_state *state, 328 int index) 329 { 330 struct ntsync_obj_sem *sem; 331 332 MPASS(obj->type == NTSYNC_OBJ_SEM); 333 NTSYNC_PRIV_ASSERT(obj->owner); 334 sem = OBJ_TO_SEM(obj); 335 return (sem->a.count != 0); 336 } 337 338 static void 339 ntsync_sem_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state, 340 int index) 341 { 342 struct ntsync_obj_sem *sem; 343 344 MPASS(obj->type == NTSYNC_OBJ_SEM); 345 NTSYNC_PRIV_ASSERT(obj->owner); 346 sem = OBJ_TO_SEM(obj); 347 MPASS(sem->a.count != 0); 348 sem->a.count--; 349 } 350 351 static bool 352 ntsync_sem_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state, 353 int index, bool *stop) 354 { 355 struct ntsync_obj_sem *sem; 356 357 MPASS(obj->type == NTSYNC_OBJ_SEM); 358 NTSYNC_PRIV_ASSERT(obj->owner); 359 sem = OBJ_TO_SEM(obj); 360 if (sem->a.count == 0) 361 return (false); 362 sem->a1 = sem->a; 363 sem->a1.count--; 364 return (true); 365 } 366 367 static void 368 ntsync_sem_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 369 int index) 370 { 371 struct ntsync_obj_sem *sem; 372 373 MPASS(obj->type == NTSYNC_OBJ_SEM); 374 NTSYNC_PRIV_ASSERT(obj->owner); 375 sem = OBJ_TO_SEM(obj); 376 sem->a = sem->a1; 377 } 378 379 static void 380 ntsync_sem_post_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 381 int index) 382 { 383 } 384 385 static int 386 ntsync_sem_close(struct file *fp, struct thread *td) 387 { 388 struct ntsync_obj_sem *sem; 389 390 sem = fp->f_data; 391 ntsync_close_obj(&sem->obj, td); 392 free(sem, M_NTSYNC); 393 return (0); 394 } 395 396 int 397 ntsync_sem_release(struct thread *td, struct file *fp, uint32_t *val) 398 { 399 struct ntsync_obj *obj; 400 struct ntsync_obj_sem *sem; 401 struct ntsync_priv *priv; 402 uint32_t prev; 403 int error; 404 405 obj = fp->f_data; 406 if (obj->type != NTSYNC_OBJ_SEM) 407 return (EINVAL); 408 sem = OBJ_TO_SEM(obj); 409 priv = obj->owner; 410 error = 0; 411 412 NTSYNC_PRIV_LOCK(priv); 413 if (sem->a.count + *val < sem->a.count || 414 sem->a.count + *val > sem->a.max) { 415 error = EOVERFLOW; 416 } else { 417 prev = sem->a.count; 418 sem->a.count += *val; 419 if (sem->a.count != 0) 420 ntsync_wakeup_waiters(obj); 421 *val = prev; 422 } 423 NTSYNC_PRIV_UNLOCK(priv); 424 return (error); 425 } 426 427 int 428 ntsync_sem_read(struct thread *td, struct file *fp, struct ntsync_sem_args *a) 429 { 430 struct ntsync_obj *obj; 431 struct ntsync_obj_sem *sem; 432 struct ntsync_priv *priv; 433 434 obj = fp->f_data; 435 if (obj->type != NTSYNC_OBJ_SEM) 436 return (EINVAL); 437 sem = OBJ_TO_SEM(obj); 438 priv = obj->owner; 439 NTSYNC_PRIV_LOCK(priv); 440 *a = sem->a; 441 NTSYNC_PRIV_UNLOCK(priv); 442 return (0); 443 } 444 445 static int 446 ntsync_sem_ioctl(struct file *fp, u_long com, void *data, 447 struct ucred *active_cred, struct thread *td) 448 { 449 int error; 450 451 switch (com) { 452 case NTSYNC_IOC_SEM_RELEASE: 453 error = ntsync_sem_release(td, fp, data); 454 break; 455 case NTSYNC_IOC_SEM_READ: 456 error = ntsync_sem_read(td, fp, data); 457 break; 458 default: 459 error = ENOTTY; 460 break; 461 } 462 return (error); 463 } 464 465 static int 466 ntsync_sem_stat(struct file *fp, struct stat *sbp, struct ucred *cred) 467 { 468 struct ntsync_obj *obj; 469 struct ntsync_obj_sem *sem; 470 471 MPASS(fp->f_type == DTYPE_NTSYNC); 472 obj = fp->f_data; 473 MPASS(obj->type == NTSYNC_OBJ_SEM); 474 sem = OBJ_TO_SEM(obj); 475 476 memset(sbp, 0, sizeof(*sbp)); 477 sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR; 478 NTSYNC_PRIV_LOCK(obj->owner); 479 sbp->st_size = sem->a.max; 480 sbp->st_nlink = sem->a.count; 481 NTSYNC_PRIV_UNLOCK(obj->owner); 482 return (0); 483 } 484 485 static int 486 ntsync_sem_fill_kinfo(struct file *fp, struct kinfo_file *kif, 487 struct filedesc *fdp) 488 { 489 struct ntsync_obj *obj; 490 struct ntsync_obj_sem *sem; 491 492 MPASS(fp->f_type == DTYPE_NTSYNC); 493 obj = fp->f_data; 494 MPASS(obj->type == NTSYNC_OBJ_SEM); 495 sem = OBJ_TO_SEM(obj); 496 497 kif->kf_type = KF_TYPE_NTSYNC; 498 kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_SEM; 499 kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner; 500 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.count = sem->a.count; 501 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.max = sem->a.max; 502 return (0); 503 } 504 505 struct fileops ntsync_sem_fops = { 506 .fo_read = invfo_rdwr, 507 .fo_write = invfo_rdwr, 508 .fo_truncate = invfo_truncate, 509 .fo_ioctl = ntsync_sem_ioctl, 510 .fo_poll = invfo_poll, 511 .fo_kqfilter = invfo_kqfilter, 512 .fo_stat = ntsync_sem_stat, 513 .fo_close = ntsync_sem_close, 514 .fo_chmod = invfo_chmod, 515 .fo_chown = invfo_chown, 516 .fo_sendfile = invfo_sendfile, 517 .fo_fill_kinfo = ntsync_sem_fill_kinfo, 518 .fo_flags = DFLAG_PASSABLE, 519 }; 520 521 static int 522 ntsync_create_sem(struct ntsync_sem_args *args, struct ntsync_priv *priv, 523 struct thread *td) 524 { 525 struct ntsync_obj_sem *sem; 526 int error; 527 528 if (args->count > args->max) 529 return (EINVAL); 530 531 sem = malloc(sizeof(*sem), M_NTSYNC, M_WAITOK | M_ZERO); 532 sem->obj.type = NTSYNC_OBJ_SEM; 533 sem->obj.is_signaled = ntsync_sem_is_signaled; 534 sem->obj.consume = ntsync_sem_consume; 535 sem->obj.prepare = ntsync_sem_prepare; 536 sem->obj.commit = ntsync_sem_commit; 537 sem->obj.post_commit = ntsync_sem_post_commit; 538 sem->a = *args; 539 540 error = ntsync_create_obj(&sem->obj, &ntsync_sem_fops, priv, td); 541 if (error != 0) 542 free(sem, M_NTSYNC); 543 544 return (error); 545 } 546 547 static bool 548 ntsync_mutex_can_lock(struct ntsync_obj_mutex *mutex, uint32_t nwa_owner) 549 { 550 return (mutex->a.owner == 0 || 551 (mutex->a.owner == nwa_owner && mutex->a.count < UINT32_MAX) || 552 mutex->abandoned); 553 } 554 555 static bool 556 ntsync_mutex_is_signaled(struct ntsync_obj *obj, 557 struct ntsync_wait_state *state, int index) 558 { 559 struct ntsync_obj_mutex *mutex; 560 561 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 562 NTSYNC_PRIV_ASSERT(obj->owner); 563 mutex = OBJ_TO_MUTEX(obj); 564 return (ntsync_mutex_can_lock(mutex, state->nwa->owner)); 565 } 566 567 static void 568 ntsync_mutex_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state, 569 int index) 570 { 571 struct ntsync_obj_mutex *mutex; 572 573 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 574 NTSYNC_PRIV_ASSERT(obj->owner); 575 mutex = OBJ_TO_MUTEX(obj); 576 MPASS(ntsync_mutex_can_lock(mutex, state->nwa->owner)); 577 if (state->nwa->owner == 0) { 578 state->error = EINVAL; 579 return; 580 } 581 if (mutex->a.owner == 0 || mutex->abandoned) 582 mutex->a.count = 1; 583 else 584 mutex->a.count++; 585 mutex->a.owner = state->nwa->owner; 586 if (mutex->abandoned && state->error == 0) 587 state->error = EOWNERDEAD; 588 mutex->abandoned = false; 589 } 590 591 static bool 592 ntsync_mutex_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state, 593 int index, bool *stop) 594 { 595 struct ntsync_obj_mutex *mutex; 596 597 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 598 NTSYNC_PRIV_ASSERT(obj->owner); 599 mutex = OBJ_TO_MUTEX(obj); 600 if (!ntsync_mutex_can_lock(mutex, state->nwa->owner)) 601 return (false); 602 if (state->nwa->owner == 0) { 603 state->error = EINVAL; 604 *stop = true; 605 return (false); 606 } 607 mutex->a1 = mutex->a; 608 if (mutex->a.owner == 0 || mutex->abandoned) 609 mutex->a1.count = 1; 610 else 611 mutex->a1.count++; 612 mutex->a1.owner = state->nwa->owner; 613 return (true); 614 } 615 616 static void 617 ntsync_mutex_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 618 int index) 619 { 620 struct ntsync_obj_mutex *mutex; 621 622 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 623 NTSYNC_PRIV_ASSERT(obj->owner); 624 mutex = OBJ_TO_MUTEX(obj); 625 mutex->a = mutex->a1; 626 if (mutex->abandoned) 627 state->error = EOWNERDEAD; 628 mutex->abandoned = false; 629 } 630 631 static void 632 ntsync_mutex_post_commit(struct ntsync_obj *obj, 633 struct ntsync_wait_state *state, int index) 634 { 635 } 636 637 static int 638 ntsync_mutex_close(struct file *fp, struct thread *td) 639 { 640 struct ntsync_obj_mutex *mutex; 641 642 mutex = fp->f_data; 643 ntsync_close_obj(&mutex->obj, td); 644 free(mutex, M_NTSYNC); 645 return (0); 646 } 647 648 int 649 ntsync_mutex_unlock(struct thread *td, struct file *fp, 650 struct ntsync_mutex_args *a) 651 { 652 struct ntsync_obj *obj; 653 struct ntsync_obj_mutex *mutex; 654 struct ntsync_priv *priv; 655 uint32_t prev; 656 int error; 657 658 obj = fp->f_data; 659 if (obj->type != NTSYNC_OBJ_MUTEX) 660 return (EINVAL); 661 mutex = OBJ_TO_MUTEX(obj); 662 priv = obj->owner; 663 664 NTSYNC_PRIV_LOCK(priv); 665 if (a->owner == 0) { 666 error = EINVAL; 667 } else if (a->owner != mutex->a.owner) { 668 error = EPERM; 669 } else { 670 error = 0; 671 prev = mutex->a.count; 672 MPASS(mutex->a.count > 0); 673 mutex->a.count--; 674 a->count = prev; 675 if (mutex->a.count == 0) { 676 mutex->a.owner = 0; 677 ntsync_wakeup_waiters(obj); 678 } 679 } 680 NTSYNC_PRIV_UNLOCK(priv); 681 return (error); 682 } 683 684 int 685 ntsync_mutex_kill(struct thread *td, struct file *fp, uint32_t val) 686 { 687 struct ntsync_obj *obj; 688 struct ntsync_obj_mutex *mutex; 689 struct ntsync_priv *priv; 690 int error; 691 692 obj = fp->f_data; 693 if (obj->type != NTSYNC_OBJ_MUTEX) 694 return (EINVAL); 695 mutex = OBJ_TO_MUTEX(obj); 696 priv = obj->owner; 697 698 NTSYNC_PRIV_LOCK(priv); 699 if (val == 0) { 700 error = EINVAL; 701 } else if (mutex->a.owner != val) { 702 error = EPERM; 703 } else { 704 error = 0; 705 mutex->a.owner = 0; 706 mutex->a.count = 0; 707 mutex->abandoned = true; 708 ntsync_wakeup_waiters(obj); 709 } 710 NTSYNC_PRIV_UNLOCK(priv); 711 return (error); 712 } 713 714 int 715 ntsync_mutex_read(struct thread *td, struct file *fp, 716 struct ntsync_mutex_args *a, bool *doco) 717 { 718 struct ntsync_obj *obj; 719 struct ntsync_obj_mutex *mutex; 720 struct ntsync_priv *priv; 721 int error; 722 723 *doco = false; 724 obj = fp->f_data; 725 if (obj->type != NTSYNC_OBJ_MUTEX) 726 return (EINVAL); 727 mutex = OBJ_TO_MUTEX(obj); 728 priv = obj->owner; 729 error = 0; 730 731 NTSYNC_PRIV_LOCK(priv); 732 *a = mutex->a; 733 if (mutex->abandoned) 734 error = EOWNERDEAD; 735 NTSYNC_PRIV_UNLOCK(priv); 736 *doco = true; 737 return (error); 738 } 739 740 static int 741 ntsync_mutex_ioctl(struct file *fp, u_long com, void *data, 742 struct ucred *active_cred, struct thread *td) 743 { 744 struct ntsync_mutex_args aa; 745 int error, error1; 746 bool doco; 747 748 doco = false; 749 switch (com) { 750 case NTSYNC_IOC_MUTEX_UNLOCK: 751 error = ntsync_mutex_unlock(td, fp, data); 752 break; 753 case NTSYNC_IOC_MUTEX_KILL: 754 error = ntsync_mutex_kill(td, fp, *(uint32_t *)data); 755 break; 756 case NTSYNC_IOC_MUTEX_READ: 757 error = ntsync_mutex_read(td, fp, &aa, &doco); 758 if (doco) { 759 error1 = ntsync_ioctl_copyout(td, &aa, sizeof(aa)); 760 if (error1 != 0) 761 error = error1; 762 } 763 break; 764 default: 765 error = ENOTTY; 766 break; 767 } 768 return (error); 769 } 770 771 static int 772 ntsync_mutex_stat(struct file *fp, struct stat *sbp, struct ucred *cred) 773 { 774 struct ntsync_obj *obj; 775 struct ntsync_obj_mutex *mutex; 776 777 MPASS(fp->f_type == DTYPE_NTSYNC); 778 obj = fp->f_data; 779 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 780 mutex = OBJ_TO_MUTEX(obj); 781 782 memset(sbp, 0, sizeof(*sbp)); 783 sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR; 784 NTSYNC_PRIV_LOCK(obj->owner); 785 sbp->st_size = mutex->a.owner; 786 sbp->st_nlink = mutex->a.count; 787 NTSYNC_PRIV_UNLOCK(obj->owner); 788 return (0); 789 } 790 791 static int 792 ntsync_mutex_fill_kinfo(struct file *fp, struct kinfo_file *kif, 793 struct filedesc *fdp) 794 { 795 struct ntsync_obj *obj; 796 struct ntsync_obj_mutex *mutex; 797 798 MPASS(fp->f_type == DTYPE_NTSYNC); 799 obj = fp->f_data; 800 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 801 mutex = OBJ_TO_MUTEX(obj); 802 803 kif->kf_type = KF_TYPE_NTSYNC; 804 kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_MUTEX; 805 kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner; 806 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.owner = 807 mutex->a.owner; 808 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.count = 809 mutex->a.count; 810 return (0); 811 } 812 813 struct fileops ntsync_mutex_fops = { 814 .fo_read = invfo_rdwr, 815 .fo_write = invfo_rdwr, 816 .fo_truncate = invfo_truncate, 817 .fo_ioctl = ntsync_mutex_ioctl, 818 .fo_poll = invfo_poll, 819 .fo_kqfilter = invfo_kqfilter, 820 .fo_stat = ntsync_mutex_stat, 821 .fo_close = ntsync_mutex_close, 822 .fo_chmod = invfo_chmod, 823 .fo_chown = invfo_chown, 824 .fo_sendfile = invfo_sendfile, 825 .fo_fill_kinfo = ntsync_mutex_fill_kinfo, 826 .fo_flags = DFLAG_PASSABLE, 827 }; 828 829 static int 830 ntsync_create_mutex(struct ntsync_mutex_args *args, struct ntsync_priv *priv, 831 struct thread *td) 832 { 833 struct ntsync_obj_mutex *mutex; 834 int error; 835 836 if ((args->owner != 0 && args->count == 0) || 837 (args->owner == 0 && args->count != 0)) 838 return (EINVAL); 839 840 mutex = malloc(sizeof(*mutex), M_NTSYNC, M_WAITOK | M_ZERO); 841 mutex->obj.type = NTSYNC_OBJ_MUTEX; 842 mutex->obj.is_signaled = ntsync_mutex_is_signaled; 843 mutex->obj.consume = ntsync_mutex_consume; 844 mutex->obj.prepare = ntsync_mutex_prepare; 845 mutex->obj.commit = ntsync_mutex_commit; 846 mutex->obj.post_commit = ntsync_mutex_post_commit; 847 mutex->a = *args; 848 mutex->abandoned = false; 849 850 error = ntsync_create_obj(&mutex->obj, &ntsync_mutex_fops, priv, td); 851 if (error != 0) 852 free(mutex, M_NTSYNC); 853 854 return (error); 855 } 856 857 static bool 858 ntsync_event_is_signaled(struct ntsync_obj *obj, 859 struct ntsync_wait_state *state, int index) 860 { 861 struct ntsync_obj_event *event; 862 863 MPASS(obj->type == NTSYNC_OBJ_EVENT); 864 NTSYNC_PRIV_ASSERT(obj->owner); 865 event = OBJ_TO_EVENT(obj); 866 return (event->a.signaled != 0); 867 } 868 869 static void 870 ntsync_event_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state, 871 int index) 872 { 873 struct ntsync_obj_event *event; 874 875 MPASS(obj->type == NTSYNC_OBJ_EVENT); 876 NTSYNC_PRIV_ASSERT(obj->owner); 877 MPASS(ntsync_event_is_signaled(obj, state, index)); 878 879 event = OBJ_TO_EVENT(obj); 880 if (event->a.manual == 0) 881 event->a.signaled = 0; 882 } 883 884 static bool 885 ntsync_event_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state, 886 int index, bool *stop) 887 { 888 struct ntsync_obj_event *event; 889 890 MPASS(obj->type == NTSYNC_OBJ_EVENT); 891 NTSYNC_PRIV_ASSERT(obj->owner); 892 event = OBJ_TO_EVENT(obj); 893 if (!ntsync_event_is_signaled(obj, state, index)) 894 return (false); 895 event->a1 = event->a; 896 return (true); 897 } 898 899 static void 900 ntsync_event_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 901 int index) 902 { 903 struct ntsync_obj_event *event; 904 905 MPASS(obj->type == NTSYNC_OBJ_EVENT); 906 NTSYNC_PRIV_ASSERT(obj->owner); 907 event = OBJ_TO_EVENT(obj); 908 event->a = event->a1; 909 if (event->pulse && event->a.manual == 0) { 910 event->a.signaled = 0; 911 event->pulse = false; 912 } 913 } 914 915 static void 916 ntsync_event_post_commit(struct ntsync_obj *obj, 917 struct ntsync_wait_state *state, int index) 918 { 919 struct ntsync_obj_event *event; 920 921 MPASS(obj->type == NTSYNC_OBJ_EVENT); 922 NTSYNC_PRIV_ASSERT(obj->owner); 923 event = OBJ_TO_EVENT(obj); 924 if (event->a.manual == 0) 925 event->a.signaled = 0; 926 } 927 928 static int 929 ntsync_event_close(struct file *fp, struct thread *td) 930 { 931 struct ntsync_obj_event *event; 932 933 event = fp->f_data; 934 ntsync_close_obj(&event->obj, td); 935 free(event, M_NTSYNC); 936 return (0); 937 } 938 939 int 940 ntsync_event_set(struct thread *td, struct file *fp, uint32_t *val) 941 { 942 struct ntsync_obj *obj; 943 struct ntsync_obj_event *event; 944 struct ntsync_priv *priv; 945 uint32_t prev; 946 947 obj = fp->f_data; 948 if (obj->type != NTSYNC_OBJ_EVENT) 949 return (EINVAL); 950 event = OBJ_TO_EVENT(obj); 951 priv = obj->owner; 952 953 NTSYNC_PRIV_LOCK(priv); 954 prev = event->a.signaled; 955 event->a.signaled = 1; 956 ntsync_wakeup_waiters(obj); 957 NTSYNC_PRIV_UNLOCK(priv); 958 959 *val = prev; 960 return (0); 961 } 962 963 int 964 ntsync_event_reset(struct thread *td, struct file *fp, uint32_t *val) 965 { 966 struct ntsync_obj *obj; 967 struct ntsync_obj_event *event; 968 struct ntsync_priv *priv; 969 uint32_t prev; 970 971 obj = fp->f_data; 972 if (obj->type != NTSYNC_OBJ_EVENT) 973 return (EINVAL); 974 event = OBJ_TO_EVENT(obj); 975 priv = obj->owner; 976 977 NTSYNC_PRIV_LOCK(priv); 978 prev = event->a.signaled; 979 event->a.signaled = 0; 980 NTSYNC_PRIV_UNLOCK(priv); 981 982 *val = prev; 983 return (0); 984 } 985 986 int 987 ntsync_event_pulse(struct thread *td, struct file *fp, uint32_t *val) 988 { 989 struct ntsync_obj *obj; 990 struct ntsync_obj_event *event; 991 struct ntsync_priv *priv; 992 uint32_t prev; 993 994 obj = fp->f_data; 995 if (obj->type != NTSYNC_OBJ_EVENT) 996 return (EINVAL); 997 event = OBJ_TO_EVENT(obj); 998 priv = obj->owner; 999 1000 NTSYNC_PRIV_LOCK(priv); 1001 prev = event->a.signaled; 1002 event->a.signaled = 1; 1003 event->pulse = true; 1004 ntsync_wakeup_waiters(obj); 1005 event->a.signaled = 0; 1006 event->pulse = false; 1007 NTSYNC_PRIV_UNLOCK(priv); 1008 1009 *val = prev; 1010 return (0); 1011 } 1012 1013 int 1014 ntsync_event_read(struct thread *td, struct file *fp, 1015 struct ntsync_event_args *a) 1016 { 1017 struct ntsync_obj *obj; 1018 struct ntsync_obj_event *event; 1019 struct ntsync_priv *priv; 1020 1021 obj = fp->f_data; 1022 if (obj->type != NTSYNC_OBJ_EVENT) 1023 return (EINVAL); 1024 event = OBJ_TO_EVENT(obj); 1025 priv = obj->owner; 1026 1027 NTSYNC_PRIV_LOCK(priv); 1028 *a = event->a; 1029 NTSYNC_PRIV_UNLOCK(priv); 1030 1031 return (0); 1032 } 1033 1034 static int 1035 ntsync_event_ioctl(struct file *fp, u_long com, void *data, 1036 struct ucred *active_cred, struct thread *td) 1037 { 1038 int error; 1039 1040 switch (com) { 1041 case NTSYNC_IOC_EVENT_SET: 1042 error = ntsync_event_set(td, fp, data); 1043 break; 1044 case NTSYNC_IOC_EVENT_RESET: 1045 error = ntsync_event_reset(td, fp, data); 1046 break; 1047 case NTSYNC_IOC_EVENT_PULSE: 1048 error = ntsync_event_pulse(td, fp, data); 1049 break; 1050 case NTSYNC_IOC_EVENT_READ: 1051 error = ntsync_event_read(td, fp, data); 1052 break; 1053 default: 1054 error = ENOTTY; 1055 break; 1056 } 1057 return (error); 1058 } 1059 1060 static int 1061 ntsync_event_stat(struct file *fp, struct stat *sbp, struct ucred *cred) 1062 { 1063 struct ntsync_obj *obj; 1064 struct ntsync_obj_event *event; 1065 1066 MPASS(fp->f_type == DTYPE_NTSYNC); 1067 obj = fp->f_data; 1068 MPASS(obj->type == NTSYNC_OBJ_EVENT); 1069 event = OBJ_TO_EVENT(obj); 1070 1071 memset(sbp, 0, sizeof(*sbp)); 1072 sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR; 1073 NTSYNC_PRIV_LOCK(obj->owner); 1074 sbp->st_size = event->a.signaled; 1075 sbp->st_nlink = event->a.manual; 1076 NTSYNC_PRIV_UNLOCK(obj->owner); 1077 return (0); 1078 } 1079 1080 static int 1081 ntsync_event_fill_kinfo(struct file *fp, struct kinfo_file *kif, 1082 struct filedesc *fdp) 1083 { 1084 struct ntsync_obj *obj; 1085 struct ntsync_obj_event *event; 1086 1087 MPASS(fp->f_type == DTYPE_NTSYNC); 1088 obj = fp->f_data; 1089 MPASS(obj->type == NTSYNC_OBJ_EVENT); 1090 event = OBJ_TO_EVENT(obj); 1091 1092 kif->kf_type = KF_TYPE_NTSYNC; 1093 kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_EVENT; 1094 kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner; 1095 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.signaled = 1096 event->a.signaled; 1097 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.manual = 1098 event->a.manual; 1099 return (0); 1100 } 1101 1102 struct fileops ntsync_event_fops = { 1103 .fo_read = invfo_rdwr, 1104 .fo_write = invfo_rdwr, 1105 .fo_truncate = invfo_truncate, 1106 .fo_ioctl = ntsync_event_ioctl, 1107 .fo_poll = invfo_poll, 1108 .fo_kqfilter = invfo_kqfilter, 1109 .fo_stat = ntsync_event_stat, 1110 .fo_close = ntsync_event_close, 1111 .fo_chmod = invfo_chmod, 1112 .fo_chown = invfo_chown, 1113 .fo_sendfile = invfo_sendfile, 1114 .fo_fill_kinfo = ntsync_event_fill_kinfo, 1115 .fo_flags = DFLAG_PASSABLE, 1116 }; 1117 1118 static int 1119 ntsync_create_event(struct ntsync_event_args *args, struct ntsync_priv *priv, 1120 struct thread *td) 1121 { 1122 struct ntsync_obj_event *event; 1123 int error; 1124 1125 event = malloc(sizeof(*event), M_NTSYNC, M_WAITOK | M_ZERO); 1126 event->obj.type = NTSYNC_OBJ_EVENT; 1127 event->obj.is_signaled = ntsync_event_is_signaled; 1128 event->obj.consume = ntsync_event_consume; 1129 event->obj.prepare = ntsync_event_prepare; 1130 event->obj.commit = ntsync_event_commit; 1131 event->obj.post_commit = ntsync_event_post_commit; 1132 event->a = *args; 1133 1134 error = ntsync_create_obj(&event->obj, &ntsync_event_fops, priv, td); 1135 if (error != 0) 1136 free(event, M_NTSYNC); 1137 1138 return (error); 1139 } 1140 1141 static void 1142 ntsync_free_priv(struct ntsync_priv *priv) 1143 { 1144 bool do_free; 1145 1146 NTSYNC_PRIV_LOCK(priv); 1147 do_free = priv->closed && priv->objs_cnt == 0; 1148 NTSYNC_PRIV_UNLOCK(priv); 1149 if (do_free) { 1150 mtx_destroy(&priv->lock); 1151 free(priv, M_NTSYNC); 1152 } 1153 } 1154 1155 static void 1156 ntsync_priv_dtr(void *data) 1157 { 1158 ntsync_free_priv(data); 1159 } 1160 1161 static int 1162 ntsync_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 1163 { 1164 struct ntsync_priv *priv; 1165 1166 priv = malloc(sizeof(*priv), M_NTSYNC, M_WAITOK); 1167 priv->closed = false; 1168 priv->objs_cnt = 0; 1169 mtx_init(&priv->lock, "ntsync", "ntsync", MTX_DEF | MTX_NEW); 1170 devfs_set_cdevpriv(priv, ntsync_priv_dtr); 1171 return (0); 1172 } 1173 1174 static int 1175 ntsync_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 1176 { 1177 struct ntsync_priv *priv; 1178 void *a; 1179 int error; 1180 1181 error = devfs_get_cdevpriv(&a); 1182 if (error == 0) { 1183 priv = a; 1184 NTSYNC_PRIV_LOCK(priv); 1185 priv->closed = true; 1186 NTSYNC_PRIV_UNLOCK(priv); 1187 } 1188 devfs_clear_cdevpriv(); 1189 return (0); 1190 } 1191 1192 static int 1193 ntsync_wait_state_get(struct ntsync_wait_args *nwa, u_long cmd, 1194 struct ntsync_priv *owner, struct ntsync_wait_state **statep, 1195 struct thread *td) 1196 { 1197 struct ntsync_wait_state *state; 1198 struct ntsync_obj *obj; 1199 int error, i, j; 1200 1201 if (nwa->count > NTSYNC_MAX_WAIT_COUNT) 1202 return (EINVAL); 1203 if ((nwa->flags & ~NTSYNC_WAIT_REALTIME) != 0) 1204 return (EINVAL); 1205 1206 state = malloc(sizeof(*state), M_NTSYNC, M_WAITOK | M_ZERO); 1207 state->nwa = nwa; 1208 state->owner = owner; 1209 state->all = cmd == NTSYNC_IOC_WAIT_ALL; 1210 state->any = !state->all; 1211 error = copyin((void *)(uintptr_t)nwa->objs, &state->fds[0], 1212 nwa->count * sizeof(state->fds[0])); 1213 if (error != 0) 1214 goto error_ret; 1215 1216 i = 0; 1217 if (nwa->alert != 0) { 1218 error = fget_cap(td, nwa->alert, &cap_no_rights, NULL, 1219 &state->fp_alert, NULL); 1220 if (error != 0) { 1221 state->fp_alert = NULL; 1222 goto error_out; 1223 } 1224 if (state->fp_alert->f_type != DTYPE_NTSYNC) { 1225 error = EINVAL; 1226 goto error_out; 1227 } 1228 obj = state->fp_alert->f_data; 1229 if (obj->type != NTSYNC_OBJ_EVENT || obj->owner != owner) { 1230 error = EINVAL; 1231 goto error_out; 1232 } 1233 state->alert_event = OBJ_TO_EVENT(obj); 1234 } 1235 1236 for (; i < nwa->count; i++) { 1237 error = fget_cap(td, state->fds[i], &cap_no_rights, NULL, 1238 &state->fps[i], NULL); 1239 if (error != 0) { 1240 state->fps[i] = NULL; 1241 goto error_out; 1242 } 1243 if (state->fps[i]->f_type != DTYPE_NTSYNC || 1244 (obj = state->fps[i]->f_data)->owner != owner) { 1245 i++; 1246 error = EINVAL; 1247 goto error_out; 1248 } 1249 } 1250 1251 state->obj_count = nwa->count; 1252 for (i = 0; i < nwa->count; i++) 1253 state->objs[i] = state->fps[i]->f_data; 1254 if (state->alert_event != NULL) { 1255 state->objs[i] = &state->alert_event->obj; 1256 state->obj_count++; 1257 } 1258 1259 if (state->all) { 1260 /* Check no dups */ 1261 for (i = 0; i < state->obj_count; i++) { 1262 obj = state->objs[i]; 1263 for (j = i + 1; j < state->obj_count; j++) { 1264 if (obj == state->objs[j]) { 1265 i = state->obj_count; 1266 error = EINVAL; 1267 goto error_out; 1268 } 1269 } 1270 } 1271 } 1272 1273 state->prec = 0; 1274 if (nwa->timeout == UINT64_MAX) { 1275 state->sb = 0; 1276 } else { 1277 state->sb = nstosbt(nwa->timeout); 1278 if ((nwa->flags & NTSYNC_WAIT_REALTIME) != 0) { 1279 struct bintime btb; 1280 1281 bintime(&btb); 1282 state->sb -= bttosbt(btb); 1283 } else { 1284 struct timespec ts; 1285 1286 nanouptime(&ts); 1287 state->sb -= tstosbt(ts); 1288 } 1289 state->sb += sbinuptime(); 1290 } 1291 1292 *statep = state; 1293 return (0); 1294 1295 error_out: 1296 for (j = 0; j < i; j++) 1297 fdrop(state->fps[j], td); 1298 if (state->fp_alert != NULL) 1299 fdrop(state->fp_alert, td); 1300 error_ret: 1301 free(state, M_NTSYNC); 1302 return (error); 1303 } 1304 1305 static void 1306 ntsync_wait_state_put(struct ntsync_wait_state *state, struct thread *td) 1307 { 1308 int i; 1309 1310 for (i = 0; i < state->nwa->count; i++) 1311 fdrop(state->fps[i], td); 1312 if (state->fp_alert != NULL) 1313 fdrop(state->fp_alert, td); 1314 free(state, M_NTSYNC); 1315 } 1316 1317 static int 1318 ntsync_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1319 struct thread *td) 1320 { 1321 struct ntsync_priv *owner; 1322 struct ntsync_wait_args *nwa; 1323 struct ntsync_wait_state *state; 1324 void *a; 1325 int error; 1326 1327 error = devfs_get_cdevpriv(&a); 1328 if (error != 0) 1329 return (error); 1330 owner = a; 1331 1332 switch (cmd) { 1333 case NTSYNC_IOC_CREATE_SEM: 1334 error = ntsync_create_sem((struct ntsync_sem_args *)data, 1335 owner, td); 1336 break; 1337 case NTSYNC_IOC_CREATE_MUTEX: 1338 error = ntsync_create_mutex((struct ntsync_mutex_args *)data, 1339 owner, td); 1340 break; 1341 case NTSYNC_IOC_CREATE_EVENT: 1342 error = ntsync_create_event((struct ntsync_event_args *)data, 1343 owner, td); 1344 break; 1345 case NTSYNC_IOC_WAIT_ANY: 1346 nwa = (struct ntsync_wait_args *)data; 1347 error = ntsync_wait_state_get(nwa, cmd, owner, &state, td); 1348 if (error != 0) 1349 break; 1350 error = ntsync_wait(state, td); 1351 if (error == 0) { 1352 nwa->index = state->index; 1353 error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa)); 1354 if (error == 0) 1355 error = state->error; 1356 } 1357 ntsync_wait_state_put(state, td); 1358 break; 1359 case NTSYNC_IOC_WAIT_ALL: 1360 nwa = (struct ntsync_wait_args *)data; 1361 error = ntsync_wait_state_get(nwa, cmd, owner, &state, td); 1362 if (error != 0) 1363 break; 1364 error = ntsync_wait(state, td); 1365 if (error == 0) { 1366 nwa->index = state->index; 1367 error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa)); 1368 if (error == 0) 1369 error = state->error; 1370 } 1371 ntsync_wait_state_put(state, td); 1372 break; 1373 1374 default: 1375 error = ENOTTY; 1376 break; 1377 } 1378 return (error); 1379 } 1380 1381 struct cdevsw ntsync_cdevsw = { 1382 .d_version = D_VERSION, 1383 .d_flags = 0, 1384 .d_open = ntsync_open, 1385 .d_close = ntsync_close, 1386 .d_ioctl = ntsync_ioctl, 1387 .d_name = "ntsync", 1388 }; 1389 1390 static int 1391 ntsync_modevent(module_t mod __unused, int type, void *data __unused) 1392 { 1393 struct make_dev_args mda; 1394 int error; 1395 1396 error = 0; 1397 switch (type) { 1398 case MOD_LOAD: 1399 make_dev_args_init(&mda); 1400 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 1401 mda.mda_devsw = &ntsync_cdevsw; 1402 mda.mda_uid = UID_ROOT; 1403 mda.mda_gid = GID_GAMES; 1404 mda.mda_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | 1405 S_IROTH | S_IWOTH; 1406 1407 error = make_dev_s(&mda, &ntsync_cdev, "ntsync"); 1408 if (error != 0) { 1409 printf("cannot create ntsync dev err %d\n", error); 1410 break; 1411 } 1412 if (bootverbose) 1413 printf("ntsync\n"); 1414 break; 1415 1416 case MOD_UNLOAD: 1417 destroy_dev(ntsync_cdev); 1418 break; 1419 1420 case MOD_SHUTDOWN: 1421 break; 1422 1423 default: 1424 error = EOPNOTSUPP; 1425 } 1426 1427 return (error); 1428 } 1429 1430 DEV_MODULE(ntsync, ntsync_modevent, NULL); 1431 MODULE_VERSION(ntsync, 1); 1432