// заметки
Firefox: профиль слетел из-за profile-sync-daemon
После перезапуска Firefox пропали расширения, хотя закладки синхронизировались. Разбор: как psd увёл профиль в пустой fuse-overlayfs, и как вернуть его с диска
После перезапуска Firefox выкинул из аккаунта: закладки и история вернулись, расширения — нет. Синхронизация тут ни при чём, браузер открыл пустой профиль, который profile-sync-daemon подменил симлинком в ОЗУ через fuse-overlayfs — временный слой поверх настоящего профиля. Ниже — как это заметить и вернуть каталог с диска
Симптом
Пустой список расширений при живых закладках — не сбой Sync. Sync переносит закладки, историю, пароли и открытые вкладки, но аддоны не переустанавливает и их файлы не хранит
Ключевая зацепка: закладки вернулись, расширения нет. Это подпись подмены профиля, а не сбой синхронизации — Firefox открыл другой, пустой каталог
Профиль остаётся на месте по пути из profiles.ini, но сама папка оказывается симлинком
$ ls -la ~/.config/mozilla/firefox/ | grep default-release
lrwxrwxrwx 1 user user 78 ... <profile>.default-release -> /run/user/$UID/psd/$USER-firefox-<profile>.default-releaseСимлинк ведёт в /run/user/$UID/psd, то есть в tmpfs — оперативную память, которая очищается при перезагрузке. Путь ~/.config/mozilla/firefox — раскладка этого дистрибутива: в других сборках профиль лежит в ~/.mozilla/firefox, сверяйтесь с profiles.ini
Диагностика
Пять проверок отвечают, что произошло
# папка профиля из profiles.ini; xxxxxxxx замените на своё
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
# 1. куда смотрит profiles.ini
cat "$HOME/.config/mozilla/firefox/profiles.ini"
# 2. профиль — симлинк?
ls -la "$HOME/.config/mozilla/firefox/"
test -e "$(readlink "$P")" && echo OK || echo "битый симлинк"
# 3. жив ли overlay-монт
mountpoint -q "/run/user/$UID/psd/$USER-firefox-$PROFILE" \
&& echo mounted || echo "НЕ смонтирован"
# 4. кто и когда это сделал
journalctl --user --user-unit psd-resync.service --since today --no-pager
# 5. когда «создан» профиль
cat "$P/times.json"Признак подмены — поле created в times.json: у нового профиля оно совпадает со временем сбоя, а не с реальной датой заведения
Рядом обычно остаётся папка <profile>.default-release-backup. Если в ней лежат places.sqlite, logins.json и extensions/, данные никуда не делись
Причина
profile-sync-daemon (psd) переносит профиль браузера в ОЗУ, чтобы меньше писать на диск. В режиме fuse-overlayfs он не копирует файлы, а собирает overlay-монт: нижний слой (lowerdir, только чтение) — настоящий профиль, верхний (upperdir) — временная папка с изменениями, поверх обоих виден объединённый каталог
При первом запуске сервис отказался работать
First time running psd so please edit ~/.config/psd/psd.conf to your liking and run againНачальную синхронизацию psd.service не выполнил, профиль остался обычной папкой. Через час сработал таймер psd-resync.timer и запустил psd-resync.service, который в журнале отчитался firefox sync successful. По коду /usr/bin/psd функция do_sync_for делает три вещи
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
T="/run/user/$UID/psd/$USER-firefox-$PROFILE"
mv "$P" "$P-backup"
fuse-overlayfs -o lowerdir="$P-backup",upperdir="$P-back-ovfs",workdir="$P-back-ovfs-work" "$T"
ln -s "$T" "$P"Монт не выжил. psd-resync.service объявлен как Type=oneshot — юнит, который завершается сразу после запуска. systemd после выхода юнита гасит всю его cgroup, группу процессов, — вместе с fuse-overlayfs. Симлинк остался, но ведёт в пустую папку в tmpfs
Firefox запустился, увидел пустой каталог и создал новый чистый профиль. Отсюда и created в times.json, равный моменту сбоя. После входа в аккаунт Sync залил закладки и историю, но расширения не появились
Настоящий профиль всё это время лежал в -backup и оставался целым: Firefox держал файлы открытыми, и переименование папки на дескрипторы не влияет. Профиль дописывался до момента, когда браузер закрыли. Целостность базы подтверждает PRAGMA quick_check — быстрая проверка структуры SQLite без полного перебора данных
sqlite3 "file:places.sqlite?mode=ro" 'PRAGMA quick_check;' # okИстория в -backup заметно больше, чем в новом профиле
Почему psd не заметил поломки: он проверяет mountpoint -q "$TMP" сразу после монтирования, и проверка проходит. Что монт доживёт до конца сеанса, никто не проверяет — отсюда sync successful при мёртвом overlay
Решение
Порядок важен: правки при работающем Firefox или живом монте грозят потерей данных
Закройте Firefox полностью, иначе он продолжит писать в пустой профиль из ОЗУ
Снимите снимок профиля — до любых правок
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
cp -a --reflink=auto "$P-backup" "$HOME/firefox-profile-snapshot-$(date +%Y%m%d-%H%M)"Дальше развилка по монту. Если mountpoint -q из диагностики вернул успех, сначала гасите сервис через stop: ExecStop у него — это psd unsync, который сольёт верхний слой обратно в профиль, и только потом отключайте автозапуск
systemctl --user stop psd.service
systemctl --user disable --now psd.service psd-resync.timer
systemctl --user reset-failed 'psd*'stop перед disable — не опечатка: disable --now тоже останавливает юнит, но явный порядок гарантирует, что psd unsync успеет отработать. Если монт уже мёртв, как в нашем случае, stop ничего не даст, и достаточно disable --now с reset-failed
Профиль возвращается вручную
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
cd "$HOME/.config/mozilla/firefox"
unlink "$P"
mv --no-target-directory "$P-backup" "$P"Снимите устаревшие блокировки: их оставил убитый процесс Firefox
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
rm -f "$P"/lock "$P"/.parentlockПеред удалением остатков посмотрите, что в них. Верхний слой (upperdir, папка <profile>.default-release-back-ovfs) мог содержать записи новее -backup — например, если Firefox что-то сохранил между синхронизациями
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
B="$P-back-ovfs"
du -sh "$B" 2>/dev/null
ls -la "$B"
cp -a --reflink=auto "$B" "$HOME/firefox-profile-back-ovfs-snapshot-$(date +%Y%m%d-%H%M)"После снимка удаляйте пустые остатки, с защитой от пустого $UID
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
[ -n "$UID" ] && [ -d "/run/user/$UID/psd" ] && rm -rf "/run/user/$UID/psd/$USER-firefox-$PROFILE"
rm -rf "$P-back-ovfs"Запускайте Firefox: profiles.ini править не нужно, путь остался тем же, вернулся сам каталог
Проверка
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
[ -L "$P" ] && echo "всё ещё симлинк — плохо" || echo "обычная папка на диске — ок"
test -d "$P/extensions" && echo "папка расширений на месте" || echo "папки нет"
find "$P/extensions" -name '*.xpi' | wc -l # столько же, сколько до сбоя
sqlite3 "file:$P/places.sqlite?mode=ro" 'select count(*) from moz_places;'Смотрите при закрытом браузере. Если extensions/ на месте, а файлы совпадают с тем, что было до сбоя, профиль вернулся целиком
Подводные камни
database is lockedпри чтенииplaces.sqliteозначает, что Firefox запущен: это норма, а не поломка, и проверять базу нужно при закрытом браузере- Не удаляйте
-backupи-back-ovfsраньше времени:-backup— это сам профиль, а в верхнем слое могли остаться более свежие записи. Сначала сверьтеlogins.json,key4.dbиcert9.db psd unsyncпри живом монте делаетrsyncизTMP/вBACK_OVFS/с--delete-after(удаляет в приёмнике то, чего нет в источнике), а затем изBACK_OVFS/вBACKUP/. Если overlay мёртв, этот путь не сработает, и снимок перед правкой обязателен- Аддоны Sync не возвращает (см. врезку выше): их приносит только папка
extensions/вместе сextensions.json - Первое включение psd требует ручного подтверждения конфига: сервис молча выходит, а таймер остаётся активным и позже срабатывает вхолостую — так и возникла поломка
Итог
Ключ к диагнозу — расхождение между вернувшимися закладками и пустыми расширениями. Профиль не потерян, если рядом лежит -backup: закройте браузер, при живом монте сначала stop сервиса, затем disable --now, уберите битый симлинк и верните каталог на место
Включать psd обратно не нужно, пока не разобрались, почему fuse-overlayfs не переживает выход oneshot-юнита. Смотреть надо в сторону cgroup и KillMode — правила, по которому systemd решает, какие процессы юнита гасить при его завершении. Кеш профиля в ОЗУ экономит запись на диск и не стоит риска остаться без профиля
Похожие заметки
pkill -f убивает сам себя: шаблон совпадает с командой
pkill -f и pgrep -f сопоставляют шаблон со всей командной строкой, включая обёртку bash -c. Разбираю, почему команда убивает себя, и какие фильтры помогают
Nix на CachyOS: установка невозможна — rootless-запуск
Nix не ставится, если корень read-only, sudo сломан, а путь /nix зашит в ELF. Разбор трёх ограничений и rootless-запуск через mount-namespace и chroot
Linux: «команда не найдена» — PATH, ifconfig, pip
command not found в Linux: как найти бинарь по PATH, почему sudo и login shell видят другой путь и что поставить вместо ifconfig и pip — разбор с командами