HEX
Server: Apache/2.4.63 (Unix)
System: Linux TOMS-220NAS 4.4.302+ #86009 SMP Wed Nov 26 18:19:17 CST 2025 x86_64
User: flavio87 (1026)
PHP: 8.3.27
Disabled: NONE
Upload Files
File: //var/packages/SMBService/scripts/util.sh
#!/bin/bash

# source guard
[[ "${_SOURCE_GUARD_SYNOSAMBA_UTIL}" != "yes" ]] || return
_SOURCE_GUARD_SYNOSAMBA_UTIL="yes"

###################################
# Normalize Package Variables
###################################
: "${SYNOPKG_PKGNAME:="SMBService"}"
: "${SYNOPKG_PKGDIR:=$(/bin/realpath "/var/packages/${SYNOPKG_PKGNAME}")}"
: "${SYNOPKG_PKGDEST:=$(/bin/realpath "${SYNOPKG_PKGDIR}/target")}"

SYNOSETKEYVALUE="/usr/syno/bin/synosetkeyvalue"
SYNOGETKEYVALUE="/usr/syno/bin/synogetkeyvalue"
SYNOSYSTEMCTL="/usr/syno/bin/synosystemctl"

SYNOSMBTOOL="${SYNOPKG_PKGDEST}/tools/synosmbtool"

###################################
# Help Functions
###################################
exit_with_ui_error()
{
    (>&2 echo "$@")
    if [[ -n "${SYNOPKG_TEMP_LOGFILE}" ]]; then
        echo "$@" > "${SYNOPKG_TEMP_LOGFILE}"
    fi
    exit 1
}

exit_with_error()
{
    (>&2 echo "$@")
    exit 1
}

logger_err()
{
    /bin/logger -s -t SMBService -p user.err "$@"
}

logger_info()
{
    /bin/logger -s -t SMBService -p user.info "$@"
}

is_smb_option_enabled()
{
    local -r opt_key=${1? "lack of smb option"}
    local -r TESTPARM="${SYNOPKG_PKGDEST}/usr/bin/testparm"
    local -r smbinfo_conf="/etc/samba/smbinfo.conf"
    local -r enable_status=$(${TESTPARM} -s --parameter-name="${opt_key}" 2>/dev/null)

    if [[ "${enable_status,,}" == "yes" ]]; then
        return 0
    fi

    return 1
}

##################################
# Manual install/uninstall
##
# - pam: TODO
##################################

samba_pam()
{
    local -r -A install_map=(
        ["${SYNOPKG_PKGDEST}/resource/pam-config/samba.pam"]="/etc/pam.d/samba"
        ["${SYNOPKG_PKGDEST}/usr/lib/security/pam_winbind.so"]="/usr/lib/security/pam_winbind.so"
        ["${SYNOPKG_PKGDEST}/usr/etc/security/pam_winbind.conf"]="/etc/security/pam_winbind.conf"
    )

    case ${1,,} in
        install)
            for key in "${!install_map[@]}"; do
                if [ -e "${install_map[$key]}" ]; then
                    continue
                fi
                echo "install ${key} --> ${install_map[$key]}"
                /bin/install -D "${key}" "${install_map[$key]}"
            done
            ;;

        uninstall)
            for key in "${!install_map[@]}"; do
                /bin/rm -f "${install_map[$key]}"
            done
            ;;
        *)
            exit_with_ui_error "Interanl Error: invalid argument '${1}' for ${FUNCNAME[0]} function"
            ;;
    esac
}

