开发者

Is it possible to create a script to save and restore permissions?

I am using a linux system and need to expe开发者_C百科riment with some permissions on a set of nested files and directories. I wonder if there is not some way to save the permissions for the files and directories, without saving the files themselves.

In other words, I'd like to save the perms, edit some files, tweak some permissions, and then restore the permissions back onto the directory structure, keeping the changed files in place.

Does that make sense?


The easiest way is to use ACL tools, even if you don't actually use ACLs. Simply call getfacl -R . >saved-permissions to back up the permissions of a directory tree and setfacl --restore=saved-permissions to restore them.

Otherwise, a way to back up permissions is with find -printf. (GNU find required, but that's what you have on Linux.)

find -depth -printf '%m:%u:%g:%p\0' >saved-permissions

You get a file containing records separated by a null character; each record contains the numeric permissions, user name, group name and file name for one file. To restore, loop over the records and call chmod and chown. The -depth option to find is in case you want to make some directories unwritable (you have to handle their contents first).

You can restore the permissions with this bash snippet derived from a snippet contributed by Daniel Alder:

while IFS=: read -r -d '' mod user group file; do
  chown -- "$user:$group" "$file"
  chmod "$mod" "$file"
done <saved-permissions

You can use the following awk script to turn the find output into some shell code to restore the permissions.

find -depth -printf '%m:%u:%g:%p\0' |
awk -v RS='\0' -F: '
BEGIN {
    print "#!/bin/sh";
    print "set -e";
    q = "\047";
}
{
    gsub(q, q q "\\" q);
    f = $0;
    sub(/^[^:]*:[^:]*:[^:]*:/, "", f);
    print "chown --", q $2 ":" $3 q, q f q;
    print "chmod", $1, q f q;
}' > restore-permissions.sh


Install the ACL package first:

sudo apt-get install acl

Recursively store permissions and ownership to file:

getfacl -R yourDirectory > permissions.acl

Restore (relative to current path):

setfacl --restore=permissions.acl


hm. so you need to 1) read file permissions 2) store them somehow, associated to each file 3) read your stored permissions and set them back

not a complete solution but some ideas:

stat -c%a filename
>644

probably in combination with

find -exec

to store this information, this so question has some interesting ideas. basically you create a temporary file structure matching your actual files, with each temp file containing the file permissions

to reset you iterate over your temp files, read permissions and chmod the actual files back.


There is also a special tool for that called metastore:

metastore is a tool to store the metadata of files/directories/links in a file tree to a separate file and to later compare and apply the stored metadata to said file tree. I wrote the tool as a supplement to git which does not store all metadata, making it unsuitable for e.g. storing /etc in a repo. metastore could also be helpful if you want to create a tarball of a file tree and make sure that "everything" (e.g. xattrs, mtime, owner, group) is stored along with the files.

It's also available as Debian package.


save: find . -type f |xargs ls -la| awk '{print "chmod "$1" "$NF}'>./filesPermissions.sh

restore: sh ./filesPermissions.sh


I have a Python script for doing this at https://github.com/robertknight/mandrawer/blob/master/save-file-attrs.py

save-file-attrs.py save

Will save the permissions, mode and modification times of files in the dir tree rooted at the current working directory to a local file (.saved-file-attrs) and:

save-file-attrs.py restore

Will restore those attributes from the file and display the changes.


you can get the file's permissions with

ls -l | awk '{print $1" "$NF}'

which will return a list of file names and their permissions. save it somewhere, and once you're done - restore (chmod) each file's permissions.


I found the answer from Dmytro L very cool. But, unfortunately, it's doesn't work, because it's generate entries like:

chmod -rw-r--r-- ./.bashrc

To avoid it, I use following command:

find . -type f | xargs stat -c "%a %n" | awk '{print "chmod "$1" "$2}' > ./filesPermissions.sh

Basically, it does the same, but generate octal entries like:

chmod 644 ./.bashrc

which works.


Here's an example for easily doing this with a single file. No additional tools, scripts, temp file, etc. are required. You could expand upon this method for working with more files if needed.

In this specific example, the permissions are saved in a varibale via the stat command. Then, the file is temporarily stripped of any restrictive permissions. Next, something is done with it (that may have failed due to those prior restrictions). Finally, the original permissions are restored.

file=$1
saved_permissions=$(sudo stat -c %a $file)
sudo chmod 777 $file
# <DO SOMETHING HERE>
sudo chmod $saved_permissions $file 


I modified Anton`s command to get the additional string chown user:group /file_or_folder_path. Now you can get a bash script which contains two string for each file/folder.

command:

find . -type f | xargs stat -c "%a %U:%G %n" | awk '{print "chown "$2" "$3"\nchmod "$1" "$3}' > ./filesPermissions.sh

Example of output:

chown root:root /file_or_folder_path
chmod 777 /file_or_folder_path


#!/bin/bash

if [ $# -ne 1 ]; then
        echo "Enter directory";
        exit 0;
fi
# dump acls
cd $1
>acl
echo "#!/bin/bash">recovery_acl.sh
echo "cd $1">>recovery_acl.sh
f='./'
# create acl file sorted by dir_level
for i in `seq 0 15`;do
  find . -mindepth $i -maxdepth $i -type d -exec getfacl {} +|grep -E '*UTS|file:'>>acl
done
sed -i 's/default\:user/\-dm\ u/g' acl
sed -i 's/default\:group/\-dm\ g/g' acl
sed -i 's/user\:/\-m\ u\:/g' acl
sed -i 's/group\:/\-m\ g\:/g' acl
sed -i 's/\#\ file\:\ /\.\//g' acl
sed -i '/^#/d' acl

while IFS='' read -r line ; do
  # grep dir name
  if echo "$line" | grep -q "$f" ; then
    dir="$line"
    continue
  fi
  echo setfacl $line '"'$dir'"'>>recovery_acl.sh
  # grep non def acl (for files)
  if echo "$line" | grep -q '\-m' ; then
    echo setfacl $line '"'$dir'"'/*>>recovery_acl.sh
  fi
done < "acl"
rm -f acl
sed -i 's/134/\\/g' recovery_acl.sh
sed -i 's/040/\ /g' recovery_acl.sh

After execution of script, will creating another "recovery_acl.sh". Replace UTS on your domain. Errors like "No such file or directory" means that dir is empty.


I found best way (for me) do it with python!

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import json
import sys
import re
try:
    import posix1e
except ImportError:
    print("No module named 'posix1e'")
    print("You need do: apt install python3-pylibacl")
    sys.exit()

def dump_acl(dir):
    acl_dict = {}
    for root, dirs, files in os.walk(dir):
        try:
            path = root.split(os.sep)
            root_acl = str(posix1e.ACL(file=root))
            root_def_acl = str(posix1e.ACL(filedef=root))
            #print(root_acl)
            acl_dict[root] = root_acl
            acl_dict["default_" + root] = root_def_acl
            for file_name in files:
                try:
                    if 'acl.json' in file_name:
                        continue
                    file_path = root + "/" + file_name
                    file_acl = str(posix1e.ACL(file=file_path))
                    acl_dict[file_path] = file_acl
                    #print(file_path)
                except Exception as e:
                    print(e)
                    print(root, '/' + file_name)
                    continue
        except Exception as e:
            print(e)
            print(root, '/' + file_name)
            continue

    with open(dir + '/acl.json', 'bw') as f:
        f.write(json.dumps(acl_dict, ensure_ascii=False).encode('utf8'))
    return


def recovery_acl(dir):
    with open(dir + '/acl.json', 'r') as f:
        acl_dict = json.load(f)
    try:
        for file_path, file_acl in acl_dict.items():
            if file_path.startswith('default_'):
                file_path = file_path.replace('default_', '', 1)
                posix1e.ACL(text = file_acl).applyto(file_path, posix1e.ACL_TYPE_DEFAULT)
                continue
            if 'acl.json' in file_path:
                continue
            file_acl = file_acl.replace('\n', ',', file_acl.count('\n') -1)
            file_acl = file_acl.replace('\134', u'\ ' [:-1])
            file_acl = file_acl.replace('\040', u' ')
            if 'effective' in file_acl:
                file_acl = file_acl.replace('\t', '')
                f_acl = re.sub('#effective:[r,w,x,-]{3}', '', file_acl)
            posix1e.ACL(text = file_acl).applyto(file_path)
    except Exception as e:
        print(e, file_path, file_acl)
    return

def help_usage():
    print("Usage:")
    print("For dump acl:   ", sys.argv[0], "-d /path/to/dir")
    print("For restore acl:", sys.argv[0], "-r /path/to/dir")
    print("File with acls (acl.json) storing in the same dir")
    sys.exit()


if len(sys.argv) == 3 and os.path.isdir(sys.argv[2]):
    if sys.argv[1] == '-d':
        dump_acl(sys.argv[2])
    elif sys.argv[1] == '-r':
        if os.path.exists(sys.argv[2] + '/acl.json'):
            recovery_acl(sys.argv[2])
        else:
            print("File not found:", sys.argv[2] + '/acl.json')
else:
    help_usage()

backup acl: dump_acl.py -d /path/to/dir

recovery acl: dump_acl.py -r /path/to/dir

After execution, script create acl.json in the same dir(witch backup/restore acls)


I borrow this answer from roaima's post.
I think this should be the best answer:
Save the permissions

find * -depth -exec stat --format '%a %u %g %n' {} + >/tmp/save-the-list

Restore the permissions

while read PERMS OWNER GROUP FILE
do
    chmod "$PERMS" "$FILE"
    chown "${OWNER}:${GROUP}" "$FILE"
done </tmp/save-the-list
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