Copying, moving, removing — and the permissions each one needs
◈ 10 cardsThe exact permission preconditions for cp, mv and rm; what mv really does within one file system; the trailing-slash and hidden-file traps that break the obvious command.
The fixture
One small tree carries the whole lesson. Your home directory holds a project and an empty backup directory:
~/proj/alpha.txt
~/proj/.hidden
~/proj/src/main.c
~/backup/ (empty)
Note the dotfile. It is there because half of what goes wrong in this lesson goes wrong because of a dotfile.
What each operation actually needs
This is the part of file processing that surprises people, and it is the part examiners like. Every one of these operations needs execute (search) permission on every directory in the pathname before anything else is even considered — that is what lets you traverse a path at all. On top of that:
cp source dest needs read on source. For the destination the rule splits:
- if
destdoes not exist yet, you need write on the destination directory, because you are creating a new name in it; - if
destdoes exist, you need write ondestitself, and you do not need write on the directory — you are changing a file's contents, not adding a name.
An existing destination is overwritten silently. cp -i prompts first; cp -f goes ahead even where a prompt or a permission problem would otherwise stop it.
mv source dest needs write and execute on the source's directory — and no permission at all on the file itself. Read that again, because it is the fact people get wrong: moving a file is an operation on the directory entry, not on the file. You need write and execute on the destination directory too, and write on the destination file if one already exists there.
rm file needs write and execute on the directory that holds it — module 4's Permissions on directories is where that rule is derived and examined, so treat this as a reminder rather than a new fact. What matters here is the consequence for file processing: rm will delete a file you have no permission on at all, pausing only to confirm, so a read-only file in a directory you own is not protected.
What mv actually does
One line of reminder, because module 2's When does a file keep its identity? owns this and derives it in full: within one file system mv moves no data and the inode number is unchanged; across file systems it copies and deletes, and the inode number changes. The reason it matters here is that the permissions mv needs follow from it — a rename edits directory entries, so the permissions checked are the directories', not the file's.
Worked example — three failures, then a success
The fastest way to learn cp's preconditions is to violate them one at a time.
$ cp nosuch.txt copy.txt
cp: cannot stat 'nosuch.txt': No such file or directory
Failure one: no source. Nothing to read.
$ ls -l secret.txt
--w------- 1 ada ada 84 Sep 1 09:12 secret.txt
$ cp secret.txt copy.txt
cp: cannot open 'secret.txt' for reading: Permission denied
Failure two: the source exists but you lack read on it. Owning a file does not help — the bits are checked, not the ownership.
$ ls -l copy.txt
-r--r--r-- 1 ada ada 12 Sep 1 09:15 copy.txt
$ cp alpha.txt copy.txt
cp: cannot create regular file 'copy.txt': Permission denied
Failure three: the destination exists and you lack write on it. Now override:
$ cp -f alpha.txt copy.txt
$ ls -l alpha.txt copy.txt
-rw-r--r-- 1 ada ada 42 Aug 28 14:03 alpha.txt
-rw-r--r-- 1 ada ada 42 Sep 1 09:17 copy.txt
-f removed the unwritable destination and recreated it, so the copy succeeded — but look at the timestamps. copy.txt is stamped now, not with alpha.txt's modification time. Add -p to carry the owner, group, permission bits and modification time across:
$ cp -fp alpha.txt copy.txt
$ ls -l alpha.txt copy.txt
-rw-r--r-- 1 ada ada 42 Aug 28 14:03 alpha.txt
-rw-r--r-- 1 ada ada 42 Aug 28 14:03 copy.txt
Identical mtimes. That difference is the entire reason -p exists, and it matters whenever anything downstream — make, a backup script, a submission deadline — reads the timestamp.
Recursive copy, and the move that loses your dotfiles
cp -r ~/proj ~/backup copies the directory and its name: afterwards ~/backup/proj exists, holding alpha.txt, .hidden and src/. To copy the contents into ~/backup instead, name the contents: cp -r ~/proj/. ~/backup.
Moving is where the dotfile bites. mv ~/proj/* ~/backup/proj/ looks like it moves everything, and ls ~/proj afterwards looks empty. It is not. ls -a ~/proj still shows .hidden, because * is expanded by the shell, and a shell glob does not match a leading dot. The file was never passed to mv at all.