Synology hack (version 2)

Remembering the previous goal: having a NAS with more capacity (CPU, RAM and storage).

Current status:

  • NAS has been solid without any issues
  • A UPS was attached to the NAS and is the NAS that is controlling the UPS over USB.
  • Version 7.1 of Synology was keeping behind.
  • RAM continues to work
  • NVME has been working as some VM/Container storage for testing as main resources are running on a dedicated Proxmox server.

Actions:

  • Update Synology software to the latest version DSM 7.2.2-72806 Update 2
  • Continue to work with NVME storage as storage and not cache.

What happenned:

As expected, the NVME was no longer recognized as normal storage. Tried to run the previous setup but it didn’t work.

As the storage was not being actively used for nothing important, I started searching for a new way to solve the issue and this was what I’ve found.

  • Script1: https://github.com/007revad/Synology_M2_volume
#!/usr/bin/env bash
#-----------------------------------------------------------------------------------
# Create volume on M.2 drive(s) on Synology models that don't have a GUI option
#
# Github: https://github.com/007revad/Synology_M2_volume
# Script verified at https://www.shellcheck.net/
# Tested on DSM 7.2 and 7.2.1
#
# To run in a shell (replace /volume1/scripts/ with path to script):
# sudo /volume1/scripts/create_m2_volume.sh
#
# Resources:
# https://academy.pointtosource.com/synology/synology-ds920-nvme-m2-ssd-volume/amp/
# https://www.reddit.com/r/synology/comments/pwrch3/how_to_create_a_usable_poolvolume_to_use_as/
#
# Over-Provisioning unnecessary on modern SSDs (since ~2014)
# https://easylinuxtipsproject.blogspot.com/p/ssd.html#ID16.2
#
# Use synostgpool instead of synopartition, mdadm and lvm
# https://www.reddit.com/r/synology/comments/17vn96n/how_to_trigger_the_online_assemble_prompt_without/
#-----------------------------------------------------------------------------------

# TODO
# Better detection if DSM is using the drive.
# Maybe add logging.
# Add option to repair damaged array? DSM can probably handle this.

# DONE
# v2 and later are for DSM 7 only.
#   For DSM 6 use v1 without the auto update option.
# Now shows "M.2 Drive #" the same as storage manager.
# Now uses synostgpool command which allows the following: (Thanks to Severe_Pea_2128 on reddit)
#   Now supports JBOD, SHR, SHR2 and RAID F1.
#   Added choice of multi-volume or single-volume storage pool. Multi-volume allows overprovisioning.
#   Added option to skip drive check.
#   No longer need to reboot after running the script.
#   No longer need to do an online assemble.
# Enables RAID F1 if not enabled and RAID F1 selected.
# Removed drive check progress as it was not possible with synostgpool.
# Removed dry run mode as it was not possible with synostgpool.
# Removed support for SATA M.2 drives.

# m2list_assoc contains associative array of [M.2 Drive #]=nvme#n#
# m2list array contains list of "M.2 Drive #"
# mdisk array contains list of selected nvme#n#


scriptver="v2.0.30"
script=Synology_M2_volume
repo="007revad/Synology_M2_volume"
scriptname=syno_create_m2_volume

# Check BASH variable is bash
if [ ! "$(basename "$BASH")" = bash ]; then
    echo "This is a bash script. Do not run it with $(basename "$BASH")"
    printf \\a
    exit 1
fi

# Check script is running on a Synology NAS
if ! /usr/bin/uname -a | grep -i synology >/dev/null; then
    echo "This script is NOT running on a Synology NAS!"
    echo "Copy the script to a folder on the Synology"
    echo "and run it from there."
    exit 1
fi

# Shell Colors
#Black='\e[0;30m'   # ${Black}
Red='\e[0;31m'      # ${Red}
#Green='\e[0;32m'   # ${Green}
Yellow='\e[0;33m'  # ${Yellow}
#Blue='\e[0;34m'    # ${Blue}
#Purple='\e[0;35m'  # ${Purple}
Cyan='\e[0;36m'     # ${Cyan}
#White='\e[0;37m'   # ${White}
Error='\e[41m'      # ${Error}
Off='\e[0m'         # ${Off}

ding(){ 
    printf \\a
}

usage(){ 
    cat <<EOF
$script $scriptver - by 007revad

Usage: $(basename "$0") [options]

Options:
  -a, --all        List all M.2 drives even if detected as active
  -s, --steps      Show the steps to do after running this script
  -h, --help       Show this help message
  -v, --version    Show the script version

EOF
    exit 0
}


scriptversion(){ 
    cat <<EOF
$script $scriptver - by 007revad

See https://github.com/$repo
EOF
    exit 0
}


declare -A m2list_assoc=( )

selectdisk(){ 
    if [[ ${#m2list[@]} -gt "0" ]]; then

        # Only show Done choice when required number of drives selected
        if [[ $single != "yes" ]] && [[ "${#mdisk[@]}" -ge "$mindisk" ]]; then
            showDone=" Done"
        else
            showDone=""
        fi

        select nvmes in "${m2list[@]}"$showDone; do
            case "$nvmes" in
                Done)
                    Done="yes"
                    selected_disk=""
                    break
                    ;;
                Quit)
                    exit
                    ;;
                nvme*)
#                    if [[ " ${m2list[*]} "  =~ " ${nvmes} " ]]; then
                        selected_disk="$nvmes"
                        break
#                    else
#                        echo -e "${Red}Invalid answer!${Off} Try again." >&2
#                        selected_disk=""
#                    fi
                    ;;
                "M.2 Drive "*)
                    selected_disk="$nvmes"
                    break
                    ;;
                *)
                    echo -e "${Red}Invalid answer!${Off} Try again." >&2
                    #echo -e "There is no menu item $?)" >&2
                    selected_disk=""
                    ;;
            esac
        done

        if [[ $Done != "yes" ]] && [[ $selected_disk ]]; then
            #mdisk+=("$selected_disk")
            mdisk+=("${m2list_assoc["$selected_disk"]}")
            # Remove selected drive from list of selectable drives
            remelement "$selected_disk"
            # Keep track of many drives user selected
            selected="$((selected +1))"
            echo -e "You selected ${Cyan}$selected_disk${Off}" >&2

            #echo "Drives selected: $selected" >&2  # debug
        fi
        echo
    else
        Done="yes"
    fi
}


showsteps(){ 
    major=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION major)
    if [[ $major -gt "6" ]]; then
        if [[ $pooltype == "single" ]]; then
            echo -e "\n${Cyan}When storage manager has finished checking the drive(s):${Off}"
        else
            echo -e "\n${Cyan}When storage manager has finished creating the storage pool:${Off}"
        fi
        cat <<EOF
  1. Create the volume as you normally would:
       Select the new Storage Pool > Create > Create Volume
  2. Optionally enable TRIM:
       Storage Pool > ... > Settings > SSD TRIM
EOF
        echo -e "\n${Error}Important${Off}" >&2
        cat <<EOF
If you later upgrade DSM and your M.2 drives are shown as unsupported
and the storage pool is shown as missing, and online assemble fails,
you should run the Synology HDD db script:
EOF
        echo -e "${Cyan}https://github.com/007revad/Synology_HDD_db${Off}\n" >&2
    fi
    #return
}


# Save options used
args=("$@")


# Check for flags with getopt
# shellcheck disable=SC2034
if options="$(getopt -o abcdefghijklmnopqrstuvwxyz0123456789 -a \
    -l all,steps,help,version,log,debug -- "$@")"; then
    eval set -- "$options"
    while true; do
        case "${1,,}" in
            -a|--all)           # List all M.2 drives even if detected as active
                all=yes
                ;;
            -s|--steps)         # Show steps remaining after running script
                showsteps
                exit
                ;;
            -h|--help)          # Show usage options
                usage
                ;;
            -v|--version)       # Show script version
                scriptversion
                ;;
            -l|--log)            # Log
                log=yes
                ;;
            -d|--debug)          # Show and log debug info
                debug=yes
                ;;
            --)
                shift
                break
                ;;
            *)                  # Show usage options
                echo -e "Invalid option '$1'\n"
                usage "$1"
                ;;
        esac
        shift
    done
else
    echo
    usage
fi


if [[ $debug == "yes" ]]; then
    set -x
    export PS4='`[[ $? == 0 ]] || echo "\e[1;31;40m($?)\e[m\n "`:.$LINENO:'
fi


# Check script is running as root
if [[ $( whoami ) != "root" ]]; then
    ding
    echo -e "${Error}ERROR${Off} This script must be run as sudo or root!"
    exit 1
fi

# Show script version
#echo -e "$script $scriptver\ngithub.com/$repo\n"
echo "$script $scriptver"

# Get DSM major and minor versions
dsm=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION majorversion)
dsminor=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION minorversion)
# shellcheck disable=SC2034
if [[ $dsm -gt "6" ]] && [[ $dsminor -gt "1" ]]; then
    dsm72="yes"
fi
if [[ $dsm -gt "6" ]] && [[ $dsminor -gt "0" ]]; then
    dsm71="yes"
fi

# Get NAS model
model=$(cat /proc/sys/kernel/syno_hw_version)
hwrevision=$(cat /proc/sys/kernel/syno_hw_revision)
if [[ $hwrevision =~ r[0-9] ]]; then showhwrev=" $hwrevision"; fi

# Get DSM full version
productversion=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION productversion)
buildphase=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION buildphase)
buildnumber=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION buildnumber)
smallfixnumber=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION smallfixnumber)

# Show DSM full version and model
if [[ $buildphase == GM ]]; then buildphase=""; fi
if [[ $smallfixnumber -gt "0" ]]; then smallfix="-$smallfixnumber"; fi
echo -e "${model}$showhwrev DSM $productversion-$buildnumber$smallfix $buildphase\n"


# Get StorageManager version
storagemgrver=$(/usr/syno/bin/synopkg version StorageManager)
# Show StorageManager version
if [[ $storagemgrver ]]; then echo -e "StorageManager $storagemgrver\n"; fi


# Show options used
echo -e "Using options: ${args[*]}\n"


#------------------------------------------------------------------------------
# Check latest release with GitHub API

# Get latest release info
# Curl timeout options:
# https://unix.stackexchange.com/questions/94604/does-curl-have-a-timeout
release=$(curl --silent -m 10 --connect-timeout 5 \
    "https://api.github.com/repos/$repo/releases/latest")

# Release version
tag=$(echo "$release" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
shorttag="${tag:1}"

# Get script location
# https://stackoverflow.com/questions/59895/
source=${BASH_SOURCE[0]}
while [ -L "$source" ]; do # Resolve $source until the file is no longer a symlink
    scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd )
    source=$(readlink "$source")
    # If $source was a relative symlink, we need to resolve it
    # relative to the path where the symlink file was located
    [[ $source != /* ]] && source=$scriptpath/$source
done
scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd )
scriptfile=$( basename -- "$source" )
echo -e "Running from: ${scriptpath}/$scriptfile\n"

#echo "Script location: $scriptpath"  # debug
#echo "Source: $source"               # debug
#echo "Script filename: $scriptfile"  # debug

#echo "tag: $tag"              # debug
#echo "scriptver: $scriptver"  # debug


# Warn if script located on M.2 drive
scriptvol=$(echo "$scriptpath" | cut -d"/" -f2)
vg=$(lvdisplay | grep /volume_"${scriptvol#volume}" | cut -d"/" -f3)
md=$(pvdisplay | grep -B 1 -E '[ ]'"$vg" | grep /dev/ | cut -d"/" -f3)
if cat /proc/mdstat | grep "$md" | grep nvme >/dev/null; then
    echo -e "${Yellow}WARNING${Off} Don't store this script on an NVMe volume!"
fi


cleanup_tmp(){ 
    # Delete downloaded .tar.gz file
    if [[ -f "/tmp/$script-$shorttag.tar.gz" ]]; then
        if ! rm "/tmp/$script-$shorttag.tar.gz"; then
            echo -e "${Error}ERROR${Off} Failed to delete"\
                "downloaded /tmp/$script-$shorttag.tar.gz!" >&2
        fi
    fi

    # Delete extracted tmp files
    if [[ -d "/tmp/$script-$shorttag" ]]; then
        if ! rm -r "/tmp/$script-$shorttag"; then
            echo -e "${Error}ERROR${Off} Failed to delete"\
                "downloaded /tmp/$script-$shorttag!" >&2
        fi
    fi
}


if ! printf "%s\n%s\n" "$tag" "$scriptver" |
        sort --check=quiet --version-sort >/dev/null ; then
    echo -e "\n${Cyan}There is a newer version of this script available.${Off}"
    echo -e "Current version: ${scriptver}\nLatest version:  $tag"
    scriptdl="$scriptpath/$script-$shorttag"
    if [[ -f ${scriptdl}.tar.gz ]] || [[ -f ${scriptdl}.zip ]]; then
        # They have the latest version tar.gz downloaded but are using older version
        echo "You have the latest version downloaded but are using an older version"
        sleep 10
    elif [[ -d $scriptdl ]]; then
        # They have the latest version extracted but are using older version
        echo "You have the latest version extracted but are using an older version"
        sleep 10
    else
        echo -e "${Cyan}Do you want to download $tag now?${Off} [y/n]"
        read -r -t 30 reply
        if [[ ${reply,,} == "y" ]]; then
            # Delete previously downloaded .tar.gz file and extracted tmp files
            cleanup_tmp

            if cd /tmp; then
                url="https://github.com/$repo/archive/refs/tags/$tag.tar.gz"
                if ! curl -JLO -m 30 --connect-timeout 5 "$url"; then
                    echo -e "${Error}ERROR${Off} Failed to download"\
                        "$script-$shorttag.tar.gz!"
                else
                    if [[ -f /tmp/$script-$shorttag.tar.gz ]]; then
                        # Extract tar file to /tmp/<script-name>
                        if ! tar -xf "/tmp/$script-$shorttag.tar.gz" -C "/tmp"; then
                            echo -e "${Error}ERROR${Off} Failed to"\
                                "extract $script-$shorttag.tar.gz!"
                        else
                            # Set script sh files as executable
                            if ! chmod a+x "/tmp/$script-$shorttag/"*.sh ; then
                                permerr=1
                                echo -e "${Error}ERROR${Off} Failed to set executable permissions"
                            fi

                            # Copy new script sh file to script location
                            if ! cp -p "/tmp/$script-$shorttag/${scriptname}.sh" "${scriptpath}/${scriptfile}";
                            then
                                copyerr=1
                                echo -e "${Error}ERROR${Off} Failed to copy"\
                                    "$script-$shorttag sh file(s) to:\n $scriptpath/${scriptfile}"
                            fi

                            # Copy new CHANGES.txt file to script location (if script on a volume)
                            if [[ $scriptpath =~ /volume* ]]; then
                                # Set permissions on CHANGES.txt
                                if ! chmod 664 "/tmp/$script-$shorttag/CHANGES.txt"; then
                                    permerr=1
                                    echo -e "${Error}ERROR${Off} Failed to set permissions on:"
                                    echo "$scriptpath/CHANGES.txt"
                                fi

                                # Copy new CHANGES.txt file to script location
                                if ! cp -p "/tmp/$script-$shorttag/CHANGES.txt"\
                                    "${scriptpath}/${scriptname}_CHANGES.txt";
                                then
                                    copyerr=1
                                    echo -e "${Error}ERROR${Off} Failed to copy"\
                                        "$script-$shorttag/CHANGES.txt to:\n $scriptpath"
                                else
                                    changestxt=" and changes.txt"
                                fi
                            fi

                            # Delete downloaded tmp files
                            cleanup_tmp

                            # Notify of success (if there were no errors)
                            if [[ $copyerr != 1 ]] && [[ $permerr != 1 ]]; then
                                echo -e "\n$tag ${scriptfile}$changestxt downloaded to: ${scriptpath}\n"

                                # Reload script
                                printf -- '-%.0s' {1..79}; echo  # print 79 -
                                exec "${scriptpath}/$scriptfile" "${args[@]}"
                            fi
                        fi
                    else
                        echo -e "${Error}ERROR${Off}"\
                            "/tmp/$script-$shorttag.tar.gz not found!"
                        #ls /tmp | grep "$script"  # debug
                    fi
                fi
                cd "$scriptpath" || echo -e "${Error}ERROR${Off} Failed to cd to script location!"
            else
                echo -e "${Error}ERROR${Off} Failed to cd to /tmp!"
            fi
        fi
    fi
fi


#--------------------------------------------------------------------
# Check there's no active resync

if grep resync /proc/mdstat >/dev/null ; then
    ding
    echo "The Synology is currently doing a RAID resync or data scrub!"
    exit
fi


#--------------------------------------------------------------------
# Get list of M.2 drives

getm2info(){ 
    local nvme
    local vendor
    local pcislot
    local cardslot
    nvmemodel=$(cat "$1/device/model")
    nvmemodel=$(printf "%s" "$nvmemodel" | xargs)  # trim leading/trailing space

    vendor=$(synonvme --vendor-get "/dev/$(basename -- "${1}")")
    vendor=" $(printf "%s" "$vendor" | cut -d":" -f2 | xargs)"
    if nvme=$(synonvme --get-location "/dev/$(basename -- "${1}")"); then
        if [[ ! $nvme =~ "PCI Slot: 0" ]]; then
            pcislot="$(echo "$nvme" | cut -d"," -f2 | awk '{print $NF}')-"
        fi
        cardslot="$(echo "$nvme" | awk '{print $NF}')"
    else
        nvme_cmd_failed="yes"
        pcislot="$(basename -- "${1}")"
        cardslot=""
    fi

    #echo "$2 M.2 $(basename -- "${1}") is $nvmemodel" >&2
    echo "$(basename -- "${1}") M.2 Drive $pcislot$cardslot -$vendor $nvmemodel" >&2
    dev="$(basename -- "${1}")"

    #echo "/dev/${dev}" >&2  # debug

    if [[ $all != "yes" ]]; then
        # Skip listing M.2 drives detected as active
        if grep -E "active.*${dev}" /proc/mdstat >/dev/null ; then
            echo -e "${Cyan}Skipping drive as it is being used by DSM${Off}" >&2
            echo "" >&2
            #active="yes"
            return
        fi
    fi

    if [[ -e /dev/${dev}p1 ]] && [[ -e /dev/${dev}p2 ]] &&\
            [[ -e /dev/${dev}p3 ]]; then
        echo -e "${Yellow}WARNING ${Cyan}Drive has a volume partition${Off}" >&2
        haspartitons="yes"
    elif [[ ! -e /dev/${dev}p3 ]] && [[ ! -e /dev/${dev}p2 ]] &&\
            [[ -e /dev/${dev}p1 ]]; then
        echo -e "${Yellow}WARNING ${Cyan}Drive has a cache partition${Off}" >&2
        haspartitons="yes"
    elif [[ ! -e /dev/${dev}p3 ]] && [[ ! -e /dev/${dev}p2 ]] &&\
            [[ ! -e /dev/${dev}p1 ]]; then
        echo "No existing partitions on drive" >&2
    fi
    if [[ $nvme_cmd_failed == "yes" ]]; then
        m2list+=("${dev}")
        m2list_assoc["$dev"]="$dev"
    else
        m2list+=("M.2 Drive $pcislot$cardslot")
        m2list_assoc["M.2 Drive $pcislot$cardslot"]="$dev"
    fi
    echo "" >&2
}

for d in /sys/block/*; do
    case "$(basename -- "${d}")" in
        nvme*)  # M.2 NVMe drives
            if [[ $d =~ nvme[0-9][0-9]?n[0-9][0-9]?$ ]]; then
                getm2info "$d" "NVMe"
            fi
        ;;
        nvc*)  # M.2 SATA drives (in PCIe card only?)
            if [[ $d =~ nvc[0-9][0-9]?$ ]]; then
                #getm2info "$d" "SATA"
                echo -e "${Cyan}Skipping SATA M.2 drive${Off}" >&2
                echo -e "Use Synology_M2_volume v1 instead.\n" >&2
            fi
        ;;
        *)
          ;;
    esac
done

echo -e "Unused M.2 drives found: ${#m2list[@]}\n"

#echo -e "NVMe list: ${m2list[@]}\n"  # debug
#echo -e "NVMe qty: ${#m2list[@]}\n"  # debug

if [[ ${#m2list[@]} == "0" ]]; then exit; fi


#--------------------------------------------------------------------
# Select RAID type (if multiple M.2 drives found)

if [[ ${#m2list[@]} -gt "0" ]]; then
    PS3="Select the RAID type: "
    if [[ ${#m2list[@]} -eq "1" ]]; then
        options=("SHR 1" "Basic" "JBOD")
    elif [[ ${#m2list[@]} -eq "2" ]]; then
        options=("SHR 1" "Basic" "JBOD" "RAID 0" "RAID 1")
    elif [[ ${#m2list[@]} -eq "3" ]]; then
        options=("SHR 1" "Basic" "JBOD" "RAID 0" "RAID 1" "RAID 5" "RAID F1")
    elif [[ ${#m2list[@]} -gt "3" ]]; then
        options=("SHR 1" "SHR 2" "Basic" "JBOD" "RAID 0" "RAID 1" "RAID 5" "RAID 6" "RAID 10" "RAID F1")
    fi
    select raid in "${options[@]}"; do
      case "$raid" in
        Basic|Single)
            raidtype="basic"
            single="yes"
            mindisk=1
            #maxdisk=1
            break
            ;;
        JBOD)
            raidtype="linear"
            mindisk=1
            #maxdisk=1
            break
            ;;
        "SHR 1")
            raidtype="SHR1"
            mindisk=1
            #maxdisk=1
            break
            ;;
        "SHR 2")
            raidtype="SHR2"
            mindisk=4
            #maxdisk=1
            break
            ;;
        "RAID 0")
            raidtype="raid0"
            mindisk=2
            #maxdisk=24
            break
            ;;
        "RAID 1")
            raidtype="raid1"
            mindisk=2
            #maxdisk=4
            break
            ;;
        "RAID 5")
            raidtype="raid5"
            mindisk=3
            #maxdisk=24
            break
            ;;
        "RAID 6")
            raidtype="raid6"
            mindisk=4
            #maxdisk=24
            break
            ;;
        "RAID 10")
            raidtype="raid10"
            mindisk=4
            #maxdisk=24
            break
            ;;
        "RAID F1")
            raidtype="raid_f1"
            mindisk=3
            #maxdisk=24
            break
            ;;
        Quit)
            exit
            ;;
        *)
            echo -e "${Red}Invalid answer!${Off} Try again."
            ;;
      esac
    done
    echo -e "You selected ${Cyan}$raidtype${Off}\n"
elif [[ ${#m2list[@]} -eq "1" ]]; then
    single="yes"
fi

# Only Basic and RAID 1 have a limit on the number of drives in DSM 7 and 6
# Later we set maxdisk to the number of M.2 drives found if not Single or RAID 1
if [[ $single == "yes" ]]; then
    maxdisk=1
elif [[ $raidtype == "raid1" ]]; then
    maxdisk=4
fi


# Ask user if they want to create a multi-volume pool
echo -e "You have a choice of Multi Volume or Single Volume Storage Pool"
echo -e " - Multi Volume Storage Pools allow creating multiple volumes and"
echo -e "   allow you to over provision to make the NVMe drive(s) last longer."
echo -e " - Single Volume Storage Pools are easier to recover data from"
echo -e "   and perform slightly faster.\n"
PS3="Select the storage pool type: "
#options=("Multi Volume (default)" "Single Volume (easier recovery)")
options=("Multi Volume (DSM 7 default)" "Single Volume")
select pool in "${options[@]}"; do
  case "$pool" in
    #"Multi Volume (default)")
    "Multi Volume (DSM 7 default)")
        pooltype="multi"
        break
        ;;
    #"Single Volume (easier recovery)")
    "Single Volume")
        pooltype="single"
        break
        ;;
    Quit)
        exit
        ;;
    *)
        echo -e "${Red}Invalid answer!${Off} Try again."
        ;;
  esac
done
echo -e "You selected ${Cyan}${pooltype^} Volume${Off} storage pool"
echo


#--------------------------------------------------------------------
# Selected M.2 drive functions

getindex(){ 
    # Get array index from value
    for i in "${!m2list[@]}"; do
        if [[ "${m2list[$i]}" == "${1}" ]]; then
            r="${i}"
        fi
    done
    return "$r"
}

remelement(){ 
    # Remove selected drive from list of other selectable drives
    if [[ $1 ]]; then
        num="0"
        while [[ $num -lt "${#m2list[@]}" ]]; do
            if [[ ${m2list[num]} == "$1" ]]; then
                # Remove selected drive from m2list array
                unset "m2list[num]"

                # Rebuild the array to remove empty indices
                for i in "${!m2list[@]}"; do
                    tmp_array+=( "${m2list[i]}" )
                done
                m2list=("${tmp_array[@]}")
                unset tmp_array
            fi
            num=$((num +1))
        done
    fi
}


#--------------------------------------------------------------------
# Select M.2 drives

mdisk=(  )

# Set maxdisk to the number of M.2 drives found if not Single or RAID 1
# Only Basic and RAID 1 have a limit on the number of drives in DSM 7 and 6
if [[ $single != "yes" ]] && [[ $raidtype != "raid1" ]]; then
    maxdisk="${#m2list[@]}"
fi

while [[ $selected -lt "$mindisk" ]] || [[ $selected -lt "$maxdisk" ]]; do
    if [[ $single == "yes" ]]; then
        PS3="Select the M.2 drive: "
    else
        PS3="Select the M.2 drive #$((selected+1)): "
    fi
    selectdisk
    if [[ $Done == "yes" ]]; then
        break
    fi
done

if [[ $selected -lt "$mindisk" ]]; then
    echo "Drives selected: $selected"
    echo -e "${Error}ERROR${Off} You need to select $mindisk or more drives for RAID $raidtype"
    exit
fi


#--------------------------------------------------------------------
# Ask user if they want to do a drive check

echo -e "Do you want perform a drive check? [y/n]"
read -r answer
if [[ ${answer,,} == "y" ]] || [[ ${answer,,} == "yes" ]]; then
    drivecheck="yes"
fi


#--------------------------------------------------------------------
# Let user confirm their choices

#if [[ $single == "yes" ]]; then
#    echo -en "Ready to create storage pool using ${Cyan}${mdisk[*]}${Off}"
#else
    #echo -en "\nReady to create ${Cyan}${raidtype^^}${Off} storage pool using "
    echo -en "\nReady to create ${Cyan}$raid${Off} storage pool using "
    echo -e "${Cyan}${mdisk[*]}${Off}"
#fi

if [[ $haspartitons == "yes" ]]; then
    echo -e "\n${Red}WARNING${Off} Everything on the selected"\
        "M.2 drive(s) will be deleted."
fi

echo -e "Type ${Cyan}yes${Off} to continue. Type anything else to quit."
read -r answer
if [[ ${answer,,} != "yes" ]]; then exit; fi


#--------------------------------------------------------------------
# Enable m2 volume support - DSM 7.1 and later only

# Backup synoinfo.conf if needed
#if [[ $dsm72 == "yes" ]]; then
if [[ $dsm71 == "yes" ]]; then
    synoinfo="/etc.defaults/synoinfo.conf"
    if [[ ! -f ${synoinfo}.bak ]]; then
        if cp "$synoinfo" "$synoinfo.bak"; then
            echo -e "\nBacked up $(basename -- "$synoinfo")" >&2
        else
            ding
            echo -e "\n${Error}ERROR 5${Off} Failed to backup $(basename -- "$synoinfo")!"
            exit 1
        fi
    fi
fi

# Check if m2 volume support is enabled
#if [[ $dsm72 == "yes" ]]; then
if [[ $dsm71 == "yes" ]]; then
    smp=support_m2_pool
    setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" "$smp")"
    enabled=""
    if [[ ! $setting ]]; then
        # Add support_m2_pool="yes"
        echo 'support_m2_pool="yes"' >> "$synoinfo"
        enabled="yes"
    elif [[ $setting == "no" ]]; then
        # Change support_m2_pool="no" to "yes"
        #sed -i "s/${smp}=\"no\"/${smp}=\"yes\"/" "$synoinfo"
        /usr/syno/bin/synosetkeyvalue "$synoinfo" "$smp" "yes"
        enabled="yes"
    elif [[ $setting == "yes" ]]; then
        echo -e "\nM.2 volume support already enabled."
    fi

    # Check if we enabled m2 volume support
    setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" "$smp")"
    if [[ $enabled == "yes" ]]; then
        if [[ $setting == "yes" ]]; then
            echo -e "\nEnabled M.2 volume support."
        else
            echo -e "\n${Error}ERROR${Off} Failed to enable M.2 volume support!"
        fi
    fi
fi

# Check if RAID F1 support is enabled
if [[ $raidtype == "raid_f1" ]]; then
    #if [[ $dsm72 == "yes" ]]; then
    if [[ $dsm71 == "yes" ]]; then
        srf1=support_diffraid
        setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" "$srf1")"
        enabled=""
        if [[ ! $setting ]]; then
            # Add support_diffraid="yes"
            echo 'support_diffraid="yes"' >> "$synoinfo"
            enabled="yes"
        elif [[ $setting == "no" ]]; then
            # Change support_diffraid="no" to "yes"
            #sed -i "s/${srf1}=\"no\"/${srf1}=\"yes\"/" "$synoinfo"
            /usr/syno/bin/synosetkeyvalue "$synoinfo" "$srf1" "yes"
            enabled="yes"
        elif [[ $setting == "yes" ]]; then
            echo -e "\nRAID F1 support already enabled."
        fi

        # Check if we enabled RAID F1 support
        setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" "$srf1")"
        if [[ $enabled == "yes" ]]; then
            if [[ $setting == "yes" ]]; then
                echo -e "\nEnabled RAID F1 support."
            else
                echo -e "\n${Error}ERROR${Off} Failed to enable RAID F1 support!"
            fi
        fi
    fi
fi


#--------------------------------------------------------------------
# Create storage pool on selected M.2 drives

# Single volume storage pool (DSM 6 style pool on md#)
# synostgpool --create -t single -l basic /dev/nvme0n1
# synostgpool --create -t single -l raid5 /dev/nvme0n1 /dev/nvme1n1 /dev/nvme2n1

# Multiple volume storage pool (DSM 7 style pool on vg#)
# synostgpool --create -l basic /dev/nvme0n1
# synostgpool --create -l raid5 /dev/nvme0n1 /dev/nvme1n1 /dev/nvme2n1


partargs=(  )
for i in "${mdisk[@]}"; do
   :
   partargs+=(
       /dev/"${i}"
   )
done

if [[ $pooltype == "single" ]]; then
    # Unset existing arguments
    while [[ $1 ]]; do shift; done
    # Set -t single arguments
    set -- "$@" "-t"
    set -- "$@" "single"
fi


echo -e "\nStarting creation of the storage pool."
if [[ $drivecheck != "yes" ]]; then
    synostgpool --create "$@" -l "$raidtype" "${partargs[@]}"
    code="$?"
    if [[ $code -gt "0" ]] &&  [[ ! $code -eq "255" ]]; then
        #ding
        #echo "$code synostgpool failed to create storage pool!"
        echo "synostgpool return code: $code"
        #exit 1
    fi
else
    synostgpool --create "$@" -l "$raidtype" -c "${partargs[@]}"
    code="$?"
    if [[ $code -gt "0" ]] &&  [[ ! $code -eq "255" ]]; then
        #ding
        #echo "$code synostgpool failed to create storage pool!"
        echo "synostgpool return code: $code"
        #exit 1
    fi
fi


#--------------------------------------------------------------------
# Notify of remaining steps

echo
showsteps  # Show the final steps to do in DSM
  • Script2: https://github.com/007revad/Synology_enable_M2_volume
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Enables using non-Synology NVMe drives so you can create a storage pool
# and volume on any M.2 drive(s) entirely in DSM Storage Manager.
#
# Github: https://github.com/007revad/Synology_enable_M2_volume
# Script verified at https://www.shellcheck.net/
# Tested on DSM 7.2 and 7.2.1
#
# To run in a shell (replace /volume1/scripts/ with path to script):
# sudo /volume1/scripts/syno_enable_m2_volume.sh
#------------------------------------------------------------------------------

scriptver="v1.1.22"
script=Synology_enable_M2_volume
repo="007revad/Synology_enable_M2_volume"
scriptname=syno_enable_m2_volume

# Check BASH variable is bash
if [ ! "$(basename "$BASH")" = bash ]; then
    echo "This is a bash script. Do not run it with $(basename "$BASH")"
    printf \\a
    exit 1
fi

# Check script is running on a Synology NAS
if ! /usr/bin/uname -a | grep -i synology >/dev/null; then
    echo "This script is NOT running on a Synology NAS!"
    echo "Copy the script to a folder on the Synology"
    echo "and run it from there."
    exit 1
fi

ding(){ 
    printf \\a
}

usage(){ 
    cat <<EOF
$script $scriptver - by 007revad

Usage: $(basename "$0") [options]

Options:
  -c, --check           Check value in file and backup file
  -r, --restore         Restore backup to undo changes
  -n, --noreboot        Don't reboot after script has run
  -e, --email           Disable colored text in output scheduler emails
      --autoupdate=AGE  Auto update script (useful when script is scheduled)
                          AGE is how many days old a release must be before
                          auto-updating. AGE must be a number: 0 or greater
  -h, --help            Show this help message
  -v, --version         Show the script version

EOF
    exit 0
}


scriptversion(){ 
    cat <<EOF
$script $scriptver - by 007revad

See https://github.com/$repo
EOF
    exit 0
}


# Save options used
args=("$@")


# Check for flags with getopt
if options="$(getopt -o abcdefghijklmnopqrstuvwxyz0123456789 -l \
    check,restore,noreboot,email,autoupdate:,help,version,log,debug -- "$@")"; then
    eval set -- "$options"
    while true; do
        case "${1,,}" in
            -c|--check)         # Check value in file and backup file
                check=yes
                ;;
            -r|--restore)       # Restore backup to undo changes
                restore=yes
                ;;
            -e|--email)         # Disable colour text in task scheduler emails
                color=no
                ;;
            -n|--noreboot)      # Don't reboot after script has run
                noreboot=yes
                ;;
            --autoupdate)       # Auto update script
                autoupdate=yes
                if [[ $2 =~ ^[0-9]+$ ]]; then
                    delay="$2"
                    shift
                else
                    delay="0"
                fi
                ;;
            -h|--help)          # Show usage options
                usage
                ;;
            -v|--version)       # Show script version
                scriptversion
                ;;
            -l|--log)           # Log
                #log=yes
                ;;
            -d|--debug)         # Show and log debug info
                debug=yes
                ;;
            --)
                shift
                break
                ;;
            *)                  # Show usage options
                echo -e "Invalid option '$1'\n"
                usage "$1"
                ;;
        esac
        shift
    done
else
    echo
    usage
fi


if [[ $debug == "yes" ]]; then
    set -x
    export PS4='`[[ $? == 0 ]] || echo "\e[1;31;40m($?)\e[m\n "`:.$LINENO:'
fi


# Shell Colors
if [[ $color != "no" ]]; then
    #Black='\e[0;30m'   # ${Black}
    Red='\e[0;31m'      # ${Red}
    #Green='\e[0;32m'   # ${Green}
    Yellow='\e[0;33m'   # ${Yellow}
    #Blue='\e[0;34m'    # ${Blue}
    #Purple='\e[0;35m'  # ${Purple}
    Cyan='\e[0;36m'     # ${Cyan}
    #White='\e[0;37m'   # ${White}
    Error='\e[41m'      # ${Error}
    Off='\e[0m'         # ${Off}
else
    echo ""  # For task scheduler email readability
fi


# Check script is running as root
if [[ $( whoami ) != "root" ]]; then
    ding
    echo -e "${Error}ERROR${Off} This script must be run as sudo or root!"
    exit 1
fi

# Get DSM major version
dsm=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION majorversion)
if [[ $dsm -lt "7" ]]; then
    ding
    echo "This script only works for DSM 7."
    exit 1
fi


# Show script version
#echo -e "$script $scriptver\ngithub.com/$repo\n"
echo "$script $scriptver"

# Get NAS model
model=$(cat /proc/sys/kernel/syno_hw_version)

# Get DSM full version
productversion=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION productversion)
buildphase=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION buildphase)
buildnumber=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION buildnumber)
smallfixnumber=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION smallfixnumber)

# Show DSM full version and model
if [[ $buildphase == GM ]]; then buildphase=""; fi
if [[ $smallfixnumber -gt "0" ]]; then smallfix="-$smallfixnumber"; fi
echo -e "$model DSM $productversion-$buildnumber$smallfix $buildphase\n"


# Get StorageManager version
storagemgrver=$(/usr/syno/bin/synopkg version StorageManager)
# Show StorageManager version
if [[ $storagemgrver ]]; then echo -e "StorageManager $storagemgrver\n"; fi


# Show options used
echo "Using options: ${args[*]}"



#------------------------------------------------------------------------------
# Check latest release with GitHub API

syslog_set(){ 
    if [[ ${1,,} == "info" ]] || [[ ${1,,} == "warn" ]] || [[ ${1,,} == "err" ]]; then
        if [[ $autoupdate == "yes" ]]; then
            # Add entry to Synology system log
            /usr/syno/bin/synologset1 sys "$1" 0x11100000 "$2"
        fi
    fi
}


# Get latest release info
# Curl timeout options:
# https://unix.stackexchange.com/questions/94604/does-curl-have-a-timeout
release=$(curl --silent -m 10 --connect-timeout 5 \
    "https://api.github.com/repos/$repo/releases/latest")

# Release version
tag=$(echo "$release" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
shorttag="${tag:1}"

# Release published date
published=$(echo "$release" | grep '"published_at":' | sed -E 's/.*"([^"]+)".*/\1/')
published="${published:0:10}"
published=$(date -d "$published" '+%s')

# Today's date
now=$(date '+%s')

# Days since release published
age=$(((now - published)/(60*60*24)))


# Get script location
# https://stackoverflow.com/questions/59895/
source=${BASH_SOURCE[0]}
while [ -L "$source" ]; do # Resolve $source until the file is no longer a symlink
    scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd )
    source=$(readlink "$source")
    # If $source was a relative symlink, we need to resolve it
    # relative to the path where the symlink file was located
    [[ $source != /* ]] && source=$scriptpath/$source
done
scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd )
scriptfile=$( basename -- "$source" )
echo "Running from: ${scriptpath}/$scriptfile"

# Warn if script located on M.2 drive
scriptvol=$(echo "$scriptpath" | cut -d"/" -f2)
vg=$(lvdisplay | grep /volume_"${scriptvol#volume}" | cut -d"/" -f3)
md=$(pvdisplay | grep -B 1 -E '[ ]'"$vg" | grep /dev/ | cut -d"/" -f3)
if cat /proc/mdstat | grep "$md" | grep nvme >/dev/null; then
    echo -e "${Yellow}WARNING${Off} Don't store this script on an NVMe volume!"
fi


cleanup_tmp(){ 
    cleanup_err=

    # Delete downloaded .tar.gz file
    if [[ -f "/tmp/$script-$shorttag.tar.gz" ]]; then
        if ! rm "/tmp/$script-$shorttag.tar.gz"; then
            echo -e "${Error}ERROR${Off} Failed to delete"\
                "downloaded /tmp/$script-$shorttag.tar.gz!" >&2
            cleanup_err=1
        fi
    fi

    # Delete extracted tmp files
    if [[ -d "/tmp/$script-$shorttag" ]]; then
        if ! rm -r "/tmp/$script-$shorttag"; then
            echo -e "${Error}ERROR${Off} Failed to delete"\
                "downloaded /tmp/$script-$shorttag!" >&2
            cleanup_err=1
        fi
    fi

    # Add warning to DSM log
    if [[ $cleanup_err ]]; then
        syslog_set warn "$script update failed to delete tmp files"
    fi
}


if ! printf "%s\n%s\n" "$tag" "$scriptver" |
        sort --check=quiet --version-sort >/dev/null ; then
    echo -e "\n${Cyan}There is a newer version of this script available.${Off}"
    echo -e "Current version: ${scriptver}\nLatest version:  $tag"
    scriptdl="$scriptpath/$script-$shorttag"
    if [[ -f ${scriptdl}.tar.gz ]] || [[ -f ${scriptdl}.zip ]]; then
        # They have the latest version tar.gz downloaded but are using older version
        echo "You have the latest version downloaded but are using an older version"
        sleep 10
    elif [[ -d $scriptdl ]]; then
        # They have the latest version extracted but are using older version
        echo "You have the latest version extracted but are using an older version"
        sleep 10
    else
        if [[ $autoupdate == "yes" ]]; then
            if [[ $age -gt "$delay" ]] || [[ $age -eq "$delay" ]]; then
                echo "Downloading $tag"
                reply=y
            else
                echo "Skipping as $tag is less than $delay days old."
            fi
        else
            echo -e "${Cyan}Do you want to download $tag now?${Off} [y/n]"
            read -r -t 30 reply
        fi

        if [[ ${reply,,} == "y" ]]; then
            # Delete previously downloaded .tar.gz file and extracted tmp files
            cleanup_tmp

            if cd /tmp; then
                url="https://github.com/$repo/archive/refs/tags/$tag.tar.gz"
                if ! curl -JLO -m 30 --connect-timeout 5 "$url"; then
                    echo -e "${Error}ERROR${Off} Failed to download"\
                        "$script-$shorttag.tar.gz!"
                    syslog_set warn "$script $tag failed to download"
                else
                    if [[ -f /tmp/$script-$shorttag.tar.gz ]]; then
                        # Extract tar file to /tmp/<script-name>
                        if ! tar -xf "/tmp/$script-$shorttag.tar.gz" -C "/tmp"; then
                            echo -e "${Error}ERROR${Off} Failed to"\
                                "extract $script-$shorttag.tar.gz!"
                            syslog_set warn "$script failed to extract $script-$shorttag.tar.gz!"
                        else
                            # Set script sh files as executable
                            if ! chmod a+x "/tmp/$script-$shorttag/"*.sh ; then
                                permerr=1
                                echo -e "${Error}ERROR${Off} Failed to set executable permissions"
                                syslog_set warn "$script failed to set permissions on $tag"
                            fi

                            # Copy new script sh file to script location
                            if ! cp -p "/tmp/$script-$shorttag/${scriptname}.sh" "${scriptpath}/${scriptfile}";
                            then
                                copyerr=1
                                echo -e "${Error}ERROR${Off} Failed to copy"\
                                    "$script-$shorttag sh file(s) to:\n $scriptpath/${scriptfile}"
                                syslog_set warn "$script failed to copy $tag to script location"
                            fi

                            # Copy new CHANGES.txt file to script location (if script on a volume)
                            if [[ $scriptpath =~ /volume* ]]; then
                                # Set permissions on CHANGES.txt
                                if ! chmod 664 "/tmp/$script-$shorttag/CHANGES.txt"; then
                                    permerr=1
                                    echo -e "${Error}ERROR${Off} Failed to set permissions on:"
                                    echo "$scriptpath/CHANGES.txt"
                                fi

                                # Copy new CHANGES.txt file to script location
                                if ! cp -p "/tmp/$script-$shorttag/CHANGES.txt"\
                                    "${scriptpath}/${scriptname}_CHANGES.txt";
                                then
                                    if [[ $autoupdate != "yes" ]]; then copyerr=1; fi
                                    echo -e "${Error}ERROR${Off} Failed to copy"\
                                        "$script-$shorttag/CHANGES.txt to:\n $scriptpath"
                                else
                                    changestxt=" and changes.txt"
                                fi
                            fi

                            # Delete downloaded tmp files
                            cleanup_tmp

                            # Notify of success (if there were no errors)
                            if [[ $copyerr != 1 ]] && [[ $permerr != 1 ]]; then
                                echo -e "\n$tag ${scriptfile}$changestxt downloaded to: ${scriptpath}\n"
                                syslog_set info "$script successfully updated to $tag"

                                # Reload script
                                printf -- '-%.0s' {1..79}; echo  # print 79 -
                                exec "${scriptpath}/$scriptfile" "${args[@]}"
                            else
                                syslog_set warn "$script update to $tag had errors"
                            fi
                        fi
                    else
                        echo -e "${Error}ERROR${Off}"\
                            "/tmp/$script-$shorttag.tar.gz not found!"
                        #ls /tmp | grep "$script"  # debug
                        syslog_set warn "/tmp/$script-$shorttag.tar.gz not found"
                    fi
                fi
                cd "$scriptpath" || echo -e "${Error}ERROR${Off} Failed to cd to script location!"
            else
                echo -e "${Error}ERROR${Off} Failed to cd to /tmp!"
                syslog_set warn "$script update failed to cd to /tmp"
            fi
        fi
    fi
fi

rebootmsg(){ 
    # Reboot prompt
    echo -e "\n${Cyan}The Synology needs to restart.${Off}"
    echo -e "Type ${Cyan}yes${Off} to reboot now."
    echo -e "Type anything else to quit (if you will restart it yourself)."
    read -r -t 10 answer
    if [[ ${answer,,} != "yes" ]]; then exit; fi

#    # Reboot in the background so user can see DSM's "going down" message
#    reboot &
    if [[ -x /usr/syno/sbin/synopoweroff ]]; then
        /usr/syno/sbin/synopoweroff -r || reboot
    else
        reboot
    fi
}


#----------------------------------------------------------
# Check file exists

file="/usr/lib/libhwcontrol.so.1"

if [[ ! -f ${file} ]]; then
    ding
    echo -e "${Error}ERROR${Off} File not found!"
    exit 1
fi


#----------------------------------------------------------
# Restore from backup file

if [[ $restore == "yes" ]]; then
    if [[ -f ${file}.bak ]]; then

        # Check if backup size matches file size
        filesize=$(wc -c "${file}" | awk '{print $1}')
        filebaksize=$(wc -c "${file}.bak" | awk '{print $1}')
        if [[ ! $filesize -eq "$filebaksize" ]]; then
            echo -e "${Yellow}WARNING Backup file size is different to file!${Off}"
            echo "Do you want to restore this backup? [yes/no]:"
            read -r answer
            if [[ $answer != "yes" ]]; then
                exit
            fi
        fi

        # Restore from backup
        if cp "$file".bak "$file" ; then
            echo "Successfully restored from backup."
            rebootmsg
            exit
        else
            ding
            echo -e "${Error}ERROR${Off} Backup failed!"
            exit 1
        fi
    else
        ding
        echo -e "${Error}ERROR${Off} Backup file not found!"
        exit 1
    fi
fi


#----------------------------------------------------------
# Backup file

if [[ ! -f ${file}.bak ]]; then
    if cp "$file" "$file".bak ; then
        echo "Backup successful."
    else
        ding
        echo -e "${Error}ERROR${Off} Backup failed!"
        exit 1
    fi
else
    # Check if backup size matches file size
    filesize=$(wc -c "${file}" | awk '{print $1}')
    filebaksize=$(wc -c "${file}.bak" | awk '{print $1}')
    if [[ ! $filesize -eq "$filebaksize" ]]; then
        echo -e "${Yellow}WARNING Backup file size is different to file!${Off}"
        echo "Maybe you've updated DSM since last running this script?"
        echo "Renaming file.bak to file.bak.old"
        mv "${file}.bak" "$file".bak.old
        if cp "$file" "$file".bak ; then
            echo "Backup successful."
        else
            ding
            echo -e "${Error}ERROR${Off} Backup failed!"
            exit 1
        fi
    else
        echo "File already backed up."
    fi
fi


#----------------------------------------------------------
# Edit file

findbytes(){ 
    # Get decimal position of matching hex string
    match=$(od -v -t x1 "$1" |
    sed 's/[^ ]* *//' |
    tr '\012' ' ' |
    grep -b -i -o "$hexstring" |
    cut -d ':' -f 1 |
    xargs -I % expr % / 3)

    # Convert decimal position of matching hex string to hex
    array=("$match")
    if [[ ${#array[@]} -gt "1" ]]; then
        num="0"
        while [[ $num -lt "${#array[@]}" ]]; do
            poshex=$(printf "%x" "${array[$num]}")
            echo "${array[$num]} = $poshex"  # debug

            seek="${array[$num]}"
            xxd=$(xxd -u -l 12 -s "$seek" "$1")
            #echo "$xxd"  # debug
            printf %s "$xxd" | cut -d" " -f1-7
            bytes=$(printf %s "$xxd" | cut -d" " -f6)
            #echo "$bytes"  # debug

            num=$((num +1))
        done
    elif [[ -n $match ]]; then
        poshex=$(printf "%x" "$match")
        echo "$match = $poshex"  # debug

        seek="$match"
        xxd=$(xxd -u -l 12 -s "$seek" "$1")
        #echo "$xxd"  # debug
        printf %s "$xxd" | cut -d" " -f1-7
        bytes=$(printf %s "$xxd" | cut -d" " -f6)
        #echo "$bytes"  # debug
    else
        bytes=""
    fi
}


# Check value in file and backup file
if [[ $check == "yes" ]]; then
    err=0

    # Check value in file
    echo -e "\nChecking value in file."
    hexstring="80 3E 00 B8 01 00 00 00 90 90 48 8B"
    findbytes "$file"
    if [[ $bytes == "9090" ]]; then
        echo -e "\n${Cyan}File already edited.${Off}"
    else
        hexstring="80 3E 00 B8 01 00 00 00 75 2. 48 8B"
        findbytes "$file"
        if [[ $bytes =~ "752"[0-9] ]]; then
            echo -e "\n${Cyan}File is unedited.${Off}"
        else
            echo -e "\n${Red}hex string not found!${Off}"
            err=1
        fi
    fi

    # Check value in backup file
    if [[ -f ${file}.bak ]]; then
        echo -e "\nChecking value in backup file."
        hexstring="80 3E 00 B8 01 00 00 00 75 2. 48 8B"
        findbytes "${file}.bak"
        if [[ $bytes =~ "752"[0-9] ]]; then
            echo -e "\n${Cyan}Backup file is unedited.${Off}"
        else
            hexstring="80 3E 00 B8 01 00 00 00 90 90 48 8B"
            findbytes "${file}.bak"
            if [[ $bytes == "9090" ]]; then
                echo -e "\n${Red}Backup file has been edited!${Off}"
            else
                echo -e "\n${Red}hex string not found!${Off}"
                err=1
            fi
        fi
    else
        echo "No backup file found."
    fi

    exit "$err"
fi


echo -e "\nChecking file."


# Check if the file is already edited
hexstring="80 3E 00 B8 01 00 00 00 90 90 48 8B"
findbytes "$file"
if [[ $bytes == "9090" ]]; then
    echo -e "\n${Cyan}File already edited.${Off}"
    exit
else
    # Check if the file is okay for editing
    hexstring="80 3E 00 B8 01 00 00 00 75 2. 48 8B"
    findbytes "$file"
    if [[ $bytes =~ "752"[0-9] ]]; then
        echo -e "\nEditing file."
    else
        ding
        echo -e "\n${Red}hex string not found!${Off}"
        exit 1
    fi
fi


# Replace bytes in file
posrep=$(printf "%x\n" $((0x${poshex}+8)))
if ! printf %s "${posrep}: 9090" | xxd -r - "$file"; then
    ding
    echo -e "${Error}ERROR${Off} Failed to edit file!"
    exit 1
fi


#----------------------------------------------------------
# Check if file was successfully edited

echo -e "\nChecking if file was successfully edited."
hexstring="80 3E 00 B8 01 00 00 00 90 90 48 8B"
findbytes "$file"
if [[ $bytes == "9090" ]]; then
    echo -e "File successfully edited."
    echo -e "\n${Cyan}You can now create your M.2 storage"\
        "pool in Storage Manager.${Off}"
    edited=yes
else
    ding
    echo -e "${Error}ERROR${Off} Failed to edit file!"
    exit 1
fi


#--------------------------------------------------------------------
# Enable m2 volume support - DSM 7.1 and later only

# Backup synoinfo.conf if needed
#if [[ $dsm72 == "yes" ]]; then
#if [[ $dsm71 == "yes" ]]; then
    synoinfo="/etc.defaults/synoinfo.conf"
    if [[ ! -f ${synoinfo}.bak ]]; then
        if cp "$synoinfo" "$synoinfo.bak"; then
            echo -e "\nBacked up $(basename -- "$synoinfo")" >&2
        else
            ding
            echo -e "\n${Error}ERROR 5${Off} Failed to backup $(basename -- "$synoinfo")!"
            exit 1
        fi
    fi
#fi


# Check if m2 volume support is enabled
#if [[ $dsm72 == "yes" ]]; then
#if [[ $dsm71 == "yes" ]]; then
    smp=support_m2_pool
    setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" "$smp")"
    enabled=""
    if [[ ! $setting ]]; then
        # Add support_m2_pool="yes"
        echo 'support_m2_pool="yes"' >> "$synoinfo"
        enabled="yes"
    elif [[ $setting == "no" ]]; then
        # Change support_m2_pool="no" to "yes"
        #sed -i "s/${smp}=\"no\"/${smp}=\"yes\"/" "$synoinfo"
        /usr/syno/bin/synosetkeyvalue "$synoinfo" "$smp" "yes"
        enabled="yes"
    elif [[ $setting == "yes" ]]; then
        echo -e "\nM.2 volume support already enabled."
    fi

    # Check if we enabled m2 volume support
    setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" "$smp")"
    if [[ $enabled == "yes" ]]; then
        if [[ $setting == "yes" ]]; then
            echo -e "\nEnabled M.2 volume support."
        else
            echo -e "\n${Error}ERROR${Off} Failed to enable m2 volume support!"
        fi
    fi
#fi


# Enable creating M.2 storage pool and volume in Storage Manager
# for currently installed NVMe drives
for nvme in /run/synostorage/disks/nvme*; do
    if [[ -f "${nvme}/m2_pool_support" ]]; then
        echo -n 1 > "${nvme}/m2_pool_support"
    fi
done


#----------------------------------------------------------
# Reboot

# Only show reboot message if $noreboot not set and we patched file
if [[ $noreboot != "yes" ]] && [[ $edited == "yes" ]]; then
    rebootmsg
fi

exit

This post is a big long due to the “safe keeping” of the script for later use in case of need but in the end, everything is working as expected.

AB

Synology hack

Goal: having a new NAS with more capacity (CPU, RAM and storage).

Steps:

  • Spend money on a new NAS (Synology 920+)
  • Spend money on 3.5” drives (4x4TB WD Red)
  • Spend money on DDR4 stick of ram (16Gb)
  • Spend money on a NVME drive for essentials (512Gb 970Pro)

Synology only officially supports 8Gb of RAM on this system but there were stories about the extra RAM possible am I give it a try. So now I have a NAS with 20Gb of RAM.

Synology also does not allow the SSDs NVMEs to function as storage, only as CACHE which was not needed for my workload and I seamed as a challenge.

Steps to reproduce:

Login via SSH and excalate to root:

ls /dev/nvme*
fdisk -l /dev/nvme0n1
synopartition --part /dev/nvme0n1 12
cat /proc/mdstat
mdadm --create /dev/md3 --level=1 --raid-devices=1 --force /dev/nvme0n1p3
mkfs.ext4 -F /dev/md3
reboot

After that, nothing really happens until you go on the 3 dotted menu and select “Online Assemble” and voilá, new SSD Volume for fun with docker and machines.

Be aware that Synology does not support this due to the lack of cooling, so keep an eye on temps!

This was done with Synology 7.0.1-42218

AB

GIT em Sintra

Post criado no workshop de GIT em Sintra

AB

Blog and life update

Another twist in my life occurred. Life at Siemens was good and peaceful and that was the problem. I was becoming frustrated with tons of meetings every day with any pratical outcome and that was making me mad. It was really annoying to hear in every call “Let’s think over a week to decide”…

Not only that but the projects presented were like “How do I use the cloud?” As a Siemens colleague once said “I believe that you will become dumb in here” and it was true… I was feeling that I was becoming dumb.

The company where I am now was searching for someone with my profile so they engaged with me to have a litle talk and I accepted.

The conversation went good and the job seemed cool so I accepted the challenge.

After 2 weeks, my time in work flies like never before, in a better environment than in Siemens (didn’t really think that was possible)!

AB

Quick Meltdown Check

Just run this simple command on the terminal:

dmesg | grep "Kernel/User page tables isolation: enabled" && echo "Cool! Patched! You are on the right way! :)" || echo "Shit, you are still vulnerable :("

AB