void * ptr1; means that ptr1 is a variable whose type is void *. This type indicates a "generic pointer" - it points to some memory location but contains no type information what what is in that location.
void * volatile ptr2; means that the variable ptr2 is also a generic pointer, but ptr2 is also volatile. The keyword volatile is called a cv-qualifier and it has the same grammar rules as const.
The meaning of a volatile variable is that when some other code says ptr2, the compiler cannot optimize that out; it must read or write the memory location where ptr2 is stored; it must allow for the possibility that some external process is also reading or writing that location.
Finally, void * volatile *x is something that can point to ptr2. For example we could have void * volatile * x = &ptr2; . If we then write *x = NULL; for example, then *x has type void * volatile which has the same implications as we just looked at for ptr2.
The compiler would complain if you omitted the qualifier, e.g. void * *y = &ptr2; . This is because the expression *y would then have type void * (non-volatile) so the compiler might perform optimizations around it, however this is incorrect behaviour because ptr2 does not permit those optimizations. (You may recognize that "volatile-correctness" is the same sort of thing as const-correctness).