Last active
February 20, 2020 15:04
-
-
Save QNimbus/8680cade5ac68e87f6308e4c72463a03 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
## | |
## Title: acl_util.sh | |
## Description: Shell script to recursively set ACE's on FreeNAS ZFS | |
## Author: B. van wetten | |
## Created date: 17-02-2020 | |
## Updated date: 20-02-2020 | |
## Version: 0.4.0 | |
## GitHub Gist: https://gist.github.com/QNimbus/8680cade5ac68e87f6308e4c72463a03 | |
## | |
## To do: - Bug fix: Problem with interpreting globbing | |
## Possible resolution is to disable globbing in the future as it's not essential | |
## To reproduce 'acl_util.sh -d -r -p /mnt/media' | |
## This results in '/usr/bin/find /mnt -type f -name media' & '/usr/bin/find /mnt -type d -name media' | |
## - Implement import/export function to easily save and restore existing ACL's | |
## | |
## Usage: acl_util.sh | |
# Shell utilities | |
ID=$(which id); [[ $? != 0 ]] && echo "Command 'id' not found" >&2 && exit 1 | |
ECHO=$(which echo); [[ $? != 0 ]] && echo "Command 'echo' not found" >&2 && exit 1 | |
FIND=$(which find); [[ $? != 0 ]] && echo "Command 'find' not found" >&2 && exit 1 | |
GREP=$(which grep); [[ $? != 0 ]] && echo "Command 'grep' not found" >&2 && exit 1 | |
CHGRP=$(which chgrp); [[ $? != 0 ]] && echo "Command 'chgrp' not found" >&2 && exit 1 | |
CHOWN=$(which chown); [[ $? != 0 ]] && echo "Command 'chown' not found" >&2 && exit 1 | |
CHMOD=$(which chmod); [[ $? != 0 ]] && echo "Command 'chmod' not found" >&2 && exit 1 | |
GETENT=$(which getent); [[ $? != 0 ]] && echo "Command 'getent' not found" >&2 && exit 1 | |
GETOPT=$(which getopt); [[ $? != 0 ]] && echo "Command 'getopt' not found" >&2 && exit 1 | |
SETFACL=$(which setfacl); [[ $? != 0 ]] && echo "Command 'setfacl' not found" >&2 && exit 1 | |
# Initialize variables | |
_ME=$(basename "${0}") | |
_PWD=$(pwd) | |
_SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")" | |
_WIDTH=${COLUMNS:-150} | |
_USERS=() | |
_GROUPS=() | |
# Option strings | |
SHORT=hvxugsptdraq | |
LONG=help,verbose,strip-executable,user,group,strip,path,template,dry-run,recursive,append,quiet | |
# Get command line arguments | |
if [[ "${#}" -gt 0 ]] | |
then | |
getopt -T > /dev/null | |
if [ $? -eq 4 ]; then | |
# GNU enhanced getopt is available | |
_ARGS=$(${GETOPT} --name "$_ME" --long ${LONG} --options ${SHORT} -- "$@") | |
else | |
# Original getopt is available (no long option names, no whitespace, no sorting) | |
_ARGS=$(${GETOPT} ${SHORT} "$@") | |
fi | |
if [ $? -ne 0 ]; then | |
echo "$_ME: usage error (use -h for help)" >&2 | |
exit 2 | |
fi | |
fi | |
############################################################################### | |
# ACL Templates # | |
############################################################################### | |
declare -A TEMPLATES_DIR | |
declare -A TEMPLATES_FILE | |
# Template names are case-insensitive (e.g. '-t HOME' is the same as '-t Home' or '-t home') | |
TEMPLATES_DIR=( | |
# 0755 | |
[DEFAULT_OWNER]="rwxpDdaARWcCos" | |
[DEFAULT_GROUP]="r-x---a-R-c---" | |
[DEFAULT_OTHER]="r-x---a-R-c---" | |
# 0777 - Similar to FreeNAS ACL preset 'OPEN' | |
[OPEN_OWNER]="rwxpDdaARWcCos" | |
[OPEN_GROUP]="rwxpDdaARWc---" | |
[OPEN_OTHER]="rwxpDdaARWc---" | |
# 0770 - Similar to FreeNAS ACL preset 'RESTRICTED' | |
[RESTRICTED_OWNER]="rwxpDdaARWcCos" | |
[RESTRICTED_GROUP]="rwxpDdaARWc---" | |
[RESTRICTED_OTHER]="------a-R-c---" | |
# 0700 - Similar to FreeNAS ACL preset 'HOME' | |
[HOME_OWNER]="rwxpDdaARWcCos" | |
[HOME_GROUP]="------a-R-c---" | |
[HOME_OTHER]="------a-R-c---" | |
# 0700 - Invisible to anyone but owner | |
[PRIVATE_OWNER]="rwxpDdaARWcCos" | |
[PRIVATE_GROUP]="--------------" | |
[PRIVATE_OTHER]="--------------" | |
# 0750 - Invisible to anyone but owner and group | |
[GROUP_PRIVATE_OWNER]="rwxpDdaARWcCos" | |
[GROUP_PRIVATE_GROUP]="r-x---a-R-c---" | |
[GROUP_PRIVATE_OTHER]="--------------" | |
) | |
TEMPLATES_FILE=( | |
# 0644 | |
[DEFAULT_OWNER]="rw-pDdaARWcCos" | |
[DEFAULT_GROUP]="r-----a-R-c---" | |
[DEFAULT_OTHER]="r-----a-R-c---" | |
# 0666 - Similar to FreeNAS ACL preset 'OPEN' | |
[OPEN_OWNER]="rw-pDdaARWcCos" | |
[OPEN_GROUP]="rw-pDdaARWc---" | |
[OPEN_OTHER]="rw-pDdaARWc---" | |
# 0660 - Similar to FreeNAS ACL preset 'RESTRICTED' | |
[RESTRICTED_OWNER]="rw-pDdaARWcCos" | |
[RESTRICTED_GROUP]="rw-pDdaARWc---" | |
[RESTRICTED_OTHER]="------a-R-c---" | |
# 0600 - Similar to FreeNAS ACL preset 'HOME' | |
[HOME_OWNER]="rw-pDdaARWcCos" | |
[HOME_GROUP]="------a-R-c---" | |
[HOME_OTHER]="------a-R-c---" | |
# 0600 - Invisible to anyone but owner | |
[PRIVATE_OWNER]="rw-pDdaARWcCos" | |
[PRIVATE_GROUP]="--------------" | |
[PRIVATE_OTHER]="--------------" | |
# 0640 - Invisible to anyone but owner and group | |
[GROUP_PRIVATE_OWNER]="rw-pDdaARWcCos" | |
[GROUP_PRIVATE_GROUP]="r-----a-R-c---" | |
[GROUP_PRIVATE_OTHER]="--------------" | |
) | |
############################################################################### | |
# Functions # | |
############################################################################### | |
# _print_usage() | |
# | |
# Usage: | |
# _print_usage | |
# | |
# Print the program usage information. | |
function _print_usage() { | |
cat <<HEREDOC | |
____ ____ _ _ _ ___ _ _ | |
|__| | | | | | | | | |
| | |___ |___ ___ |__| | | |___ | |
Usage: | |
${_ME} -h | |
${_ME} -a [-u user1 user2 user3] [-g group1 group2] | |
${_ME} [-d] [-r] [-p path] [-t template] | |
${_ME} [-d] [-s] [-p path] | |
${_ME} [-v] [-p path] [-t template] | |
Example: | |
${_ME} -d -r -u root -g wheel -t DEFAULT -p /root | |
${_ME} -d -r -a -u bas -g users -t HOME -p /mnt/media | |
Options: | |
-h Show this screen | |
-p Path to process (defaults to current directory) | |
File globs are possible when used with the '-r' flag e.g. | |
'/home/user/*.log' | |
'/etc/*.conf' | |
-a Append user(s)/group(s) to ACL instead of chown/chgp | |
-u user Change user ownership to user | |
Used with '-a' will append to ACL | |
-g group Change group ownership to group | |
Used with '-a' will append to ACL | |
-r Perform actions recursively | |
-x Strip file executable permissions | |
-s Strip all ACL's | |
-v Verbose output | |
-d Dry-run with verbose output | |
-q Quiet mode - no ouput or confirmation | |
-t Use ACL template (case-insensitive) | |
Available templates: | |
- default: Default ACL (0755 & 0644) | |
- open: Public ACL | |
- restricted: Restricted ACL user and group writable (0775 & 0664) | |
- home: Home ACL for use in home directories (0700 & 0600) | |
- private: Private ACL files are invisible to anyone but user | |
- group_private: Private ACL files are invisible to anyone but user & group | |
HEREDOC | |
exit ${1:-0} | |
} | |
# _set_variable() | |
# | |
# Usage: | |
# _set_variable variable value | |
# | |
# Sets the variable to a value if not already set. Otherwise exits with an error message | |
function _set_variable() { | |
local varname="$1" | |
shift | |
if [ -z "${!varname:-}" ]; then | |
eval "$varname=\"$@\"" | |
else | |
echo "Error: $varname already set" | |
usage | |
fi | |
} | |
# _user_exists() | |
# | |
# Usage: | |
# _user_exists <uid or username> | |
# | |
# Returns true or false depending on whether the user exists | |
function _user_exists() { | |
# Check if $_USER is set and exist | |
local _USER="${1:-}" | |
if [[ -n "${_USER}" ]] | |
then | |
${ID} -u "${_USER}" > /dev/null 2>&1 | |
return $? | |
else | |
# Return true | |
return 0 | |
fi | |
} | |
# _group_exists() | |
# | |
# Usage: | |
# _group_exists <gid or groupname> | |
# | |
# Returns true or false depending on whether the group exists | |
function _group_exists() { | |
# Check if $_GROUP is set and exist | |
local _GROUP="${1:-}" | |
if [[ -n "${_GROUP}" ]] | |
then | |
${GETENT} group "${_GROUP}" > /dev/null 2>&1 | |
return $? | |
else | |
# Return true | |
return 0 | |
fi | |
} | |
# _parse_commandline_arguments() | |
# | |
# Usage: | |
# _parse_commandline_arguments | |
# | |
# Parses and validates commandline arguments and populates appropriate variables. | |
function _parse_commandline_arguments() { | |
while true; | |
do | |
case "${1:-}" in | |
-x|--strip-executable) | |
_STRIP_EXECUTABLE=true | |
_SET_EXECUTABLE=false | |
shift 1 | |
;; | |
-r|--recursive) | |
_RECURSIVE=true | |
shift 1 | |
;; | |
-a|--append) | |
_APPEND_USER_GROUP=true | |
shift 1 | |
;; | |
-s|--strip) | |
_STRIP=true | |
shift 1 | |
;; | |
-d|--dry-run) | |
_DRYRUN=true | |
_VERBOSE=true | |
shift 1 | |
;; | |
-v|--verbose) | |
_VERBOSE=true | |
shift 1 | |
;; | |
-q|--quiet) | |
_QUIET=true | |
shift 1 | |
;; | |
-p|--path) | |
[[ -z "${2}" || "${2}" == *[[:space:]]* || "${2}" == -* ]] && { echo "$_ME: $1 needs a value" >&2; _print_usage 1; } | |
_set_variable _PATH "${2}" | |
_PFLAG=true | |
shift 2 | |
;; | |
-t|--template) | |
[[ -z "${2}" || "${2}" == *[[:space:]]* || "${2}" == -* ]] && { echo "$_ME: $1 needs a value" >&2; _print_usage 1; } | |
_set_variable _TEMPLATE $(${ECHO} "${2^^}") | |
shift 2 | |
;; | |
-u|--user) | |
[[ -z "${2}" || "${2}" == *[[:space:]]* || "${2}" == -* ]] && { echo "$_ME: $1 needs a value" >&2; _print_usage 1; } | |
while | |
if ! [[ -z "${2}" || "${2}" =~ ^-{1,2} ]] | |
then | |
_USERS+=("${2}") | |
shift 1 | |
fi | |
! [[ -z "${2}" || "${2}" =~ ^-{1,2} ]] | |
do | |
: | |
done | |
shift 1 | |
;; | |
-g|--group) | |
[[ -z "${2}" || "${2}" == *[[:space:]]* || "${2}" == -* ]] && { echo "$_ME: $1 needs a value" >&2; _print_usage 1; } | |
while | |
if ! [[ -z "${2}" || "${2}" =~ ^-{1,2} ]] | |
then | |
_GROUPS+=("${2}") | |
shift 1 | |
fi | |
! [[ -z "${2}" || "${2}" =~ ^-{1,2} ]] | |
do | |
: | |
done | |
shift 1 | |
;; | |
\?) echo "$_ME: Unknown option -$1" >&2; | |
exit 1 | |
;; | |
:) echo "$_ME: -$1 needs a value" >&2; | |
exit 1 | |
;; | |
*) shift | |
break | |
;; | |
esac | |
done | |
# Set defaults: | |
_DRYRUN="${_DRYRUN:-false}" | |
_PFLAG="${_PFLAG:-false}" | |
_STRIP="${_STRIP:-false}" | |
_QUIET="${_QUIET:-false}" | |
_VERBOSE="${_VERBOSE:-false}" | |
_RECURSIVE="${_RECURSIVE:-false}" | |
_SET_EXECUTABLE="${_SET_EXECUTABLE:-true}" | |
_STRIP_EXECUTABLE="${_STRIP_EXECUTABLE:-false}" | |
_APPEND_USER_GROUP="${_APPEND_USER_GROUP:-false}" | |
} | |
# _validate_parameters() | |
# | |
# Usage: | |
# _validate_parameters | |
# | |
# Performs several checks on the supplied command line arguments | |
function _validate_parameters() { | |
# User/Group validation | |
if [[ "${_APPEND_USER_GROUP}" = true ]] | |
then | |
# Verify that at least one user or one group was passed as parameter when appending | |
[[ ${#_USERS[@]} -lt 1 && ${#_GROUPS[@]} -lt 1 ]] && echo -e "$_ME: Append user and/or group requires at least 1 user or 1 group parameter\n" && _print_usage 1; | |
# Clear variables since we won't be using them | |
unset _USER | |
unset _GROUP | |
# Verifiy that user(s) is/are valid | |
for user in "${_USERS[@]}" | |
do | |
! _user_exists ${user} && echo -e "$_ME: User '${user}' does not exist\n" && _print_usage 1; | |
done | |
# Verifiy that group(s) is/are valid | |
for group in "${_GROUPS[@]}" | |
do | |
! _group_exists ${group} && echo -e "$_ME: Group '${group}' does not exist\n" && _print_usage 1; | |
done | |
else | |
# Verify that at most one user or one group was passed as parameter when changing ownership | |
[[ ${#_USERS[@]} -gt 1 ]] && echo -e "$_ME: -u expects exactly 1 argument. Maybe you want to run this command recursively with the '-a' option?'\n" && _print_usage 1; | |
[[ ${#_GROUPS[@]} -gt 1 ]] && echo -e "$_ME: -g expects exactly 1 argument. Maybe you want to run this command recursively with the '-a' option?'\n" && _print_usage 1; | |
# Set _USER & _GROUP variables | |
_USER=${_USERS[0]:-} | |
_GROUP=${_GROUPS[0]:-} | |
# Verify that user is valid | |
! _user_exists ${_USER} && echo -e "$_ME: User '${_USER}' does not exist\n" && _print_usage 1; | |
# Verify that group is valid | |
! _group_exists ${_GROUP} && echo -e "$_ME: Group '${_GROUP}' does not exist\n" && _print_usage 1; | |
fi | |
# Ensure that '-v' and '-q' flag are not both set | |
[[ "${_VERBOSE}" == true && "${_QUIET}" == true ]] && echo -e "$_ME: Cannot set '-v' and '-q' flag simultaneously\n" && _print_usage 1; | |
# Check if path was given as argument otherwise set path to current working directory | |
_PATH=${_PATH:-"${_PWD}"} | |
# Check if path is a glob | |
if [[ "${_PATH}" ]] | |
then | |
# Get last part (glob) into seperate variable | |
_GLOB="${_PATH##*/}" | |
if [[ ! -z "${_PATH##*/*}" ]] | |
then | |
# If path does not contain a '/' character assume current folder | |
_PATH="." | |
else | |
# Otherwise get everything up to and including the last '/' character | |
_PATH="${_PATH%/*}" | |
fi | |
fi | |
# Remove trailing slash unless root | |
case "${_PATH}" in | |
*[!/]*/) | |
_PATH=${_PATH%"${_PATH##*[!/]}"} | |
;; | |
*[/]) | |
_PATH="/" | |
;; | |
esac | |
# Check if path argument exists as a directory | |
[[ ! -d "${_PATH}" || ! -w "${_PATH}" ]] && echo -e "Error: '${_PATH}${_GLOB:+/$_GLOB}' does not exist or current user does not have writing permissions\n" && _print_usage 1; | |
# If path is relative, make it absolute | |
[[ ! "${_PATH}" =~ ^\/ ]] && _PATH=$(cd "${_PWD}/${_PATH}"; pwd) | |
# Check if templates existst | |
TEMPLATE="${_TEMPLATE:-DEFAULT}" | |
[[ -z ${TEMPLATES_DIR["${TEMPLATE}_OWNER"]+_} ]] && echo -e "Directory template '${TEMPLATE}_OWNER' not found\n" && _print_usage 1; | |
[[ -z ${TEMPLATES_DIR["${TEMPLATE}_GROUP"]+_} ]] && echo -e "Directory template '${TEMPLATE}_GROUP' not found\n" && _print_usage 1; | |
[[ -z ${TEMPLATES_DIR["${TEMPLATE}_OTHER"]+_} ]] && echo -e "Directory template '${TEMPLATE}_OTHER' not found\n" && _print_usage 1; | |
[[ -z ${TEMPLATES_FILE["${TEMPLATE}_OWNER"]+_} ]] && echo -e "File template '${TEMPLATE}_OWNER' not found\n" && _print_usage 1; | |
[[ -z ${TEMPLATES_FILE["${TEMPLATE}_GROUP"]+_} ]] && echo -e "File template '${TEMPLATE}_GROUP' not found\n" && _print_usage 1; | |
[[ -z ${TEMPLATES_FILE["${TEMPLATE}_OTHER"]+_} ]] && echo -e "File template '${TEMPLATE}_OTHER' not found\n" && _print_usage 1; | |
# If we're doing a dry-run replace the commands | |
if [[ "${_DRYRUN}" == true ]] | |
then | |
SETFACL="${ECHO} |--> ${SETFACL}" | |
CHOWN="${ECHO} |--> ${CHOWN}" | |
CHMOD="${ECHO} |--> ${CHMOD}" | |
CHGRP="${ECHO} |--> ${CHGRP}" | |
fi | |
} | |
# _is_file_executable() | |
# | |
# Usage: | |
# _is_file_executable [user|group|other|any] <file> | |
# | |
# Check if a file is readable and executable | |
function _is_file_executable() { | |
local is_executable | |
case "${1:-}" in | |
owner|user|u) | |
is_executable="$(${FIND} -L "${2:-}" -type f -perm -u=rx 2> /dev/null)" | |
[[ $? -eq 0 ]] || exit 1 | |
# Return false | |
[[ -z "${is_executable}" ]] && return 1 | |
# Return true | |
return 0 | |
;; | |
group|g) | |
is_executable="$(${FIND} -L "${2:-}" -type f -perm -g=rx 2> /dev/null)" | |
[[ $? -eq 0 ]] || exit 1 | |
# Return false | |
[[ -z "${is_executable}" ]] && return 1 | |
# Return true | |
return 0 | |
;; | |
other|o) | |
is_executable="$(${FIND} -L "${2:-}" -type f -perm -o=rx 2> /dev/null)" | |
[[ $? -eq 0 ]] || exit 1 | |
# Return false | |
[[ -z "${is_executable}" ]] && return 1 | |
# Return true | |
return 0 | |
;; | |
any|a|*) | |
is_executable="$(${FIND} -L "${2:-}" -type f \( -perm -u=rx -o -perm -g=rx -o -perm -o=rx \) 2> /dev/null)" | |
[[ $? -eq 0 ]] || exit 1 | |
# Return false | |
[[ -z "${is_executable}" ]] && return 1 | |
# Return true | |
return 0 | |
;; | |
esac | |
} | |
# _print_separator() | |
# | |
# Usage: | |
# _print_separator | |
# | |
# Prints a line separator to the output | |
function _print_separator() { | |
local _row | |
printf -v _row "%${_WIDTH}s"; echo ${_row// /-} | |
} | |
# _progress_bar() | |
# | |
# Usage: | |
# _progress_bar <current_value> <total_value> [label] | |
# | |
# Prints a progress bar | |
# e.g. Progress : [##############--------------------------] 35% | |
function _progress_bar() { | |
local _progress | |
local _done | |
local _left | |
local _label=${3:-Progress:} | |
let _progress=(${1}*100/${2}*100)/100 | |
let _done=(${_progress}*4)/10 | |
let _left=40-$_done | |
local _fill=$(printf "%${_done}s") | |
local _empty=$(printf "%${_left}s") | |
# Clear line | |
printf "\r%${_WIDTH}s" | |
# Print progress bar | |
printf "\r%-25s [${_fill// /#}${_empty// /-}] ${_progress}%%" "${_label}" | |
} | |
############################################################################### | |
# Main # | |
############################################################################### | |
# _main() | |
# | |
# Usage: | |
# _main [<options>] [<arguments>] | |
# | |
# Description: | |
# Entry point for the program, handling basic option parsing and dispatching. | |
_main() { | |
# Avoid complex option parsing when only one program option is expected. | |
if [[ "${@:-}" =~ -h|--help ]] | |
then | |
_print_usage | |
else | |
_parse_commandline_arguments "$@" | |
_validate_parameters | |
if [[ "${_RECURSIVE}" == true ]] | |
then | |
_FILES=$(${FIND} "${_PATH}" -type f -name "${_GLOB:-*}") | |
_FOLDERS=$(${FIND} "${_PATH}" -type d -name "${_GLOB:-*}") | |
else | |
[[ -f "${_PATH}/${_GLOB:-}" ]] && _FILES="${_PATH}/${_GLOB:-}" | |
[[ -d "${_PATH}/${_GLOB:-}" ]] && _FOLDERS="${_PATH}/${_GLOB:-}" | |
fi | |
_FILE_COUNT=$([[ ! -z "${_FILES}" ]] && printf '%s\0' "${_FILES}" | grep -c '^') | |
_FOLDER_COUNT=$([[ ! -z "${_FOLDERS}" ]] && printf '%s\0' "${_FOLDERS}" | grep -c '^') | |
_FILE_COUNT=${_FILE_COUNT:-0} | |
_FOLDER_COUNT=${_FOLDER_COUNT:-0} | |
# If no directories of files matched display message and exit | |
if [[ "${_FILE_COUNT}" -eq 0 && "${_FOLDER_COUNT}" -eq 0 ]] | |
then | |
[[ "${_RECURSIVE}" == true && "${_VERBOSE}" == true ]] && echo -e "No directories or files matched '${_PATH}/${_GLOB}'\n" | |
[[ "${_RECURSIVE}" == false && "${_VERBOSE}" == true ]] && echo -e "File or directory '${_PATH}/${_GLOB}' not found. Maybe you want to run this command recursively with the '-r' option?'\n" | |
exitcode=1 | |
return | |
fi | |
# No explicit path or glob was given - display prompt for confirmation | |
if [[ "${_DRYRUN}" == false && "${_QUIET}" == false ]] | |
then | |
[[ "${_PFLAG:-false}" == false ]] && echo -e "*** Warning ***: No explicit path provided. Double-check path below!\n" | |
echo -e "Effective path: ${_PATH}/${_GLOB}" | |
echo -e "${_FILE_COUNT} files will be modified" | |
echo -e "${_FOLDER_COUNT} folders will be modified\n" | |
[[ "${_RECURSIVE}" == true ]] && read -p "This will recursively modify ACL's in '${_PATH}/${_GLOB}' directory. Do you want to continue? " -n 1 -r REPLY | |
[[ "${_RECURSIVE}" == false ]] && read -p "This will modify the ACL of '${_PATH}/${_GLOB}'. Do you want to continue? " -n 1 -r REPLY | |
echo | |
if [[ ! "${REPLY:-n}" =~ ^[Yy]$ ]] | |
then | |
exitcode=0 | |
return | |
fi | |
fi | |
# Output line separator unless in quiet mode | |
[[ "${_QUIET}" == false ]] && _print_separator | |
# Counter for ACL rule index | |
counter=0 | |
[[ ! -z "${_FOLDERS}" ]] && while read FOLDER | |
do | |
# Output progress bar | |
if [[ "${_VERBOSE}" == false && "${_QUIET}" == false ]] | |
then | |
_progress_bar $((counter=counter+1)) "${_FOLDER_COUNT}" "Processing folders:" | |
fi | |
# Double check we are not following symlinked folders | |
if [[ -L "${FOLDER}" ]] | |
then | |
[[ "${_VERBOSE}" == true ]] && printf "%-32s %s\n" "Skipping directory (no symlinks)" "${FOLDER}" | |
continue | |
fi | |
[[ "${_VERBOSE}" == true ]] && printf "%-32s %s\n" "Processing directory" "${FOLDER}" | |
# Clear custom ACE's | |
${SETFACL} -bn "${FOLDER}" | |
# Only apply ACL's if '-s' option was not set | |
if [[ "${_STRIP}" == false ]] | |
then | |
# Counter for ACL rule index | |
rule_index=-1 | |
# Remove existing entries | |
while [[ "${_DRYRUN}" == false ]] && ${SETFACL} -x 0 "${FOLDER}" 2> /dev/null | |
do | |
continue | |
done | |
# Owner permissions | |
${SETFACL} -a $((rule_index=rule_index+1)) owner@:"${TEMPLATES_DIR["${TEMPLATE}_OWNER"]}":d:allow "${FOLDER}" | |
${SETFACL} -a $((rule_index=rule_index+1)) owner@:"${TEMPLATES_FILE["${TEMPLATE}_OWNER"]}":f:allow "${FOLDER}" | |
# Do we need to add users? | |
if [[ "${_APPEND_USER_GROUP}" == true ]] | |
then | |
# Loop through users that need to be appended | |
for user in "${_USERS[@]}" | |
do | |
${SETFACL} -a $((rule_index=rule_index+1)) user:"${user}":"${TEMPLATES_DIR["${TEMPLATE}_OWNER"]}":d:allow "${FOLDER}" | |
${SETFACL} -a $((rule_index=rule_index+1)) user:"${user}":"${TEMPLATES_FILE["${TEMPLATE}_OWNER"]}":f:allow "${FOLDER}" | |
done | |
fi | |
# Group permissions | |
${SETFACL} -a $((rule_index=rule_index+1)) group@:"${TEMPLATES_DIR["${TEMPLATE}_GROUP"]}":d:allow "${FOLDER}" | |
${SETFACL} -a $((rule_index=rule_index+1)) group@:"${TEMPLATES_FILE["${TEMPLATE}_GROUP"]}":f:allow "${FOLDER}" | |
# Do we need to add groups? | |
if [[ "${_APPEND_USER_GROUP}" == true ]] | |
then | |
# Loop through groups that need to be appended | |
for group in "${_GROUPS[@]}" | |
do | |
${SETFACL} -a $((rule_index=rule_index+1)) group:"${group}":"${TEMPLATES_DIR["${TEMPLATE}_GROUP"]}":d:allow "${FOLDER}" | |
${SETFACL} -a $((rule_index=rule_index+1)) group:"${group}":"${TEMPLATES_FILE["${TEMPLATE}_GROUP"]}":f:allow "${FOLDER}" | |
done | |
fi | |
# Other permissions | |
${SETFACL} -a $((rule_index=rule_index+1)) everyone@:"${TEMPLATES_DIR["${TEMPLATE}_OTHER"]}":d:allow "${FOLDER}" | |
${SETFACL} -a $((rule_index=rule_index+1)) everyone@:"${TEMPLATES_FILE["${TEMPLATE}_OTHER"]}":f:allow "${FOLDER}" | |
# Cleanup remaining entries | |
while [[ "${_DRYRUN}" == false ]] && ${SETFACL} -x $((rule_index=rule_index+1)) "${FOLDER}" 2> /dev/null | |
do | |
continue | |
done | |
fi | |
# Change ownership if user and/or group parameter were used | |
[[ -n "${_USER_ID}" ]] && ${CHOWN} "${_USER_ID}" "${FOLDER}" | |
[[ -n "${_GROUP_ID}" ]] && ${CHGRP} "${_GROUP_ID}" "${FOLDER}" | |
done <<< "${_FOLDERS}" | |
# Continue on next line | |
[[ ! -z "${_FOLDERS}" && "${_QUIET}" == false ]] && printf "\n" | |
# Counter for ACL rule index | |
counter=0 | |
[[ ! -z "${_FILES}" ]] && while read FILE | |
do | |
# Output progress bar | |
if [[ "${_VERBOSE}" == false && "${_QUIET}" == false ]] | |
then | |
_progress_bar $((counter=counter+1)) "${_FILE_COUNT}" "Processing files:" | |
fi | |
# Double check we are not following symlinked files | |
if [[ -L "${FILE}" ]] | |
then | |
[[ "${_VERBOSE}" == true ]] && printf "%-32s %s\n" "Skipping file (no symlinks)" "${FILE}" | |
continue | |
fi | |
# Do not modify this scripts ACL's by accident | |
# True if $FILE and $_SELF refer to the same device and inode numbers. | |
if [[ "${FILE}" -ef "${_SELF:-}" ]] | |
then | |
[[ "${_VERBOSE}" == true ]] && printf "%-32s %s\n" "Skipping file (self)" "${FILE}" | |
continue | |
fi | |
[[ "${_VERBOSE}" == true ]] && printf "%-32s %s\n" "Processing file" "${FILE}" | |
# Reset values | |
_OWNER_EXECUTABLE=false | |
_GROUP_EXECUTABLE=false | |
_OTHER_EXECUTABLE=false | |
_ANY_EXECUTABLE=false | |
# Logic checks whether file is originaly executable (i.e. has 'r+x' attribute set for respective permission group) | |
# If the '-x' flag is used then file is considered executable if ANY permission group had 'r+x' set | |
[[ "${_STRIP_EXECUTABLE}" == false ]] && _is_file_executable owner "${FILE}" && _OWNER_EXECUTABLE=true | |
[[ "${_STRIP_EXECUTABLE}" == false ]] && _is_file_executable group "${FILE}" && _GROUP_EXECUTABLE=true | |
[[ "${_STRIP_EXECUTABLE}" == false ]] && _is_file_executable other "${FILE}" && _OTHER_EXECUTABLE=true | |
[[ "${_SET_EXECUTABLE}" == true ]] && _is_file_executable any "${FILE}" && _ANY_EXECUTABLE=true | |
# Clear custom ACE's | |
${SETFACL} -bn "${FILE}" | |
# Only apply ACL's if '-s' option was not set | |
if [[ "${_STRIP}" == false ]] | |
then | |
# Counter for ACL rule index | |
rule_index=-1 | |
# Remove existing entries | |
while [[ "${_DRYRUN}" == false ]] && ${SETFACL} -x 0 "${FILE}" 2> /dev/null | |
do | |
continue | |
done | |
# Owner permissions | |
if [[ "${_ANY_EXECUTABLE}" == true || "${_OWNER_EXECUTABLE}" == true ]] && [[ ${TEMPLATES_FILE["${TEMPLATE}_OWNER"]:0:1} =~ ^r$ ]] | |
then | |
${SETFACL} -a $((rule_index=rule_index+1)) owner@:"${TEMPLATES_FILE["${TEMPLATE}_OWNER"]:0:2}x${TEMPLATES_FILE["${TEMPLATE}_OWNER"]:4}"::allow "${FILE}" | |
# Do we need to add users? | |
if [[ "${_APPEND_USER_GROUP}" == true ]] | |
then | |
# Loop through users that need to be appended | |
for user in "${_USERS[@]}" | |
do | |
${SETFACL} -a $((rule_index=rule_index+1)) user:"${user}":"${TEMPLATES_FILE["${TEMPLATE}_OWNER"]:0:2}x${TEMPLATES_FILE["${TEMPLATE}_OWNER"]:4}"::allow "${FILE}" | |
done | |
fi | |
else | |
${SETFACL} -a $((rule_index=rule_index+1)) owner@:"${TEMPLATES_FILE["${TEMPLATE}_OWNER"]}"::allow "${FILE}" | |
# Do we need to add users? | |
if [[ "${_APPEND_USER_GROUP}" == true ]] | |
then | |
# Loop through users that need to be appended | |
for user in "${_USERS[@]}" | |
do | |
${SETFACL} -a $((rule_index=rule_index+1)) user:"${user}":"${TEMPLATES_FILE["${TEMPLATE}_OWNER"]}"::allow "${FILE}" | |
done | |
fi | |
fi | |
# Group permissions | |
if [[ "${_ANY_EXECUTABLE}" == true || "${_GROUP_EXECUTABLE}" == true ]] && [[ ${TEMPLATES_FILE["${TEMPLATE}_GROUP"]:0:1} =~ ^r$ ]] | |
then | |
${SETFACL} -a $((rule_index=rule_index+1)) group@:"${TEMPLATES_FILE["${TEMPLATE}_GROUP"]:0:2}x${TEMPLATES_FILE["${TEMPLATE}_GROUP"]:4}"::allow "${FILE}" | |
# Do we need to add groups? | |
if [[ "${_APPEND_USER_GROUP}" == true ]] | |
then | |
# Loop through groups that need to be appended | |
for group in "${_GROUPS[@]}" | |
do | |
${SETFACL} -a $((rule_index=rule_index+1)) group:"${group}":"${TEMPLATES_FILE["${TEMPLATE}_GROUP"]:0:2}x${TEMPLATES_FILE["${TEMPLATE}_GROUP"]:4}"::allow "${FILE}" | |
done | |
fi | |
else | |
${SETFACL} -a $((rule_index=rule_index+1)) group@:"${TEMPLATES_FILE["${TEMPLATE}_GROUP"]}"::allow "${FILE}" | |
# Do we need to add groups? | |
if [[ "${_APPEND_USER_GROUP}" == true ]] | |
then | |
# Loop through groups that need to be appended | |
for group in "${_GROUPS[@]}" | |
do | |
${SETFACL} -a $((rule_index=rule_index+1)) group:"${group}":"${TEMPLATES_FILE["${TEMPLATE}_GROUP"]}"::allow "${FILE}" | |
done | |
fi | |
fi | |
# Other permissions | |
if [[ "${_ANY_EXECUTABLE}" == true || "${_OTHER_EXECUTABLE}" == true ]] && [[ ${TEMPLATES_FILE["${TEMPLATE}_OTHER"]:0:1} =~ ^r$ ]] | |
then | |
${SETFACL} -a $((rule_index=rule_index+1)) everyone@:"${TEMPLATES_FILE["${TEMPLATE}_OTHER"]:0:2}x${TEMPLATES_FILE["${TEMPLATE}_OTHER"]:4}"::allow "${FILE}" | |
else | |
${SETFACL} -a $((rule_index=rule_index+1)) everyone@:"${TEMPLATES_FILE["${TEMPLATE}_OTHER"]}"::allow "${FILE}" | |
fi | |
# Cleanup remaining entries | |
while [[ "${_DRYRUN}" == false ]] && ${SETFACL} -x $((rule_index=rule_index+1)) "${FILE}" 2> /dev/null | |
do | |
continue | |
done | |
fi | |
# Change ownership if user and/or group parameter were used | |
[[ -n "${_USER_ID}" ]] && ${CHOWN} "${_USER_ID}" "${FILE}" | |
[[ -n "${_GROUP_ID}" ]] && ${CHGRP} "${_GROUP_ID}" "${FILE}" | |
done <<< "${_FILES}" | |
# Continue on next line | |
[[ ! -z "${_FILES}" && "${_QUIET}" == false ]] && printf "\n" | |
# Output summary in verbose mode | |
[[ "${_QUIET}" == false ]] && _print_separator | |
[[ "${_QUIET}" == false ]] && printf "%-22s %11s\n" "Files processed:" "${_FILE_COUNT}" | |
[[ "${_QUIET}" == false ]] && printf "%-22s %11s\n\n" "Directories processed:" "${_FOLDER_COUNT}" | |
fi | |
} | |
# Call `_main` after everything has been defined. | |
_main "$@" | |
exit ${exitcode:-0} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example commands for personal use
The public SMB share is configured like this:
The dev SMB share is configured like this:
The media SMB share is configured like this: