VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use