7-add-legacy-bios-support #8
43
README.md
43
README.md
@ -6,14 +6,31 @@ Helper script to install Arch Linux with ZFSBootMenu from within a running Arch
|
||||
|
||||
We expect minimal prep on your end. Please make sure that before execution the following conditions are met.
|
||||
|
||||
### UEFI
|
||||
|
||||
On a UEFI system ensure these conditions are met. See [How to prep](#how-to-prep) for details on how to meet these conditions.
|
||||
|
||||
- One GPT-partitioned disk
|
||||
- Arch Linux live CD ISO image sees exactly one partition with partition type code `BF00` ("Solaris root")
|
||||
- Arch Linux live CD ISO image sees exactly one partition with partition type code `EF00` ("EFI system partition")
|
||||
- The `EF00` EFI partition is mountable, in practical terms this usually only means it has a file system.
|
||||
- No ZFS zpool exists
|
||||
|
||||
### How to prep
|
||||
### Legacy BIOS
|
||||
|
||||
On a blank example disk `/dev/sda` you can fulfill the requirements (One `EF00` partition with a file system plus one `BF00` partition) for example like so:
|
||||
If you are instead running a legacy BIOS machine ensure these conditions are met. See [How to prep](#how-to-prep) for details on how to meet these conditions.
|
||||
|
||||
- One MBR-partitioned disk
|
||||
- Arch Linux live CD ISO image sees exactly one partition with partition type code `BF` ("Solaris root")
|
||||
- Arch Linux live CD ISO image sees exactly one partition with partition type code `83` ("Linux")
|
||||
- The `83` Linux partition is mountable, in practical terms this usually only means it has a file system.
|
||||
- No ZFS zpool exists
|
||||
|
||||
## How to prep
|
||||
|
||||
### UEFI
|
||||
|
||||
On a blank example disk `/dev/sda` you can fulfill the UEFI requirements (One `EF00` partition with a file system plus one `BF00` partition) for example like so:
|
||||
```
|
||||
sgdisk --new '1::+512M' --new '2' --typecode '1:EF00' --typecode '2:BF00' /dev/sda
|
||||
mkfs.vfat /dev/sda1
|
||||
@ -37,6 +54,28 @@ NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
|
||||
└─/dev/sda2 202:2 0 9.5G 0 part
|
||||
```
|
||||
|
||||
### Legacy BIOS
|
||||
|
||||
For a legacy BIOS machine you'll be using a master boot record (MBR) on your disk.
|
||||
```
|
||||
printf -- '%s\n' 'label: dos' 'start=1MiB, size=512MiB, type=83, bootable' 'start=513MiB, size=+, type=bf' | sfdisk /dev/sda
|
||||
mkfs.vfat /dev/sda1
|
||||
```
|
||||
> `label: dos`: Create the following partition layout in a master boot record.
|
||||
>
|
||||
> `start=1MiB, size=512MiB, type=83, bootable`: Partition 1 begins 1 Mebibyte after disk start and is 512 Mebibyte in size. We're setting its bootable flag and setting partition type code `83` ("Linux").
|
||||
>
|
||||
> `start=513MiB, size=+, type=bf`: Partition 2 begins right at the start of Mebibyte 513, this is the very next sector after the end of partition 1. It takes up the remaining disk space, we're assigning type code `bf` ("Solaris").
|
||||
|
||||
The result will be something like this at which point you can start the `setup.sh` script, see [How to run this?](#how-to-run-this) below for more details.
|
||||
```
|
||||
# lsblk --paths --output 'NAME,SIZE,FSTYPE,PARTTYPE,PARTTYPENAME,PTTYPE' /dev/sda
|
||||
NAME SIZE FSTYPE PARTTYPE PARTTYPENAME PTTYPE
|
||||
/dev/sda 10G dos
|
||||
├─/dev/sda1 512M vfat 0x83 Linux dos
|
||||
└─/dev/sda2 9.5G 0xbf Solaris dos
|
||||
```
|
||||
|
||||
## ZFS dataset layout
|
||||
|
||||
The script will create a single ZFS zpool `zpool` on the `BF00` partition with dataset child `zpool/root` which itself has one child `zpool/root/archlinux`, that's where Arch Linux gets installed. Parallel to `zpool/root` it'll create `zpool/data` with a `zpool/data/home` child dataset that gets mounted at `/home`.
|
||||
|
209
setup.sh
209
setup.sh
@ -24,11 +24,17 @@ declare zpool_name zfs_arch_dataset_name
|
||||
zpool_name='zpool'
|
||||
zfs_arch_dataset_name='archlinux'
|
||||
|
||||
declare -A partition_types
|
||||
partition_types[gpt_zfs]='6a85cf4d-1dd2-11b2-99a6-080020736631'
|
||||
partition_types[gpt_efi]='c12a7328-f81f-11d2-ba4b-00a0c93ec93b'
|
||||
partition_types[mbr_zfs]='0xbf'
|
||||
partition_types[mbr_boot]='0x83'
|
||||
|
||||
# https://unix.stackexchange.com/a/48550
|
||||
set -E
|
||||
trap '[ "$?" -ne 77 ] || exit 77' ERR
|
||||
|
||||
declare zpool_drive efi_drive
|
||||
declare zpool_drive efi_drive boot_drive part_schema
|
||||
|
||||
function we_are_changerooted () {
|
||||
if [ "$(stat -c '%d:%i' '/')" != "$(stat -c '%d:%i' '/proc/1/root/.')" ]; then
|
||||
@ -56,6 +62,12 @@ function resize_cow_space () {
|
||||
function update_pacman_db () {
|
||||
#1.4
|
||||
printf -- '%s\n' 'Refreshing mirror list ...'
|
||||
printf -- '%s\n' \
|
||||
'--save /etc/pacman.d/mirrorlist' \
|
||||
'--protocol https' \
|
||||
'--latest 5' \
|
||||
'--sort age' \
|
||||
> '/etc/xdg/reflector/reflector.conf'
|
||||
systemctl start reflector
|
||||
# In an ISO and for the minimal number of packages we need we do not
|
||||
# care about partial upgrades
|
||||
@ -75,6 +87,35 @@ function install_zfs () {
|
||||
printf -- "${reset_colors}"
|
||||
}
|
||||
|
||||
function uefi_or_bios () {
|
||||
#1.7
|
||||
local part_count_linux part_count_efi
|
||||
# Select disks with at least one partition. Among them count how many
|
||||
# with the given partition type code we have.
|
||||
part_count_linux="$(get_partitions | jq --raw-output '[.[][] | select(.children | length > 0) | .children[] | select(.parttype=="'"${partition_types[mbr_boot]}"'") | .path] | length')"
|
||||
part_count_efi="$(get_partitions | jq --raw-output '[.[][] | select(.children | length > 0) | .children[] | select(.parttype=="'"${partition_types[gpt_efi]}"'") | .path] | length')"
|
||||
if [[ "${part_count_linux}" -eq '1' ]] && [[ "${part_count_efi}" -eq '0' ]]; then
|
||||
part_schema='mbr'
|
||||
>&3 printf -- '%s\n' \
|
||||
'Treating this as an MBR/legacy BIOS machine ...'
|
||||
elif [[ "${part_count_linux}" -eq '0' ]] && [[ "${part_count_efi}" -eq '1' ]]; then
|
||||
part_schema='gpt'
|
||||
>&3 printf -- '%s\n' \
|
||||
'Treating this as a GPT/UEFI machine ...'
|
||||
else
|
||||
>&3 printf -- '%s\n' \
|
||||
'We are seeing partitions as follows:' \
|
||||
'- Partition type code '"${partition_types[mbr_boot]}"': '"${part_count_linux}" \
|
||||
'- Partition type code '"${partition_types[gpt_efi]}"': '"${part_count_efi}" \
|
||||
'' \
|
||||
'We are instead expecting either 1 and 0 indicating a legacy' \
|
||||
'BIOS setup or 0 and 1 indicating UEFI. '"${part_count_linux}"' and '"${part_count_efi}"' is' \
|
||||
'neither. Cowardly exiting ...'
|
||||
exit 77
|
||||
fi
|
||||
export part_schema="${part_schema}"
|
||||
}
|
||||
|
||||
function get_partitions () {
|
||||
declare partitions_json
|
||||
partitions_json="$(lsblk --output PATH,PARTTYPE --json --tree)" || return 1
|
||||
@ -91,16 +132,24 @@ function get_part_parent () {
|
||||
}
|
||||
|
||||
function get_parts () {
|
||||
local zfs_install_drive
|
||||
local zfs_install_drive part_type_code
|
||||
declare parttype parts
|
||||
parttype="${1:?}"
|
||||
zfs_install_drive="${2:-}"
|
||||
case "${parttype}" in
|
||||
zfs)
|
||||
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | .children[] | select(.parttype=="6a85cf4d-1dd2-11b2-99a6-080020736631") | .path')" || exit 1
|
||||
if [[ "${part_schema}" = 'mbr' ]]; then
|
||||
part_type_code="${partition_types[mbr_zfs]}"
|
||||
else
|
||||
part_type_code="${partition_types[gpt_zfs]}"
|
||||
fi
|
||||
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | .children[] | select(.parttype=="'"${part_type_code}"'") | .path')" || exit 1
|
||||
;;
|
||||
boot)
|
||||
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.path=="'"${zfs_install_drive:?}"'") | .children[] | select(.parttype=="'"${partition_types[mbr_boot]}"'") | .path')" || exit 1
|
||||
;;
|
||||
efi)
|
||||
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.path=="'"${zfs_install_drive:?}"'") | .children[] | select(.parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b") | .path')" || exit 1
|
||||
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.path=="'"${zfs_install_drive:?}"'") | .children[] | select(.parttype=="'"${partition_types[gpt_efi]}"'") | .path')" || exit 1
|
||||
;;
|
||||
*)
|
||||
>&3 printf -- '%s\n' 'Unknown partition type '"'"'"${parttype}"'"'"', exiting ...'
|
||||
@ -147,15 +196,26 @@ function get_drive_id () {
|
||||
}
|
||||
|
||||
function select_part () {
|
||||
local parts enriched_parts enriched_parts_count part_number part_type zfs_install_drive
|
||||
local parts enriched_parts enriched_parts_count part_number part_type zfs_install_drive part_type_code specific_part_type
|
||||
declare part
|
||||
part_type="${1:?}" # 'efi' or 'zfs'
|
||||
part_type="${1:?}" # 'boot' or 'zfs'
|
||||
zfs_install_drive="${2:-}"
|
||||
if [[ "${zfs_install_drive}" ]]; then
|
||||
# This is intended to find correct EFI partition
|
||||
parts="$(get_parts "${part_type}" "${zfs_install_drive}")"
|
||||
|
||||
if [[ "${part_type}" = 'boot' ]]; then
|
||||
if [[ "${part_schema}" = 'mbr' ]]; then
|
||||
specific_part_type='boot'
|
||||
else
|
||||
parts="$(get_parts "${part_type}")"
|
||||
specific_part_type='efi'
|
||||
fi
|
||||
else
|
||||
specific_part_type="${part_type}"
|
||||
fi
|
||||
|
||||
if [[ "${zfs_install_drive}" ]]; then
|
||||
# This is intended to find correct boot/EFI partition
|
||||
parts="$(get_parts "${specific_part_type}" "${zfs_install_drive}")"
|
||||
else
|
||||
parts="$(get_parts "${specific_part_type}")"
|
||||
fi
|
||||
|
||||
if [[ ! "${parts}" ]]; then
|
||||
@ -164,7 +224,12 @@ function select_part () {
|
||||
part_type_human_readable='EFI system partition (ESP) with partition type code EF00'
|
||||
;;
|
||||
zfs)
|
||||
part_type_human_readable='ZFS zpool partition with partition type code BF00'
|
||||
if [[ "${part_schema}" = 'mbr' ]]; then
|
||||
part_type_code='bf'
|
||||
else
|
||||
part_type_code='BF00'
|
||||
fi
|
||||
part_type_human_readable='ZFS zpool partition with partition type code '"${part_type_code}"
|
||||
;;
|
||||
esac
|
||||
>&3 printf -- '%s\n' \
|
||||
@ -256,7 +321,7 @@ function export_pool () {
|
||||
}
|
||||
|
||||
function setup_zpool () {
|
||||
#1.7
|
||||
#1.8
|
||||
local drive_by_id
|
||||
zpool_drive="$(select_part 'zfs')"
|
||||
drive_by_id="$(get_drive_id "${zpool_drive}")"
|
||||
@ -276,20 +341,30 @@ function setup_zpool () {
|
||||
}
|
||||
|
||||
function mount_system () {
|
||||
#1.8
|
||||
#1.9
|
||||
zfs mount "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
|
||||
zfs mount -a
|
||||
|
||||
local zfs_parent efi_part
|
||||
local zfs_parent efi_part boot_part
|
||||
zfs_parent="$(get_part_parent "${zpool_drive:?}")"
|
||||
|
||||
if [[ "${part_schema}" = 'mbr' ]]; then
|
||||
boot_drive="${zfs_parent}"
|
||||
export boot_drive="${boot_drive}"
|
||||
boot_part="$(select_part 'boot' "${zfs_parent:?}")"
|
||||
mkdir -p '/mnt/boot/syslinux'
|
||||
mount "${boot_part}" '/mnt/boot/syslinux'
|
||||
else
|
||||
efi_drive="${zfs_parent}"
|
||||
efi_part="$(select_part 'efi' "${zfs_parent:?}")"
|
||||
export efi_drive="${efi_drive}"
|
||||
efi_part="$(select_part 'boot' "${zfs_parent:?}")"
|
||||
mkdir -p '/mnt/efi'
|
||||
mount "${efi_part}" '/mnt/efi'
|
||||
fi
|
||||
}
|
||||
|
||||
function copy_zpool_cache () {
|
||||
#1.9
|
||||
#1.10
|
||||
mkdir -p '/mnt/etc/zfs'
|
||||
zpool set 'cachefile=/etc/zfs/'"${zpool_name}"'.cache' "${zpool_name}"
|
||||
}
|
||||
@ -309,7 +384,7 @@ function pacman_dont_check_space () {
|
||||
}
|
||||
|
||||
function install_archlinux () {
|
||||
#1.10
|
||||
#1.11
|
||||
pacman_dl_parallel
|
||||
pacman_dont_check_space
|
||||
pacstrap /mnt \
|
||||
@ -336,7 +411,7 @@ function install_archlinux () {
|
||||
}
|
||||
|
||||
function gen_fstab () {
|
||||
#1.11
|
||||
#1.12
|
||||
genfstab -U /mnt | grep -v "${zpool_name}" | tr -s '\n' | sed -r -e 's/\/mnt//' -e '/./,$!d' > '/mnt/etc/fstab'
|
||||
}
|
||||
|
||||
@ -349,7 +424,7 @@ EOF
|
||||
}
|
||||
|
||||
function set_hostname () {
|
||||
#1.12
|
||||
#1.13
|
||||
declare new_hostname
|
||||
install_pkgs 'pwgen'
|
||||
new_hostname="$(pwgen --no-numerals --no-capitalize --ambiguous 8)"
|
||||
@ -358,7 +433,7 @@ function set_hostname () {
|
||||
}
|
||||
|
||||
function set_locale () {
|
||||
#1.13
|
||||
#1.14
|
||||
printf -- '%s\n' \
|
||||
'KEYMAP=de-latin1' \
|
||||
'FONT=Lat2-Terminus16' \
|
||||
@ -369,7 +444,7 @@ function set_locale () {
|
||||
}
|
||||
|
||||
function add_zfs_hook_to_initramfs () {
|
||||
#1.14
|
||||
#1.15
|
||||
# Add zfs hook, remove fsck hook from initramfs.
|
||||
sed -ri \
|
||||
-e 's'$'\x1''(HOOKS=)(.*?[\(| ])(filesystems)([\)| ][^\r\n\f]*)'$'\x1''\1\2zfs \3\4'$'\x1''g' \
|
||||
@ -384,7 +459,7 @@ function add_zfs_hook_to_initramfs () {
|
||||
}
|
||||
|
||||
function set_initramfs_build_list () {
|
||||
#1.15
|
||||
#1.16
|
||||
# No need to build fallback initramfs, our new fallback is ZFS snapshots
|
||||
sed -ri \
|
||||
-e '/^#/d' \
|
||||
@ -398,7 +473,7 @@ function set_initramfs_build_list () {
|
||||
}
|
||||
|
||||
function add_zfs_files_to_new_os () {
|
||||
#1.16
|
||||
#1.17
|
||||
for zfs_file in '/etc/hostid' '/etc/zfs/zpool.cache' $([[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && printf -- '%s' '/etc/zfs/'"${zpool_name}"'.key'); do
|
||||
rsync -av --itemize-changes {'','/mnt'}"${zfs_file}"
|
||||
done
|
||||
@ -550,9 +625,39 @@ function paru_install () {
|
||||
fi
|
||||
}
|
||||
|
||||
function configure_syslinux () {
|
||||
paru_install 'syslinux'
|
||||
cp /usr/lib/syslinux/bios/*.c32 /boot/syslinux
|
||||
extlinux --install /boot/syslinux
|
||||
cat > /boot/syslinux/syslinux.cfg <<EOF
|
||||
UI menu.c32
|
||||
PROMPT 0
|
||||
|
||||
MENU TITLE ZFSBootMenu
|
||||
TIMEOUT 100
|
||||
|
||||
DEFAULT zfsbootmenu
|
||||
|
||||
LABEL zfsbootmenu
|
||||
MENU LABEL ZFSBootMenu
|
||||
KERNEL /zfsbootmenu/vmlinuz-bootmenu
|
||||
INITRD /zfsbootmenu/initramfs-bootmenu.img
|
||||
APPEND zfsbootmenu quiet loglevel=0
|
||||
|
||||
LABEL zfsbootmenu-backup
|
||||
MENU LABEL ZFSBootMenu (Backup)
|
||||
KERNEL /zfsbootmenu/vmlinuz-bootmenu-backup
|
||||
INITRD /zfsbootmenu/initramfs-bootmenu-backup.img
|
||||
APPEND zfsbootmenu quiet loglevel=0
|
||||
EOF
|
||||
dd bs=440 count=1 conv=notrunc if='/usr/lib/syslinux/bios/mbr.bin' of="${boot_drive}"
|
||||
}
|
||||
|
||||
function configure_zfsbootmenu () {
|
||||
#2.9
|
||||
paru_install 'zfsbootmenu'
|
||||
|
||||
if [[ "${part_schema}" = 'gpt' ]]; then
|
||||
mkdir -p '/etc/zfsbootmenu/posthooks.d'
|
||||
cat > '/etc/zfsbootmenu/config.yaml' <<EOF
|
||||
Global:
|
||||
@ -571,7 +676,28 @@ Kernel:
|
||||
CommandLine: ro loglevel=0 zbm.import_policy=hostid
|
||||
Prefix: vmlinuz
|
||||
EOF
|
||||
# Up here maybe 'ro quiet' instead of 'ro'
|
||||
get_known_good_stub_loader
|
||||
else
|
||||
configure_syslinux
|
||||
cat > '/etc/zfsbootmenu/config.yaml' <<EOF
|
||||
Global:
|
||||
ManageImages: true
|
||||
BootMountPoint: /boot/syslinux
|
||||
InitCPIO: true
|
||||
Components:
|
||||
Enabled: true
|
||||
Versions: false
|
||||
ImageDir: /boot/syslinux/zfsbootmenu
|
||||
Kernel:
|
||||
Prefix: vmlinuz
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Up here maybe 'ro quiet' instead of 'ro'. This is ZFSBootMenu's kernel
|
||||
# command line.
|
||||
|
||||
# Assign cmdline for final kernel start. This is our Arch Linx kernel
|
||||
# command line.
|
||||
zfs set org.zfsbootmenu:commandline='rw nowatchdog rd.vconsole.keymap=de-latin1' "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
|
||||
}
|
||||
|
||||
@ -588,7 +714,7 @@ function get_disks_with_one_efipart () {
|
||||
# Find disks that have exactly one EFI partition and where that EFI
|
||||
# partition is partition number 1. We expect exactly one disk to meet
|
||||
# these criteria. Anything else and we bail.
|
||||
disks_with_one_efipart="$(lsblk --output PATH,PARTTYPE --json --tree | jq --raw-output '.[][] | select(.children | length > 0) | select( any (.children[]; (.path | test("^[^[:digit:]]+1")) and (.parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) and ([select(.children[].parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b")] | length == 1) ) | .path')"
|
||||
disks_with_one_efipart="$(lsblk --output PATH,PARTTYPE --json --tree | jq --raw-output '.[][] | select(.children | length > 0) | select( any (.children[]; (.path | test("^[^[:digit:]]+1")) and (.parttype=="'"${partition_types[gpt_efi]}"'")) and ([select(.children[].parttype=="'"${partition_types[gpt_efi]}"'")] | length == 1) ) | .path')"
|
||||
if [[ "$(wc -l <<<"${disks_with_one_efipart}")" -eq '1' ]] && [[ "$(wc -c <<<"${disks_with_one_efipart}")" -gt '1' ]]; then
|
||||
printf -- '%s' "${disks_with_one_efipart}"
|
||||
return 0
|
||||
@ -622,7 +748,9 @@ function install_os_in_chroot () {
|
||||
|
||||
# Install ZFSBootMenu image
|
||||
configure_zfsbootmenu #2.9
|
||||
get_known_good_stub_loader #2.10
|
||||
generate-zbm
|
||||
|
||||
# Yes, we do this twice so we immediately get a functional backup file
|
||||
generate-zbm
|
||||
}
|
||||
|
||||
@ -730,7 +858,11 @@ function insert_zbm_postconf_hook () {
|
||||
|
||||
function umount_all () {
|
||||
#3.10
|
||||
if [[ "${part_schema}" = 'mbr' ]]; then
|
||||
umount '/mnt/boot/syslinux'
|
||||
else
|
||||
umount '/mnt/efi'
|
||||
fi
|
||||
zfs umount -a
|
||||
zpool export "${zpool_name}"
|
||||
}
|
||||
@ -743,8 +875,10 @@ function finalize_os_setup () {
|
||||
configure_reflector #3.5
|
||||
configure_zfs #3.6
|
||||
configure_zfs_mount_gen #3.7
|
||||
if [[ "${part_schema}" = 'gpt' ]]; then
|
||||
set_new_uefi_boot_entries #3.8
|
||||
insert_zbm_postconf_hook #3.9
|
||||
fi
|
||||
umount_all #3.10
|
||||
}
|
||||
|
||||
@ -756,18 +890,19 @@ function main () {
|
||||
set_ntp #1.2
|
||||
resize_cow_space #1.3
|
||||
update_pacman_db #1.4
|
||||
install_pkgs 'base-devel' 'git' 'jq' #1.5
|
||||
install_pkgs 'jq' #1.5
|
||||
install_zfs #1.6
|
||||
setup_zpool #1.7
|
||||
mount_system #1.8
|
||||
copy_zpool_cache #1.9
|
||||
install_archlinux #1.10
|
||||
gen_fstab #1.11
|
||||
set_hostname #1.12
|
||||
set_locale #1.13
|
||||
add_zfs_hook_to_initramfs #1.14
|
||||
set_initramfs_build_list #1.15
|
||||
add_zfs_files_to_new_os #1.16
|
||||
uefi_or_bios #1.7
|
||||
setup_zpool #1.8
|
||||
mount_system #1.9
|
||||
copy_zpool_cache #1.10
|
||||
install_archlinux #1.11
|
||||
gen_fstab #1.12
|
||||
set_hostname #1.13
|
||||
set_locale #1.14
|
||||
add_zfs_hook_to_initramfs #1.15
|
||||
set_initramfs_build_list #1.16
|
||||
add_zfs_files_to_new_os #1.17
|
||||
enter_chroot #2.1
|
||||
# We're done in chroot
|
||||
finalize_os_setup #3.1
|
||||
|
Loading…
x
Reference in New Issue
Block a user