Forum général.général Manipulation des permissions Unix

Posté par . Licence CC by-sa
Tags :
2
30
oct.
2016

Ce post fait suite à ma question sur Git.

En effet, Git n’enregistre pas les permissions Unix des fichiers qu’il suit, excepté le droit d’exécution. Je sais qu’il existe des sur-couches à Git pour répondre à ce genre de problématique (Git possède un mécanisme de hooks) mais je suis arrivé à la conclusion que la sauvegarde des droits Unix était un problème plus général qui ne concerne pas seulement Git.

Il y a plusieurs autres cas de figure qui peuvent faire que l’on se retrouve avec une copie des fichiers, en ayant perdu l’information sur les droits : copie sur un système de fichier qui ne les gère pas toujours (NTFS, CIFS), mauvaise manipulation ou bug dans un script de maintenance.

Par ailleurs, même si on est pas censé avoir perdu cette information il peut être judicieux de vérifier que les permissions effectives soient bien conformes à une référence extérieure, voir forcer cette conformité.

J’ai donc écrit un petit outil que je vous présente ici dans le but d’avoir vos retours. Peut-être que je réinvente la roue… je n’ai pas cherché si un tel outil existait déjà.

Voici le script :

#!/usr/bin/env bash

# Store and deploy Unix file permissions.
# Write on stdout and read on stdin.
#
# File format :
# user\0group\0PERM\0FILENAME
# user\0group\0PERM\0FILENAME
# …
# …

# Help message
help() {
echo "
Usage: $0 <list|store|deploy|keep|recover|cmd> [DIR]
"
}

# Show help and exit if no action specified.
test -z $1 && help && exit 1

# Set directory from second argument. Current directory if not specified.
DIR=$2; test -z "$DIR" && DIR='.'

# store : write file permisions on stdout using the null character as field separator
function store() {

    find "$1" -printf "%u\0%g\0%m\0%h/%f\n"
}

# deploy : read file permissions from stdin and apply them
function deploy() {

    cd "$1"
    awk 'BEGIN {FS="\0";} {print "chmod -v " $3 " \"" $4 "\"" | "sh"; print "chown -v " $1 ":" $2 " \"" $4 "\"" | "sh";}' -
    cd - 1>/dev/null
}

# Switch according to chosen action
case $1 in

    # store : write file permisions on stdout using the null character as field separator
    store|s)
        store "$DIR"
    ;;

    # deploy : read file permissions from stdin and apply them
    deploy|d)
        deploy "$DIR"
    ;;

    # cmd : read file permissions from stdin (separator = ^@) and write shell commands on stdout
    cmd|c)
        cd "$DIR"
        awk 'BEGIN {FS="\0"} {print "chmod -v " $3 " \"" $4 "\""; print "chown -v " $1 ":" $2 " \"" $4 "\"";}' -
        cd - 1>/dev/null
    ;;    

    # list : write file permissions on stdout using a tabulation as field separator
    list|l)
        find "$DIR" -printf "%u\t%g\t%m\t%h/%f\n"
    ;;  

    # keep : add a '.pkeep' file to the directory itself so it can be deployed with action 'recover'
    keep|k)
        store "${DIR}" > "${DIR}/.pkeep"
    ;;

    # recover : deploy permission from the '.pkeep' file
    recover|r)
        deploy "${DIR}" < "${DIR}/.pkeep"
    ;;

    # Show help and exit if unrecognized action   
    *)
        help && exit 1    
esac

exit 0

Il est hébergé ici. Voici la documentation :

PermKeeper

This script can be used to store owner, group and permissions of all files in a given directory to restore them later.

$ ./pkeep store /var/www/html > files

… install things from a developper archive …

then:

$ ./pkeep deploy < files

Of course, it wont set permissions on newly created files.

In addition of working with standard input and output, the 'keep' and 'recover' actions can be used to store/recover the permissions in/from a '.pkeep' file inside the directory itself.

$ ./pkeep keep /tmp
will create a /tmp/.pkeep file storing the permissions of /tmp

$ ./pkeep recover /tmp
will then re-apply them.

The 'list' action print out the permissions using tabulations for a human readable output (it still reads its input using the null character as separator).

$ .pkeep list < /tmp/.pkeep

  • # À tester

    Posté par . Évalué à 2.

    Je n’ai pas testé mais il doit être possible d’utiliser un fichier d’entrée contenant des wildcards, pour une vérification ou un forçage sur un ensembles de fichiers.

    • [^] # Re: À tester

      Posté par . Évalué à 2. Dernière modification le 30/10/16 à 22:44.

      Ajout d’une action 'udeploy', pour "unquoted deploy" qui fait comme 'deploy' mais sans mettre le nom de fichier entre guillemets pour les commandes chown et chmod, permettant ainsi d’utiliser des jokers pour appliquer des droits à un ensemble de fichier, exemple '/home/*/.ssh' pour appliquer les droits sur tous les répertoires '.ssh' de tous les répertoires de /home.

      Une limitation évidente et qu’il n’est alors pas possible d’avoir un autre caractère spécial dans le chemin, exemple /home/he llo/*/h.

  • # Erratum

    Posté par . Évalué à 2.

    La commande suivante est fausse :

    $ .pkeep list < /tmp/.pkeep
    

    L’action 'list' s’utilise en fait ainsi : ./pkeep list /path/to/directory

    Pour afficher « proprement » le contenu d’un fichier .pkeep j’ai ajouté l’action 'print'

    $ ./pkeep print < /tmp/.pkeep

  • # Relatif/absolu ?

    Posté par . Évalué à 2.

    Les chemins relatifs ou absolus sont-ils stockés ? Les permissions étendues (ACL) sont-elles prévues ?

    Une autre remarque :
    test -z $1
    ne fonctionnera pas correctement si le premier argument contient des espaces.
    En effet, $1 n'est pas protégé par des guillemets, il sera donc étendu en autant d'arguments que le shell peut le couper (éventuellement 0 si la chaine est vide), or test -z n'accepte au maximum qu'un argument supplémentaire :
    $ foo="bar baz"; test -z $foo
    bash: test: bar : opérateur binaire attendu

    (Vous pouvez en lire un peu plus sur cet article)

    Dans ce script, si le premier argument contient un espace, ce sera de toute façon attrapé par le case, mais cela évite que bash n'affiche une erreur que vous ne controlez pas.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.