samba_nss()
{
    local -r -A install_map=(
        ["${SYNOPKG_PKGDEST}/usr/lib/libnss_wins.so.2"]="/usr/lib/libnss_wins.so.2"
        ["${SYNOPKG_PKGDEST}/usr/lib/libnss_winbind.so.2"]="/usr/lib/libnss_winbind.so.2"
    )
    local -r -A link_map=(
        ["/usr/lib/libnss_wins.so.2"]="/usr/lib/libnss_wins.so"
        ["/usr/lib/libnss_winbind.so.2"]="/usr/lib/libnss_winbind.so"
    )
    case ${1,,} in
        install)
            for key in "${!install_map[@]}"; do
                if [ -e "${install_map[$key]}" ]; then
                    continue
                fi
                echo "install ${key} --> ${install_map[$key]}"
                /bin/install -D "${key}" "${install_map[$key]}"
            done
            for key in "${!link_map[@]}"; do
                if [ -e "${link_map[$key]}" ]; then
                    continue
                fi
                echo "link ${key} --> ${link_map[$key]}"
                /bin/ln -s "${key}" "${link_map[$key]}"
            done
            ;;

        uninstall)
            for key in "${!install_map[@]}"; do
                /bin/rm -f "${install_map[$key]}"
            done
            for key in "${!link_map[@]}"; do
                /bin/rm -f "${link_map[$key]}"
            done
            ;;
        *)
            exit_with_ui_error "Interanl Error: invalid argument '${1}' for ${FUNCNAME[0]} function"
            ;;
    esac
}

