UNIX cp multithreaded implementation in C
Use ftw() to crawl directories
ftw() is a POSIX function that will recursively crawl directories, and calls a callback function for every directory entry found.
Consider using strdup() and asprintf()
Instead of copying strings using some kind of strlen()+malloc()+strcpy(), use the POSIX function strdup() if possible, which takes care of all this for you.
Linux and most BSDs also implement asprintf(), which takes care of allocating a correctly sized buffer for you. Be aware that this is definitely not cross-platform.
Don't throw away threads
While thread creation and destruction is quite fast on Linux, it still has an overhead. If you are copying many small files, that overhead might start dominating. It would be much better to have a fixed number of threads that are waiting for work to do.
A typical implementation would use a thread-safe queue for this. Have the directory crawler fill a queue of filenames to copy. The queue is guarded by a mutex, and has an associated condition variable. Whenever it pushes an item to the queue, it signals the condition variable. The threads wait for elements to be pushed, and when woken up, they will pop one entry from the queue and start copying it.
However, assuming the directory crawler is much faster than files can be copied, it would be a waste of memory to fill a queue with all filenames encountered, especially if the directory tree is large. You only need a few entries in the queue for worker threads to pick up work. You can make the crawler block itself if the queue gets too large. You should add a second condition variable then that gets signalled whenever a worker dequeues a filename to copy.
Find the right number of threads to use
It's hard to predict how many threads will be optimal. It could be that one thread is optimal, depending on how fast the storage is compared to the overhead of having multiple threads. Having as many threads as files to copy might also not be ideal; even if they don't use much CPU time, performing I/O on multiple files at the same time might result in more disk seeks (which is mostly an issue on harddisks, but some solid state drives also have some performance penalty for doing non-sequential access). You could benchmark your code and optimize the number of threads for your system, but the performance might be different on other systems.
Alternatives for threads
Threads are a solution to the problem that there is some implicit serialization when doing regular I/O operations on files in a single thread. However, there are also other techniques to have multiple I/O operations submitted to the kernel simultaneously. POSIX AIO is one way to do this, however it has its limitations. In particular, I don't think you can easily hand off a file copy operation to it. What is more promising is Linux's io_uring, although this is of course not a cross-platform solution to your problem. If you are interested in the latter, then also see this LWN article and this excellent guide. Most BSD flavors have something similar called kqueue.