diff --git a/pvesnap/explorer.py b/pvesnap/explorer.py index a388f19..a04d2bf 100644 --- a/pvesnap/explorer.py +++ b/pvesnap/explorer.py @@ -649,9 +649,7 @@ class Explorer: if not self.session or len(self.session.mounts) < 2: message(win, "Es ist nur ein Dateisystem eingebunden.") return - entries = [] - for device, mountpoint in sorted(self.session.mounts.items()): - entries.append((mountpoint, "%s -> %s" % (device, mountpoint))) + entries = self.session.mount_entries() chosen = choose(win, "Welches Dateisystem?", entries) if chosen: self.panes[0].root = chosen @@ -857,16 +855,15 @@ def _main(stdscr, args): try: _show_notes(stdscr, session.notes) - mountpoints = sorted(set(session.mounts.values())) - if not mountpoints: + entries = session.mount_entries() + if not entries: _show_notes(stdscr, ["Es wurde kein Dateisystem eingehaengt."] + session.notes, "Nichts zu zeigen") return 3 - start = mountpoints[0] - if len(mountpoints) > 1: - entries = [(m, m) for m in mountpoints] - start = choose(stdscr, "Welches Dateisystem zuerst?", entries) or mountpoints[0] + start = entries[0][0] + if len(entries) > 1: + start = choose(stdscr, "Welches Dateisystem zuerst?", entries) or start left = Pane(path=start, root=start, readonly=True, title="%s @ %s" % (guest.label, name)) diff --git a/pvesnap/snapfs.py b/pvesnap/snapfs.py index ffd7f0d..458100a 100644 --- a/pvesnap/snapfs.py +++ b/pvesnap/snapfs.py @@ -505,6 +505,22 @@ class Session: break return devices + @staticmethod + def _probe(path): + """Dateisystem eines Geraets direkt ermitteln: (Typ, Bezeichnung). + + Noetig, weil `lsblk` den Typ aus der udev-Datenbank nimmt - und die + ist bei frisch eingebundenen rbd-Geraeten leer. lsblk meldet dann + ueberall "kein Dateisystem", obwohl ext4 und vfat da sind. + `blkid -p` umgeht Datenbank und Zwischenspeicher und schaut nach. + """ + output = run(["blkid", "-p", "-o", "export", path], check=False, timeout=30) + values = {} + for line in output.splitlines(): + key, _, value = line.partition("=") + values[key.strip()] = value.strip() + return values.get("TYPE", ""), values.get("LABEL", "") + def _lsblk(self, device_path, volume): try: output = run(["lsblk", "-J", "-b", "-o", @@ -527,6 +543,9 @@ class Session: volume=volume, ) children = entry.get("children") or [] + if device.path and not device.fstype and not children: + device.fstype, probed_label = self._probe(device.path) + device.label = device.label or probed_label if device.path: # Eine Platte mit Partitionen selbst nicht anbieten if not (children and not device.fstype): @@ -633,6 +652,66 @@ class Session: last = exc raise SnapfsError("%s nicht einhaengbar: %s" % (device.path, last)) + # -- Uebersicht fuer die Oberflaechen --------------------------------- + + def mount_entries(self): + """[(Mountpunkt, Beschriftung)] - wahrscheinlichste Wurzel zuerst. + + Sonst landet man beim Oeffnen leicht auf der 500-MB-EFI-Partition + statt im eigentlichen System. + """ + by_mount = {} + for device in self.devices: + mountpoint = self.mounts.get(device.path) + if mountpoint: + by_mount[mountpoint] = device + for mountpoint in self.mounts.values(): + by_mount.setdefault(mountpoint, None) + + scored = [(self._score(mountpoint, device), mountpoint, device) + for mountpoint, device in by_mount.items()] + scored.sort(key=lambda item: -item[0]) + return [(mountpoint, self._describe_mount(mountpoint, device)) + for _score, mountpoint, device in scored] + + @staticmethod + def _score(mountpoint, device): + try: + names = set(os.listdir(mountpoint)) + except OSError: + names = set() + score = 0 + if {"etc", "usr"} <= names: + score += 1000 # Linux-Wurzel + elif "etc" in names or "Windows" in names: + score += 500 + elif {"EFI"} & names: + score -= 200 # reine Startpartition + score += int((device.size if device else 0) / (1024 ** 3)) + return score + + @staticmethod + def _describe_mount(mountpoint, device): + try: + names = set(os.listdir(mountpoint)) + except OSError: + names = set() + if {"etc", "usr"} <= names: + hint = "Linux-Wurzelverzeichnis" + elif "Windows" in names: + hint = "Windows" + elif "EFI" in names or "bootmgr" in names: + hint = "Startpartition" + elif "vmlinuz" in names or "grub" in names: + hint = "Boot" + else: + hint = ", ".join(sorted(names)[:3]) or "leer" + if device is None: + return "%s (%s)" % (os.path.basename(mountpoint), hint) + return "%-12s %-6s %8s %s%s" % ( + os.path.basename(device.path), device.fstype or "?", + device.human_size, ('"%s" ' % device.label) if device.label else "", hint) + # -- aufraeumen ------------------------------------------------------- def _remember(self, kind, value): diff --git a/pvesnap/web/server.py b/pvesnap/web/server.py index a0fb781..33cd4b7 100644 --- a/pvesnap/web/server.py +++ b/pvesnap/web/server.py @@ -84,8 +84,8 @@ class AppState: self.guest = guest self.snapname = snapname self.notes = list(session.notes) - self.roots = [(mountpoint, mountpoint) - for mountpoint in sorted(set(session.mounts.values()))] + self.roots = [(label, mountpoint) + for mountpoint, label in session.mount_entries()] def close_locked(self): if self.session: