When using /bin/cp source_file destination_file, and one of the arguments is block special.
1 Answer
When the source file is regular file, and the destination block device (and when without -i commandline switch, or got affimative from -i), POSIX says [1] that the destination shall be open()ed with O_WRONLY|O_TRUNC, and the content of source written. And POSIX says [2] that the behavior of block devices (among other specials) open()ed with O_TRUNC is implementation-defined, which, in my experience, is ususally overwriting from the beginning, and leaving the remainder unmodified if the source is smaller. GNU cp additionally has --remove-destination commandline switch, which, when the source file is regular and the destination block device, would unlink() the destination first, then create a regular file in its place.
When the source file is block device, and with -R commandline switch, POSIX says that the destination shall be created with the same file type as source. This is usually mknod()ing destination with corresponding major:minor.
Now the tricky part, when the source file is block device, and without -R, it seems that POSIX omitted this. The FreeBSD implementation [4] would open() the destination and write the content. By contrast, the GNU one [3] would default to mknod() the destination, or, if with --copy-contents, open() and write the content.
[1] cp.1posix, POSIX.1-2017 Shell & Utilities https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html
[2] open.3posix
[3] cp, GNU Coreutils https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html
‘-R’ ‘-r’ ‘--recursive’ (...) Special files are copied by creating a destination file of the same type as the source; see the --copy-contents option.
This text is in the "info" format document, but not in the "man".
[4] freebsd release/12.1.0 https://github.com/freebsd/freebsd/blob/release/12.1.0/bin/cp/cp.c#L461
case S_IFBLK:
case S_IFCHR:
if (Rflag && !sflag) {
if (copy_special(curr->fts_statp, !dne))
badcp = rval = 1;
} else {
if (copy_file(curr, dne))
badcp = rval = 1;
}
int copy_special() and int copy_file() are defined in bin/cp/utils.c. One mknod(), while the other open() and writes the content.
toybox (a bsd licensed alternative to busybox multi-call, shipped with android) seems to be always writing the content. Maybe I shall do more investigation into this later.