zfs_hook 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #
  2. # WARNING: This script is parsed by ash in busybox at boot time, not bash!
  3. # http://linux.die.net/man/1/ash
  4. # https://wiki.ubuntu.com/DashAsBinSh
  5. # http://www.jpsdomain.org/public/2008-JP_bash_vs_dash.pdf
  6. #
  7. ZPOOL_FORCE=""
  8. ZPOOL_IMPORT_FLAGS=""
  9. ZFS_BOOT_ONLY=""
  10. zfs_get_bootfs () {
  11. for zfs_dataset in $(zpool list -H -o bootfs); do
  12. case ${zfs_dataset} in
  13. "" | "-")
  14. # skip this line/dataset
  15. ;;
  16. "no pools available")
  17. return 1
  18. ;;
  19. *)
  20. ZFS_DATASET=${zfs_dataset}
  21. return 0
  22. ;;
  23. esac
  24. done
  25. return 1
  26. }
  27. zfs_decrypt_fs() {
  28. dataset=$1
  29. # Make sure dataset is encrypted; get fails if ZFS does not support encryption
  30. encryption="$(zfs get -H -o value encryption "${dataset}" 2>/dev/null)" || return 0
  31. [ "${encryption}" != "off" ] || return 0
  32. # Make sure the dataset is locked
  33. keystatus="$(zfs get -H -o value keystatus "${dataset}")" || return 0
  34. [ "${keystatus}" != "available" ] || return 0
  35. # Make sure the encryptionroot is sensible
  36. encryptionroot="$(zfs get -H -o value encryptionroot "${dataset}")" || return 0
  37. [ "${encryptionroot}" != "-" ] || return 0
  38. # Export encryption root to be used by other hooks (SSH)
  39. echo "${encryptionroot}" > /.encryptionroot
  40. # If key location is a file, determine if it can by overridden by prompt
  41. prompt_override=""
  42. if keylocation="$(zfs get -H -o value keylocation "${dataset}")"; then
  43. if [ "${keylocation}" != "prompt" ]; then
  44. if keyformat="$(zfs get -H -o value keyformat "${dataset}")"; then
  45. [ "${keyformat}" = "passphrase" ] && prompt_override="yes"
  46. fi
  47. fi
  48. fi
  49. # Loop until key is loaded here or by another vector (SSH, for instance)
  50. while [ "$(zfs get -H -o value keystatus "${encryptionroot}")" != "available" ]; do
  51. # Try the default loading mechanism
  52. zfs load-key "${encryptionroot}" && break
  53. # Load failed, try a prompt if the failure was not a prompt
  54. if [ -n "${prompt_override}" ]; then
  55. echo "Unable to load key ${keylocation}; please type the passphrase"
  56. echo "To retry the file, interrupt now or repeatedly input a wrong passphrase"
  57. zfs load-key -L prompt "${encryptionroot}" && break
  58. fi
  59. # Throttle retry attempts
  60. sleep 2
  61. done
  62. if [ -f /.encryptionroot ]; then
  63. rm /.encryptionroot
  64. fi
  65. }
  66. zfs_mount_handler () {
  67. if [ "${ZFS_DATASET}" = "bootfs" ] ; then
  68. if ! zfs_get_bootfs ; then
  69. # Lets import everything and try again
  70. zpool import ${ZPOOL_IMPORT_FLAGS} -N -a ${ZPOOL_FORCE}
  71. if ! zfs_get_bootfs ; then
  72. err "ZFS: Cannot find bootfs."
  73. exit 1
  74. fi
  75. fi
  76. fi
  77. local pool="${ZFS_DATASET%%/*}"
  78. local rwopt_exp="${rwopt:-rw}"
  79. if ! zpool list -H "${pool}" > /dev/null 2>&1; then
  80. if [ ! "${rwopt_exp}" = "rw" ]; then
  81. msg "ZFS: Importing pool ${pool} readonly."
  82. ZPOOL_IMPORT_FLAGS="${ZPOOL_IMPORT_FLAGS} -o readonly=on"
  83. else
  84. msg "ZFS: Importing pool ${pool}."
  85. fi
  86. if ! zpool import ${ZPOOL_IMPORT_FLAGS} -N "${pool}" ${ZPOOL_FORCE} ; then
  87. err "ZFS: Unable to import pool ${pool}."
  88. exit 1
  89. fi
  90. fi
  91. local node="$1"
  92. local rootmnt=$(zfs get -H -o value mountpoint "${ZFS_DATASET}")
  93. local tab_file="${node}/etc/fstab"
  94. local zfs_datasets="$(zfs list -H -o name -t filesystem -r ${ZFS_DATASET})"
  95. # Mount the root, and any child datasets
  96. for dataset in ${zfs_datasets}; do
  97. mountpoint=$(zfs get -H -o value mountpoint "${dataset}")
  98. canmount=$(zfs get -H -o value canmount "${dataset}")
  99. # skip dataset
  100. [ ${dataset} != "${ZFS_DATASET}" -a \( ${canmount} = "off" -o ${canmount} = "noauto" -o ${mountpoint} = "none" \) ] && continue
  101. if [ ${mountpoint} = "legacy" ]; then
  102. if [ -f "${tab_file}" ]; then
  103. if findmnt -snero source -F "${tab_file}" -S "${dataset}" > /dev/null 2>&1; then
  104. opt=$(findmnt -snero options -F "${tab_file}" -S "${dataset}")
  105. mnt=$(findmnt -snero target -F "${tab_file}" -S "${dataset}")
  106. zfs_decrypt_fs "${dataset}"
  107. mount -t zfs -o "${opt}" "${dataset}" "${node}${mnt}"
  108. fi
  109. fi
  110. else
  111. zfs_decrypt_fs "${dataset}"
  112. mount -t zfs -o "zfsutil,${rwopt_exp}" "${dataset}" "${node}/${mountpoint##${rootmnt}}"
  113. fi
  114. done
  115. }
  116. set_flags() {
  117. # Force import the pools, useful if the pool has not properly been exported using 'zpool export <pool>'
  118. [ ! "${zfs_force}" = "" ] && ZPOOL_FORCE="-f"
  119. # Disable late hook, useful if we want to use zfs-import-cache.service instead
  120. [ ! "${zfs_boot_only}" = "" ] && ZFS_BOOT_ONLY="1"
  121. # Add import directory to import command flags
  122. [ ! "${zfs_import_dir}" = "" ] && ZPOOL_IMPORT_FLAGS="${ZPOOL_IMPORT_FLAGS} -d ${zfs_import_dir}"
  123. [ "${zfs_import_dir}" = "" ] && [ -f /etc/zfs/zpool.cache.org ] && ZPOOL_IMPORT_FLAGS="${ZPOOL_IMPORT_FLAGS} -c /etc/zfs/zpool.cache.org"
  124. }
  125. run_hook() {
  126. set_flags
  127. # Wait 15 seconds for ZFS devices to show up
  128. [ "${zfs_wait}" = "" ] && ZFS_WAIT="15" || ZFS_WAIT="${zfs_wait}"
  129. case ${root} in
  130. # root=zfs
  131. "zfs")
  132. mount_handler="zfs_mount_handler"
  133. ;;
  134. # root=ZFS=... syntax (grub)
  135. "ZFS="*)
  136. mount_handler="zfs_mount_handler"
  137. ZFS_DATASET="${root#*[=]}"
  138. ;;
  139. # root=zfs:... syntax (dracut)
  140. "zfs:"*)
  141. mount_handler="zfs_mount_handler"
  142. ZFS_DATASET="${root#*[:]}"
  143. ;;
  144. esac
  145. case ${zfs} in
  146. "")
  147. # skip this line/dataset
  148. ;;
  149. auto|bootfs)
  150. ZFS_DATASET="bootfs"
  151. mount_handler="zfs_mount_handler"
  152. local pool="[a-zA-Z][^ ]*"
  153. ;;
  154. *)
  155. ZFS_DATASET="${zfs}"
  156. mount_handler="zfs_mount_handler"
  157. local pool="${ZFS_DATASET%%/*}"
  158. ;;
  159. esac
  160. # Allow at least n seconds for zfs device to show up. Especially
  161. # when using zfs_import_dir instead of zpool.cache, the listing of
  162. # available pools can be slow, so this loop must be top-tested to
  163. # ensure we do one 'zpool import' pass after the timer has expired.
  164. sleep ${ZFS_WAIT} & pid=$!
  165. local break_after=0
  166. while :; do
  167. kill -0 $pid > /dev/null 2>&1 || break_after=1
  168. if [ -c "/dev/zfs" ]; then
  169. zpool import ${ZPOOL_IMPORT_FLAGS} | awk "
  170. BEGIN { pool_found=0; online=0; unavail=0 }
  171. /^ ${pool} .*/ { pool_found=1 }
  172. /^\$/ { pool_found=0 }
  173. /UNAVAIL/ { if (pool_found == 1) { unavail=1 } }
  174. /ONLINE/ { if (pool_found == 1) { online=1 } }
  175. END { if (online == 1 && unavail != 1)
  176. { exit 0 }
  177. else
  178. { exit 1 }
  179. }" && break
  180. fi
  181. [ $break_after == 1 ] && break
  182. sleep 1
  183. done
  184. kill $pid > /dev/null 2>&1
  185. }
  186. run_latehook () {
  187. set_flags
  188. # only run zpool import, if flags were set (cache file found / zfs_import_dir specified) and zfs_boot_only is not set
  189. [ ! "${ZPOOL_IMPORT_FLAGS}" = "" ] && [ "${ZFS_BOOT_ONLY}" = "" ] && zpool import ${ZPOOL_IMPORT_FLAGS} -N -a ${ZPOOL_FORCE}
  190. }
  191. # vim:set ts=4 sw=4 ft=sh et: