I accidently ran
sudo chmod 755 -R /
instead of
sudo chmod 755 -R ./
I stopped it after few seconds, but now there is some problems such as
sudo: must be setuid root
How can I revert permissions back?
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It only takes a minute to sign up.
Sign up to join this communityI accidently ran
sudo chmod 755 -R /
instead of
sudo chmod 755 -R ./
I stopped it after few seconds, but now there is some problems such as
sudo: must be setuid root
How can I revert permissions back?
In short: you can't, reinstall your system.
I mean, Posix permissions are used and relied on heavily; there's a multitude of places in the filesystem where wrong permissions would break the OS (SUID flags) or even worse, make it exposed security-wise (/etc/ssh/ssh_host_rsa_key
) while it appears to be working OK.
Hence, such a recovery is hard to do properly. Miss one thing — and you screw it up. You already screwed up your sudo chmod
command (if that's your friend rather than you, she might as well learn some Linux lesson, too) — and that's a very simple of a command. Proper recovery would demand way more commands and way more vigilance. Even if you use someone's script.
So trust me, just reinstall. It's a safe bet and guaranteed to keep you out of trouble.
Finally, some tips relevant here.
First: reinstalls will be less painful if you setup your /home
on a separate partition next time. Actually, they will be a breeze.
Second: consider doing crazy Linux science in a virtual machine like the VirtualBox, and do your snapshots.
Third: chmod -R .
works. A dot by itself .
is valid directory name. There's no real need to append that slash. You could've avoided the catastrophic risk of skipping the dot entrirely;
mere chmod: missing operand after ‘755’
VS a ruined system.
I wrote and have been using for several years a couple of Ruby scripts to rsync
permissions and ownership. Script get-filesystem-acl
collects all the information by recursively traversing all the files and puts it all into the file .acl
. Script .acl-restore
will read .acl
and apply all the chown
's and chmod
's.
You can run get-filesystem-acl
on a similar Ubuntu installation and then copy over the .acl
file to your chmod-damaged box, put .acl
and .acl-restore
in /, and run .acl-restore
.
You will need to have root so fix your sudo
as Marco Ceppi suggested.
I can generate and give you the .acl
file for my Ubuntu.
get-filesystem-acl
#!/usr/bin/ruby
RM = "/bin/rm"
SORT = "/usr/bin/sort"
TMP = "/tmp/get_acl_#{Time.now.to_i}_#{rand * 899 + 100}"
require 'find'
IGNORE = [".git"]
def numeric2human(m)
return sprintf("%c%c%c%c%c%c%c%c%c",
(m & 0400 == 0 ? ?- : ?r),
(m & 0200 == 0 ? ?- : ?w),
(m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
(m & 04000 == 0 ? ?x : ?s)),
(m & 0040 == 0 ? ?- : ?r),
(m & 0020 == 0 ? ?- : ?w),
(m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
(m & 02000 == 0 ? ?x : ?s)),
(m & 0004 == 0 ? ?- : ?r),
(m & 0002 == 0 ? ?- : ?w),
(m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
(m & 01000 == 0 ? ?x : ?t)))
end
File.open(TMP, "w") do |acl_file|
# TODO: Instead of the current dir, find the .git dir, which could be
# the same or outside of the current dir
Find.find(".") do |path|
next if IGNORE.collect {|ig| !!(path[2..-1] =~ /\A#{ig}/)}.include? true
next if File.symlink?(path)
stat = File.lstat(path)
group_id = stat.gid
rules = "#{type}#{numeric2human(stat.mode)}"
acl_file.puts "#{path} #{rules} #{owner_id} #{group_id}"
end
end
`#{SORT} #{TMP} > .acl`
`#{RM} #{TMP}`
.acl-restore
#!/usr/bin/ruby
# This script will only work with .acl_ids
# Restore from...
FROM = ".acl"
MKDIR = "/bin/mkdir"
CHMOD = "/bin/chmod"
CHOWN = "/bin/chown"
known_content_missing = false
def numeric2human(m)
return sprintf("%c%c%c%c%c%c%c%c%c",
(m & 0400 == 0 ? ?- : ?r),
(m & 0200 == 0 ? ?- : ?w),
(m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
(m & 04000 == 0 ? ?x : ?s)),
(m & 0040 == 0 ? ?- : ?r),
(m & 0020 == 0 ? ?- : ?w),
(m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
(m & 02000 == 0 ? ?x : ?s)),
(m & 0004 == 0 ? ?- : ?r),
(m & 0002 == 0 ? ?- : ?w),
(m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
(m & 01000 == 0 ? ?x : ?t)))
end
def human2chmod(mode)
raise unless mode =~ /([r-][w-][xtsTS-])([r-][w-][xtsTS-])([r-][w-][xtsTS-])/
triple = [$1, $2, $3]
u,g,o = triple.collect do |i|
i.sub('s', 'sx').sub('t', 'tx').downcase.gsub('-', '')
end
return "u=#{u},g=#{g},o=#{o}"
end
File.open(FROM).each do |acl|
raise unless acl =~ /\A(([^ ]*? )+)([^ ]+) ([^ ]+) ([^ ]+)\Z/
path, rules, owner_id, group_id = $1, $3, $4, $5
path = path.strip
owner_id = owner_id.to_i
group_id = group_id.to_i
if !File.exists?(path) and !File.symlink?(path)
if rules =~ /\Ad/
STDERR.puts "Restoring a missing directory: #{path}"
STDERR.puts "Probably it was an empty directory. Git goes not track them."
`#{MKDIR} -p '#{path}'` # Creating the any parents
else
known_content_missing = true
STDERR.puts "ERROR: ACL is listed but the file is missing: #{path}"
next
end
end
s = File.lstat(path)
t = s.ftype[0..0].sub('f', '-') # Single character for the file type
# But a "-" istead of "f"
# Actual, but not neccesarely Desired
actual_rules = "#{t}#{numeric2human(s.mode)}"
actual_owner_id = s.uid
actual_group_id = s.gid
unless [actual_rules, actual_owner_id, actual_group_id] ==
[rules, owner_id, group_id]
chmod_argument = human2chmod(rules)
# Debug
#p chmod_argument
#p s.mode
## Verbose
puts path
puts "Wrong: #{[actual_rules, actual_owner_id, actual_group_id].inspect}"
puts "Fixed: #{[rules, owner_id, group_id].inspect}"
`#{CHMOD} #{chmod_argument} '#{path}'`
#puts
end
end
if known_content_missing
STDERR.puts "-" * 80
STDERR.puts "Some files that are listed in #{FROM.inspect} are missing in " +
"the current directory."
STDERR.puts
STDERR.puts "Is #{FROM.inspect} outdated?"
STDERR.puts "(Try retrograding the current directory to an earlier version)"
STDERR.puts
STDERR.puts "Or is the current directory incomplete?"
STDERR.puts "(Try to recover the current directory)"
STDERR.puts "-" * 80
end
find SOME_DIR -depth -printf 'chmod %m %p\n' > saved_permission
– reflog
Jul 16 '15 at 7:17
In long: you can. You'll need to mount the the file system from the a Live CD and begin reverting the permissions in the appropriate places. At a minimum to get sudo back you'll want to run sudo chmod u+s /usr/bin/sudo
while in the LiveCD session - that will fix the must be setuid root.
However, it would likely be easier to simply reinstall the system.
I would try to reinstall all packages with apt-get install --reinstall
, possibly using the output of dpkg --get-selections | grep install
to get a list of them.
sudo apt-get autoremove
?
– Wilf
Feb 22 '14 at 15:21
autoremove
only removes packages that you haven't installed manually.
– Dmitry Grigoryev
Jun 28 '17 at 12:17
apt-mark auto $pkg
/apt-mark manual $pkg
which allow you to modify the status of "manually installed/autoinstalled" per-package.
– ulidtko
Oct 31 '19 at 8:21
Alright, I haven't tested this (so use at your own risk), but it still might work. I Will test this in a virtual machine when I get the chance to:
First, in a still working system, I did the following to get all file permissions in a list, skipping the /home/
directory:
sudo find / -not -path /home -printf "%m:%p\0" > /tmp/fileper.log
This will print the permissions and file name for each file or directory on the system, followed by a \0
character (this is needed later on to deal with weird file names such as those containing newlines).
Then, on a system where the file permissions have been compromised:
while IFS=: read -r -d '' perm file; do
chmod "$perm" "$file"
done < /tmp/fileper.log
This will read each line of fileper.log
, saving the permissions as$perm
and the file name as $file
and then will set the file (or directory's) permissions to whatever was listed in the fileper.log
A few things to note here:
/tmp/fileper.log
, you might be listing custom settings, and proc, etc. What I would suggest is boot up a LiveCD with the Linux version you have on your disk, run the command, modify the path to where you have the local disk mounted, and run the second command!
I have tested that when booted from an Ubuntu CD/USB, I can choose not to format disk, meaning it will replace everything in the /
directory, BUT skip the /home/
directory. Meaning your users will have the configuration of apps/DATA(Music,Video,Documents) still intact. And by replacing the system files, the chmod
is set to there proper number.
chmod $(echo $LINE)
instead of just chmod $LINE
? Also, you can use just find
without stat
: find … -printf "%#m %p\n"
. Better yet, you can create the entire command: find … -printf "chmod %#m %p\n"
, then execute the file as a script.
– muru
May 11 '16 at 21:03
michael@NEXUS-TWO:~$ sudo find / -name '*' -exec stat -c "%a %n" {} \; >> /tmp/fileper.log
but then as well it runs over /proc
and some other places which you might not want in your list.
– Videonauth
May 12 '16 at 3:59
(I know I shouldn't comment in an answer, but not enough reputation to comment.)
blade19899's answer worked for me except for symlinks. E.g. it applied 755 to /bin/bash, but then applied 777 to the symlink /bin/rbash, effectively 777-ing /bin/bash.
As I already had the fileper.log file, I just modified the destination-end command:
while IFS=: read -r -d '' perm file; do
if [[ ! -L "$file" ]]; then
chmod "$perm" "$file"
fi
done < /tmp/fileper.log
chmod
.
– Dmitry Grigoryev
Jun 28 '17 at 12:24
... effectively 777-ing /bin/bash
— nope; that's not how it works. Are you saying I can replace /usr/bin/apt
with my own thing as a non-root user, just by writing to it through a 777 symlink? :) Exercise some critical thinking; symlinks can't, and don't, work that way. 777 permissions for symlinks are common and normal.
– ulidtko
Oct 31 '19 at 8:33
You can try restoring permissions with apt-get
.
If you can not run these commands with sudo you may need to boot to recovery mode and run them as root.
For booting to recovery mode see https://wiki.ubuntu.com/RecoveryMode.
From http://hyperlogos.org/page/Restoring-Permissions-Debian-System
Note: This was originally posted on the Ubuntu Forums but I can not find the original post.
Try, in order,
sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1`
If that fails:
sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | egrep -v '(package1|package2)'`
And finally, as a last resort,
sudo dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | xargs apt-get --reinstall -y --force-yes install
Using apt-get
Here's the relevant snip, EDITED FOR CORRECTNESS and reformatted:
sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1`
Let's say you get messages about some packages that can't be reinstalled, and the command fails. Here's one way to fix it by skipping the packages in question:
sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | egrep -v '(package1|package2)'`
And finally, if you should somehow have so many things installed that the above command fails saying your argument list is too long, here's the fix, which will run apt-get many more times than you might like:
sudo dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | xargs apt-get --reinstall -y --force-yes install
Note the
-y
and--force-yes
options, which will stopapt-get
from prompting you over and over again. These are always fun options, if you're sure you know what you're doing.
There is a closed question (with a great answer) as duplicate of this one from:
As such I will give Terdon the credit but here is my own answer based his.
During development of a new script, the variable $TMP_DIRECTORY/
had unexpected result of /
. This was followed by chmod 700
and chown -R $UDO_USER
pointing to $TMP_DIRECTORY
which was really /
.
System slowly crashed and burned. After that it would not reboot. Everyone screams "YOU MUST REINSTALL" but that means loosing years of work. This script is a variation of Terdon's answer:
#!/bin/bash
if [[ $(id -u) != 0 ]]; then # root powers needed to call this script
echo >&2 "$0 must be called with sudo powers"
exit 1
fi
# Parameters blank?
[[ $1 == "" ]] && echo "Parm 1 must be source directory" && exit 1
[[ $2 == "" ]] && echo "Parm 2 must be target directory" && exit 1
# Parameters valid directories?
[[ ! -d "$1" ]] && echo "Parm 1 must be a directory" && exit 1
[[ ! -d "$2" ]] && echo "Parm 2 must be a directory" && exit 1
# [[ ! "$2" == "/mnt/"* ]] && echo "Parm 2 must start with /mnt/..." && exit 1
# shopt -s globstar # From original
shopt -s dotglob
liveSystem="$1"
deadSystem="$2"
# Parameters must end in same subdirectory
[[ "${liveSystem##*/}" != "${deadSystem##*/}" ]] && \
echo "subdirectory ${liveSystem##*/} not same as ${deadSystem##*/}" && \
exit 1
cd "$deadsystem"
Count=0
NoRef=0
one() {
path="$1"
MountlessPath="${path##*$deadSystem/}"
if [ -e "$liveSystem/$MountlessPath" ] ; then
echo chown --reference "$liveSystem/$MountlessPath" "$path"
echo chmod --reference "$liveSystem/$MountlessPath" "$path"
(( Count++ ))
else
echo "No reference for: $path"
(( NoRef++ ))
fi
}
# From: https://stackoverflow.com/questions/51654041/loop-through-all-files-in-a-directory-and-subdirectories-using-bash
# Note typo "If" instead of "if" in above link
recurse() {
path="$1"
if [ -d "$path" ] ; then
# echo "Path: $path"
one "$path" # Enhancement: chown of directories too
for i in "$path/"* # "$path/."* # Enhancement: do hidden directories
do
# Skip over .. which gives endless loop
[[ "$path" == *".cache/speech-dispatcher"* ]] && continue
[[ "$path" == *"/usr/bin/X11"* ]] && continue
BaseName=${path##*/}
[[ "${#BaseName}" -gt 300 ]] && continue
# [[ ${#BaseName[@]} -gt 1 ]] && continue
# echo BaseName: $BaseName
# [[ $BaseName == ".." || $BaseName == "*" ]] && continue
recurse "$i"
done
# Original code selected only files but we want links too
# elif [ -f "$path" ] ; then
else
[ ! -f "$path" ] && printf "Not a file -> "
one "$path"
fi
}
recurse "$deadSystem"
echo "========================================================================"
echo "$Count references found for chown command to process"
echo "$NoRef files, directories and links, etc. could not be processed"
# From: https://unix.stackexchange.com/questions/193368/can-scp-create-a-directory-if-it-doesnt-exist/193372?noredirect=1#comment1111576_193372
# Original didn't have much luck?
#for file in **/*; do
# [ -e "$liveSystem/$file" ] &&
# echo sudo chown --reference "$liveSystem/$file" "$file"
#done
When running the script with $ sudo reset-owner /mnt/old/var /var | grep "No reference"
the tail end says this:
Not a file -> No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-rtkit-daemon.service-jvV8tG/tmp/*
No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-systemd-timesyncd.service-vmgqTh
No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-systemd-timesyncd.service-vmgqTh/tmp
Not a file -> No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-systemd-timesyncd.service-vmgqTh/tmp/*
No reference for: /var/tmp/uefi.dat
No reference for: /var/tmp/uefi.dsl
========================================================================
12369 references found for chown command to process
7676 files, directories and links, etc. could not be processed
As daunting as it seems it only took an hour to work it all out. By the time I was done I was left with this:
$ sudo reset-owner /mnt/old/var /var | grep "No reference" | grep -v /var/tmp/ | grep -v /var/spool/ | grep -v '\/\*' | grep -v /var/run | grep -v /unattended | grep -v /upstart | grep -v /log/journal | grep -v /log/pm | grep -v /log/cron | grep -v /log/dpkg | grep -v /log/apt | grep -v /log/altern | grep -v /lib/u | grep -v /lib/systemd | grep -v /lib/snapd | grep -v /lib/shim | grep -v /lib/NetworkManager | grep -v /lib/lightdm | grep -v /lib/initram | grep -v /lib/dpkg | grep -v /lib/doc-base | grep -v /var/lib/dkms | grep -v /lib/blue | grep -v /lib/binfmts | grep -v /lib/apt | grep -v /lib/app-info | grep -v /lib/Accounts | grep -v /var/crash | grep -v /var/cache
No reference for: /var
No reference for: /var/lib/flashplugin-installer
No reference for: /var/lib/gems
No reference for: /var/lib/gems/2.3.0
No reference for: /var/lib/git
No reference for: /var/lib/tlp/rfkill-saved
sudo
means, that you have think twice what you will do! – antivirtel May 18 '11 at 13:37Upgrade from Ubuntu 11.04 to Ubuntu 11.04
. Accept this option, and it will effectively re-install Ubuntu for you, in the most painless way. – user4124 May 18 '11 at 17:18/
in the end of directory name to specify the directory as a target. It's a bad habit, don't do it, never! The.
is by itself valid directory name, there is no need to append/
to it. If everyone followed this rule, then very much mistypedsudo
operations would have no effect on the root directory, so no harm would've been done to their systems. Don't do it! – ulidtko May 18 '11 at 19:10cd .
, for example, does nothing.ls .
is the same asls
. Also, the..
is a directory name which means "the parent of.
", and you probably knew it already. – ulidtko May 18 '11 at 19:24/
at the end. If you want to do pathname expansion for directories only. Example of listing directories inside the current directory:echo */
– pabouk Nov 15 '13 at 9:16