samba_services()
{
    local -r backup_file="${SYNOPKG_PKGDIR}/etc/service-status.conf"
    local -r services=(
        pkg-DirectoryServerForWindowsDomain
        pkg-synosamba-nmbd
        pkg-synosamba-smbd
        pkg-synosamba-winbindd
        pkg-synosamba-wstransfer-genconf
    )

    logger_info "samba_service: ${1,,}, status: ${SYNOPKG_PKG_STATUS}"

    case ${1,,} in
        backup_status)
            /bin/rm -f "${backup_file}"

            for service in ${services[*]}; do
                if enable_status=$(${SYNOSYSTEMCTL} get-enable-status "${service}"); then
                    ${SYNOSETKEYVALUE} "${backup_file}" "${service}" "${enable_status}"
                fi
            done

            ${SYNOSYSTEMCTL} disable "${services[@]}"
            ${SYNOSYSTEMCTL} stop "${services[@]}"
            ;;
        restore_status)
            local enabled_services=()

            if [[ -f ${backup_file} ]]; then
                for service in ${services[*]}; do
                    if [[ "$(${SYNOGETKEYVALUE} "${backup_file}" "${service}")" = 'enabled' ]]; then
                        enabled_services+=("${service}")
                    fi
                done
            else
                enabled_services+=("pkg-synosamba-nmbd")
                enabled_services+=("pkg-synosamba-wstransfer-genconf")
                enabled_services+=("pkg-synosamba-smbd")
                logger_info "no previous status of samba services, start default enabled services."
            fi

            logger_info "enabled_service: ${enabled_services[*]}"

            if [[ ${#enabled_services[@]} -ne 0 ]]; then
                ${SYNOSYSTEMCTL} enable "${enabled_services[@]}"
                ${SYNOSYSTEMCTL} start "${enabled_services[@]}"
            fi
            ;;
        *)
            ;;
    esac
}

ctdb_samba_services()
{
    local -r backup_file="${SYNOPKG_PKGDIR}/etc/service-status.conf"
    local -r all_services=(
        pkg-DirectoryServerForWindowsDomain
        pkg-synosamba-nmbd
        pkg-synosamba-wstransfer-genconf
        pkg-synosamba-ctdbd
        pkg-synosamba-smbd
        pkg-synosamba-winbindd
    )
    local -r ctdb_services=(
        pkg-synosamba-smbd
        pkg-synosamba-winbindd
    )

    logger_info "samba_service: ${1,,}, status: ${SYNOPKG_PKG_STATUS}"

    case ${1,,} in
        backup_status)
            /bin/rm -f "${backup_file}"

            for service in ${all_services[*]}; do
                if enable_status=$(${SYNOSYSTEMCTL} get-enable-status "${service}"); then
                    ${SYNOSETKEYVALUE} "${backup_file}" "${service}" "${enable_status}"
                fi
            done

            ${SYNOSYSTEMCTL} disable "${all_services[@]}"
            ${SYNOSYSTEMCTL} stop "${all_services[@]}"
            ;;
        restore_status)
            local enabled_services=()

            if [[ -f ${backup_file} ]]; then
                for service in ${all_services[*]}; do
                    if [[ "$(${SYNOGETKEYVALUE} "${backup_file}" "${service}")" = 'enabled' ]]; then
                        enabled_services+=("${service}")
                    fi
                done
            else
                enabled_services+=("pkg-synosamba-nmbd")
                enabled_services+=("pkg-synosamba-wstransfer-genconf")
                enabled_services+=("pkg-synosamba-ctdbd")
                enabled_services+=("pkg-synosamba-smbd")

                logger_info "no previous status of samba services, start default enabled services."
            fi

            logger_info "enabled_service: ${enabled_services[*]}"

            if [[ ${#enabled_services[@]} -ne 0 ]]; then
                ${SYNOSYSTEMCTL} enable "${enabled_services[@]}"
                # remove ctdb_services, ctdb_services will startup via ctdb
                for service in ${ctdb_services[*]}; do
                    enabled_services=("${enabled_services[@]/$service}")
                done
                ${SYNOSYSTEMCTL} start "${enabled_services[@]}"
            fi
            ;;
        *)
            ;;
    esac
}

is_path_in_btrfs()
{
    local -r target_path=${1? "lack of target_path"}
    local -r dev=$(df "${target_path}" --output=source | tail -1)

    local -r fstype=$(blkid -ovalue -sTYPE "${dev}")

    if [ "${fstype,,}" != "btrfs" ]; then
        return 1
    fi

    return 0
}

is_path_CoW_on()
{
    local -r target_path=${1? "lack of target_path"}

    if /bin/lsattr -ld "${target_path}" | grep -q "No_COW"; then
        return 1
    fi

    return 0
}

disable_CoW_on_path_if_btrfs()
{
    local -r target_path=${1? "lack of target_path"}

    if ! is_path_in_btrfs "${target_path}"; then
        return
    fi

    if ! is_path_CoW_on "${target_path}"; then
        return
    fi

    if ! /bin/chattr +C "${target_path}"; then
        echo "Unable to disable CoW on [${target_path}]"
        return
    fi

    echo "Disable CoW on [${target_path}] successfully"
}

prepare_samba_folders()
{
    # XXX: should be rewritten to systemd options if our systemd is upgraded.
    # see the comment in pkg-synosamba-nmbd.service
    local -r state_directory="/var/lib/samba"
    local -r cache_directory="/var/cache/samba"
    local -r logs_directory="/var/log/samba"
    local -r configuration_directory="/etc/samba"
    local -r -a directory_list=(
        "${state_directory}"
        "${cache_directory}"
        "${logs_directory}"
        "${configuration_directory}"
    )

    /bin/install -d "${directory_list[@]}"

    disable_CoW_on_path_if_btrfs "${cache_directory}"
}

is_new_installation()
{
    /bin/diff "/etc/VERSION" "/etc.defaults/VERSION" > /dev/null 2>&1
}

_setkeyvalue_noquote()
{
    local -r file="${1?"file should be specified"}"
    local -r key="${2?"key should be specified"}"
    local -r value="${3?"value should be specified"}"
    local -r tmpfile=$(/bin/mktemp "${file}.XXXXXXXX")

    /bin/rm -f "${tmpfile}"

    while IFS= read -r line; do
        local trimkey
        local trimline
        trimkey=$(echo "$key" | tr -d '[:space:]')
        trimline=$(echo "$line" | tr -d '[:space:]')
        if ! [[ ${trimline,,} =~ ^${trimkey,,}= ]]; then
            printf '%s\n' "${line}" >> "${tmpfile}"
            continue
        fi
    done < "${file}"

    if [[ -n "${value}" ]]; then
        printf '\t%s = %s\n' "${key}" "${value}" >> "${tmpfile}"
    fi

    /bin/mv "${tmpfile}" "${file}"
}

_smb_set_directory_leasing_home_only_conf()
{
    local -r conf_file="${1? "lack of conf_file"}"

    _setkeyvalue_noquote "${conf_file}" \
        "syno directory lease grant" "yes"

    _setkeyvalue_noquote "${conf_file}" \
        "syno directory lease grant restrict home only" "yes"
}

smb_enable_directory_leasing_home_only()
{
    local -r smbinfo_conf="/etc/samba/smbinfo.conf"

    init_smbinfo_conf
    _smb_set_directory_leasing_home_only_conf "${smbinfo_conf}"
}

init_smbinfo_conf()
{
    local -r smbinfo_conf="/etc/samba/smbinfo.conf"
    local -r synoinfo_conf="/etc/synoinfo.conf"
    local -r smbinfo_default_conf="${SYNOPKG_PKGDIR}/conf/smbinfo_default.conf"
    if [[ ! -e "${smbinfo_conf}" ]]; then
        echo "install ${smbinfo_default_conf}"
        /bin/install -D -m 644 "${smbinfo_default_conf}" "${smbinfo_conf}"

        # if this DS is newly install in DSM7.0, default on catia
        if is_new_installation; then
            _setkeyvalue_noquote "${smbinfo_conf}" "syno catia" "yes"
            _setkeyvalue_noquote "${smbinfo_conf}" "follow symlinks" "yes"
            _setkeyvalue_noquote "${smbinfo_conf}" "smb2 leases" "yes"
            _smb_set_directory_leasing_home_only_conf "${smbinfo_conf}"
        fi
    fi

    # move "oplock_disabled" key from synoinfo.conf to smbinfo.conf
    # and change name to "oplocks"
    oplock_disabled=$(${SYNOGETKEYVALUE} "${synoinfo_conf}" "oplock_disabled")
    if [[ -n "${oplock_disabled}" ]]; then
        oplocks="yes"
        if [[ "yes" == "${oplock_disabled,,}" ]]; then
            oplocks="no"
        fi
        ${SYNOSETKEYVALUE} "${synoinfo_conf}" "oplock_disabled" ""
        _setkeyvalue_noquote "${smbinfo_conf}" "oplocks" "${oplocks}"
    fi
}

smbinfo_conf_check() {
    # we will check empty value and remove it
    # this can prevent include /etc/samba/smbinfo.conf which has key but no value

    local -r smbinfo_conf="/etc/samba/smbinfo.conf"
    local -r smbinfo_conf_temp="/etc/samba/smbinfo.conf.tmp"

    # clean tmp file
    /bin/rm -f "${smbinfo_conf_temp}"

    while IFS=$' \t\n' read -r line; do
        if [[ -z "${line}" ]] || [[ "[" == "${line:0:1}" ]] || [[ ";" == "${line:0:1}" ]] || [[ "#" == "${line:0:1}" ]]; then
            echo "$line" >> "${smbinfo_conf_temp}"
            continue
        fi

        # remove deprecated key
        local key=${line%=*}
        case "$key" in
        "advanced_domain_option")
            # "advanced_domain_option" deprecate from DSM7.0
            continue;;
        "register nic")
            # "register nic" is removed from DSM7.0
            continue;;
        "btrfs clone")
            # "btrfs clone" deprecate from DSM#98173
            continue;;
        esac

        if [[ ${line} != *"=" ]]; then
            echo -e "\t${line}" >> "${smbinfo_conf_temp}"
        fi
    done < "${smbinfo_conf}"

    if ! /bin/diff "${smbinfo_conf_temp}" "${smbinfo_conf}" > /dev/null 2>&1; then
        /bin/mv "${smbinfo_conf_temp}" "${smbinfo_conf}"
    else
        /bin/rm -f "${smbinfo_conf_temp}"
    fi
}

