├── .gitignore ├── README.md ├── btrfdeck_post_update.sh ├── modified ├── format-sdcard.sh └── sdcard-mount.sh └── unmodified ├── format-sdcard.sh └── sdcard-mount.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /backup/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Why is this repo archived? 2 | I tested a different version of this script. Though it may not have as cool of a name, it is easier to use, more actively developed, and offers a `/home` conversion as well! So, after testing on my own Deck, I have decided to archive my repo with a link to theirs. If you like this version better, feel free to fork it. If you just want to use btrfs, use this: [SteamOS Btrfs](https://gitlab.com/popsulfr/steamos-btrfs/) 3 | 4 | # btrfdeck - (butter-f-deck) 5 | Improve your Steam Deck with eight simple steps! 6 | 7 | The instructions and files on [this page](https://github.com/Trevo525/btrfdeck) are designed to help you format microSD cards for your Steam Deck with [BTRFS](https://btrfs.wiki.kernel.org/index.php/Main_Page). This can lead to [tremendous storage space savings](#but-why) and even [faster loading performance](#but-why). 8 | 9 | This process involves changing two files in the protected SteamOS partition, which will allow you to format your card more easily and then enable the Deck to automatically mount the card with compression-on-write on insert! 10 | 11 | # But why? 12 | Great question. There are three reasons why you would want to do this. 13 | 14 | 1. Saving storage space: btrfs allows you to apply compression directly to the file system. This can save as much as 40% of space on games. [But don't take my word for it](https://www.reddit.com/r/SteamDeck/comments/t79fqj/formatted_my_sd_card_to_btrfs_managed_to_squeeze/). Another storage saving thing that btrfs allows is de-duplication. I haven't looked into this much so I haven't implement it yet but [I read](https://www.reddit.com/r/SteamDeck/comments/t79fqj/comment/hzh7fyh/) that there is a lot of redundant data in Proton prefixes. 15 | 2. Faster loading times: In my experience with the deck (limited), loading from the microSD card has not been bad. However, microSD is going to be the bottleneck of loading many bigger games because it has only so much bandwidth. If compression saves you ~40% of storage then that means you have 40% less data to load from the microSD, since it's decompressed AFTER being loaded from the card. There is another [reddit thread with more tests](https://www.reddit.com/r/SteamDeck/comments/t8ztuv/btrfs_vs_ext4_tested/), it has a small sample size (tested on one game) but it still looks positive for using compression at the file system level. 16 | 3. Cross-OS compatibility: This is something I never considered when making this but thanks to [The Phawx's Video](https://youtu.be/Pt-Y5DYy9mU) you can see that with the [Open-Source BTRFS Windows Driver](https://github.com/maharmstone/btrfs) you can use the same microSD Steam Library accross SteamOS and Windows. 17 | * **Warning!** The video only shows one game. I saw in the comments from people experienced with this exact thing on other Linux distros, that many games have different versions on Windows vs Linux (Think Linux Native vs Proton ports). What this might result in is every time you change form one OS to another, it might redownload one or many of your games to match the OS you are currently running. If you intend to try this, I would be aware of this and if a game does this, be sure to remove those games from the shared library. 18 | 19 | # Why not? 20 | The are two arguments that might suggest that this isn't a good idea. 21 | 1. Adding compression/decompression will decrease your battery's runtime since it's more work for your CPU. 22 | * Though that is definetely true, I would argue that the amount that it might decrease would be so small you that it wouldn't even be noticable. I don't know of any tests that have shown how much it affects the power usage, but will update here when I find one. :-D 23 | 2. Changing files in the read-only partition is risky. 24 | * This is also true. Don't do it if you don't know what you are doing. It can however be done without editing anything in the readonly partition. You just have to go in and mount it via desktop mode at every boot and insertion. 25 | 26 | # What is changed? 27 | There are two files from the Steam Deck that handle mounting and formatting a microSD card formatted with btrfs. Both of these files are located in the `/usr/lib/hwsupport/` directory. I copied them to `./unmodified` and `./modified` here in this repository and edited in the modified folder. 28 | 29 | ## sdcard-mount.sh 30 | This script is called when a microSD card is inserted in the slot on the Deck. You can compare the version in `/modified` to `/unmodified` to see exact what I changed but I will summarize the two changes to this file below. 31 | 32 | ``` 33 | if [[ ${ID_FS_TYPE} != "ext4" && ${ID_FS_TYPE} != "btrfs" ]]; then 34 | exit 1 35 | fi 36 | ``` 37 | This was already in the file but I added `&& ${ID_FS_TYPE} != "btrfs"`. The if statement without this addition says to not automatically mount anything that is not the filesystem type `ext4`. The addition adds btrfs to the list, simply not closing this script if the drive is formatted with btrfs. 38 | 39 | ``` 40 | if [[ ${ID_FS_TYPE} == "btrfs" ]]; then 41 | OPTS+=",compress-force=zstd:15" 42 | fi 43 | ``` 44 | The block above was added and not just changed like the previous one. This section means that if filesystem is btrfs add the zstd compression with a level of 15 to the mounting options. I selected 15 as the compression level but I am open to change that if someone provides a good reason to. :-) 45 | 46 | 47 | ## format-sdcard.sh 48 | When you press the "Format SD Card" button in the Steam Deck UI it calls this script. Below is the change I made. 49 | 50 | ``` 51 | # mkfs.ext4 -m 0 -O casefold -E "$EXTENDED_OPTIONS" -F "$SDCARD_PARTITION" 52 | mkfs.btrfs -f "$SDCARD_PARTITION" 53 | ``` 54 | The top line is commented out, that's what the '#' symbol means. The second line is my replacement that formats with btrfs. 55 | 56 | # Guide: 57 | ## 1. Download this project to your home directory of the deck. 58 | cd ~ 59 | git clone https://github.com/Trevo525/btrfdeck 60 | cd btrfdeck 61 | ## 2. Setup `deck` user with a password so that you can run things that need sudo. 62 | passwd deck 63 | ## 3. Backup the old script. 64 | mkdir ./backup/ 65 | cp /usr/lib/hwsupport/sdcard-mount.sh ./backup/sdcard-mount.sh 66 | cp /usr/lib/hwsupport/format-sdcard.sh ./backup/format-sdcard.sh 67 | ## 4. Change the filesystem to read-write so that you can make changes to the protected files. 68 | #### **NOTE: this makes your entire "SteamOS" partition read-write. Basically, removing all barriers keeping you from breaking your system. You've been warned**. 69 | sudo steamos-readonly disable 70 | ## 5. Replace with modified configs. 71 | sudo rm /usr/lib/hwsupport/sdcard-mount.sh && sudo rm /usr/lib/hwsupport/format-sdcard.sh 72 | sudo cp ./modified/sdcard-mount.sh /usr/lib/hwsupport/sdcard-mount.sh 73 | sudo cp ./modified/format-sdcard.sh /usr/lib/hwsupport/format-sdcard.sh 74 | sudo chmod 755 /usr/lib/hwsupport/sdcard-mount.sh /usr/lib/hwsupport/format-sdcard.sh 75 | ## 6. Change the filesystem back to read-only so that you can no longer make changes to the protected files. 76 | sudo steamos-readonly enable 77 | ## 7. Remove the password from the deck user. (Skip this and next step to use btrfdeck_post_update.sh to re-run steps 3-6 after an OS update) 78 | sudo passwd -d deck 79 | ## 8. Format the SD card. 80 | Press the "Format SD Card" button in the Steam Deck UI. 81 | 82 | At this point it should automatically mount the drive because both files were changed. If it does not here is how I would troubleshoot: 83 | * Try ejecting the microSD card and reinserting. (Sometimes it'll take a few seconds to mount a large drive.) 84 | * Restart Steam. 85 | * Go to the desktop and check if it is mounted. 86 | * If that doesn't work, try to format in the SteamUI again. I need to figure out where this logs to so I can add that here. 87 | * You could try formatting manually, either through KDE Partition Manager or though the terminal. 88 | * Lastly, if all else fails, you can follow the [Undo the changes](#undo-the-changes) section below to make it as if you never made a change. 89 | 90 | # After a SteamOS update 91 | 92 | ## Don't remove password and run the btrfdec_post_update.sh script by right-clicking it and selecting "Run in Konsole". 93 | 94 | # Undo the changes: 95 | ## 1. Setup `deck` user with a password. 96 | passwd deck 97 | ## 2. Change the filesystem to read-write. 98 | sudo steamos-readonly disable 99 | ## 3. restore from the backup. 100 | sudo rm /usr/lib/hwsupport/sdcard-mount.sh && sudo rm /usr/lib/hwsupport/format-sdcard.sh 101 | cp ./backup/sdcard-mount.sh /usr/lib/hwsupport/sdcard-mount.sh 102 | cp ./backup/format-sdcard.sh /usr/lib/hwsupport/format-sdcard.sh 103 | ## 4. Change the filesystem back to read-only. 104 | sudo steamos-readonly enable 105 | ## 5. Remove the password from the deck user. 106 | sudo passwd -d deck 107 | 108 | # Issues 109 | I don't know how to quantify some of the weird issues I am having so just know this isn't perfect but after I've tested it for a little while longer I'll leave some more notes here. If you try this and you have any issues feel free to leave an [issue here](https://github.com/Trevo525/btrfdeck/issues). 110 | 111 | # Credits 112 | 113 | Thanks to [u/ClinicallyInPain](https://www.reddit.com/user/ClinicallyInPain/) on reddit for compiling some of the resources and to [u/Hanntac](https://www.reddit.com/user/Hanntac/) and [u/leo_vir](https://www.reddit.com/user/leo_vir/) for their contributions! Both of the last two credits helped me through PM so I am incredibly thankful for the both of them. Also to [u/sporkyuncle](https://www.reddit.com/user/sporkyuncle/) for helping me make it more user-friendly. 114 | 115 | The following Github users: 116 | * [taotien](https://github.com/taotien) 117 | * [ktully](https://github.com/ktully) 118 | 119 | Sources: 120 | * https://www.reddit.com/r/SteamDeck/comments/taixhw/new_user_questions_current_user_recommendations/ 121 | * https://docs.kde.org/trunk5/en/partitionmanager/partitionmanager/partitionmanager.pdf 122 | * https://linuxhint.com/btrfs-filesystem-mount-options/ 123 | * https://www.reddit.com/r/SteamDeck/comments/t76wh6/compressing_storage_with_btrfs/ 124 | * https://www.reddit.com/r/SteamDeck/comments/t79fqj/comment/hziqcf5/ 125 | * https://serverfault.com/a/767079 126 | * https://github.com/Trevo525/btrfdeck/issues/1 127 | -------------------------------------------------------------------------------- /btrfdeck_post_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TODO - compare current files against old backup in case Valve made changes 4 | # - requires password to be set, perhaps edit readme to include that 5 | # - better logic 6 | 7 | set -e 8 | 9 | cd /home/deck/btrfdeck || echo "Could not find btrfdeck repo!" 10 | echo "Backing up current files..." 11 | cp /usr/lib/hwsupport/sdcard-mount.sh ./backup/sdcard-mount.sh 12 | #cp /usr/lib/hwsupport/format-sdcard.sh ./backup/format-sdcard.sh 13 | echo "Temporarily disabling readonly filesystem..." 14 | sudo steamos-readonly disable 15 | echo "Removing current files..." 16 | sudo rm /usr/lib/hwsupport/sdcard-mount.sh 17 | #sudo rm /usr/lib/hwsupport/format-sdcard.sh 18 | echo "Copying modified files..." 19 | sudo cp ./modified/sdcard-mount.sh /usr/lib/hwsupport/sdcard-mount.sh 20 | #sudo cp ./modified/format-sdcard.sh /usr/lib/hwsupport/format-sdcard.sh 21 | echo "Editing new file permissions..." 22 | sudo chmod 755 /usr/lib/hwsupport/sdcard-mount.sh /usr/lib/hwsupport/format-sdcard.sh 23 | echo "Re-enabling readonly filesystem..." 24 | sudo steamos-readonly enable 25 | echo "Done!" 26 | read -n1 -sr -p "Press any key to exit..." 27 | exit 28 | -------------------------------------------------------------------------------- /modified/format-sdcard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | MOUNT_LOCK="/var/run/sdcard-mount.lock" 6 | SDCARD_DEVICE="/dev/mmcblk0" 7 | SDCARD_PARTITION="/dev/mmcblk0p1" 8 | RUN_VALIDATION=1 9 | EXTENDED_OPTIONS="nodiscard" 10 | 11 | while [[ $# -gt 0 ]]; do 12 | case "$1" in 13 | --force) RUN_VALIDATION=0; shift ;; 14 | --skip-validation) RUN_VALIDATION=0; shift ;; 15 | --full) EXTENDED_OPTIONS="$EXTENDED_OPTIONS,discard"; shift ;; 16 | --quick) EXTENDED_OPTIONS="$EXTENDED_OPTIONS,nodiscard"; shift ;; 17 | *) echo "Unknown option $1"; exit 22;; 18 | esac 19 | done 20 | 21 | if [[ ! -e "$SDCARD_DEVICE" ]]; then 22 | exit 19 #ENODEV 23 | fi 24 | 25 | systemctl stop sdcard-mount@mmcblk0p1.service 26 | 27 | # lock file prevents the mount service from re-mounting as it gets triggered by udev rules 28 | on_exit() { rm -f -- "$MOUNT_LOCK"; } 29 | trap on_exit EXIT 30 | echo $$ > "$MOUNT_LOCK" 31 | 32 | # Test the sdcard 33 | # Some fake cards advertise a larger size than their actual capacity, 34 | # which can result in data loss or other unexpected behaviour. It is 35 | # best to try to detect these issues as early as possible. 36 | if [[ "$RUN_VALIDATION" != "0" ]]; then 37 | echo "stage=testing" 38 | if ! f3probe --destructive "$SDCARD_DEVICE"; then 39 | # Fake sdcards tend to only behave correctly when formatted as exfat 40 | # The tricks they try to pull fall apart with any other filesystem and 41 | # it renders the card unusuable. 42 | # 43 | # Here we restore the card to exfat so that it can be used with other devices. 44 | # It won't be usable with the deck, and usage of the card will most likely 45 | # result in data loss. We return a special error code so we can surface 46 | # a specific error to the user. 47 | echo "stage=rescuing" 48 | echo "Bad sdcard - rescuing" 49 | for i in {1..3}; do # Give this a couple of tries since it fails sometimes 50 | echo "Create partition table: $i" 51 | dd if=/dev/zero of="$SDCARD_DEVICE" bs=512 count=1024 # see comment in similar statement below 52 | if ! parted --script "$SDCARD_DEVICE" mklabel msdos mkpart primary 0% 100% ; then 53 | echo "Failed to create partition table: $i" 54 | continue # try again 55 | fi 56 | 57 | echo "Create exfat filesystem: $i" 58 | sync 59 | if ! mkfs.exfat "$SDCARD_PARTITION"; then 60 | echo "Failed to exfat filesystem: $i" 61 | continue # try again 62 | fi 63 | 64 | echo "Successfully restored device" 65 | break 66 | done 67 | 68 | # Return a specific error code so the UI can warn the user about this bad device 69 | exit 14 # EFAULT 70 | fi 71 | fi 72 | 73 | # Clear out the garbage bits generated by f3probe from the partition table sectors 74 | # Otherwise parted may think we have existing partitions in a bogus state 75 | dd if=/dev/zero of="$SDCARD_DEVICE" bs=512 count=1024 76 | 77 | # Format as EXT4 with casefolding for proton compatibility 78 | echo "stage=formatting" 79 | sync 80 | parted --script "$SDCARD_DEVICE" mklabel gpt mkpart primary 0% 100% 81 | sync 82 | # mkfs.ext4 -m 0 -O casefold -E "$EXTENDED_OPTIONS" -F "$SDCARD_PARTITION" 83 | mkfs.btrfs -f "$SDCARD_PARTITION" 84 | sync 85 | 86 | rm "$MOUNT_LOCK" 87 | systemctl start sdcard-mount@mmcblk0p1.service 88 | 89 | exit 0 90 | -------------------------------------------------------------------------------- /modified/sdcard-mount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Originally from https://serverfault.com/a/767079 4 | 5 | # This script is called from our systemd unit file to mount or unmount 6 | # a USB drive. 7 | 8 | usage() 9 | { 10 | echo "Usage: $0 {add|remove} device_name (e.g. sdb1)" 11 | exit 1 12 | } 13 | 14 | if [[ $# -ne 2 ]]; then 15 | usage 16 | fi 17 | 18 | ACTION=$1 19 | DEVBASE=$2 20 | DEVICE="/dev/${DEVBASE}" 21 | 22 | MOUNT_LOCK="/var/run/sdcard-mount.lock" 23 | if [[ -e $MOUNT_LOCK && $(pgrep -F "$MOUNT_LOCK") ]]; then 24 | echo "$MOUNT_LOCK is active: ignoring action $ACTION" 25 | # Do not return a success exit code: it could end up putting the service in 'started' state without doing the mount work (further start commands will be ignored after that) 26 | exit 1 27 | fi 28 | 29 | # See if this drive is already mounted, and if so where 30 | MOUNT_POINT=$(mount | grep -F "${DEVICE}" | awk '{ print $3 }') 31 | 32 | # From https://gist.github.com/HazCod/da9ec610c3d50ebff7dd5e7cac76de05 33 | urlencode() 34 | { 35 | [ -z "$1" ] || echo -n "$@" | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g' 36 | } 37 | 38 | do_mount() 39 | { 40 | if [[ -n ${MOUNT_POINT} ]]; then 41 | echo "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}" 42 | exit 1 43 | fi 44 | 45 | # Get info for this drive: $ID_FS_LABEL, and $ID_FS_TYPE 46 | dev_json=$(lsblk -o PATH,LABEL,FSTYPE --json -- "$DEVICE" | jq '.blockdevices[0]') 47 | ID_FS_LABEL=$(jq -r '.label | select(type == "string")' <<< "$dev_json") 48 | ID_FS_TYPE=$(jq -r '.fstype | select(type == "string")' <<< "$dev_json") 49 | 50 | # Figure out a mount point to use 51 | LABEL=${ID_FS_LABEL} 52 | if [[ -z "${LABEL}" ]]; then 53 | LABEL=${DEVBASE} 54 | elif /bin/grep -qF " /run/media/${LABEL} " /etc/mtab; then 55 | # Already in use, make a unique one 56 | LABEL+="-${DEVBASE}" 57 | fi 58 | MOUNT_POINT="/run/media/${LABEL}" 59 | 60 | echo "Mount point: ${MOUNT_POINT}" 61 | 62 | /bin/mkdir -p -- "${MOUNT_POINT}" 63 | 64 | # Global mount options 65 | OPTS="rw,noatime" 66 | 67 | # File system type specific mount options 68 | #if [[ ${ID_FS_TYPE} == "vfat" ]]; then 69 | # OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush" 70 | #fi 71 | 72 | # Custom btrfs addition from https://github.com/Trevo525/Steam-Deck-sdcard-mount 73 | if [[ ${ID_FS_TYPE} == "btrfs" ]]; then 74 | OPTS+=",compress-force=zstd:15" 75 | fi 76 | 77 | # We need symlinks for Steam for now, so only automount ext4 as that'll Steam will format right now 78 | if [[ ${ID_FS_TYPE} != "ext4" && ${ID_FS_TYPE} != "btrfs" ]]; then 79 | exit 1 80 | fi 81 | 82 | if ! /bin/mount -o "${OPTS}" -- "${DEVICE}" "${MOUNT_POINT}"; then 83 | echo "Error mounting ${DEVICE} (status = $?)" 84 | /bin/rmdir -- "${MOUNT_POINT}" 85 | exit 1 86 | fi 87 | 88 | chown 1000:1000 -- "${MOUNT_POINT}" 89 | 90 | echo "**** Mounted ${DEVICE} at ${MOUNT_POINT} ****" 91 | 92 | url=$(urlencode "${MOUNT_POINT}") 93 | 94 | # If Steam is running, notify it 95 | if pgrep -x "steam" > /dev/null; then 96 | # TODO use -ifrunning and check return value - if there was a steam process and it returns -1, the message wasn't sent 97 | # need to retry until either steam process is gone or -ifrunning returns 0, or timeout i guess 98 | systemd-run -M 1000@ --user --collect --wait sh -c "./.steam/root/ubuntu12_32/steam steam://addlibraryfolder/${url@Q}" 99 | fi 100 | } 101 | 102 | do_unmount() 103 | { 104 | url=$(urlencode "${MOUNT_POINT}") 105 | 106 | # If Steam is running, notify it 107 | if pgrep -x "steam" > /dev/null; then 108 | # TODO use -ifrunning and check return value - if there was a steam process and it returns -1, the message wasn't sent 109 | # need to retry until either steam process is gone or -ifrunning returns 0, or timeout i guess 110 | systemd-run -M 1000@ --user --collect --wait sh -c "./.steam/root/ubuntu12_32/steam steam://removelibraryfolder/${url@Q}" 111 | fi 112 | 113 | if [[ -z ${MOUNT_POINT} ]]; then 114 | echo "Warning: ${DEVICE} is not mounted" 115 | else 116 | /bin/umount -l -- "${DEVICE}" 117 | echo "**** Unmounted ${DEVICE}" 118 | fi 119 | 120 | # Delete all empty dirs in /media that aren't being used as mount 121 | # points. This is kind of overkill, but if the drive was unmounted 122 | # prior to removal we no longer know its mount point, and we don't 123 | # want to leave it orphaned... 124 | for f in /run/media/* ; do 125 | if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then 126 | if ! /bin/grep -qF " $f " /etc/mtab; then 127 | echo "**** Removing mount point $f" 128 | /bin/rmdir "$f" 129 | fi 130 | fi 131 | done 132 | } 133 | 134 | case "${ACTION}" in 135 | add) 136 | do_mount 137 | ;; 138 | remove) 139 | do_unmount 140 | ;; 141 | *) 142 | usage 143 | ;; 144 | esac 145 | 146 | -------------------------------------------------------------------------------- /unmodified/format-sdcard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | MOUNT_LOCK="/var/run/sdcard-mount.lock" 6 | SDCARD_DEVICE="/dev/mmcblk0" 7 | SDCARD_PARTITION="/dev/mmcblk0p1" 8 | RUN_VALIDATION=1 9 | EXTENDED_OPTIONS="nodiscard" 10 | 11 | while [[ $# -gt 0 ]]; do 12 | case "$1" in 13 | --force) RUN_VALIDATION=0; shift ;; 14 | --skip-validation) RUN_VALIDATION=0; shift ;; 15 | --full) EXTENDED_OPTIONS="$EXTENDED_OPTIONS,discard"; shift ;; 16 | --quick) EXTENDED_OPTIONS="$EXTENDED_OPTIONS,nodiscard"; shift ;; 17 | *) echo "Unknown option $1"; exit 22;; 18 | esac 19 | done 20 | 21 | if [[ ! -e "$SDCARD_DEVICE" ]]; then 22 | exit 19 #ENODEV 23 | fi 24 | 25 | systemctl stop sdcard-mount@mmcblk0p1.service 26 | 27 | # lock file prevents the mount service from re-mounting as it gets triggered by udev rules 28 | on_exit() { rm -f -- "$MOUNT_LOCK"; } 29 | trap on_exit EXIT 30 | echo $$ > "$MOUNT_LOCK" 31 | 32 | # Test the sdcard 33 | # Some fake cards advertise a larger size than their actual capacity, 34 | # which can result in data loss or other unexpected behaviour. It is 35 | # best to try to detect these issues as early as possible. 36 | if [[ "$RUN_VALIDATION" != "0" ]]; then 37 | echo "stage=testing" 38 | if ! f3probe --destructive "$SDCARD_DEVICE"; then 39 | # Fake sdcards tend to only behave correctly when formatted as exfat 40 | # The tricks they try to pull fall apart with any other filesystem and 41 | # it renders the card unusuable. 42 | # 43 | # Here we restore the card to exfat so that it can be used with other devices. 44 | # It won't be usable with the deck, and usage of the card will most likely 45 | # result in data loss. We return a special error code so we can surface 46 | # a specific error to the user. 47 | echo "stage=rescuing" 48 | echo "Bad sdcard - rescuing" 49 | for i in {1..3}; do # Give this a couple of tries since it fails sometimes 50 | echo "Create partition table: $i" 51 | dd if=/dev/zero of="$SDCARD_DEVICE" bs=512 count=1024 # see comment in similar statement below 52 | if ! parted --script "$SDCARD_DEVICE" mklabel msdos mkpart primary 0% 100% ; then 53 | echo "Failed to create partition table: $i" 54 | continue # try again 55 | fi 56 | 57 | echo "Create exfat filesystem: $i" 58 | sync 59 | if ! mkfs.exfat "$SDCARD_PARTITION"; then 60 | echo "Failed to exfat filesystem: $i" 61 | continue # try again 62 | fi 63 | 64 | echo "Successfully restored device" 65 | break 66 | done 67 | 68 | # Return a specific error code so the UI can warn the user about this bad device 69 | exit 14 # EFAULT 70 | fi 71 | fi 72 | 73 | # Clear out the garbage bits generated by f3probe from the partition table sectors 74 | # Otherwise parted may think we have existing partitions in a bogus state 75 | dd if=/dev/zero of="$SDCARD_DEVICE" bs=512 count=1024 76 | 77 | # Format as EXT4 with casefolding for proton compatibility 78 | echo "stage=formatting" 79 | sync 80 | parted --script "$SDCARD_DEVICE" mklabel gpt mkpart primary 0% 100% 81 | sync 82 | mkfs.ext4 -m 0 -O casefold -E "$EXTENDED_OPTIONS" -F "$SDCARD_PARTITION" 83 | sync 84 | 85 | rm "$MOUNT_LOCK" 86 | systemctl start sdcard-mount@mmcblk0p1.service 87 | 88 | exit 0 89 | -------------------------------------------------------------------------------- /unmodified/sdcard-mount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Originally from https://serverfault.com/a/767079 4 | 5 | # This script is called from our systemd unit file to mount or unmount 6 | # a USB drive. 7 | 8 | usage() 9 | { 10 | echo "Usage: $0 {add|remove} device_name (e.g. sdb1)" 11 | exit 1 12 | } 13 | 14 | if [[ $# -ne 2 ]]; then 15 | usage 16 | fi 17 | 18 | ACTION=$1 19 | DEVBASE=$2 20 | DEVICE="/dev/${DEVBASE}" 21 | 22 | MOUNT_LOCK="/var/run/sdcard-mount.lock" 23 | if [[ -e $MOUNT_LOCK && $(pgrep -F "$MOUNT_LOCK") ]]; then 24 | echo "$MOUNT_LOCK is active: ignoring action $ACTION" 25 | # Do not return a success exit code: it could end up putting the service in 'started' state without doing the mount work (further start commands will be ignored after that) 26 | exit 1 27 | fi 28 | 29 | # See if this drive is already mounted, and if so where 30 | MOUNT_POINT=$(mount | grep -F "${DEVICE}" | awk '{ print $3 }') 31 | 32 | # From https://gist.github.com/HazCod/da9ec610c3d50ebff7dd5e7cac76de05 33 | urlencode() 34 | { 35 | [ -z "$1" ] || echo -n "$@" | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g' 36 | } 37 | 38 | do_mount() 39 | { 40 | if [[ -n ${MOUNT_POINT} ]]; then 41 | echo "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}" 42 | exit 1 43 | fi 44 | 45 | # Get info for this drive: $ID_FS_LABEL, and $ID_FS_TYPE 46 | dev_json=$(lsblk -o PATH,LABEL,FSTYPE --json -- "$DEVICE" | jq '.blockdevices[0]') 47 | ID_FS_LABEL=$(jq -r '.label | select(type == "string")' <<< "$dev_json") 48 | ID_FS_TYPE=$(jq -r '.fstype | select(type == "string")' <<< "$dev_json") 49 | 50 | # Figure out a mount point to use 51 | LABEL=${ID_FS_LABEL} 52 | if [[ -z "${LABEL}" ]]; then 53 | LABEL=${DEVBASE} 54 | elif /bin/grep -qF " /run/media/${LABEL} " /etc/mtab; then 55 | # Already in use, make a unique one 56 | LABEL+="-${DEVBASE}" 57 | fi 58 | MOUNT_POINT="/run/media/${LABEL}" 59 | 60 | echo "Mount point: ${MOUNT_POINT}" 61 | 62 | /bin/mkdir -p -- "${MOUNT_POINT}" 63 | 64 | # Global mount options 65 | OPTS="rw,noatime" 66 | 67 | # File system type specific mount options 68 | #if [[ ${ID_FS_TYPE} == "vfat" ]]; then 69 | # OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush" 70 | #fi 71 | 72 | # We need symlinks for Steam for now, so only automount ext4 as that'll Steam will format right now 73 | if [[ ${ID_FS_TYPE} != "ext4" ]]; then 74 | exit 1 75 | fi 76 | 77 | if ! /bin/mount -o "${OPTS}" -- "${DEVICE}" "${MOUNT_POINT}"; then 78 | echo "Error mounting ${DEVICE} (status = $?)" 79 | /bin/rmdir -- "${MOUNT_POINT}" 80 | exit 1 81 | fi 82 | 83 | chown 1000:1000 -- "${MOUNT_POINT}" 84 | 85 | echo "**** Mounted ${DEVICE} at ${MOUNT_POINT} ****" 86 | 87 | url=$(urlencode "${MOUNT_POINT}") 88 | 89 | # If Steam is running, notify it 90 | if pgrep -x "steam" > /dev/null; then 91 | # TODO use -ifrunning and check return value - if there was a steam process and it returns -1, the message wasn't sent 92 | # need to retry until either steam process is gone or -ifrunning returns 0, or timeout i guess 93 | systemd-run -M 1000@ --user --collect --wait sh -c "./.steam/root/ubuntu12_32/steam steam://addlibraryfolder/${url@Q}" 94 | fi 95 | } 96 | 97 | do_unmount() 98 | { 99 | url=$(urlencode "${MOUNT_POINT}") 100 | 101 | # If Steam is running, notify it 102 | if pgrep -x "steam" > /dev/null; then 103 | # TODO use -ifrunning and check return value - if there was a steam process and it returns -1, the message wasn't sent 104 | # need to retry until either steam process is gone or -ifrunning returns 0, or timeout i guess 105 | systemd-run -M 1000@ --user --collect --wait sh -c "./.steam/root/ubuntu12_32/steam steam://removelibraryfolder/${url@Q}" 106 | fi 107 | 108 | if [[ -z ${MOUNT_POINT} ]]; then 109 | echo "Warning: ${DEVICE} is not mounted" 110 | else 111 | /bin/umount -l -- "${DEVICE}" 112 | echo "**** Unmounted ${DEVICE}" 113 | fi 114 | 115 | # Delete all empty dirs in /media that aren't being used as mount 116 | # points. This is kind of overkill, but if the drive was unmounted 117 | # prior to removal we no longer know its mount point, and we don't 118 | # want to leave it orphaned... 119 | for f in /run/media/* ; do 120 | if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then 121 | if ! /bin/grep -qF " $f " /etc/mtab; then 122 | echo "**** Removing mount point $f" 123 | /bin/rmdir "$f" 124 | fi 125 | fi 126 | done 127 | } 128 | 129 | case "${ACTION}" in 130 | add) 131 | do_mount 132 | ;; 133 | remove) 134 | do_unmount 135 | ;; 136 | *) 137 | usage 138 | ;; 139 | esac 140 | 141 | --------------------------------------------------------------------------------