123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- #!/usr/bin/python
- # -*- coding: utf-8 -*-
- #
- # /etc/fstab updater/generator
- # Copyright (C) 2017, Suleyman POYRAZ (AquilaNipalensis)
- #
- # This program is free software; you can redistribute it and/or modify it under
- # the terms of the GNU General Public License as published by the Free
- # Software Foundation; either version 2 of the License, or (at your option)
- # any later version.
- #
- import os
- import sys
- import glob
- import parted
- # Default options
- default_options = {
- "vfat": ("quiet", "shortname=mixed", "dmask=007", "fmask=117", "utf8", "gid=6"),
- "ext3": ("noatime", ),
- "ext2": ("noatime", ),
- "ntfs-3g": ("dmask=007", "fmask=117", "gid=6"),
- "reiserfs": ("noatime", ),
- "xfs": ("noatime", ),
- "defaults": ("defaults", ),
- }
- default_mount_dir = "/mnt"
- excluded_file_systems = ("proc", "tmpfs", "sysfs", "linux-swap", "swap", "nfs", "nfs4", "cifs")
- sulin_labels = ("SULIN_ROOT", "SULIN_HOME", "SULIN_SWAP")
- # Utility functions
- def blockDevices():
- devices = []
- for dev_type in ["hd*", "sd*"]:
- sysfs_devs = glob.glob("/sys/block/" + dev_type)
- for sysfs_dev in sysfs_devs:
- if not int(open(sysfs_dev + "/removable").read().strip()):
- devlink = os.readlink(sysfs_dev + "/device")
- devlink = os.path.realpath(os.path.join(sysfs_dev, "device", devlink))
- if (not "/usb" in devlink) and (not "/fw-host" in devlink):
- devices.append("/dev/" + os.path.basename(sysfs_dev))
- devices.sort()
- return devices
- def blockPartitions(path):
- device = parted.Device(path)
- try:
- disk = parted.Disk(device)
- except:
- # FIXME: This is suck! Must be changed!
- disk = parted.freshDisk(device, parted.diskType['msdos'])
- partition = disk.getFirstPartition()
- while partition:
- if partition.fileSystem and partition.fileSystem.type != "linux-swap":
- yield partition.path, partition.fileSystem.type
- partition = partition.nextPartition()
- def blockNameByLabel(label):
- path = os.path.join("/dev/disk/by-label", label)
- if os.path.islink(path):
- return "/dev/%s" % os.readlink(path)[6:]
- else:
- return None
- def blockNameByUuid(uuid):
- path = os.path.join("/dev/disk/by-uuid", uuid)
- if os.path.islink(path):
- return "/dev/%s" % os.readlink(path)[6:]
- else:
- return None
- def getLocale():
- try:
- for line in file("/etc/env.d/03locale"):
- if "LC_ALL" in line:
- return line[7:].strip()
- except:
- pass
- return "tr_TR.UTF-8"
- # Fstab classes
- class FstabEntry:
- def __init__(self, line=None):
- defaults = [ None, None, "auto", "defaults", 0, 0 ]
- args = []
- if line:
- args = line.split()
- args = args[:len(args)] + defaults[len(args):]
- self.device_node = args[0]
- self.mount_point = args[1]
- self.file_system = args[2]
- self.options = args[3]
- self.dump_freq = args[4]
- self.pass_no = args[5]
- def __str__(self):
- return "%-23s %-23s %-7s %-15s %s %s" % (
- self.device_node,
- self.mount_point,
- self.file_system,
- self.options,
- self.dump_freq,
- self.pass_no
- )
- class Fstab:
- comment = """# See the manpage fstab(5) for more information.
- #
- # <fs> <mountpoint> <type> <opts> <dump/pass>
- """
- def __init__(self, path=None):
- if not path:
- path = "/etc/fstab"
- self.path = path
- self.entries = []
- self.partitions = None
- self.labels = {}
- self.uuids = {}
- for line in file(path):
- if line.strip() != "" and not line.startswith('#'):
- self.entries.append(FstabEntry(line))
- def __str__(self):
- return "\n".join(map(str, self.entries))
- def scan(self):
- self.partitions = {}
- for device in blockDevices():
- for partition, fstype in blockPartitions(device):
- self.partitions[partition] = fstype, device
- if os.path.exists("/dev/disk/by-label"):
- for label in os.listdir("/dev/disk/by-label/"):
- self.labels[blockNameByLabel(label)] = label
- elif os.path.exists("/dev/disk/by-uuid"):
- for uuid in os.listdir("/dev/disk/by-uuid/"):
- self.uuids[blockNameByUuid(uuid)] = uuid
- def write(self, path=None):
- if not path:
- path = self.path
- # Make sure mount points exist
- for entry in self.entries:
- if entry.mount_point != "none" and not os.path.exists(entry.mount_point):
- os.makedirs(entry.mount_point)
- f = open(path, "w")
- f.write(self.comment)
- f.write(str(self))
- f.write("\n")
- f.close()
- def removeEntry(self, device_node):
- for i, entry in enumerate(self.entries):
- if entry.device_node == device_node and entry.mount_point != "/":
- del self.entries[i]
- def addEntry(self, device_node, mount_point=None):
- if not self.partitions:
- self.scan()
- if not mount_point:
- mount_point = os.path.join(default_mount_dir, os.path.basename(device_node))
- file_system = self.partitions.get(device_node)[0]
- if file_system in ("fat16", "fat32"):
- file_system = "vfat"
- if file_system == "ntfs":
- file_system = "ntfs-3g"
- if file_system == "hfs+":
- file_system = "hfsplus"
- options = default_options.get(file_system, None)
- if not options:
- options = default_options.get("defaults")
- entry = FstabEntry()
- entry.device_node = device_node
- entry.mount_point = mount_point
- entry.file_system = file_system
- entry.options = ",".join(options)
- if file_system == "ntfs-3g":
- entry.options += ",locale=%s" % getLocale()
- self.entries.append(entry)
- return entry
- def refresh(self):
- if not self.partitions:
- self.scan()
- # Carefully remove non existing partitions
- removal = []
- for i, entry in enumerate(self.entries):
- node = entry.device_node
- if entry.mount_point == "/":
- # Root partition is never removed
- continue
- elif not entry.mount_point.startswith("/mnt"):
- # Only remove partitions that were added in /mnt
- continue
- elif entry.file_system in excluded_file_systems:
- # Virtual file systems are never removed
- continue
- elif node.startswith("UUID="):
- uuid = node.split("=", 1)[1]
- if blockNameByUuid(uuid) not in self.partitions:
- removal.append(node)
- elif node.startswith("LABEL="):
- label = node.split("=", 1)[1]
- if label in sulin_labels:
- # Labelled Pardus system partitions are never removed
- continue
- if blockNameByLabel(label) not in self.partitions:
- removal.append(node)
- elif node.startswith("UUID="):
- uuid = node.split("=", 1)[1]
- if blockNameByUuid(uuid) not in self.partitions:
- removal.append(node)
- else:
- if node not in self.partitions:
- removal.append(node)
- list(map(self.removeEntry, removal))
- # Append all other existing non-removable partitions
- mounted = set([x.device_node for x in self.entries])
- for part in self.partitions:
- if not part in mounted:
- if part in self.labels:
- if "LABEL=%s" % self.labels[part] in mounted:
- continue
- elif part in self.uuids:
- if "UUID=%s" % self.uuids[part] in mounted:
- continue
- self.addEntry(part)
- # Command line driver
- def refresh_fstab(path=None, debug=False):
- f = Fstab(path)
- if debug:
- print("Fstab file:", f.path)
- print("--- Current table ---")
- print(f)
- f.refresh()
- if debug:
- print("--- Refreshed table ---")
- print(f)
- else:
- f.write()
- def main(args):
- path = None
- debug = False
- if "--debug" in args:
- args.remove("--debug")
- debug = True
- if len(args) > 0:
- path = args[0]
- refresh_fstab(path, debug)
- if __name__ == "__main__":
- main(sys.argv[1:])
|