remove_old_config()
{
    local -r old_config=(
        "/etc/logrotate.d/synowstransferd"
        "/etc/syslog-ng/patterndb.d/synowstransferd.conf"
        "/etc/syslog-ng/patterndb.d/include/not2msg/synowstransfer.conf"
        "/usr/syno/etc/services.d/synowstrasnfer.sc"
        "/usr/syno/etc/synowstransfer_nginx.conf"
    )
    /bin/rm -f "${old_config[@]}"
    ${SYNOSYSTEMCTL} reload syslog-ng
}

smb_transfer_log_check()
{
    #init when smbxferlog in /etc/synoinfo.conf is not exist
    smb_transfer_log=$(/usr/syno/bin/synogetkeyvalue "/etc/synoinfo.conf" "smbxferlog")
    if [[ -z "${smb_transfer_log}" ]]; then
        /bin/cat << XFERLOG_LEVEL >> "/usr/syno/etc/xferloglevel.conf"

[WinFileService]
    create=0
    write=0
    delete=1
    rename=1
    read=0
    move=0
    permission change=0
XFERLOG_LEVEL
        /usr/syno/bin/synosetkeyvalue "/etc/synoinfo.conf" "smbxferlog" "yes"
    fi
}

has_smb_connection()
{
    share_name=$1
    /var/packages/SMBService/target/usr/bin/smbstatus --shares-syno | awk -F '\t' -v share="${share_name}" '{
        if(tolower($5) == tolower(share)) {
            _has_connection = 1
            exit
        }
    } END {
        if (_has_connection) {
            exit 0
        } else {
            exit 1
        }
    }'
}

smb_checkconf()
{
    if ! ${SYNOSMBTOOL} --checkconf; then
        logger_info "smb check conf failed"
    fi
}

remove_option_from_file()
{
    local -r target_file=${1?"lack of target file"}
    local -r target_option=${2?"lack of target option"}

    if ! grep -qi "${target_option}" "${target_file}"; then
        return
    fi

    echo "remove '${target_option}' from ${target_file}"
    sed -i "/^\s*${target_option}\s*=/Id" "${target_file}"
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    cmd="$1"
    arg_type=$(type -t "$cmd")
    shift
    if [[ "${arg_type}" != "function" ]] || [[ "${cmd}" == _* ]]; then
        logger_info "$0: Invalid argument: $1"
        exit 1
    fi
    ${cmd} "$@"
fi

remove_deprecated_settings_from_smbconf()
{
    local -r smbconf="/etc/samba/smb.conf"
    local -r deprecated_keys=(
        "client ntlmv2 auth"
        "syno sync dctime"
    )

    # `client ntlmv2 auth = no` was set when DSM joined to a domain in the NT4
    # way. since DSM7.0, ntlmv1 is disabled by default and we don't support
    # joining NT4 domain anymore. this setting is considered to be remove to
    # prevent access denied due to no acceptable NTLM version between SMB
    # clients and the SMB server.
    for KEY in "${deprecated_keys[@]}"
    do
        if grep -i "${KEY}" "${smbconf}"; then
            logger_info "remove '${KEY}' from ${smbconf}"
            _setkeyvalue_noquote "${smbconf}" "${KEY}" ""
        fi
    done
}

remove_smbd_volatile_db()
{
    local -r db_path=(
        "/var/cache/samba/autoblock_tolerance.tdb"
    )

    for path in "${db_path[@]}"; do
        /bin/rm -f "${path}"
    done
}

is_HCI()
{
    local -r synoinfo_conf="/etc/synoinfo.conf"
    local -r hci_key="support_hyper_converged"
    if [[ "$(${SYNOGETKEYVALUE} "${synoinfo_conf}" "${hci_key}")" == "yes" ]]; then
        return 0
    fi
    return 1
}

is_cluster_controller() {
    local role
    role=$(/usr/syno/bin/hcm-sentinel-tool get-role)
    if [[ "controller" == "$role" ]]; then
        return 0
    fi
    return 1
}

# vim:set ts=4 sw=4 sts=4 expandtab: