IMHO strcpy() is perfectly fine if you know the length of the source, and ensure the destination is always big enough. strcpy() will always write strlen(src)+1 bytes, nothing more and nothing less. That said, code should be written so those facts are obvious, and documentation is a preferred way to show that. (Being explicit about whether null-terminators are included or not in length specifications is one thing that seems to be often overlooked, for example.)
Microsoft's _s functions don't help if you don't know what the lengths should be, and if you do, they just make it more confusing.
Yup, I'm gonna stick my neck out here and say I quite like strcpy()
In good code it spells out in one glance that there exists a guarantee on the size of the destination buffer.
Supplying the string's length just brings redundant code and, with it, ambiguity.
Discouraging strcpy() use is fine and understood; I'm aware of all the caveats and bugs and security implications.
But the kind of black-and-white thinking that says "always use strncpy instead of strcpy" is bad; the idea that truncating a string magically absolves us of any security implications or the need for exit paths.
And then let's look at how much code has made a mess of strncpy() whilst thinking it was doing the right thing.
I can probably count on one hand the number of times I've wanted to copy a string but been happy for it to be quietly trimmed, even in extreme cases. Whereas anything from alloca to flexible array member, or copying a string back to its original buffer, are all very appropriate use of strcpy().
Microsoft's _s functions don't help if you don't know what the lengths should be, and if you do, they just make it more confusing.