- Timestamp:
- Sep 24, 2007 9:35:20 AM (17 years ago)
- Location:
- trunk/src/VBox/Additions/linux
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/linux/module/vboxmod.c
r4924 r4998 24 24 #include "waitcompat.h" 25 25 #include <VBox/log.h> 26 #include <iprt/asm.h> 27 #include <iprt/assert.h> 26 28 27 29 MODULE_DESCRIPTION("VirtualBox Guest Additions for Linux Module"); … … 85 87 EXPORT_SYMBOL (vboxadd_cmc_close); 86 88 89 #define MAX_HGCM_CONNECTIONS 1024 90 91 /** 92 * Structure for keeping track of HGCM connections owned by user space processes, so that 93 * we can close the connection if a process does not clean up properly (for example if it 94 * was terminated too abruptly). 95 */ 96 /* We just define a fixed number of these so far. This can be changed if it ever becomes 97 a problem. */ 98 static struct { 99 /** Open file structure that this connection handle is associated with */ 100 struct file *filp; 101 /** HGCM connection ID */ 102 uint32_t client_id; 103 } hgcm_connections[MAX_HGCM_CONNECTIONS] = { { 0 } }; 104 105 /** 106 * Register an HGCM connection as being connected with a given file descriptor, so that it 107 * will be closed automatically when that file descriptor is. 108 * 109 * @returns 0 on success or Linux kernel error number 110 * @param clientID the client ID of the HGCM connection 111 * @param filep the file structure that the connection is to be associated with 112 */ 113 static int vboxadd_register_hgcm_connection(uint32_t client_id, struct file *filp) 114 { 115 int i; 116 bool found = false; 117 118 for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) { 119 Assert(hgcm_connections[i].client_id != client_id); 120 } 121 for (i = 0; (i < MAX_HGCM_CONNECTIONS) && (false == found); ++i) { 122 if (ASMAtomicCmpXchgU32(&hgcm_connections[i].client_id, client_id, 0)) { 123 hgcm_connections[i].filp = filp; 124 found = true; 125 } 126 } 127 return found ? 0 : -ENFILE; /* Any ideas for a better error code? */ 128 } 129 130 /** 131 * Unregister an HGCM connection associated with a given file descriptor without closing 132 * the connection. 133 * 134 * @returns 0 on success or Linux kernel error number 135 * @param clientID the client ID of the HGCM connection 136 */ 137 static int vboxadd_unregister_hgcm_connection_no_close(uint32_t client_id) 138 { 139 int i; 140 bool found = false; 141 142 for (i = 0; (i < MAX_HGCM_CONNECTIONS) && (false == found); ++i) { 143 if (hgcm_connections[i].client_id == client_id) { 144 hgcm_connections[i].client_id = 0; 145 found = true; 146 } 147 } 148 for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) { 149 Assert(hgcm_connections[i].client_id != client_id); 150 } 151 return found ? 0 : -ENOENT; 152 } 153 154 /** 155 * Unregister all HGCM connections associated with a given file descriptor, closing 156 * the connections in the process. This should be called when a file descriptor is 157 * closed. 158 * 159 * @returns 0 on success or Linux kernel error number 160 * @param clientID the client ID of the HGCM connection 161 */ 162 static int vboxadd_unregister_all_hgcm_connections(struct file *filp) 163 { 164 int i; 165 166 for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) { 167 if (hgcm_connections[i].filp == filp) { 168 hgcm_connections[i].client_id = 0; 169 } 170 } 171 return 0; 172 } 173 174 87 175 /** 88 176 * File open handler … … 96 184 97 185 /** 98 * File close handler 99 * 186 * File close handler. Clean up any HGCM connections associated with the open file 187 * which might still be open. 100 188 */ 101 189 static int vboxadd_release(struct inode *inode, struct file * filp) 102 190 { 103 /* no action required */104 return 0;191 vboxadd_unregister_all_hgcm_connections(filp); 192 return 0; 105 193 } 106 194 … … 129 217 130 218 /** 219 * IOCTL handler. Initiate an HGCM connection for a user space application. If the connection 220 * succeeds, it will be associated with the file structure used to open it, so that it will be 221 * automatically shut down again if the file descriptor is closed. 222 * 223 * @returns 0 on success, or a Linux kernel errno value 224 * @param filp the file structure with which the application opened the driver 225 * @param userspace_info userspace pointer to the hgcm connection information 226 * (VBoxGuestHGCMConnectInfo structure) 227 * @retval userspace_info userspace pointer to the hgcm connection information 228 */ 229 static int vboxadd_hgcm_connect(struct file *filp, unsigned long userspace_info) 230 { 231 VBoxGuestHGCMConnectInfo info; 232 VBoxGuestHGCMDisconnectInfo infoDisconnect; 233 int rc = 0, rcVBox; 234 235 if (0 != copy_from_user ((void *)&info, (void *)userspace_info, sizeof (info))) { 236 LogRelFunc (("IOCTL_VBOXGUEST_HGCM_CONNECT: can not get connection info\n")); 237 return -EFAULT; 238 } 239 rcVBox = vboxadd_cmc_call(vboxDev, IOCTL_VBOXGUEST_HGCM_CONNECT, &info); 240 if (RT_FAILURE(rcVBox) || (RT_FAILURE(info.result))) { 241 LogRelFunc(("IOCTL_VBOXGUEST_HGCM_CONNECT: hgcm connection failed. internal ioctl result %Vrc, hgcm result %Vrc\n", rcVBox, info.result)); 242 rc = RT_FAILURE(rcVBox) ? -RTErrConvertToErrno(rcVBox) 243 : -RTErrConvertToErrno(info.result); 244 } else { 245 /* Register that the connection is associated with this file pointer. */ 246 rc = vboxadd_register_hgcm_connection(info.u32ClientID, filp); 247 if (0 != rc) { 248 LogRelFunc(("IOCTL_VBOXGUEST_HGCM_CONNECT: failed to register the HGCM connection\n")); 249 } else { 250 if (copy_to_user ((void *)userspace_info, (void *)&info, 251 sizeof(info))) { 252 LogRelFunc (("IOCTL_VBOXGUEST_HGCM_CONNECT: failed to return the connection structure\n")); 253 rc = -EFAULT; 254 } else { 255 return 0; 256 } 257 /* Unregister again, as we didn't get as far as informing userspace. */ 258 vboxadd_unregister_hgcm_connection_no_close(info.u32ClientID); 259 } 260 /* And disconnect the hgcm connection again, as we told userspace it failed. */ 261 infoDisconnect.u32ClientID = info.u32ClientID; 262 vboxadd_cmc_call(vboxDev, IOCTL_VBOXGUEST_HGCM_DISCONNECT, 263 &infoDisconnect); 264 } 265 return rc; 266 } 267 268 /** 131 269 * IOCTL handler 132 270 * … … 144 282 if (copy_from_user (&info, ptr, sizeof (info))) 145 283 { 146 printk (KERN_ERR "vboxadd_ioctl: can not get event info\n");284 LogRelFunc (("IOCTL_VBOXGUEST_WAITEVENT: can not get event info\n")); 147 285 return -EFAULT; 148 286 } … … 153 291 if (put_user (info.u32EventFlagsOut, (uint32_t*)ptr)) 154 292 { 155 printk (KERN_ERR "vboxadd_ioctl: can not put out_mask\n");293 LogRelFunc (("IOCTL_VBOXGUEST_WAITEVENT: can not put out_mask\n")); 156 294 return -EFAULT; 157 295 } … … 169 307 if (_IOC_SIZE(cmd) != sizeof(VMMDevRequestHeader)) 170 308 { 171 printk(KERN_ERR "vboxadd_ioctl: invalid VMM request structure size: %d\n",172 _IOC_SIZE(cmd));309 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: invalid VMM request structure size: %d\n", 310 _IOC_SIZE(cmd))); 173 311 return -EINVAL; 174 312 } 175 313 if (copy_from_user(&reqHeader, (void*)arg, _IOC_SIZE(cmd))) 176 314 { 177 printk(KERN_ERR "vboxadd_ioctl: copy_from_user failed for vmm request!\n");315 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: copy_from_user failed for vmm request!\n")); 178 316 return -EFAULT; 179 317 } … … 182 320 if (!cbVanillaRequestSize) 183 321 { 184 printk(KERN_ERR "vboxadd_ioctl: invalid request type: %d\n",185 reqHeader.requestType);322 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: invalid request type: %d\n", 323 reqHeader.requestType)); 186 324 return -EINVAL; 187 325 } … … 190 328 if (cbRequestSize < cbVanillaRequestSize) 191 329 { 192 printk(KERN_ERR 193 "vboxadd_ioctl: invalid request size: %d min: %d type: %d\n", 194 cbRequestSize, 195 cbVanillaRequestSize, 196 reqHeader.requestType); 330 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: invalid request size: %d min: %d type: %d\n", 331 cbRequestSize, 332 cbVanillaRequestSize, 333 reqHeader.requestType)); 197 334 return -EINVAL; 198 335 } … … 201 338 if (VBOX_FAILURE(rc)) 202 339 { 203 printk(KERN_ERR 204 "vboxadd_ioctl: could not allocate request structure! rc = %d\n", rc); 340 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: could not allocate request structure! rc = %d\n", rc)); 205 341 return -EFAULT; 206 342 } … … 208 344 if (copy_from_user(reqFull, (void*)arg, cbRequestSize)) 209 345 { 210 printk(KERN_ERR 211 "vboxadd_ioctl: failed to fetch full request from user space!\n"); 346 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: failed to fetch full request from user space!\n")); 212 347 VbglGRFree(reqFull); 213 348 return -EFAULT; … … 228 363 if (VBOX_FAILURE(rc) || VBOX_FAILURE(reqFull->rc)) 229 364 { 230 printk(KERN_ERR "vboxadd_ioctl: request execution failed!\n");365 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: request execution failed!\n")); 231 366 VbglGRFree(reqFull); 232 return -EFAULT; 367 return VBOX_FAILURE(rc) ? -RTErrConvertToErrno(rc) 368 : -RTErrConvertToErrno(reqFull->rc); 233 369 } 234 370 else … … 237 373 if (copy_to_user((void*)arg, (void*)reqFull, cbRequestSize)) 238 374 { 239 printk(KERN_ERR 240 "vboxadd_ioctl: error copying request result to user space!\n"); 375 LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: error copying request result to user space!\n")); 241 376 VbglGRFree(reqFull); 242 377 return -EFAULT; … … 249 384 case IOCTL_VBOXGUEST_HGCM_CALL: 250 385 { 251 252 253 254 255 256 257 258 return vbox_ioctl_hgcm_call(arg, vboxDev);259 } 260 261 case IOCTL_VBOXGUEST_ CLIPBOARD_CONNECT:386 /* This IOCTL allows the guest to make an HGCM call from user space. The 387 OS-independant part of the Guest Additions already contain code for making an 388 HGCM call from the guest, but this code assumes that the call is made from the 389 kernel's address space. So before calling it, we have to copy all parameters 390 to the HGCM call from user space to kernel space and reconstruct the structures 391 passed to the call (which include pointers to other memory) inside the kernel's 392 address space. */ 393 return vbox_ioctl_hgcm_call(arg, vboxDev); 394 } 395 396 case IOCTL_VBOXGUEST_HGCM_CONNECT: 262 397 { 263 static uint32_t u32ClientID = 0; 264 VMMDevHGCMDisconnect *reqDisconnect = NULL; 265 VMMDevHGCMConnect *reqConnect = NULL; 266 size_t cbRequestSize; 267 int rc; 268 269 /* First, disconnect any old client. */ 270 if (u32ClientID != 0) 271 { 272 /* get the request size */ 273 cbRequestSize = vmmdevGetRequestSize(VMMDevReq_HGCMDisconnect); 274 /* request storage for the request */ 275 rc = VbglGRAlloc((VMMDevRequestHeader **) &reqDisconnect, cbRequestSize, 276 VMMDevReq_HGCMDisconnect); 277 if (VBOX_FAILURE(rc)) 278 { 279 printk(KERN_ERR 280 "vboxadd_ioctl: could not allocate request structure! rc = %d\n", rc); 281 return -EFAULT; 282 } 283 /* now get the full request */ 284 vmmdevInitRequest(&reqDisconnect->header.header, VMMDevReq_HGCMDisconnect); 285 reqDisconnect->u32ClientID = u32ClientID; 286 287 /* now issue the request */ 288 rc = VbglGRPerform(&reqDisconnect->header.header); 289 290 /* asynchronous processing? */ 291 if (rc == VINF_HGCM_ASYNC_EXECUTE) 292 { 293 VMMDevHGCMRequestHeader *reqHGCM = &reqDisconnect->header; 294 wait_event (vboxDev->eventq, reqHGCM->fu32Flags & VBOX_HGCM_REQ_DONE); 295 rc = reqHGCM->header.rc; 296 } 297 298 /* failed? */ 299 if (VBOX_FAILURE(rc) || VBOX_FAILURE(reqDisconnect->header.header.rc)) 300 { 301 printk(KERN_ERR "vboxadd_ioctl: request execution failed!\n"); 302 VbglGRFree(&reqDisconnect->header.header); 303 return -EFAULT; 304 } 305 VbglGRFree(&reqDisconnect->header.header); 306 } 307 308 /* And connect... */ 309 /* get the request size */ 310 cbRequestSize = vmmdevGetRequestSize(VMMDevReq_HGCMConnect); 311 /* request storage for the request */ 312 rc = VbglGRAlloc((VMMDevRequestHeader **) &reqConnect, cbRequestSize, VMMDevReq_HGCMConnect); 313 if (VBOX_FAILURE(rc)) 314 { 315 printk(KERN_ERR 316 "vboxadd_ioctl: could not allocate request structure! rc = %d\n", rc); 317 return -EFAULT; 318 } 319 /* now get the full request */ 320 vmmdevInitRequest((VMMDevRequestHeader*)reqConnect, VMMDevReq_HGCMConnect); 321 reqConnect->loc.type = VMMDevHGCMLoc_LocalHost_Existing; 322 strcpy (reqConnect->loc.u.host.achName, "VBoxSharedClipboard"); 323 324 /* now issue the request */ 325 rc = VbglGRPerform(&reqConnect->header.header); 326 327 /* asynchronous processing? */ 328 if (rc == VINF_HGCM_ASYNC_EXECUTE) 329 { 330 VMMDevHGCMRequestHeader *reqHGCM = &reqConnect->header; 331 wait_event (vboxDev->eventq, reqHGCM->fu32Flags & VBOX_HGCM_REQ_DONE); 332 rc = reqHGCM->header.rc; 333 } 334 335 /* failed? */ 336 if (VBOX_FAILURE(rc) || VBOX_FAILURE(reqConnect->header.header.rc)) 337 { 338 printk(KERN_ERR "vboxadd_ioctl: request execution failed!\n"); 339 VbglGRFree(&reqConnect->header.header); 340 return -EFAULT; 341 } 342 else 343 { 344 /* success, copy the result data to user space */ 345 u32ClientID = reqConnect->u32ClientID; 346 if (copy_to_user((void*)arg, (void*)&(reqConnect->u32ClientID), sizeof(uint32_t))) 347 { 348 printk(KERN_ERR 349 "vboxadd_ioctl: error copying request result to user space!\n"); 350 VbglGRFree(&reqConnect->header.header); 351 return -EFAULT; 352 } 353 } 354 VbglGRFree(&reqConnect->header.header); 355 break; 398 return vboxadd_hgcm_connect(filp, arg); 356 399 } 357 400 358 401 default: 359 402 { 360 elog("vboxadd_ioctl: unknown command: %x, IOCTL_VBOXGUEST_HGCM_CALL is %x\n", cmd, 361 IOCTL_VBOXGUEST_HGCM_CALL); 362 Log(("vboxadd_ioctl: unknown command: %x, IOCTL_VBOXGUEST_HGCM_CALL is %x\n", cmd, 363 IOCTL_VBOXGUEST_HGCM_CALL)); 403 LogRelFunc(("unknown command: %x\n", cmd)); 364 404 return -EINVAL; 365 405 } … … 448 488 { 449 489 /* impossible... */ 450 printk(KERN_ERR 451 "vboxadd: failed acknowledging IRQ! rc = %x, header.rc = %d\n", 452 rcVBox, vboxDev->irqAckRequest->header.rc); 490 LogRelFunc(("IRQ was not acknowledged! rc = %Vrc, header.rc = %Vrc\n", 491 rcVBox, vboxDev->irqAckRequest->header.rc)); 453 492 BUG (); 454 493 } … … 488 527 if (VBOX_FAILURE(rcVBox)) 489 528 { 490 printk(KERN_ERR "vboxadd: failed to allocate hypervisor info structure! rc = %d\n", 491 rcVBox); 529 LogRelFunc(("failed to allocate hypervisor info structure! rc = %Vrc\n", rcVBox)); 492 530 goto bail_out; 493 531 } … … 521 559 else 522 560 { 523 printk(KERN_ERR "vboxadd: failed to set hypervisor region! " 524 "rc = %d, header.rc = %d\n", 525 rcVBox, req->header.rc); 561 LogRelFunc(("failed to set hypervisor region! rc = %Vrc, header.rc = %Vrc\n", 562 rcVBox, req->header.rc)); 526 563 goto bail_out; 527 564 } … … 529 566 else 530 567 { 531 printk(KERN_ERR "vboxadd: failed to allocate 0x%x bytes of IO space\n", 532 hypervisorSize); 568 LogRelFunc(("failed to allocate 0x%x bytes of IO space\n", hypervisorSize)); 533 569 goto bail_out; 534 570 } … … 537 573 else 538 574 { 539 printk(KERN_ERR "vboxadd: failed to query hypervisor info! rc = %d, header.rc = %d\n",540 rcVBox, req->header.rc);575 LogRelFunc(("failed to query hypervisor info! rc = %Vrc, header.rc = %Vrc\n", 576 rcVBox, req->header.rc)); 541 577 goto bail_out; 542 578 } … … 568 604 if (VBOX_FAILURE(rcVBox)) 569 605 { 570 printk(KERN_ERR 571 "vboxadd: failed to allocate hypervisor info structure! rc = %d\n", rcVBox); 606 LogRelFunc(("failed to allocate hypervisor info structure! rc = %Vrc\n", rcVBox)); 572 607 goto bail_out; 573 608 } … … 584 619 else 585 620 { 586 printk(KERN_ERR "vboxadd: failed to reset hypervisor info! rc = %d, header.rc = %d\n",587 rcVBox, req->header.rc);621 LogRelFunc(("failed to reset hypervisor info! rc = %Vrc, header.rc = %Vrc\n", 622 rcVBox, req->header.rc)); 588 623 goto bail_out; 589 624 } … … 644 679 645 680 printk(KERN_INFO "vboxadd: initializing version %s\n", VBOX_VERSION_STRING); 646 LogRel(("Starting VirtualBox Guest Additions version %s\n",647 VBOX_VERSION_STRING));648 681 649 682 if (vboxadd_cmc_init ()) … … 671 704 } 672 705 706 LogRel(("Starting VirtualBox version %s Guest Additions\n", 707 VBOX_VERSION_STRING)); 673 708 /* register a character device */ 674 709 err = register_chrdev(vbox_major, "vboxadd", &vbox_fops); … … 677 712 printk(KERN_ERR "vboxadd: register_chrdev failed: vbox_major: %d, err = %d\n", 678 713 vbox_major, err); 714 LogRelFunc(("register_chrdev failed: vbox_major: %d, err = %d\n", 715 vbox_major, err)); 679 716 PCI_DEV_PUT(pcidev); 680 717 return -ENODEV; … … 689 726 { 690 727 printk(KERN_ERR "vboxadd: cannot allocate device!\n"); 728 LogRelFunc(("cannot allocate device!\n")); 691 729 err = -ENOMEM; 692 730 goto fail; … … 706 744 { 707 745 printk(KERN_ERR "vboxadd: did not find expected hardware resources!\n"); 746 LogRelFunc(("did not find expected hardware resources!\n")); 708 747 goto fail; 709 748 } … … 713 752 { 714 753 printk(KERN_ERR "vboxadd: failed to request adapter memory!\n"); 754 LogRelFunc(("failed to request adapter memory!\n")); 715 755 goto fail; 716 756 } … … 722 762 { 723 763 printk (KERN_ERR "vboxadd: ioremap failed\n"); 764 LogRelFunc(("ioremap failed\n")); 724 765 goto fail; 725 766 } … … 730 771 "vboxadd: invalid VMM device memory version! (got 0x%x, expected 0x%x)\n", 731 772 vboxDev->pVMMDevMemory->u32Version, VMMDEV_MEMORY_VERSION); 773 LogRelFunc(("invalid VMM device memory version! (got 0x%x, expected 0x%x)\n", 774 vboxDev->pVMMDevMemory->u32Version, VMMDEV_MEMORY_VERSION)); 732 775 goto fail; 733 776 } … … 738 781 { 739 782 printk(KERN_ERR "vboxadd: could not initialize VBGL subsystem! rc = %d\n", rcVBox); 783 LogRelFunc(("could not initialize VBGL subsystem! rc = %Vrc\n", rcVBox)); 740 784 goto fail; 741 785 } … … 747 791 { 748 792 printk(KERN_ERR "vboxadd: could not allocate request structure! rc = %d\n", rcVBox); 793 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox)); 749 794 goto fail; 750 795 } … … 763 808 "vboxadd: error reporting guest info to host! rc = %d, header.rc = %d\n", 764 809 rcVBox, infoReq->header.rc); 810 LogRelFunc(("error reporting guest info to host! rc = %Vrc, header.rc = %Vrc\n", 811 rcVBox, infoReq->header.rc)); 765 812 VbglGRFree(&infoReq->header); 766 813 goto fail; … … 780 827 { 781 828 printk(KERN_ERR "vboxadd: could not allocate request structure! rc = %d\n", rcVBox); 829 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox)); 782 830 goto fail; 783 831 } … … 794 842 { 795 843 printk(KERN_ERR "vboxadd: Could not request IRQ %d, err: %d\n", pcidev->irq, err); 844 LogRelFunc(("could not request IRQ %d, err: %d\n", pcidev->irq, err)); 796 845 goto fail; 797 846 } … … 808 857 vboxDev->vmmdevmem, vboxDev->vmmdevmem_size, 809 858 vboxDev->hypervisorStart, vboxDev->hypervisorSize); 859 LogRelFunc(("major code: %d, using irq %d, " 860 "io port 0x%x, memory at 0x%x (size %d bytes), " 861 "hypervisor window at 0x%p (size 0x%x bytes)\n", 862 vbox_major, vboxDev->irq, vboxDev->io_port, 863 vboxDev->vmmdevmem, vboxDev->vmmdevmem_size, 864 vboxDev->hypervisorStart, vboxDev->hypervisorSize)); 810 865 811 866 /* successful return */ … … 827 882 { 828 883 printk(KERN_DEBUG "vboxadd: unloading...\n"); 884 LogRelFunc(("unloading...\n")); 829 885 830 886 unregister_chrdev(vbox_major, "vboxadd"); … … 832 888 vboxadd_cmc_fini (); 833 889 printk(KERN_DEBUG "vboxadd: unloaded\n"); 890 LogRelFunc(("unloaded\n")); 834 891 } 835 892 -
trunk/src/VBox/Additions/linux/xclient/clipboard.cpp
r4350 r4998 1556 1556 } 1557 1557 1558 rc = ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_CLIPBOARD_CONNECT, (void*)&g_ctx.client); 1558 VBoxGuestHGCMConnectInfo info; 1559 info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; 1560 strcpy (info.Loc.u.host.achName, "VBoxSharedClipboard"); 1561 rc = ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_HGCM_CONNECT, (void*)&info); 1559 1562 if (rc >= 0) 1560 1563 {
Note:
See TracChangeset
for help on using the changeset viewer.