VirtualBox

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

Last change on this file since 44528 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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

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