ZFS boot environment entries for grub2

Asked by Paul Lagerweij on 2017-07-11

Hello, I have added dynamic ZFS boot environment entries to a grub.d script (/etc/grub.d/10_linux) in my Ubuntu 16.04 installation. Now I would like to find a sponsor and request a review, but I don't know which Ubuntu version this feature belongs to and if I have to file a bug or not. Thanks.

Question information

Language:
English Edit question
Status:
Solved
For:
Ubuntu grub2 Edit question
Assignee:
No assignee Edit question
Solved by:
Manfred Hampl
Solved:
2017-07-12
Last query:
2017-07-12
Last reply:
2017-07-12

If you paste the text as an update on your question then we can see what's what.

What issues are you seeing?

Paul Lagerweij (p-a-lagerweij) said : #2

Thanks for answering. I know I can file a bug for grub2 here:

https://launchpad.net/ubuntu/+source/grub2/+filebug

but what I wrote isn't a bug, so I looked how to commit my code and got to the point of making a branch, except I am not sure of which Ubuntu version of the grub2 source I should make a branch. I found the grub2 versions here:

https://packages.ubuntu.com/search?keywords=grub2&searchon=names&suite=all&section=all

I think this feature should go to Ubuntu 16.04 and onwards, because the ZFS on Linux wiki right now suggests Ubuntu 16.04 and 16.10:

https://github.com/zfsonlinux/zfs/wiki/Ubuntu-17.04-Root-on-ZFS

Paul Lagerweij (p-a-lagerweij) said : #3

My diff in case that's what you asked for:

--- 10_linux-old 2017-07-03 14:44:44.000000000 +0200
+++ 10_linux 2017-07-11 15:03:10.780607741 +0200
@@ -25,6 +25,7 @@
 quick_boot="1"
 gfxpayload_dynamic="1"
 vt_handoff="1"
+zfs_be="0"

 . "${datarootdir}/grub/grub-mkconfig_lib"

@@ -78,9 +79,15 @@
  fi;;
     xzfs)
  rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
- bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
- LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs}"
- ;;
+ zfs_active_bootfs="`zpool list -H -o bootfs ${rpool} || true`"
+ if [ -n "${zfs_active_bootfs}" ] && [ "${zfs_active_bootfs}" != "-" ] && \
+ [ `echo ${zfs_active_bootfs} | grep -o '/' | wc -l` -gt 1 ]; then
+ zfs_be="1"
+ LINUX_ROOT_DEVICE="ZFS=${zfs_active_bootfs}"
+ else
+ bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
+ LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs}"
+ fi;;
 esac

 title_correction_code=
@@ -275,6 +282,16 @@
 export linux_gfx_mode
 EOF

+if [ "${zfs_be}" = 1 ]; then
+ while read ZFS_NAME ZFS_ORIGIN; do
+ if [ "${ZFS_ORIGIN}" != "-" ]; then
+ zfs_be_list="${zfs_be_list} ${ZFS_NAME}"
+ fi
+ done << EOF
+`zfs list -H -t filesystem -S creation -o name,origin -d 1 ${zfs_active_bootfs%/*} || true`
+EOF
+fi
+
 # Extra indentation to add to menu entries in a submenu. We're not in a submenu
 # yet, so it's empty. In a submenu it will be equal to '\t' (one tab).
 submenu_indentation=""
@@ -293,6 +310,10 @@
   basename=`basename $linux`
   dirname=`dirname $linux`
   rel_dirname=`make_system_path_relative_to_its_root $dirname`
+ # If ZFS BE support and /boot is in ZFS.
+ if [ "${zfs_be}" = 1 ] && [ -n "${rel_dirname}" ]; then
+ rel_dirname="/""${zfs_active_bootfs#*/}""@/boot"
+ fi
   version=`echo $basename | sed -e "s,^[^0-9]*-,,g"`
   alt_version=`echo $version | sed -e "s,\.old$,,g"`
   linux_root_device_thisversion="${LINUX_ROOT_DEVICE}"
@@ -347,19 +368,60 @@
     is_top_level=false
   fi

