Ask Ubuntu is a question and answer site for Ubuntu users and developers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
13  
oh dear... sudo means, that you have think twice what you will do! – antivirtel May 18 '11 at 13:37
2  
The easiest is to reinstall. Put the LiveCD/USB, and at the screen where it asks you to partition your disk, it should give you the option to Upgrade 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
8  
Just now you have learned a lesson. You don't need to write / 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 mistyped sudo 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:10
1  
@ulidtko oh great! So . is the same as ./? I'll learn it :) – fl00r May 18 '11 at 19:11
3  
@fl00r, yes. It's a directory name which means this, or "current" directory. cd ., for example, does nothing. ls . is the same as ls. Also, the .. is a directory name which means "the parent of .", and you probably knew it already. – ulidtko May 18 '11 at 19:24
up vote 26 down vote accepted

In short: you can't, reinstall your system.


[edit]

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 some guy's script.

So trust me, just reinstall. It's a safe bet and guaranteed to keep you out of trouble.


Finally, two 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.

share|improve this answer
    
Ahhh :) so sad. – fl00r May 18 '11 at 13:21
10  
Well you could by getting all the permissions for every file from another system, but doing this is so much work that it'd probably be easier and safer just to reinstall. – Oli May 18 '11 at 13:49
    
@Oli, ok, I've just reinstalled Ubuntu, thanks – fl00r May 18 '11 at 14:10
    
And don't be sad! With great power comes great responsibility – ulidtko Jul 22 at 15:10

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
share|improve this answer
    
Ubuntu 11.04. But I've reinstalled it already. Thanks! – fl00r May 18 '11 at 14:17
    
your script fails as owner_id is undefined – Eliran Malka Mar 29 '14 at 21:53
2  
kinda an overkill... find does that quite nicely: 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.

share|improve this answer
    
Yeap, I've just reinstalled it, thanks :) – fl00r May 18 '11 at 14:11

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.

share|improve this answer
    
This isn't a bad idea but you'd need to exclude things that are automatically installed or you'd permanently end up with those packages (even if you removed the dependant packages)... But then they wouldn't get reinstalled. Tough one. Perhaps getting a list of the automatic packages first, then reinstall every package then go through the list of autos, re-marking them as auto. – Oli May 18 '11 at 14:43
    
@Oli - wouldn't (some of) that be solved by running sudo apt-get autoremove? – Wilf Feb 22 '14 at 15: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:

  • While outputting to the file: /tmp/fileper.log, you might be listing custom settings, and proc, etc.
  • you might not be able to boot, or run commands,

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.

share|improve this answer
    
Why 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 at 21:03
    
The find line isn't working as it is, it should be 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 at 3:59
    
@muru wrote this in the middle of the night. Will edit the code... – blade19899 May 12 at 6:39
    
Not able to test, will rely on user input – blade19899 May 12 at 8:50

(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 
share|improve this answer
    
"I know I shouldn't X but I'm going to X anyway" is a sign that you may well a sociopath :-) Probably the right thing to do would have been to garner enough rep then comment. Let's get that process started. – paxdiablo Jul 21 at 11:03

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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