AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 788561
Accepted
Carlo Wood
Carlo Wood
Asked: 2024-12-23 07:33:07 +0800 CST2024-12-23 07:33:07 +0800 CST 2024-12-23 07:33:07 +0800 CST

如何使用 udev 和 systemd 自动挂载 USB 驱动器的(加密)分区

  • 772

我有以下要求:

  • 如果插入了特定的 USB 存储设备,系统会自动检测并安装它。安装位置信息/etc/fstab(当然)会显示在这里。
  • 如果 USB 驱动器坏了,必须自动进行彻底清理;从而不会留下任何残留痕迹,例如它曾被安装(或仍然被安装)和/或任何未完成的 systemd 单元。
  • 如果手动编辑了有问题的分区umount,那么所有内容也应该被清理,即使该棒仍然存在;将其从插头中拔出应该只会清理之前无法清理的内容(可能是单元.device)。

为了以干净的方式实现上述操作,插入 USB 驱动器将导致自定义 systemd 单元的启动,然后我们将对其进行配置以完成其余操作。

写这个需要什么?

假设我们有一个有三个分区的 USB 驱动器: /dev/sdc1和/dev/sdc2- 它们是 LUKS 加密的,以及/dev/sdc3- 它们是未加密的。

例如,

>sudo blkid /dev/sdc{1,2,3}
/dev/sdc1: UUID="de9a000f-fa60-4050-8d11-864e97829b8a" TYPE="crypto_LUKS" PARTUUID="219578f5-5905-41cd-895d-7b937ac0756a"
/dev/sdc2: UUID="012059c1-6b88-4937-889a-a21448740492" TYPE="crypto_LUKS" PARTUUID="59c7f6f3-a5cb-41af-82fa-b515031d85d5"
/dev/sdc3: LABEL="README" UUID="9d262deb-2343-4096-bccf-bb26ed4415ad" BLOCK_SIZE="1024" TYPE="ext2" PARTUUID="165db3da-4ebb-4f59-ab09-ee5e5b26ed43"

以下UUID是所使用的内容/dev/disk/by-uuid:

>ls -l /dev/disk/by-uuid | grep sdc
lrwxrwxrwx 1 root root 10 Dec 22 18:27 012059c1-6b88-4937-889a-a21448740492 -> ../../sdc2
lrwxrwxrwx 1 root root 10 Dec 22 18:27 9d262deb-2343-4096-bccf-bb26ed4415ad -> ../../sdc3
lrwxrwxrwx 1 root root 10 Dec 22 18:27 de9a000f-fa60-4050-8d11-864e97829b8a -> ../../sdc1

由于设备名称/dev/sdc相当随机,从现在开始我们应该只使用 UUID。

为了手动挂载未加密的分区,我们需要执行(确保 /mnt 和/或 /mnt/usb 上尚未挂载任何内容!):

UUID=9d262deb-2343-4096-bccf-bb26ed4415ad
SERIAL=408D5CBECAC3E7C0E9170AEC
sudo /bin/mkdir -p /mnt/usb/$SERIAL/part3
sudo mount -t ext2 /dev/disk/by-uuid/$UUID /mnt/usb/$SERIAL/part3

我确实选择使用 USB 设备的值ID_SERIAL_SHORT,例如可以通过以下方式获取:

>udevadm info -n /dev/sdc3 | grep -E '(ID_SERIAL_SHORT|ID_FS_UUID)='
E: ID_SERIAL_SHORT=408D5CBECAC3E7C0E9170AEC
E: ID_FS_UUID=9d262deb-2343-4096-bccf-bb26ed4415ad

/etc/fstab让我们通过向该文件添加以下内容来存储挂载点:

UUID=9d262deb-2343-4096-bccf-bb26ed4415ad   /mnt/usb/408D5CBECAC3E7C0E9170AEC/part3 ext2    rw,noauto   0 0

请注意,此后我们可以执行以下操作:

sudo mount /dev/sdc3

只要该块设备具有预期的 UUID,它就能顺利完成挂载。

另外两个分区/dev/sdc{1,2}已加密,首先需要

UUID1=573bfda0-69f5-4fb9-9d7e-333a70a51710
UUID2=6d7bdc1d-3eb0-4774-a9fb-3d1ac1027010
echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID1 usb-$UUID1
echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID2 usb-$UUID2

要执行的命令,我假设环境变量LUKS_PASS包含解​​密 LUKS 分区所需的密码。这里的 UUID 是 blkid 返回的:

>sudo blkid /dev/sdc{1,2}
/dev/sdc1: UUID="573bfda0-69f5-4fb9-9d7e-333a70a51710" TYPE="crypto_LUKS" PARTUUID="caa57a9d-7389-4990-a243-34b19a179368"
/dev/sdc2: UUID="6d7bdc1d-3eb0-4774-a9fb-3d1ac1027010" TYPE="crypto_LUKS" PARTUUID="10813450-6dcc-4700-9e88-d0577c0c9aeb"

这将为/dev/mapper/usb-$UUID每个分区创建一个设备,每个设备都有自己的 UUID:

>sudo blkid /dev/mapper/usb*
/dev/mapper/usb-573bfda0-69f5-4fb9-9d7e-333a70a51710: LABEL="gold1-2024-12-21" UUID="67f056be-dbbc-4f7a-979d-6ff077d16e93" BLOCK_SIZE="4096" TYPE="ext2"
/dev/mapper/usb-6d7bdc1d-3eb0-4774-a9fb-3d1ac1027010: LABEL="gold2-2024-12-21" UUID="94bfcc55-6b52-431f-bc76-3aa198c107c7" BLOCK_SIZE="4096" TYPE="ext2"

这些就是我们想要添加的 UUID /etc/fstab。

为了让生活更轻松,我编写了以下脚本,该脚本/etc/fstab在插入 USB 后运行时打印所需的配置(就我的特定情况而言):

# Get the device path.
DEVPATH=$(/bin/ls /dev/disk/by-id/usb-Kingston_DataTraveler* | grep -E -v -- '-part[0-9]+$')
# Get the serial of the USB stick.
ID_SERIAL_SHORT=$(udevadm info -n $DEVPATH | grep ID_SERIAL_SHORT | sed -e 's/.*ID_SERIAL_SHORT=//')
# Run over all existing partitions.
for p in $(/bin/ls $DEVPATH-part*); do
  # Extract the "part?" string.
  PART=$(echo $p | sed -r -e 's/.*-(part[0-9]+)$/\1/')
  # Get the UUID of the block device of this partition iff it is a LUKS encrypted partition.
  UUID=$(blkid --match-token TYPE=crypto_LUKS $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/' || true)
  if [ -n "$UUID" ]; then
    # Decrypt the partition.
    echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID usb-$UUID
    # Get the UUID of the encrypted partition.
    UUID2=$(blkid /dev/mapper/usb-$UUID | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
    echo -e "UUID=$UUID2\t/mnt/usb/$ID_SERIAL_SHORT/$PART\text2\trw,noauto\t0 0"
    sudo cryptsetup luksClose usb-$UUID
  else
    # Get UUID of non-encrypted partition.
    UUID=$(blkid $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
    echo -e "UUID=$UUID\t/mnt/usb/$ID_SERIAL_SHORT/$PART\text2\trw,noauto\t0 0"
  fi
done

导出后,LUKS_PASS请确保运行上述脚本sudo -E以保存环境。示例输出:

>sudo -E ./foo.sh
UUID=db97c4b0-8f92-4edc-bacb-dc90e62de2e2       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part1 ext2    rw,noauto       0 0
UUID=c45c6a7a-1c0f-49c4-bcfb-15766114daa1       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part2 ext2    rw,noauto       0 0
UUID=9d262deb-2343-4096-bccf-bb26ed4415ad       /mnt/usb/408D5CBECAC3E7C0E9170AEC/part3 ext2    rw,noauto       0 0

设置完毕/etc/fstab并确切知道需要什么命令后,剩下的就是使用udev和来自动执行此操作systemd units。

问题是:如何做?

udev
  • 2 2 个回答
  • 59 Views

2 个回答

  • Voted
  1. Carlo Wood
    2024-12-25T05:53:28+08:002024-12-25T05:53:28+08:00

    编辑:TLDR;您想先阅读我的其他答案。

    这是我研究的逐步报告。

    调试日志记录

    可能有更好的方法,但以下方法有效:我创建了一个可执行脚本,/usr/local/sbin/log_service内容如下:

    #! /bin/sh
    echo "$1 : \"$2\""
    

    当使用它运行时,/bin/systemd-cat -t usbtest它会将脚本写入标准输出的输出写入日志。

    为了监控输出,我们在单独的终端窗口中运行:

    journalctl -b -t systemd -t usbtest -f
    

    实时监控输出。

    假冒 .device

    首先,我创建了一个虚假的服务,将其用作虚假的“.device”(尽管这是一个.service)。

    创建以下文件/etc/systemd/system/A.service:

    [Unit]
    Description=A fake device (service A)
    
    [Service]
    ExecStart=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service A start
    ExecStop=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service A stop
    
    [Install]
    WantedBy=multi-user.target
    

    然后运行sudo systemctl daemon-reload以使正在运行的 systemd 知道它。

    尝试启动新服务:

    sudo systemctl start A
    

    这会导致立即的日志输出:

    Dec 23 14:20:16 daniel systemd[1]: Started A fake device (service A).
    Dec 23 14:20:16 daniel usbtest[1583423]: A : "start"
    Dec 23 14:20:16 daniel usbtest[1583428]: A : "stop"
    Dec 23 14:20:16 daniel systemd[1]: A.service: Deactivated successfully.
    

    原因是正在运行的脚本 ( /usr/local/sbin/log_service) 立即返回。由于我们想要模拟可以通过 systemctl 启动和停止的服务,因此我们添加了以下行RemainAfterExit=yes:

    [Unit]
    Description=A fake device (service A)
    Wants=B.service         # See below.
    
    [Service]
    Type=oneshot            # See below.
    RemainAfterExit=yes
    ExecStart=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service A start
    ExecStop=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service A stop
    
    [Install]
    WantedBy=multi-user.target
    

    现在我们可以sudo systemctl start A分别了sudo systemctl stop A。

    假挂载 .service

    让我们对新服务再次执行此操作B。我们的想法是,我们希望在启动B时自动启动,并且停止时必须导致停止。但是,手动停止不应停止。AABBA

    因为 B 将成为 A 的依赖项,并且两者都进行日志记录,所以我们希望 B在A 完成日志记录(A 的 ExecStart 完成)后启动,因此A.service再次更改并添加Type=oneshot;我们希望确保所有 ExecStart 在任何依赖服务启动之前完成。请注意,另一个区别是,对于 类型oneshot,如果有多ExecStart行,则它们将按顺序完成,并且下一行仅在前一行完成后启动Type=simple,而 (默认值)则ExecStart并行启动所有行(因为它们预计不会快速完成,或者根本不会完成)。但是,这与我们无关:我们只有一行ExecStart(一个确实“立即”返回的行,否则您无法使用oneshot)。

    我不会重复我在这里尝试过的所有事情;只重复最终有效的结果:

    [Unit]
    Description=Fake mount service (service B)
    After=A.service
    Requisite=A.service
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service B start
    ExecStop=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service B stop
    
    [Install]
    WantedBy=A.service
    

    (不要忘记重新运行sudo systemctl daemon-reload)。

    我们的需要After=A.service不言而喻:如果设备不存在(即 .device 单元处于活动状态),则无法安装该设备;因此,我们不想在 A 启动之后才启动 B。

    为了使每次启动 A 时都启动 B,仅有 是不够的WantedBy=A.service,我们需要添加Wants=B.service到[Unit]部分A.service!

    这样,如果我们启动 A,B 也会启动。如果我们尝试启动 B,我们不希望 A 启动:这没有意义。因此我们使用Requisite=A.service而不是Requires=A.service(或BindsTo=A.service)。因此,如果 A 未处于活动状态,这将巧妙地拒绝启动 B(根据文档):

    >sudo systemctl start B
    A dependency job for B.service failed. See 'journalctl -xe' for details.
    

    而不是启动 A。

    一旦两者都处于活动状态,我们也可以停止 B;这只会停止 B(我们甚至无法停止 .device,因为那没有意义:它仍然插入)。systemctl start A此后运行(即使 A已经处于活动状态)会再次启动 B,这可能正是我们想要的。

    最后,在两个活动状态都处于活动状态时停止 A,会先停止 B,然后停止 A - 这正是我们想要的。然而,这很奇怪,因为从文档中我了解到这是一个BindsTo=具有以下描述的功能:

    配置需求依赖项,风格与 Requires= 非常相似。但是,这种依赖项类型更强大:除了 Requires= 的效果外,它还声明如果绑定到的单元停止,此单元也将停止。这意味着绑定到另一个突然进入非活动状态的单元的单元也将停止。单元可能由于各种原因突然、意外地进入非活动状态:服务单元的主进程可能会自行终止,设备单元的支持设备可能会被拔出,或者挂载单元的挂载点可能会在无需系统和服务管理器参与的情况下被卸载。

    它准确地描述了我们需要什么(“设备单元可能被拔掉”)。虽然的文档Requisite=是

    与 Requires= 类似。但是,如果此处列出的单元尚未启动,则它们将不会启动,并且此单元的启动将立即失败。Requisite= 不表示顺序依赖性,即使两个单元在同一个事务中启动。因此,此设置通常应与 After= 结合使用,以确保此单元不会在其他单元之前启动。

    所以,我们想要这个(如果 A 尚未启动,则启动 B 将失败,而不是尝试启动 B),但它没有提到BindsTo我们还需要的部分(停止 A 将停止 B)。

    但是,如果我添加BindsTo=A.service的功能Requisite=无效,启动 B 只会启动 A(首先)。systemd 中存在错误(在 Arch 上使用版本 257-1 进行测试)?

    使用真实设备(udev)

    我们可以添加一条udev规则来检测我想要检测的 USB 存储棒的插入情况。请注意,即使没有这样的规则,插入 USB 存储棒也会导致.device创建单元:

    >systemctl list-units --type=device | grep usb8
      sys-devices-pci0000:00-0000:00:08.1-0000:0c:00.3-usb8-8\x2d4-8\x2d4.3-8\x2d4.3:1.0-host12-target12:0:0-12:0:0:0-block-sdc-sdc1.device loaded active plugged DataTraveler_3.0 1
      sys-devices-pci0000:00-0000:00:08.1-0000:0c:00.3-usb8-8\x2d4-8\x2d4.3-8\x2d4.3:1.0-host12-target12:0:0-12:0:0:0-block-sdc-sdc2.device loaded active plugged DataTraveler_3.0 2
      sys-devices-pci0000:00-0000:00:08.1-0000:0c:00.3-usb8-8\x2d4-8\x2d4.3-8\x2d4.3:1.0-host12-target12:0:0-12:0:0:0-block-sdc-sdc3.device loaded active plugged DataTraveler_3.0 README
      sys-devices-pci0000:00-0000:00:08.1-0000:0c:00.3-usb8-8\x2d4-8\x2d4.3-8\x2d4.3:1.0-host12-target12:0:0-12:0:0:0-block-sdc.device      loaded active plugged DataTraveler_3.0
    

    但是因为这些设备的名称.device可能会改变(如果仅基于我插入它们的位置),我们不能像以前那样使用它们的名称A.service。此外,我们需要Wants=B.service为这些设备添加 - 这些设备没有。最后,我们不想B.service为任何USB 设备及其分区和母设备触发,而只想为我上面列出的分区触发。

    所有这些都可以通过创建新的 udev 规则来实现。

    当 USB 驱动器插入时(它有第三个分区 /dev/sdc3),我们可以运行例如:

    >sudo udevadm test /sys/class/block/sdc3 2>/dev/null | grep -E '(DEVPATH|DEVNAME|ACTION|SUBSYSTEM|TAGS|ID_BUS|ID_SERIAL|DEVLINKS|SYSTEMD_WANTS)'
      DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/target12:0:0/12:0:0:0/block/sdc/sdc3
      DEVNAME=/dev/sdc3
      ACTION=add
      SUBSYSTEM=block
      TAGS=:systemd:
      ID_BUS=usb
      ID_SERIAL=Kingston_DataTraveler_3.0_408D5CBF5F0AE7C0E9150B70-0:0
      ID_SERIAL_SHORT=408D5CBF5F0AE7C0E9150B70
      DEVLINKS=/dev/disk/by-diskseq/115-part3 /dev/disk/by-path/pci-0000:0c:00.3-usb-0:4.4:1.0-scsi-0:0:0:0-part/by-partuuid/deb41f73-7955-4eba-8fac-2c7d01b93af4 /dev/disk/by-id/usb-Kingston_DataTraveler_3.0_408D5CBF5F0AE7C0E9150B70-0:0-part3 /dev/disk/by-path/pci-0000:0c:00.3-usb-0:4.4:1.0-scsi-0:0:0:0-part/by-label/README /dev/disk/by-path/pci-0000:0c:00.3-usb-0:4.4:1.0-scsi-0:0:0:0-part3 /dev/disk/by-partuuid/deb41f73-7955-4eba-8fac-2c7d01b93af4 /dev/disk/by-path/pci-0000:0c:00.3-usbv3-0:4.4:1.0-scsi-0:0:0:0-part3 /dev/disk/by-path/pci-0000:0c:00.3-usb-0:4.4:1.0-scsi-0:0:0:0-part/by-partnum/3 /dev/disk/by-uuid/046ba81b-7d9c-4a54-a69f-6fba06ac7754 /dev/disk/by-label/README /dev/disk/by-path/pci-0000:0c:00.3-usb-0:4.4:1.0-scsi-0:0:0:0-part/by-uuid/046ba81b-7d9c-4a54-a69f-6fba06ac7754
      CURRENT_TAGS=:systemd:
    

    请注意,这告诉我们 udev 知道一切(特别是 UUID 和 ID_SERIAL_SHORT),但 SYSTEMD_WANTS 尚未设置。我们需要将其设置为B.service。

    接下来我们通过创建内容如下的文件来创建一个新的 udev 规则/etc/udev/rules.d/69-gold-usb.rules:

    SUBSYSTEMS=="block", ENV{ID_SERIAL_SHORT}=="408D5CBECBBDE7C0E9160666|408D5CBF5F0AE7C0E9150B70|408D5CBECAC3E7C0E9170AEC", ENV{SYSTEMD_WANTS}="B.service"
    

    并使用 重新加载sudo udevadm control --reload-rules。sudo udevadm test /sys/class/block/sdc3再次运行时(如上所示),我们得到相同的结果,但多出一行:

    >sudo udevadm test /sys/class/block/sdc3 2>/dev/null | grep -E '(DEVPATH|DEVNAME|ACTION|SUBSYSTEM|TAGS|ID_BUS|ID_SERIAL|DEVLINKS|SYSTEMD_WANTS)'
    ...
      SYSTEMD_WANTS=B.service
    

    表明该规则有效。

    我们可以在运行时插入和拔出 USB 来测试同样的事情

    sudo udevadm monitor --property | grep -E '^UDEV.*(add|remove)|^SYSTEMD_WANTS' | grep -B1 SYSTEMD_WANTS
    

    添加 USB 记忆棒后,会出现以下结果:

    UDEV  [182604.679907] add      /devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/target12:0:0/12:0:0:0/block/sdc (block)
    SYSTEMD_WANTS=B.service
    UDEV  [182604.703373] add      /devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/target12:0:0/12:0:0:0/block/sdc/sdc2 (block)
    SYSTEMD_WANTS=B.service
    UDEV  [182604.705526] add      /devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/target12:0:0/12:0:0:0/block/sdc/sdc1 (block)
    SYSTEMD_WANTS=B.service
    UDEV  [182604.708620] add      /devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/target12:0:0/12:0:0:0/block/sdc/sdc3 (block)
    SYSTEMD_WANTS=B.service
    

    拔掉电源插头时也一样remove。

    请注意规则是如何针对这两个分区/dev/sdc以及三个分区中的每一个分区触发的。因为我们只对分区感兴趣,所以让我们添加ENV{DEVTYPE}=="partition"到 udev 规则中:

    SUBSYSTEMS=="block", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL_SHORT}=="408D5CBECBBDE7C0E9160666|408D5CBF5F0AE7C0E9150B70|408D5CBECAC3E7C0E9170AEC", ENV{SYSTEMD_WANTS}="B.service"
    

    我们现在有一条udev规则,可以检测三个 USB 棒中的一个的插入,然后启动B.service。但是B.service它本身缺少正确的和After=,因此拔下 USB 棒还不能达到预期的效果。Requisite=WantedBy=

    将 .device 的依赖添加到 B.service

    如果我们只有一个分区需要处理(比如,这个特定 USB 驱动器的 /dev/sdc3),那么添加依赖项的一个直接方法似乎是使用以下方法之一DEVLINKS- 最值得注意的是,

    /dev/disk/by-uuid/046ba81b-7d9c-4a54-a69f-6fba06ac7754
    

    当然,对于给定的分区,这是完全固定的。但是,这些路径名必须转义:所有斜杠都必须替换为破折号 ( -),如果我们尝试:

    Requisite=dev-disk-by-uuid-046ba81b-7d9c-4a54-a69f-6fba06ac7754.device
    

    B.service然后我们在启动时得到以下有趣的错误:

    Dec 23 20:01:45 daniel systemd[1]: /dev/disk/by/uuid/046ba81b/7d9c/4a54/a69f/6fba06ac7754 is inactive.
    Dec 23 20:01:45 daniel systemd[1]: Dependency failed for Fake mount service (service B).
    

    -请注意,UUID 中的也被转换为/。生成此字符串的正确方法是使用

    >systemd-escape dev/disk/by-uuid/046ba81b-7d9c-4a54-a69f-6fba06ac7754.device
    dev-disk-by\x2duuid-046ba81b\x2d7d9c\x2d4a54\x2da69f\x2d6fba06ac7754.device
    

    但是,使用这个字符串B.service代替字符串A.service表明,即使我们拔掉 USB 棒,也不会B停止(正如查看文档所预期的那样)。因此,我们确实需要添加一行。BindsTo=

    B.service然后变成:

    [Unit]
    Description=Fake mount service (service B)
    After=dev-disk-by\x2duuid-046ba81b\x2d7d9c\x2d4a54\x2da69f\x2d6fba06ac7754.device
    Requisite=dev-disk-by\x2duuid-046ba81b\x2d7d9c\x2d4a54\x2da69f\x2d6fba06ac7754.device
    BindsTo=dev-disk-by\x2duuid-046ba81b\x2d7d9c\x2d4a54\x2da69f\x2d6fba06ac7754.device
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service B start
    ExecStop=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service B stop
    
    [Install]
    WantedBy=dev-disk-by\x2duuid-046ba81b\x2d7d9c\x2d4a54\x2da69f\x2d6fba06ac7754.device
    

    有趣的是,现在尝试启动sudo systemctl start B不会失败,但如果没有插入 USB,它就会挂起,直到您将其插入。我想这还挺巧妙的。

    无论如何,插入 USB 都会自动启动B。此时我们可以B像以前一样手动停止,此时B插入 USB 后系统会停止,然后我们可以手动(重新)启动,B而不会挂起。

    我们可以将(动态)UUID 传递给服务,稍微像 hack 一样,操作如下:

    ENV{SYSTEMD_WANTS}="B@$env{ID_FS_UUID}.service"
    

    and then use %i in the .service to access the UUID. I call this a "hack" because it only works because an UUID doesn't contain illegal character for an escaped string: you SHOULD pass a systemd escaped string here; in that case %i would be that escaped string and %I would be the original, unescaped string.

    However, this is not usable: we NEED the escaped string for After=, Requisite=, BindsTo= and WantedBy= because there is no way to manipulate %i: it must be used as-is.

    There is a way to create the escaped string during the udev rule, but due to a bug in udev it is impossible to actually use that if it contains any backslashes (it is beyond me how such a horrible bug can exist for years without anyone fixing it).

    The only remaining option therefore is to use SYMLINK with a path that doesn't contain any characters that need to be escaped with a backslash (aka, no '-' characters). In fact, we can pass any information using this trick!

    SUBSYSTEMS=="block", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL_SHORT}=="408D5CBECBBDE7C0E9160666|408D5CBF5F0AE7C0E9150B70|408D5CBECAC3E7C0E9170AEC", PROGRAM="/usr/local/sbin/udev-escape '$env{ID_FS_UUID}'", SYMLINK+="usb/%c", ENV{SYSTEMD_WANTS}="B@usb-%c.service"
    

    This way we can pass (any) information (in this case only ID_FS_UUID) while still using %i for the dependency stuff in the @.service template! We only have to make sure that the %c, the output of PROGRAM (and thus the SYMLINK) does not contain any characters that need to be escaped or are escaped with a backslash.

    The /usr/local/sbin/udev-escape that I use contains:

    #! /bin/sh
    
    # Utility to convert any string to something that will
    # survive being passed through %c of an udev rule to
    # a systemd service using SYSTEMD_WANTS.
    # Written by Carlo Wood (Dec 2024).
    
    echo "$1" | /usr/bin/base64 -w0 | /usr/bin/tr '+/=' '.:_'
    

    Simple, but you have to admit, brilliant.

    The counter part is /usr/local/sbin/udev-unescape:

    #! /bin/sh
    
    # Utility to decode string that were produced by udev-unescape.
    # Carlo Wood (Dec 2024).
    
    echo "$1" | /usr/bin/tr '.:_' '+/=' | /usr/bin/base64 -d
    

    Warning: the output of this program can contain ANYTHING, including newline characters. So you should be very careful how to use it in a service, that is being run by root and stuff. Probably best to apply some filtering.

    Using the above, %i is going to have a value like MDQ2YmE4MWItN2Q5Yy00YTU0LWE2OWYtNmZiYTA2YWM3NzU0Cg__. The device path then is /dev/%I, or /dev/usb/MDQ2YmE4MWItN2Q5Yy00YTU0LWE2OWYtNmZiYTA2YWM3NzU0Cg__, which will be a symlink to for example /dev/sdc3. And inside the service scripts we can retrieve our information by passing %i to udev-unescape:

    >udev-unescape MDQ2YmE4MWItN2Q5Yy00YTU0LWE2OWYtNmZiYTA2YWM3NzU0Cg__
    046ba81b-7d9c-4a54-a69f-6fba06ac7754
    

    Hence, [email protected] now becomes:

    [Unit]
    Description=Fake mount service (service B)
    After=dev-%i.device
    Requisite=dev-%i.device
    BindsTo=dev-%i.device
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service "%i" start
    ExecStop=/bin/systemd-cat -t usbtest /usr/local/sbin/log_service "%I" stop
    
    [Install]
    WantedBy=dev-%i.device
    

    Passing arbitrary information from udev to a service.

    Udev has a lot of information available, it would be a waste not to pass that. With the USB stick inserted, you can now easily see which partitions are available:

    >ls -l /dev/usb
    lrwxrwxrwx  1 root root       7 Dec 24 15:58 MDQ2YmE4MWItN2Q5Yy00YTU0LWE2OWYtNmZiYTA2YWM3NzU0Cg__ -> ../sdc3
    lrwxrwxrwx  1 root root       7 Dec 24 15:58 NDc1NTNmNjQtZTRiMS00YWQxLTg5ZTItNzMxMzNiZGFhNjVkCg__ -> ../sdc2
    lrwxrwxrwx  1 root root       7 Dec 24 15:58 YTUyZWU4ZjAtYThmYS00MjAwLThjMTktZDc5ODMwM2FjMTE4Cg__ -> ../sdc1
    

    Lets say we want to investigate what information is available for sdc1. We can list the ATTR values with:

    >sudo udevadm info -a -p $(udevadm info -q path -n /dev/sdc1)
    ...
      looking at device '/devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/targ>
        KERNEL=="sdc1"
        SUBSYSTEM=="block"
        DRIVER==""
        ATTR{alignment_offset}=="0"
        ATTR{discard_alignment}=="0"
        ATTR{inflight}=="       0        0"
    ...etc
    

    And we can inspect the udev environment variables using - say:

    >sudo udevadm test $(udevadm info -q path -n /dev/usb/YTUyZWU4ZjAtYThmYS00MjAwLThjMTktZDc5ODMwM2FjMTE4Cg__)
    ...
    Reading rules file: /etc/udev/rules.d/69-gold-usb.rules
    ...
    sdc1: /etc/udev/rules.d/69-gold-usb.rules:2 Running PROGRAM="/usr/local/sbin/udev-escape 'a52ee8f0-a8fa-4200-8c19-d798303ac118'"
    sdc1: Running in test mode, skipping execution of '/usr/local/sbin/udev-escape 'a52ee8f0-a8fa-4200-8c19-d798303ac118 crypto_LUKS''.
    ...
    Properties:
      DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:0c:00.3/usb8/8-4/8-4.4/8-4.4:1.0/host12/target12:0:0/12:0:0:0/block/sdc/sdc1
      DEVNAME=/dev/sdc1
      DEVTYPE=partition
    ...
      ACTION=add
      SUBSYSTEM=block
      TAGS=:systemd:
      ID_BUS=usb
      ID_MODEL=DataTraveler_3.0
      ID_MODEL_ENC=DataTraveler\x203.0
      ID_MODEL_ID=1666
      ID_SERIAL=Kingston_DataTraveler_3.0_408D5CBF5F0AE7C0E9150B70-0:0
      ID_SERIAL_SHORT=408D5CBF5F0AE7C0E9150B70
    ...
      ID_TYPE=disk
      ID_FS_UUID=a52ee8f0-a8fa-4200-8c19-d798303ac118
      ID_FS_TYPE=crypto_LUKS
    ...
      [email protected]
    

    Note that SYSTEMD_WANTS has an empty %i because we never really did run udev-escape in this case.

    We're going to need the value of ID_FS_TYPE too, so lets change the udev rule to its FINAL VERSION:

    daniel:/etc/udev/rules.d>cat 69-gold-usb.rules
    # Detect insertion of any of the three "Kingston DataTravel SE9 G3 64GB" USB security sticks.
    SUBSYSTEMS=="block", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL_SHORT}=="408D5CBECBBDE7C0E9160666|408D5CBF5F0AE7C0E9150B70|408D5CBECAC3E7C0E9170AEC", ACTION=="add", PROGRAM="/usr/local/sbin/udev-escape '$env{ID_FS_UUID} $env{ID_FS_TYPE}'", SYMLINK+="usb/%c", ENV{SYSTEMD_WANTS}="mount-gold@usb-%c.service"
    
    # Added 22 December 2024.
    # After changing this file run: sudo udevadm control --reload-rules
    

    Here I added $env{ID_FS_TYPE} to the encoding of the symlink.

    Adding auto-mounting

    We're now done with udev. [email protected] is started properly and we can pass any information (from udev) to it that we need.

    To finish this project, lets rename [email protected] to something normal and change it to call some real scripts.

    What I did was create the file /etc/systemd/system/[email protected] with the following content:

    [Unit]
    Description=Mount Gold USB device /dev/%I.
    After=dev-%i.device
    Requisite=dev-%i.device
    BindsTo=dev-%i.device
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/usr/local/sbin/usb-gold-start %i
    ExecStop=/usr/local/sbin/usb-gold-stop %i
    
    [Install]
    WantedBy=dev-%i.device
    

    This incorporates everything we learned about the unit file leaving us only with the task of writing the scripts.

    TODO

    This answer is getting so long that I decided to post it already. I'll make updates to the remaining parts once I got it fully working.

    My current files are: /usr/local/sbin/usb-gold-start:

    #! /bin/sh
    
    # Strip the leading "usb-" from the argument.
    ENCODED_DATA=${1:4}
    # Decode the data that was passed.
    DATA="$(/usr/local/sbin/udev-unescape $ENCODED_DATA)"
    # Extract the value of ID_FS_UUID (the first argument).
    ID_FS_UUID=${DATA%%\ *}
    # Extract the value of ID_FS_TYPE (the second argument).
    ID_FS_TYPE=${DATA#*\ }
    # Cache the block device path of the partition.
    DEVPATH="/dev/disk/by-uuid/$ID_FS_UUID"
    
    # Use /etc/fstab to mount the partition.
    if [ "$ID_FS_TYPE" = "ext2" ]; then
      mount $DEVPATH
    elif [ "$ID_FS_TYPE" = "crypto_LUKS" ]; then
      /usr/local/sbin/get-usb-gold-luks-pass | cryptsetup -q luksOpen $DEVPATH usb-$ID_FS_UUID
      mount $(blkid --output export /dev/mapper/usb-$ID_FS_UUID | grep '^UUID=')
    fi
    

    /usr/local/sbin/usb-gold-stop:

    #! /bin/sh
    
    # Strip the leading "usb-" from the argument.
    ENCODED_DATA=${1:4}
    # Decode the data that was passed.
    DATA="$(/usr/local/sbin/udev-unescape $ENCODED_DATA)"
    # Extract the value of ID_FS_UUID (the first argument).
    ID_FS_UUID=${DATA%%\ *}
    # Extract the value of ID_FS_TYPE (the second argument).
    ID_FS_TYPE=${DATA#*\ }
    
    ACTION=stop
    
    echo $1 > /tmp/"$ACTION"_$ID_FS_UUID
    echo $ENCODED_DATA >> /tmp/"$ACTION"_$ID_FS_UUID
    echo $DATA >> /tmp/"$ACTION"_$ID_FS_UUID
    echo $ID_FS_UUID >> /tmp/"$ACTION"_$ID_FS_UUID
    echo $ID_FS_TYPE >> /tmp/"$ACTION"_$ID_FS_UUID
    
    if [ "$ID_FS_TYPE" = "ext2" ]; then
      # Nothing to be done: it is already unmounted by the time the service is stopping.
      #umount /dev/disk/by-uuid/$ID_FS_UUID
      true
    fi
    

    EDIT (28 December 2024):

    It turned out that also the SYMLINK trick can't be used, because that ".device" is killed during the cryptsetup, causing the .service to be terminated before cryptsetup returns. The latter still gets the chance to finish what it is doing, but mounting afterwards is a no-go.

    Therefore, we need(ed) another method: One can use IMPORT{program} which preserves backslashes. For example: IMPORT{program}="/usr/local/sbin/udev-gold-usb-systemd_wants.sh $env{ID_SERIAL_SHORT}-part$env{PARTN}", in which case it is theoretically possible to write double backslashes to the output of udev-gold-usb-systemd_wants.sh which then become single backslashes. However, this is deprecated because it has been decided (I think) that this 8-year-old bug just has to be fixed (see the previous link to the systemd issue; which has considerably grown since I added it). For the time being, it is best to stay away from trying to pass backslashes completely.

    • 2
  2. Best Answer
    Carlo Wood
    2024-12-30T00:35:06+08:002024-12-30T00:35:06+08:00

    This answer is written after I solved the problem (as opposed to writing it while I was doing all the research).

    systemd already supports mounting encrypted filesystems out of the box. The following points are required:

    • A new udev rule to trigger a custom template service unit.
    • A custom service to trigger systemd's mount unit.
    • An entry in /etc/fstab for each partition, from which systemd-fstab-generator will generate the required mount unit.
    • An entry in /etc/crypttab for each partition that is encrypted, from which systemd-cryptsetup-generator will generate the required [email protected] template unit.

    While implementing this we have to take into account that udev rules and systemd escape functionality do not play well together. We need to avoid the use of backslashes or it either won't work or will break in the near future when these bugs are fixed.

    The custom service is only required because a mount unit can not be a template.

    The udev rule

    The udev rule can contain tests like ATTR{removable}=="1". To get a full list of possible ATTR tests, run:

    sudo udevadm info --attribute-walk --name=/dev/sdc1
    

    where /dev/sdc1 is the device path of a currently inserted USB stick partition.

    Likewise, one can get a list of possible ENV{<property name>}=="<regular expression>" values with:

    sudo udevadm info --query=property --export --name=/dev/sdc1
    

    which will list lines as ID_SERIAL_SHORT='408D5CBF5F0AE7C0E9150B70'. For example, my /etc/udev/rules.d/69-gold-usb.rules contains:

    # Detect insertion of any of the three "Kingston DataTravel SE9 G3 64GB" USB security sticks.
    SUBSYSTEMS=="block", \
    ENV{DEVTYPE}=="partition", \
    ENV{ID_SERIAL_SHORT}=="408D5CBECBBDE7C0E9160666|408D5CBF5F0AE7C0E9150B70|408D5CBECAC3E7C0E9170AEC", \
    ACTION=="add", \
    IMPORT{program}="/usr/local/sbin/udev-gold-usb-systemd_wants.sh $env{ID_SERIAL_SHORT}-part$env{PARTN}"
    
    # Added 28 December 2024.
    # After changing this file run: sudo udevadm control --reload-rules
    

    And yes, you can use backslashes to split up long lines. Just don't forget to use == (not =) and don't forget the comma's.

    Note that this rule uses IMPORT{program} which is currently (December 2024) the only way to pass double backslashes to a SYSTEMD_WANTS property, but as the behavior of systemd is going to change in that regard (no longer requiring double backslashes), we shouldn't be using backslashes. This means that we can use slashes ('/' for paths) which are converted to a hyphen ('-'), but we can't use a hypen ('-' as appear in UUIDs) which would be escaped as '\x2d'.

    The script /usr/local/sbin/udev-gold-usb-systemd_wants.sh contains:

    #! /bin/sh
    echo "SYSTEMD_WANTS=mount-gold@$1.service"
    

    and we are passing to it something like 408D5CBF5F0AE7C0E9150B70-part1 where the hypen is an escaped '/'! This string therefore must be used as, and thus exist, as part of a path that contains "408D5CBF5F0AE7C0E9150B70/part1" or it can't work.

    Don't forget to run

    sudo udevadm control --reload-rules
    

    to re-read all udev rules after you made changes.

    The template service [email protected]

    As said before, this custom service is only required to allow the udev rule to have a variable path (although, I copied the idea from here without really testing if we can't specify the .mount directly).

    For the rest is does nothing, but a StartExec is required. Therefore this unit file contains:

    [Unit]
    Description=Mount Gold USB device /mnt/usb/%I.
    Requires=mnt-usb-%i.mount
    After=mnt-usb-%i.mount
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/bin/true
    

    And as .mount unit files use their mount point as name, this means that this specifies the mount point as /mnt/usb/408D5CBF5F0AE7C0E9150B70/part1! Fortunately we don't have to create that directory ourselves, see below.

    The /etc/fstab entry.

    /etc/fstab can contain entries that will cause systemd to generate .mount unit files, normally at boot time, but you can re-trigger this by running:

    sudo systemctl daemon-reload
    

    Here is an example of what is in my /etc/fstab:

    UUID=5b214986-7db3-439c-99ba-f4da0cebdf34       /mnt/usb/408D5CBF5F0AE7C0E9150B70/part1 ext2    rw,noauto,noatime,x-mount.mkdir,x-systemd.requires=systemd-cryptsetup@usba52ee8f0a8fa42008c19d798303ac118.service       0 0
    UUID=a3c7ffb6-3883-42a9-a880-e51f10e69f30       /mnt/usb/408D5CBF5F0AE7C0E9150B70/part2 ext2    rw,noauto,noatime,x-mount.mkdir,x-systemd.requires=systemd-cryptsetup@usb47553f64e4b14ad189e273133bdaa65d.service       0 0
    UUID=046ba81b-7d9c-4a54-a69f-6fba06ac7754       /mnt/usb/408D5CBF5F0AE7C0E9150B70/part3 ext2    rw,noauto,noatime,x-mount.mkdir 0 0
    

    Note how this contains the same mount point path /mnt/usb/408D5CBF5F0AE7C0E9150B70/part1, otherwise it wouldn't cause the generation of the mnt-usb-408D5CBF5F0AE7C0E9150B70-part1.mount unit.

    The first column, UUID=5b214986-7db3-439c-99ba-f4da0cebdf34, contains the UUID of the partition that must be mounted: the decrypted /dev/mapper/usba52ee8f0a8fa42008c19d798303ac118, not the encrypted /dev/sdc1 partition. x-mount.mkdir causes the mount point path to be automatically created if it doesn't exist. And x-systemd.requires=systemd-cryptsetup@usba52ee8f0a8fa42008c19d798303ac118.service makes sure that cryptsetup is being run first in order to create that decrypted /dev/mapper/usba52ee8f0a8fa42008c19d798303ac118 that we want to mount.

    Also note that partition 3 is not encrypted, so 046ba81b-7d9c-4a54-a69f-6fba06ac7754 is the UUID of /dev/sdc3 and that line doesn't have a x-systemd.requires=.

    The /etc/crypttab entry.

    For the encrypted partitions to work, we also need an entry in /etc/crypttab:

    usba52ee8f0a8fa42008c19d798303ac118     /dev/disk/by-uuid/a52ee8f0-a8fa-4200-8c19-d798303ac118  none    luks,noauto
    usb47553f64e4b14ad189e273133bdaa65d     /dev/disk/by-uuid/47553f64-e4b1-4ad1-89e2-73133bdaa65d  none    luks,noauto
    

    Here /dev/disk/by-uuid/a52ee8f0-a8fa-4200-8c19-d798303ac118 is the encrypted partition (it points to /dev/sdc1) and usba52ee8f0a8fa42008c19d798303ac118 is a random id, which as it turns out, can not contain backslashes [...]. In order to avoid collisions I used the UUID, but had to remove the hypens from it.

    Entering the pass phrase to unlock the LUKS partitions.

    With the above in place and having reloaded the configs (see above), inserting the USB stick will send a broadcast message to all tty's with something like:

    Broadcast message from root@<your host name> (Sun 2024-12-29 16:46:37 CET):
    
    Password entry required for '<some description>' (PID 281235).
    Please enter password with the systemd-tty-ask-password-agent tool.
    

    Just hit Enter to see your prompt again (assuming it was still empty and your weren't in the middle of typing), and run:

    sudo systemd-tty-ask-password-agent
    

    This will then allow you to enter whatever secret is required. If the USB contains more than one partition, but have the same pass phrase, then you only have to enter it once (the second one will try the last entered pass phrase first and since that works won't require you to enter it again). I didn't test what happens if they have a different pass phrase, I can imagine that in that case you have a problem; maybe need to run sudo systemd-tty-ask-password-agent again after a failure or something like that.

    Using a YubiKey

    Install the package libfido2. Then, with your YubiKey inserted, run:

    sudo systemd-cryptenroll --fido2-device=auto /dev/sdc1
    

    (and afterwards for all other encrypted partitions, of course). This will ask you to enter the passphrase of /dev/sdc1, then it will ask for the FIDO2 PIN. If you didn't change that yet, it will be the default 123456.

    You can change this PIN with the following command:

    sudo ykman fido access change-pin
    

    Doesn't matter if you do this before or after. It has no effect on the FIDO credentials.

    You can inspect the result with

    sudo cryptsetup luksDump /dev/sdc1
    

    This now should show one more key under Keyslots:, at the very least the one containing your pass phrase, but also a new one. Under Tokens: you will see an entry with systemd-fido2, and a reference to the Keyslot that it uses.

    To make the system use the YubiKey, change the entries in your /etc/crypttab to contain fido2-device=auto. For example,

    usba52ee8f0a8fa42008c19d798303ac118     /dev/disk/by-uuid/a52ee8f0-a8fa-4200-8c19-d798303ac118  none    luks,noauto,fido2-device=auto
    

    and rerun sudo systemctl daemon-reload.

    If now you insert the USB key it should do a broadcast message as before, but if then you run sudo systemd-tty-ask-password-agent it will ask for Please enter LUKS2 token PIN:. Enter the PIN that you set (or still 123456) and then tap your YubiKey once for every partition that needs to be decrypted.

    Now your partitions should be mounted. If they are not mounted then you are in deep shit because I didn't explain anything, I just hand-held you with the final answer :(. Expect many days of reading documents and learning all kinds of commands to find out intermediate results and debug output (e.g. journalctl -b -t systemd). Asking an A.I. for help with learning these commands actually helps here.

    Finally, here is a little script that I used to print the required entries for /etc/fstab and /etc/crypttab, because that is a lot of work and I had three of these USB sticks (in case of failure of one in the future):

    # Get the device path.
    DEVPATH=$(/bin/ls /dev/disk/by-id/usb-Kingston_DataTraveler* | grep -E -v -- '-part[0-9]+$')
    ID_SERIAL_SHORT=$(udevadm info -n $DEVPATH | grep ID_SERIAL_SHORT | sed -e 's/.*ID_SERIAL_SHORT=//')
    echo "/etc/fstab:"
    for p in $(/bin/ls $DEVPATH-part*); do
      PART=$(echo $p | sed -r -e 's/.*-(part[0-9]+)$/\1/')
      UUID=$(blkid --match-token TYPE=crypto_LUKS $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/' || true)
      if [ -n "$UUID" ]; then
        MAPPERID=$(echo "usb$UUID" | sed -e 's/-//g')
        echo $LUKS_PASS | sudo cryptsetup -q luksOpen /dev/disk/by-uuid/$UUID $MAPPERID 2>/dev/null
        UUID2=$(blkid /dev/mapper/$MAPPERID | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
        ID_FS_TYPE=$(udevadm info -n /dev/mapper/$MAPPERID | grep 'ID_FS_TYPE=' | sed -e 's/.*ID_FS_TYPE=//')
        echo -e "UUID=$UUID2\t/mnt/usb/$ID_SERIAL_SHORT/$PART\t$ID_FS_TYPE\trw,noauto,noatime,x-mount.mkdir,x-systemd.requires=systemd-cryptsetup@$MAPPERID.service\t0 0"
        sudo cryptsetup luksClose $MAPPERID 2>/dev/null
      else
        UUID=$(blkid $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/')
        ID_FS_TYPE=$(udevadm info -n $p | grep 'ID_FS_TYPE=' | sed -e 's/.*ID_FS_TYPE=//')
        echo -e "UUID=$UUID\t/mnt/usb/$ID_SERIAL_SHORT/$PART\t$ID_FS_TYPE\trw,noauto,noatime,x-mount.mkdir\t0 0"
      fi
    done
    echo "/etc/crypttab:"
    for p in $(/bin/ls $DEVPATH-part*); do
      PART=$(echo $p | sed -r -e 's/.*-(part[0-9]+)$/\1/')
      UUID=$(blkid --match-token TYPE=crypto_LUKS $p | sed -e 's/.* UUID="\([^ ]*\)".*/\1/' || true)
      if [ -n "$UUID" ]; then
        MAPPERID=$(echo "usb$UUID" | sed -e 's/-//g')
        echo -e "$MAPPERID\t/dev/disk/by-uuid/$UUID\tnone\tluks,noauto,fido2-device=auto"
      fi
    done
    
    • 2

相关问题

  • 如何从 USB 到串行设备 CP2105 为串行设备分配符号链接?

  • 为什么 Udev 为单个 USB 设备加载两个内核模块?

  • 通过自定义 ACCEL_MOUNT_MATRIX 更改 iio-sensors 数据

  • 在 Chrome OS 上通过 udev 规则运行脚本 [关闭]

  • udev、udisks、dbus 和 dd 命令

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve