VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp@ 76530

Last change on this file since 76530 was 76358, checked in by vboxsync, 6 years ago

*: string.h sans err.h fix. bugref:9344

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: VBoxUSBFilterMgr.cpp 76358 2018-12-22 01:33:23Z vboxsync $ */
2/** @file
3 * VirtualBox Ring-0 USB Filter Manager.
4 */
5
6/*
7 * Copyright (C) 2007-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <VBox/usbfilter.h>
32#include "VBoxUSBFilterMgr.h"
33
34#include <iprt/err.h>
35#include <iprt/handletable.h>
36#include <iprt/mem.h>
37#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
38# include <iprt/spinlock.h>
39#else
40# include <iprt/semaphore.h>
41#endif
42#include <iprt/string.h>
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48
49/** @def VBOXUSBFILTERMGR_LOCK
50 * Locks the filter list. Careful with scoping since this may
51 * create a temporary variable. Don't call twice in the same function.
52 */
53
54/** @def VBOXUSBFILTERMGR_UNLOCK
55 * Unlocks the filter list.
56 */
57#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
58
59# define VBOXUSBFILTERMGR_LOCK() \
60 RTSpinlockAcquire(g_Spinlock)
61
62# define VBOXUSBFILTERMGR_UNLOCK() \
63 RTSpinlockRelease(g_Spinlock)
64
65#else
66
67# define VBOXUSBFILTERMGR_LOCK() \
68 do { int rc2 = RTSemFastMutexRequest(g_Mtx); AssertRC(rc2); } while (0)
69
70# define VBOXUSBFILTERMGR_UNLOCK() \
71 do { int rc2 = RTSemFastMutexRelease(g_Mtx); AssertRC(rc2); } while (0)
72
73#endif
74
75
76/*********************************************************************************************************************************
77* Structures and Typedefs *
78*********************************************************************************************************************************/
79/** Pointer to an VBoxUSB filter. */
80typedef struct VBOXUSBFILTER *PVBOXUSBFILTER;
81/** Pointer to PVBOXUSBFILTER. */
82typedef PVBOXUSBFILTER *PPVBOXUSBFILTER;
83
84/**
85 * VBoxUSB internal filter representation.
86 */
87typedef struct VBOXUSBFILTER
88{
89 /** The core filter. */
90 USBFILTER Core;
91 /** The filter owner. */
92 VBOXUSBFILTER_CONTEXT Owner;
93 /** The filter Id. */
94 uint32_t uHnd;
95 /** Pointer to the next filter in the list. */
96 PVBOXUSBFILTER pNext;
97} VBOXUSBFILTER;
98
99/**
100 * VBoxUSB filter list.
101 */
102typedef struct VBOXUSBFILTERLIST
103{
104 /** The head pointer. */
105 PVBOXUSBFILTER pHead;
106 /** The tail pointer. */
107 PVBOXUSBFILTER pTail;
108} VBOXUSBFILTERLIST;
109/** Pointer to a VBOXUSBFILTERLIST. */
110typedef VBOXUSBFILTERLIST *PVBOXUSBFILTERLIST;
111
112
113/*********************************************************************************************************************************
114* Global Variables *
115*********************************************************************************************************************************/
116#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
117/** Spinlock protecting the filter lists. */
118static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
119#else
120/** Mutex protecting the filter lists. */
121static RTSEMFASTMUTEX g_Mtx = NIL_RTSEMFASTMUTEX;
122#endif
123/** The per-type filter lists.
124 * @remark The first entry is empty (USBFILTERTYPE_INVALID). */
125static VBOXUSBFILTERLIST g_aLists[USBFILTERTYPE_END];
126/** The handle table to match handles to the right filter. */
127static RTHANDLETABLE g_hHndTableFilters = NIL_RTHANDLETABLE;
128
129
130
131/**
132 * Initializes the VBoxUSB filter manager.
133 *
134 * @returns IPRT status code.
135 */
136int VBoxUSBFilterInit(void)
137{
138#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
139 int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxUSBFilter");
140#else
141 int rc = RTSemFastMutexCreate(&g_Mtx);
142#endif
143 if (RT_SUCCESS(rc))
144 {
145 uint32_t fFlags;
146#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
147 fFlags = RTHANDLETABLE_FLAGS_LOCKED_IRQ_SAFE;
148#else
149 fFlags = RTHANDLETABLE_FLAGS_LOCKED;
150#endif
151 rc = RTHandleTableCreateEx(&g_hHndTableFilters, fFlags, 1 /* uBase */, 8192 /* cMax */,
152 NULL, NULL);
153 if (RT_SUCCESS(rc))
154 {
155 /* not really required, but anyway... */
156 for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
157 g_aLists[i].pHead = g_aLists[i].pTail = NULL;
158 }
159 else
160 {
161#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
162 RTSpinlockDestroy(g_Spinlock);
163 g_Spinlock = NIL_RTSPINLOCK;
164#else
165 RTSemFastMutexDestroy(g_Mtx);
166 g_Mtx = NIL_RTSEMFASTMUTEX;
167#endif
168 }
169 }
170 return rc;
171}
172
173
174/**
175 * Internal worker that frees a filter.
176 *
177 * @param pFilter The filter to free.
178 */
179static void vboxUSBFilterFree(PVBOXUSBFILTER pFilter)
180{
181 USBFilterDelete(&pFilter->Core);
182 pFilter->Owner = VBOXUSBFILTER_CONTEXT_NIL;
183 pFilter->pNext = NULL;
184 RTMemFree(pFilter);
185}
186
187
188/**
189 * Terminates the VBoxUSB filter manager.
190 */
191void VBoxUSBFilterTerm(void)
192{
193#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
194 RTSpinlockDestroy(g_Spinlock);
195 g_Spinlock = NIL_RTSPINLOCK;
196#else
197 RTSemFastMutexDestroy(g_Mtx);
198 g_Mtx = NIL_RTSEMFASTMUTEX;
199#endif
200
201 for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
202 {
203 PVBOXUSBFILTER pCur = g_aLists[i].pHead;
204 g_aLists[i].pHead = g_aLists[i].pTail = NULL;
205 while (pCur)
206 {
207 PVBOXUSBFILTER pNext = pCur->pNext;
208 RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
209 vboxUSBFilterFree(pCur);
210 pCur = pNext;
211 }
212 }
213
214 RTHandleTableDestroy(g_hHndTableFilters, NULL, NULL);
215}
216
217
218/**
219 * Adds a new filter.
220 *
221 * The filter will be validate, duplicated and added.
222 *
223 * @returns IPRT status code.
224 * @param pFilter The filter.
225 * @param Owner The filter owner. Must be non-zero.
226 * @param puId Where to store the filter ID.
227 */
228int VBoxUSBFilterAdd(PCUSBFILTER pFilter, VBOXUSBFILTER_CONTEXT Owner, uintptr_t *puId)
229{
230 /*
231 * Validate input.
232 */
233 int rc = USBFilterValidate(pFilter);
234 if (RT_FAILURE(rc))
235 return rc;
236 if (!Owner || Owner == VBOXUSBFILTER_CONTEXT_NIL)
237 return VERR_INVALID_PARAMETER;
238 if (!VALID_PTR(puId))
239 return VERR_INVALID_POINTER;
240
241 /*
242 * Allocate a new filter.
243 */
244 PVBOXUSBFILTER pNew = (PVBOXUSBFILTER)RTMemAlloc(sizeof(*pNew));
245 if (!pNew)
246 return VERR_NO_MEMORY;
247 memcpy(&pNew->Core, pFilter, sizeof(pNew->Core));
248 pNew->Owner = Owner;
249 pNew->pNext = NULL;
250
251 rc = RTHandleTableAlloc(g_hHndTableFilters, pNew, &pNew->uHnd);
252 if (RT_SUCCESS(rc))
253 {
254 *puId = pNew->uHnd;
255
256 /*
257 * Insert it.
258 */
259 PVBOXUSBFILTERLIST pList = &g_aLists[pFilter->enmType];
260
261 VBOXUSBFILTERMGR_LOCK();
262
263 if (pList->pTail)
264 pList->pTail->pNext = pNew;
265 else
266 pList->pHead = pNew;
267 pList->pTail = pNew;
268
269 VBOXUSBFILTERMGR_UNLOCK();
270 }
271 else
272 RTMemFree(pNew);
273
274 return rc;
275}
276
277
278/**
279 * Removes an existing filter.
280 *
281 * The filter will be validate, duplicated and added.
282 *
283 * @returns IPRT status code.
284 * @retval VINF_SUCCESS if successfully removed.
285 * @retval VERR_FILE_NOT_FOUND if the specified filter/owner cannot be found.
286 *
287 * @param Owner The filter owner.
288 * @param uId The ID of the filter that's to be removed.
289 * Returned by VBoxUSBFilterAdd().
290 */
291int VBoxUSBFilterRemove(VBOXUSBFILTER_CONTEXT Owner, uintptr_t uId)
292{
293 /*
294 * Validate input.
295 */
296 if (!uId || uId != (uint32_t)uId)
297 return VERR_INVALID_PARAMETER;
298 if (!Owner || Owner == VBOXUSBFILTER_CONTEXT_NIL)
299 return VERR_INVALID_PARAMETER;
300
301 /*
302 * Locate and unlink it.
303 */
304 uint32_t uHnd = (uint32_t)uId;
305 PVBOXUSBFILTER pCur = NULL;
306
307 VBOXUSBFILTERMGR_LOCK();
308
309 for (unsigned i = USBFILTERTYPE_FIRST; !pCur && i < RT_ELEMENTS(g_aLists); i++)
310 {
311 PVBOXUSBFILTER pPrev = NULL;
312 pCur = g_aLists[i].pHead;
313 while (pCur)
314 {
315 if ( pCur->uHnd == uHnd
316 && pCur->Owner == Owner)
317 {
318 PVBOXUSBFILTER pNext = pCur->pNext;
319 if (pPrev)
320 pPrev->pNext = pNext;
321 else
322 g_aLists[i].pHead = pNext;
323 if (!pNext)
324 g_aLists[i].pTail = pPrev;
325 break;
326 }
327
328 pPrev = pCur;
329 pCur = pCur->pNext;
330 }
331 }
332
333 VBOXUSBFILTERMGR_UNLOCK();
334
335 /*
336 * Free it (if found).
337 */
338 if (pCur)
339 {
340 void *pv = RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
341 Assert(pv == pCur); NOREF(pv);
342 vboxUSBFilterFree(pCur);
343 return VINF_SUCCESS;
344 }
345
346 return VERR_FILE_NOT_FOUND;
347}
348
349VBOXUSBFILTER_CONTEXT VBoxUSBFilterGetOwner(uintptr_t uId)
350{
351 Assert(uId);
352 /*
353 * Validate input.
354 */
355 if (!uId || uId != (uint32_t)uId)
356 return VBOXUSBFILTER_CONTEXT_NIL;
357
358 /*
359 * Result.
360 */
361 VBOXUSBFILTER_CONTEXT Owner = VBOXUSBFILTER_CONTEXT_NIL;
362
363 VBOXUSBFILTERMGR_LOCK();
364
365 PVBOXUSBFILTER pCur = (PVBOXUSBFILTER)RTHandleTableLookup(g_hHndTableFilters, (uint32_t)uId);
366 if (pCur)
367 Owner = pCur->Owner;
368
369 Assert(Owner != VBOXUSBFILTER_CONTEXT_NIL);
370
371 VBOXUSBFILTERMGR_UNLOCK();
372
373 return Owner;
374}
375
376/**
377 * Removes all filters belonging to the specified owner.
378 *
379 * This is typically called when an owner disconnects or
380 * terminates unexpectedly.
381 *
382 * @param Owner The owner
383 */
384void VBoxUSBFilterRemoveOwner(VBOXUSBFILTER_CONTEXT Owner)
385{
386 /*
387 * Collect the filters that should be freed.
388 */
389 PVBOXUSBFILTER pToFree = NULL;
390
391 VBOXUSBFILTERMGR_LOCK();
392
393 for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
394 {
395 PVBOXUSBFILTER pPrev = NULL;
396 PVBOXUSBFILTER pCur = g_aLists[i].pHead;
397 while (pCur)
398 {
399 if (pCur->Owner == Owner)
400 {
401 PVBOXUSBFILTER pNext = pCur->pNext;
402 if (pPrev)
403 pPrev->pNext = pNext;
404 else
405 g_aLists[i].pHead = pNext;
406 if (!pNext)
407 g_aLists[i].pTail = pPrev;
408
409 pCur->pNext = pToFree;
410 pToFree = pCur;
411
412 pCur = pNext;
413 }
414 else
415 {
416 pPrev = pCur;
417 pCur = pCur->pNext;
418 }
419 }
420 }
421
422 VBOXUSBFILTERMGR_UNLOCK();
423
424 /*
425 * Free any filters we've found.
426 */
427 while (pToFree)
428 {
429 PVBOXUSBFILTER pNext = pToFree->pNext;
430 void *pv = RTHandleTableFree(g_hHndTableFilters, pToFree->uHnd);
431 Assert(pv == pToFree); NOREF(pv);
432 vboxUSBFilterFree(pToFree);
433 pToFree = pNext;
434 }
435}
436
437/**
438 * Match the specified device against the filters.
439 * Unlike the VBoxUSBFilterMatch, returns Owner also if exclude filter is matched
440 *
441 * @returns Owner on if matched, VBOXUSBFILTER_CONTEXT_NIL it not matched.
442 * @param pDevice The device data as a filter structure.
443 * See USBFilterMatch for how to construct this.
444 * @param puId Where to store the filter id (optional).
445 * @param fRemoveFltIfOneShot Whether or not to remove one-shot filters on
446 * match.
447 * @param pfFilter Where to store whether the device must be filtered or not
448 * @param pfIsOneShot Where to return whetehr the match was a one-shot
449 * filter or not. Optional.
450 *
451 */
452VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatchEx(PCUSBFILTER pDevice, uintptr_t *puId,
453 bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
454{
455 /*
456 * Validate input.
457 */
458 int rc = USBFilterValidate(pDevice);
459 AssertRCReturn(rc, VBOXUSBFILTER_CONTEXT_NIL);
460
461 *pfFilter = false;
462 if (puId)
463 *puId = 0;
464
465 /*
466 * Search the lists for a match.
467 * (The lists are ordered by priority.)
468 */
469 VBOXUSBFILTERMGR_LOCK();
470
471 for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
472 {
473 PVBOXUSBFILTER pPrev = NULL;
474 PVBOXUSBFILTER pCur = g_aLists[i].pHead;
475 while (pCur)
476 {
477 if (USBFilterMatch(&pCur->Core, pDevice))
478 {
479 /*
480 * Take list specific actions and return.
481 *
482 * The code does NOT implement the case where there are two or more
483 * filter clients, and one of them is releasing a device that's
484 * requested by some of the others. It's just too much work for a
485 * situation that noone will encounter.
486 */
487 if (puId)
488 *puId = pCur->uHnd;
489 VBOXUSBFILTER_CONTEXT Owner = pCur->Owner;
490 *pfFilter = !!(i != USBFILTERTYPE_IGNORE
491 && i != USBFILTERTYPE_ONESHOT_IGNORE);
492
493 if ( i == USBFILTERTYPE_ONESHOT_IGNORE
494 || i == USBFILTERTYPE_ONESHOT_CAPTURE)
495 {
496 if (fRemoveFltIfOneShot)
497 {
498 /* unlink */
499 PVBOXUSBFILTER pNext = pCur->pNext;
500 if (pPrev)
501 pPrev->pNext = pNext;
502 else
503 g_aLists[i].pHead = pNext;
504 if (!pNext)
505 g_aLists[i].pTail = pPrev;
506 }
507 }
508
509 VBOXUSBFILTERMGR_UNLOCK();
510
511 if ( i == USBFILTERTYPE_ONESHOT_IGNORE
512 || i == USBFILTERTYPE_ONESHOT_CAPTURE)
513 {
514 if (fRemoveFltIfOneShot)
515 {
516 void *pv = RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
517 Assert(pv == pCur); NOREF(pv);
518 vboxUSBFilterFree(pCur);
519 }
520 if (pfIsOneShot)
521 *pfIsOneShot = true;
522 }
523 else
524 {
525 if (pfIsOneShot)
526 *pfIsOneShot = false;
527 }
528 return Owner;
529 }
530
531 pPrev = pCur;
532 pCur = pCur->pNext;
533 }
534 }
535
536 VBOXUSBFILTERMGR_UNLOCK();
537 return VBOXUSBFILTER_CONTEXT_NIL;
538}
539
540/**
541 * Match the specified device against the filters.
542 *
543 * @returns Owner on if matched, VBOXUSBFILTER_CONTEXT_NIL it not matched.
544 * @param pDevice The device data as a filter structure.
545 * See USBFilterMatch for how to construct this.
546 * @param puId Where to store the filter id (optional).
547 */
548VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatch(PCUSBFILTER pDevice, uintptr_t *puId)
549{
550 bool fFilter = false;
551 VBOXUSBFILTER_CONTEXT Owner = VBoxUSBFilterMatchEx(pDevice, puId,
552 true, /* remove filter is it's a one-shot*/
553 &fFilter, NULL /* bool * fIsOneShot */);
554 if (fFilter)
555 {
556 Assert(Owner != VBOXUSBFILTER_CONTEXT_NIL);
557 return Owner;
558 }
559 return VBOXUSBFILTER_CONTEXT_NIL;
560}
561
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette