Does cp make a copy or create a new file?

It has default settings like any other application. You should know them.

This question is neither meant as a philosophical headscratcher or a question about storage technology. Using cp you get a file (destination) that has the same contents as the old (source), that much is clear. That doesn’t answer these questions, though:

  • What permissions is the destination file born with?
  • What group does it belong to?

To my mind there were two good answers, equally logical in their own way. Either cp creates a new file or it creates a copy.

If it’s new-file-creation then – I thought – the file should belong to user:user_primary_group and start with whatever permissions user‘s umask allows, similar to how, e.g. touch would create a new file. If it’s a copy, then it should inherit the source file’s permissions and be shared with the same group as the source file.

Place your bets now.

Or just guess.

Don’t test in a terminal. That’s cheating.

All set for the reveal?

Ok, here we go.

It was a trick question. Neither of those two options are the actual cp defaults:

[ /tmp/test $ ] cp rootfile copyfile
[ /tmp/test $ ] ls -lh
total 0
-rw-r----- 1 mads mads  0 May 27 12:55 copyfile
-rw-r----- 1 root media 0 May 27 12:54 rootfile

It’s a bit of both. copyfile has inherited the permissions of the source file but it belongs to my user’s primary group, not media as the original.

There are flags to change these defaults to either of my two suggestions. For the “file creation” approach I need to specify that the permissions of the original should not be preserved:

[ /tmp/test $ ] ls -lh
total 0
-rw-rw-r-- 1 root docker 0 May 27 12:54 rootfile
[ /tmp/test $ ] cp --no-preserve=mode rootfile copyfile
[ /tmp/test $ ] ls -lh
total 0
-rw-r--r-- 1 mads mads   0 May 27 13:22 copyfile
-rw-rw-r-- 1 root docker 0 May 27 12:54 rootfile
 test

-rw-r--r-- corresponds to 644 and rhymes with my umask of 022. Note that execute permissions on files are never set on file creation for security reasons – you always have to do that explicitly.

For the “copy” approach, I can make explicit what additional information from the source file I do want to preserve:

[ /tmp/test $ ] ls -lh
total 0
-rw-rw-r-- 1 root docker 0 May 27 12:54 rootfile
[ /tmp/test $ ] cp --preserve=mode,ownership,timestamps rootfile copyfile
[ /tmp/test $ ] ls -lh
total 0
-rw-rw-r-- 1 mads mads   0 May 27 12:54 copyfile
-rw-rw-r-- 1 root docker 0 May 27 12:54 rootfile
[ /tmp/test $ ] 

One thing sticks out here, though. Did I not ask cp to preserve ownership? This ought to have made the file belong to user:docker, not user:user_primary_group, surely?

The problem is my primary user does not belong to the docker group. A non-root user is not allowed to share one of their own files with a group they do not belong to:

[ /tmp/test $ ] touch sharefile
[ /tmp/test $ ] chown mads:docker sharefile
chown: changing ownership of 'sharefile': Operation not permitted
[ /tmp/test $ ] chown mads:media sharefile
[ /tmp/test $ ] 

My primary user does not belong to the docker group, but it does belong to the media group. So the former command fails where the latter succeeds.

As we have seen, though, where chown balks, cp perseveres, silently ignoring the preserve=ownership flag. Not even a non-zero exit status code to tell me that there was an issue. Odd? Certainly. A bug? Maybe.

To answer the question at the top: cp is by default run with implicit --preserve=mode --no-preserve=ownership,timestamps flags. There might be other attributes preserved but certainly not ownership or timestamps.

At any rate, I hope this deep dive has been illuminating. I have used cp for the better part of two decades and never stopped to think of what a copy actually is before today.

Photocopy © Bruce Bortin, CC BY-NC 2.0 license

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.