群晖权限导出脚本

功能

通过该脚本可以将当前共享文件夹中设置的权限导出到文件,在需要时可以通过该文件将权限恢复到之前的状态。
由于该脚本仅匹配用户名,所以当设备重装或更换设备后只要用户名一致就可以恢复对应用户的权限。

导出脚本

synoacl_export.sh

#!/bin/bash

LOG_FILE="acl_recover.sh"

shell_quote() {
  printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\"'\"'/g")"
}

if [ -z "$1" ]; then
  echo '错误:请提供要备份的目录路径 ./synoacl_export.sh "/volume1/share"' >&2
  exit 1
fi

: > "$LOG_FILE"
echo '#!/bin/bash' >> "$LOG_FILE"
echo >> "$LOG_FILE"

find "$1" -maxdepth 3 -type d \
  -not -regex '.*\(@eaDir\|#recycle\|#snapshot\).*' |
while IFS= read -r dir; do

  ACL_OUTPUT=$(synoacltool -get "$dir" 2>/dev/null) || {
    echo "跳过无法读取 ACL 的目录: $dir" >&2
    continue
  }

  q_dir=$(shell_quote "$dir")

  # 是否继承
  if ! echo "$ACL_OUTPUT" | grep -q "Archive: .*is_inherit"; then
    echo "_dir=$q_dir" >> "$LOG_FILE"
    echo 'synoacltool -del-archive "$_dir" is_inherit' >> "$LOG_FILE"
    echo >> "$LOG_FILE"
  fi

  echo "$ACL_OUTPUT" | grep -E '^\s*\[[0-9]+\]' |
  while IFS= read -r line; do
    level=$(echo "$line" | grep -o 'level:[0-9]' | cut -d: -f2)

    [ "$level" -ne 0 ] && continue

    acl_entry=$(echo "$line" |
      awk -F'\\[|\\]|\\(|\\)' '{print $3}' |
      sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//'
    )

    case "$acl_entry" in
      group::*) continue ;;
      user::*)  continue ;;
    esac

    # 修正 everyone / owner
    acl_entry=${acl_entry/#everyone::/everyone:everyone:}
    acl_entry=${acl_entry/#owner::/owner:owner:}

    [ -z "$acl_entry" ] && continue

    q_acl=$(shell_quote "$acl_entry")

    echo "_dir=$q_dir" >> "$LOG_FILE"
    echo "synoacltool -add \"\$_dir\" $q_acl" >> "$LOG_FILE"
    echo >> "$LOG_FILE"
  done
done

chmod +x "$LOG_FILE"

检查脚本

acl_check.sh

#!/bin/bash

failed_items="failed_items.txt"
: > "$failed_items"

if [ -z "$1" ]; then
  echo '用法: ./acl_check.sh acl_recover.sh' >&2
  exit 1
fi

ACL_FILE="$1"

declare -A seen_users
declare -A seen_groups
declare -A seen_paths

# ---- 1. 提取 user / group(来自 synoacltool -add 行)----
while IFS= read -r line; do
  # 只关心 -add
  [[ $line =~ synoacltool[[:space:]]+-add ]] || continue

  # 提取 ACL 字符串(单引号内容)
  acl=$(echo "$line" | sed -n "s/.*'\(.*\)'.*/\1/p")

  case "$acl" in
    user:*)
      user="${acl#user:}"
      user="${user%%:*}"
      [[ -n $user && -z ${seen_users[$user]} ]] && seen_users[$user]=1
      ;;
    group:*)
      group="${acl#group:}"
      group="${group%%:*}"
      [[ -n $group && -z ${seen_groups[$group]} ]] && seen_groups[$group]=1
      ;;
  esac
done < "$ACL_FILE"

# ---- 2. 校验用户 ----
for user in "${!seen_users[@]}"; do
  if ! synouser --get "$user" >/dev/null 2>&1; then
    echo "用户不存在: $user" >> "$failed_items"
  fi
done

# ---- 3. 校验用户组 ----
for group in "${!seen_groups[@]}"; do
  if ! synogroup --get "$group" >/dev/null 2>&1; then
    echo "用户组不存在: $group" >> "$failed_items"
  fi
done

# ---- 4. 校验路径(安全 source,仅解析 _dir)----
(
  set -e
  unset synoacltool
  synoacltool() { :; }   # 空函数,防止误执行

  while IFS= read -r line; do
    # 只处理 _dir= 行
    [[ $line == _dir=* ]] || continue

    eval "$line"         # 只展开 _dir
    [[ -n "$_dir" ]] || continue

    if [[ -z ${seen_paths[$_dir]} ]]; then
      seen_paths["$_dir"]=1
      if [ ! -e "$_dir" ]; then
        echo "路径不存在: $_dir" >> "$failed_items"
      fi
    fi
  done < "$ACL_FILE"
)

echo "检查完成,失败项列表保存在 $failed_items"

如果有错误则将错误内容存储在failed_items.txt:

共享文件夹测试

运行脚本获取权限

例如共享文件夹是/volume1/acltest

运行:
./synoacl_export.sh "/volume1/acltest"

查看是否生成acl_revover.sh

清除共享文件夹权限

执行恢复

检查是否有用户、用户组、路径缺失
./acl_check.sh acl_recover.sh

如果没有failed_items.txt则对脚本进行赋权
chmod 755 acl_recover.sh

执行恢复并输出日志到recover.log
./acl_recover.sh > recover.log

检查是否有错误
cat recover.log | grep "synoacltool.c"

检查权限是否恢复

从snapshot快照执行恢备份

配置让有权限的快照可见

查找包含权限的快照文件夹

运行脚本获取权限

需要修改脚本中的过滤快照规则,将|#snapshot\去除:

find "$1" -maxdepth 3 -type d \
  -not -regex '.*\(@eaDir\|#recycle\|).*' |
while IFS= read -r dir; do

例如共享文件夹的快照路径是/volume1/acltest/#snapshot/GMT+08-2025.05.20-16.24.56

运行:
./synoacl_export.sh "/volume1/acltest/#snapshot/GMT+08-2025.05.20-16.24.56"

查看是否生成acl_revover.sh

清除共享文件夹权限

执行恢复

处理恢复脚本中不需要的快照路径
例如不需要的路径为/#snapshot/GMT+08-2025.05.20-16.24.56
sed -i "s|/#snapshot/GMT+08-2025.05.20-16.24.56||g" acl_recover.sh

检查是否有用户、用户组、路径缺失
./acl_check.sh acl_recover.sh

如果没有failed_items.txt则对脚本进行赋权
chmod 755 acl_recover.sh

执行恢复并输出日志到recover.log
./acl_recover.sh > recover.log

检查是否有错误
cat recover.log | grep "synoacltool.c"

检查权限是否恢复,并将快照可见关闭。