- linux_entry "${OS}" "${version}" advanced \
- "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
- for supported_init in ${SUPPORTED_INITS}; do
- init_path="${supported_init#*:}"
- if [ -x "${init_path}" ] && [ "$(readlink -f /sbin/init)" != "${init_path}" ]; then
- linux_entry "${OS}" "${version}" "init-${supported_init%%:*}" \
- "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} init=${init_path}"
+ for menu_entries_group in default ${zfs_be_list}; do
+ found_entry="0"
+ if [ "${menu_entries_group}" = "default" ]; then
+ found_entry="1"
+ os_title="${OS}"
+ else
+ found_zfs_be="0"
+ zfs_be_name=${menu_entries_group}
+ # If /boot is not in ZFS.
+ if [ -z "${rel_dirname}" ]; then
+ found_zfs_be="1"
+ else
+ zfs_be_mounted="`zfs list -H -o mounted ${zfs_be_name}`"
+ if [ "${zfs_be_mounted}" = "yes" ]; then
+ ZFSMNT=`mount | grep -m 1 "^${zfs_be_name} " | cut -d' ' -f3)`
+ else
+ ZFSMNT=`mktemp -d "/tmp/$(echo ${zfs_be_name} | sed 's^/^-^g').XXX" || true`
+ zfs set mountpoint=legacy ${zfs_be_name} || true
+ mount -t zfs -o ro ${zfs_be_name} ${ZFSMNT} || true
+ fi
+ if [ -n "${ZFSMNT}" ] && [ -f "${ZFSMNT}/boot/vmlinuz-${version}" ]; then
+ found_zfs_be="1"
+ rel_dirname="/""${zfs_be_name#*/}""@/boot"
+ fi
+ if [ "${zfs_be_mounted}" != "yes" ]; then
+ umount ${ZFSMNT} && rm -rf ${ZFSMNT} || true
+ zfs set mountpoint=/ ${zfs_be_name} || true
+ fi
+ fi
+
+ if [ "${found_zfs_be}" = 1 ]; then
+ found_entry="1"
+ os_title="${OS} (${zfs_be_name##*/} BE)"
+ gettext_printf "Found ZFS boot environment: (%s BE) %s\n" "${zfs_be_name##*/}" "$linux" >&2
+ linux_root_device_thisversion="ZFS=${zfs_be_name}"
+ fi
+ fi
+
+ if [ "${found_entry}" = 1 ]; then
+ linux_entry "${os_title}" "${version}" advanced \
+ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+ for supported_init in ${SUPPORTED_INITS}; do
+ init_path="${supported_init#*:}"
+ if [ -x "${init_path}" ] && [ "$(readlink -f /sbin/init)" != "${init_path}" ]; then
+ linux_entry "${os_title}" "${version}" "init-${supported_init%%:*}" \
+ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} init=${init_path}"
+ fi
+ done
+ if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then
+ linux_entry "${os_title}" "${version}" recovery \
+ "${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}"
+ fi
     fi
   done
- if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then
- linux_entry "${OS}" "${version}" recovery \
- "${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}"
- fi

   list=`echo $list | tr ' ' '\n' | fgrep -vx "$linux" | tr '\n' ' '`
 done

Best Manfred Hampl (m-hampl) said : #4

Please clarify whether this is a change pertaining the version of grub in Ubuntu only (if yes, which grub version(s) and which Ubuntu release(s)), or if this something that should be changed in the upstream source of grub http://www.gnu.org/software/grub/ and http://savannah.gnu.org/projects/grub and thus for all implementations of grub on all operating systems that use grub.

It seems to me that your proposed changes are not present in the upstream source http://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob_plain;f=util/grub.d/10_linux.in;hb=refs/heads/master so I assume that you should suggest that your modification be incorporated in the GNU grub project (probably in form of a bug report with your solution added as patch).

If the GNU Grub project accepts this change, then it will make its way to all implementations that are derived from it, including Ubuntu.

Paul Lagerweij (p-a-lagerweij) said : #5

My changes are not specific to the version of grub in Ubuntu, but I knew the changes would work in Ubuntu and I saw proposing it in Ubuntu first as a starting point to see what people think before going to the upstream source. If you suggest going to the GNU grub project first, then I will do that.

If all goes well I will propably submit a beadm package to Debian next.

Paul Lagerweij (p-a-lagerweij) said : #6

Thanks Manfred Hampl, that solved my question.