When using /bin/cp source_file destination_file, and one of the arguments is block special.

  • 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.