forked from IQ.Lvbs/IQ.Pilot
46 lines
1.5 KiB
Python
46 lines
1.5 KiB
Python
import os
|
|
|
|
from openpilot.common.swaglog import cloudlog
|
|
from openpilot.system.hardware.hw import Paths
|
|
|
|
PRESERVE_ATTR_NAME = b"user.preserve"
|
|
PRESERVE_ATTR_VALUE = b"1"
|
|
|
|
|
|
def recover_unclean_segments(log_root: str | None = None) -> list[str]:
|
|
# Segments with leftover .lock files are from a loggerd that never closed
|
|
# cleanly (power cut, crash). The video/log data in them is valid up to the
|
|
# last durable sync. Clear the stale locks so the deleter can manage them
|
|
# again, and preserve them: footage from an unclean shutdown is exactly the
|
|
# footage a dashcam must not throw away.
|
|
root = log_root if log_root is not None else Paths.log_root()
|
|
recovered = []
|
|
try:
|
|
dirs = os.listdir(root)
|
|
except OSError:
|
|
return recovered
|
|
|
|
for d in dirs:
|
|
seg_path = os.path.join(root, d)
|
|
if not os.path.isdir(seg_path):
|
|
continue
|
|
try:
|
|
locks = [f for f in os.listdir(seg_path) if f.endswith(".lock")]
|
|
if not locks:
|
|
continue
|
|
for lock in locks:
|
|
os.unlink(os.path.join(seg_path, lock))
|
|
setxattr = getattr(os, "setxattr", None) # not available on darwin
|
|
if setxattr is not None:
|
|
try:
|
|
setxattr(seg_path, PRESERVE_ATTR_NAME, PRESERVE_ATTR_VALUE)
|
|
except OSError:
|
|
pass
|
|
recovered.append(d)
|
|
except OSError:
|
|
cloudlog.exception(f"crash_recovery: failed to recover {seg_path}")
|
|
|
|
if recovered:
|
|
cloudlog.event("crash_recovery.recovered_unclean_segments", segments=sorted(recovered), error=True)
|
|
return recovered
|