23

I was trying to chown within /opt and for some reason chown jumped up to the parent and chowned everything.

Can anyone suggest why/how this might happen, and how to avoid doing it in future? It's a bit concerning that running a command in a given dir can effectively jump up and run it in root dir.

ubuntu: /opt > sudo chown -R root:www-data .*
chown: changing ownership of '../var/lib/lxcfs/proc/cpuinfo': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/proc/meminfo': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/proc/stat': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/proc/uptime': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/proc/diskstats': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/proc/swaps': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/proc': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/devices': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/blkio': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/hugetlb': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/rdma': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/pids': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/freezer': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/cpuset': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/memory': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/perf_event': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/cpu,cpuacct': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/net_cls,net_prio': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/name=systemd': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup/unified': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs/cgroup': Operation not permitted
chown: changing ownership of '../var/lib/lxcfs': No such file or directory
^C
:ubuntu: /opt >
guntbert
  • 12,914
  • 37
  • 45
  • 86
Duke Dougal
  • 1,447
  • 1
  • 11
  • 17
  • 2
    I would have done it like this: `sudo chown -R root:wwwdata /opt` as per the --help dialog... perhaps using that pipe caused some issue??? – Joshua Besneatte Jul 24 '19 at 21:54
  • I figure it's something to do with me specifying dotstar .* – Duke Dougal Jul 24 '19 at 21:54
  • 13
    `.*` matches `..` (the parent directory, which is `/`) - see [Does “chmod 777 .* -R” chmod parent directories (..)?](https://superuser.com/questions/90837/does-chmod-777-r-chmod-parent-directories) – steeldriver Jul 24 '19 at 21:55
  • dotStar... too close to a deathStar to be safe :/ – Joshua Besneatte Jul 24 '19 at 21:55
  • 7
    @steeldriver that sounds like it should be posted as an answer ;) – Joshua Besneatte Jul 24 '19 at 21:56
  • Hmmm. So presumably the entire system is now in a somewhat unknown state and I should restore/rebuild? – Duke Dougal Jul 24 '19 at 21:57
  • 2
    So what is the correct way to set permissions on hidden files, which is what I was trying to do? – Duke Dougal Jul 24 '19 at 22:00
  • @DukeDougal See here: https://askubuntu.com/q/43621/167115 – mchid Jul 25 '19 at 09:18
  • 4
    @JoshuaBesneatte I try to avoid running recursive commands on arguments beginning with / because most keyboards put / fairly close to the Enter key, and it's too easy to accidentally hit Enter before typing the rest of the command. To mitigate this risk, one can either `cd` to the root directory and omit the leaing /, or start the command with `(`, which means the command won't be executed until the matching `)` is typed, giving the opportunity to hit Ctrl-C and bail out of a bad mistake (such as `rm -rf /tmp/foo-install` and hitting Enter instead of T). – Monty Harder Jul 25 '19 at 16:23
  • @MontyHarder Excellent tip! Thanks! – Joshua Besneatte Jul 25 '19 at 17:30

3 Answers3

45

The shell glob .* matches .. (the parent directory) in this case unfortunately that's /:

steeldriver@t400s:/opt$ ls .*
.:

..:
bin  boot  cdrom  dev  etc  home  initrd.img  initrd.img.old  lib  lib32  lib64
libx32  lost+found  media  mnt  opt  proc  root  run  sbin  snap  srv  swapfile  sys
tmp  usr  var  vmlinuz  vmlinuz.old

For additional discussion see:

steeldriver
  • 131,985
  • 21
  • 239
  • 326
25

This happened because you used:

sudo chown -R root:www-data .*

when you should have used this instead:

sudo chown -R root:www-data ./*

First, -R is recursive for all directories under the target directory.

Additionally, * will match all files and directories under the current directory. Next, .* will match all files and directories one level above the current directory.

To avoid this in the future, you can use the ls command to verify the path before you execute the chown command like in these examples:

ls -a ./*
ls -a *
ls -a .*
ls -a ../*

Another way to avoid this is to always use the full path to the directory you wish to run a command to.

Here is an example:

sudo chown -R root:www-data /opt/*

Edit:

You can use the following command to chmod all hidden files or directories directly under /opt (assuming the first character after the . that makes them hidden is a letter, a number, a dash or an underscore which should be true for most files).

for i in /opt/.[A-Za-z0-9-_]*; do sudo chmod root:www-data "/opt/$i"; done

You can verify what files this will chmod by running the following command:

ls /opt/.[A-Za-z0-9-_]*

The first part of the command: for i in /opt/.[A-Za-z0-9-_]* says that, for all the results of the glob /opt/.[A-Za-z0-9-_]* assign each result to the variable "i".

The glob here says that the first character must be . and that the next character [A-Za-z0-9-_] must be any character that is A-Z or a-z or any number 0-9 or a - or a _.

This will exclude the results . and .. which represent the current directory and the directory above the current directory and will only include hidden files and directories.

The second part of the command: do sudo chmod root:www-data "/opt/$i" says to run the command for all variables that match the current value of $i.

The third part of the command: done says that I am finished.


Additionally, you used the -R option with chmod and the -R option is recursive and will apply to all directories and files.

When you only only use the chmod command with no options, the command will only apply to the specific file or directory you gave it and will not apply recursively to directories.

terdon
  • 98,183
  • 15
  • 197
  • 293
mchid
  • 42,315
  • 7
  • 94
  • 147
  • 5
    My intent was to target hidden files. I mistakenly assumed that the syntax used for grepping hidden files as described here https://stackoverflow.com/questions/10375689/how-can-i-grep-hidden-files is generally valid syntax for hidden files. Seems not. – Duke Dougal Jul 24 '19 at 22:07
  • @DukeDougal The problem with that answer you have is that it also matches `.` and `..` which represent the current directory and the directory above your current directory *as well as* hidden files. – mchid Jul 24 '19 at 22:17
  • 2
    @DukeDougal You shouldn't accept the first answer that comes up right away. It's generally better to wait, say, 24 hours before accepting. In that time other more useful or better written answers may come up that would deserve accepting. You can upvote all answers you think are useful. StackExchange is not (or should not be) about "who answers first" but "who provides the best answer" (both in terms of content and clarity). – Giacomo Alzetta Jul 25 '19 at 11:06
  • 1
    should this answer perhaps explain _why_, exactly using `.*` is wrong here? (And well, you could also use `chmod -R .` instead of `chmod -R ./*`, but of course they're also slightly different...) – ilkkachu Jul 25 '19 at 12:30
  • 11
    Edit is terrible. It suggests to parse `ls` output and is very slow while answer is to use `find`. – val - disappointed in SE Jul 25 '19 at 14:22
  • 9
    (1) No wildcard (glob/pattern) is recursive in bash except for `**`, and even that has to be explicitly enabled.  IMHO, you should be clearer about the role of `-R`.  (2) People are advised to avoid using plain `*` because it can match filenames beginning with **`-`**, which will then be interpreted as options.  _`command `_ `-- *` should protect against that, but I’m not sure all commands honor that convention. … (Cont’d) – Scott - Слава Україні Jul 25 '19 at 15:11
  • 6
    (Cont’d) …  (3) `*`, `./*` and even `/opt/*` fail to find “dot files” (`.*`) unless the `dotglob` option is set.  As [Joshua Besneatte](https://askubuntu.com/q/1160828/309110#comment1933467_1160828) and [ilkkachu](https://askubuntu.com/q/1160828/309110#comment1933762_1160834) say, ``chown -R /opt`` and ``chown -R .`` are better.  … (Cont’d) – Scott - Слава Україні Jul 25 '19 at 15:11
  • @DukeDougal _"I mistakenly assumed that the syntax used for grepping hidden files as described here … is generally valid syntax for hidden files."_ - Actually, this problem _also_ exists for grep (because it's nothing to do with chmod and grep, it's bash's fault for including `..` in globbing `.*`). This is pointed out by the cumments below the accepted answer! – marcelm Jul 25 '19 at 19:53
  • 1
    This analysis is incorrect. The command is only recursive because `-R` means "recursive" to `chmod`. And `.*` does not mean what you say it means. – alexis Jul 26 '19 at 09:15
  • Are you aware of `ls -A`? It will automatically exclude `.` and `..`. Regardless though, don't parse `ls`. – wjandrea Jul 26 '19 at 12:20
  • 1
    @val please see the latest edit. There's no reason to use `find` for something like that. A (proper) shell loop is fine. It might be slower, yes, but on modern machines and for manually run commands the difference will be negligible. – terdon Jul 26 '19 at 15:26
  • 1
    I don't get it. A day after and this answer is still totally wrong. `*` is not recursive. – rexkogitans Jul 26 '19 at 15:53
  • 1
    @rexkogitans it is when used with `chmod /path/*`: that will cause `chmod` to change the permission of `path` and all files and directories under it. So it is recursive in that sense. Compare it to `chmod /path/` which will only change the permissions of `/path` and nothing under it. – terdon Jul 26 '19 at 16:55
  • @wjandrea Thanks, I was not aware because I have an alias set for `ls` where `ls=ls -a`. – mchid Jul 27 '19 at 00:08
  • @terdon Shell glob of `*` is not recursive, but`chmod` with the switch `-R` is. – rexkogitans Jul 27 '19 at 05:19
  • @rexkogitans yes, but `chmod dir/*` is also recursive since that will change the permissions of the contents of `dir`. I'm not saying that `*` is recursive, I'm saying that `dir/*` will cause a command to recurse one level down into `dir`. – terdon Jul 28 '19 at 13:14
  • @terdon This is not what the word "recursion" means, because also this shell glob is not recursive. – rexkogitans Jul 28 '19 at 15:14
5

Your troubles came because .* matches everything that begins with a dot. The context is the current directory, since this expression does not include a path. So, if there are any hidden files or folders like .git in the current directory, you'll match them. But (as you'll see by running ls -a in that folder), you will also match . and ..

And .., of course, is the parent directory, so chmod -R recursively targeted everything in the parent directory.

alexis
  • 1,028
  • 7
  • 9
  • An absolute path like `/opt/.*` wouldn't help, `/opt/..` is the same as `..` with CWD = `/opt`. – Peter Cordes Jul 26 '19 at 19:26
  • @Peter: Yes, that is correct: If the expression included a path, that would give the context (the starting point), instead of it being the current directory. The OP meant to use `.` as the context, but it didn't work out that way due to the missing slash... – alexis Jul 27 '19 at 08:14