def supported(fstab="/etc/fstab"): """ verify that the system supports apt btrfs snapshots by checking if the right fs layout is used etc """ # check for the helper binary if not os.path.exists("/sbin/btrfs"): return False # check the fstab fstab = Fstab(fstab) entry = fstab.get_supported_btrfs_root_fstab_entry() return entry is not None
def fstab_entry(): os.system('sudo blkid > data.txt') hdds = open( 'data.txt', "r+", ) y = "" finalstring = "" for line in hdds: if line.startswith("/dev/sda1"): if "PIHDD" and "ntfs" in line: j = line.find('UUID') i = line.find('"', j) b = line.find('"', i + 1) y = line[i:b].replace('"', '') finalstring = "UUID=" + y + " /opt/PIHDD " + "ntfs" + " defaults,nofail,x-systemd.device-timeout=1,noatime 0 0" elif "PIHDD" and "ext4" in line: j = line.find('UUID') i = line.find('"', j) b = line.find('"', i + 1) y = line[i:b].replace('"', '') finalstring = "UUID=" + y + " /opt/PIHDD " + "ext4" + " defaults,nofail,x-systemd.device-timeout=1,noatime 0 0" hdds.close() if os.path.exists('/opt/PIHDD/KOLIBRI_DATA/content/storage'): pass else: os.system('sudo mkdir /opt/PIHDD/KOLIBRI_DATA/content') fstab = Fstab() other_file = open('data1.txt', 'r+') other_file.truncate() fs_file = open('/etc/fstab', 'r+') fs_file.seek(0) t = fs_file.readlines() for line in t: if "PIHDD" in line: pass else: other_file.write(line) fs_file.seek(0) fs_file.truncate() fs_file.close() other_file.close() fs_file1 = open('/etc/fstab', 'r+') other_files = open('data1.txt', 'r+') for x in other_files.readlines(): fs_file1.write(x) fs_file1.write(finalstring) other_files.close() fs_file1.close()
def __init__(self): self.fstab = Fstab(FSTAB_PATH) builder = Gtk.Builder() builder.add_from_file("/usr/lib/snowlinux/snowMount/snowMount.ui") window = builder.get_object("main_window") self.disk_treeview = builder.get_object("disk_treeview") self.disk_label = builder.get_object("disk_label") self.disk_label2 = builder.get_object("disk_label2") self.part_treeview = builder.get_object("part_treeview") self.part_store = Gtk.ListStore(str, str, str, str) self.part_treeview.set_model(self.part_store) renderer_text = Gtk.CellRendererText() renderer_filesystem_text = Gtk.CellRendererText() renderer_filesystem_text.set_property("editable", True) renderer_filesystem_text.connect("edited", self.onFileSystemEdited) renderer_mountpoint_text = Gtk.CellRendererText() renderer_mountpoint_text.set_property("editable", True) renderer_mountpoint_text.connect("edited", self.onMountpointEdited) renderer_mountoptions_text = Gtk.CellRendererText() renderer_mountoptions_text.set_property("editable", True) renderer_mountoptions_text.connect("edited", self.onMountoptionsEdited) column = Gtk.TreeViewColumn("Partition", renderer_text, text=0) self.part_treeview.append_column(column) column = Gtk.TreeViewColumn("Filesystem", renderer_filesystem_text, text=1) self.part_treeview.append_column(column) column = Gtk.TreeViewColumn("Mountpoint", renderer_mountpoint_text, text=2) self.part_treeview.append_column(column) column = Gtk.TreeViewColumn("Options", renderer_mountoptions_text, text=3) self.part_treeview.append_column(column) self.part_filesystem = builder.get_object("part_filesystem") self.part_size = builder.get_object("part_size") self.part_label = builder.get_object("part_label") self.disk_store = Gtk.ListStore(str) self.disk_treeview.set_model(self.disk_store) renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Disks", renderer, text=0) self.disk_treeview.append_column(column) self.createDiskStore() self.aboutdialog = builder.get_object("aboutdialog") self.aboutdialog.set_version(VERSION) self.aboutdialog.set_license(LICENSE) self.aboutdialog.set_copyright(COPYRIGHT) self.aboutdialog.set_comments("A tool to manage mountpoints and options of devices.") handlers = { "onDeleteWindow": Gtk.main_quit, "onButtonRefreshClicked": self.onButtonRefreshClicked, "onButtonSaveClicked": self.onButtonSaveClicked, "onButtonAboutClicked": self.onButtonAboutClicked, "onDiskCursorChanged": self.onDiskCursorChanged, "onPartCursorChanged": self.onPartCursorChanged, } builder.connect_signals(handlers) window.show_all()
def setupDisks(numDisks): print "Setup Disks" devLetters = list(string.ascii_lowercase) for x in range(1, int(numDisks) + 1): try: mkfs("/dev/xvd" + devLetters[x], "-text4", "-E lazy_itable_init=1") except Exception as e: pass if not os.path.exists("/mnt/data" + str(x)): os.makedirs("/mnt/data" + str(x)) try: mount("/dev/xvd" + devLetters[x], "/mnt/data" + str(x), "-text4") except Exception as e: pass try: Fstab.add("/dev/xvd" + devLetters[x], "/mnt/data" + str(x), filesystem="ext4") except Exception as e: pass
def __init__(self, fstab="/etc/fstab", sandbox=None): self.fstab = Fstab(fstab) self.commands = LowLevelCommands() # if we haven't been given a testing ground to play in, mount the real # root volume self.test = sandbox is not None self.mp = sandbox if self.mp is None: uuid = self.fstab.uuid_for_mountpoint("/") mountpoint = tempfile.mkdtemp(prefix="apt-btrfs-snapshot-mp-") if not self.commands.mount(uuid, mountpoint): os.rmdir(mountpoint) raise Exception("Unable to mount root volume") self.mp = mountpoint snapshots.setup(self.mp)
def fstab_add(dev, mp, fs, options=None): """Adds the given device entry to the /etc/fstab file """ return Fstab.add(dev, mp, fs, options=options)
def fstab_remove(mp): """Remove the given mountpoint entry from /etc/fstab """ return Fstab.remove_by_mountpoint(mp)
def main(): # to parse fdisk output, look for partitions after partitions header line fdisk_pars_begin_pattern = re.compile( r'^Device\s+Start\s+End\s+Sectors\s+Size\s+Type\s*$') # to parse partitions from fdisk output after parted creates partition table fdisk_par_pattern = re.compile( r'^(?P<device>\S+)\s+(?P<start>\d+)\s+(?P<end>\d+)\s+(?P<sectors>\d+)\s+(?P<size>\S+)\s+(?P<type>.*)$' ) # extract arguments from the command line parser = argparse.ArgumentParser( description='sensor-capture-disk-config.py', add_help=False, usage='sensor-capture-disk-config.py [options]') parser.add_argument('-i', '--interactive', dest='interactive', type=str2bool, nargs='?', const=True, default=False, help="Interactive") parser.add_argument( '-u', '--umount', dest='umount', type=str2bool, nargs='?', const=True, default=False, help="Unmount capture directories before determining candidate drives") parser.add_argument('-v', '--verbose', dest='debug', type=str2bool, nargs='?', const=True, default=False, help="Verbose output") parser.add_argument('-n', '--dry-run', dest='dryrun', type=str2bool, nargs='?', const=True, default=False, help="Dry run (don't perform actions)") parser.add_argument('-c', '--crypto', dest='encrypt', type=str2bool, nargs='?', const=True, default=False, help="Encrypt formatted volumes") try: parser.error = parser.exit args = parser.parse_args() except SystemExit: parser.print_help() exit(2) debug = args.debug if debug: eprint(f"Arguments: {sys.argv[1:]}") if debug: eprint(f"Arguments: {args}") # unmount existing mounts if requested if args.umount and (not args.dryrun): if (not args.interactive ) or YesOrNo(f'Unmount any mounted capture path(s)?'): if debug: eprint(f"Attempting unmount of capture path(s)...") run_process( f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR)}" ) run_process( f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR)}" ) run_process(f"umount {CAPTURE_MOUNT_ROOT_PATH}") # also luksClose any luks volumes devices we might have set up for cryptDev in [ remove_prefix(x, '/dev/mapper/') for x in glob.glob( f"/dev/mapper/{CAPTURE_CRYPT_DEV_PREFIX}*") ]: if debug: eprint(f"Running crypsetup luksClose on {cryptDev}...") _, cryptOut = run_process( f"/sbin/cryptsetup --verbose luksClose {cryptDev}", stdout=True, stderr=True, timeout=300) if debug: for line in cryptOut: eprint(f"\t{line}") _, reloadOut = run_process(f"systemctl daemon-reload") # check existing mounts, if the capture path(s) are already mounted, then abort with open('/proc/mounts', 'r') as f: for line in f.readlines(): mountDetails = line.split() if (len(mountDetails) >= 2): mountPoint = mountDetails[1] if mountPoint.startswith(CAPTURE_MOUNT_ROOT_PATH): eprint( f"It appears there is already a device mounted under {CAPTURE_MOUNT_ROOT_PATH} at {mountPoint}." ) eprint( f"If you wish to continue, you may run this script with the '-u|--umount' option to umount first." ) eprint() parser.print_help() exit(2) # get physical disks, partitions, device maps, and any mountpoints and UUID associated allDisks = defaultdict(list) if debug: eprint(f"Block devices:") for device in GetInternalDevices(): ecode, deviceTree = run_process( f'/bin/lsblk -o name,uuid,mountpoint --paths --noheadings /dev/{device}', stdout=True, stderr=False) if (ecode == 0): currentDev = None currentPar = None currentMapper = None for line in deviceTree: line = line.rstrip() if (len(line) > 0): if debug: eprint(f"\t{line}") if (line == f"/dev/{device}"): currentDev = line currentPar = None currentMapper = None allDisks[currentDev].append( PartitionInfo(device=currentDev)) elif (currentDev is not None) and (line[2:2 + len(f"/dev/{device}")] == f"/dev/{device}"): parInfo = f"/{line.split('/', 1)[-1]}".split() if (len(parInfo) >= 2): currentPar = PartitionInfo( device=currentDev, partition=parInfo[0], uuid=parInfo[1], mount=parInfo[2] if (len(parInfo) > 2) else None) currentMapper = None allDisks[currentDev].append(currentPar) elif (currentPar is not None) and ("/dev/mapper/" in line): parInfo = f"/{line.split('/', 1)[-1]}".split() if (len(parInfo) >= 2): currentMapper = PartitionInfo( device=currentDev, partition=currentPar.partition, mapper=parInfo[0], uuid=parInfo[1], mount=parInfo[2] if (len(parInfo) > 2) else None) allDisks[currentDev].append(currentMapper) # at this point allDisks might look like this: # defaultdict(<class 'list'>, # {'/dev/sda': [PartitionInfo(device='/dev/sda', partition=None, mapper=None, uuid=None, mount=None), # PartitionInfo(device='/dev/sda', partition='/dev/sda1', mapper=None, uuid='B42B-7414', mount=None), # PartitionInfo(device='/dev/sda', partition='/dev/sda2', mapper=None, uuid='6DF8-D966', mount='/boot/efi'), # PartitionInfo(device='/dev/sda', partition='/dev/sda3', mapper=None, uuid='f6b575e4-0ec2-47ab-8d0a-9d677ac4fe3c', mount='/boot'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper=None, uuid='Lmx30A-U9qR-kDZF-WOju-zlOi-otrR-WNjh7j', mount=None), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-swap', uuid='00987200-7157-45d1-a233-90cbb22554aa', mount='[SWAP]'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-root', uuid='b53ea5c3-8771-4717-9d3d-ef9c5b18bbe4', mount='/'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-var', uuid='45aec3eb-68be-4eaa-bf79-de3f2a85c103', mount='/var'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-audit', uuid='339ee49c-0e45-4510-8447-55f46f2a3653', mount='/var/log/audit'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-tmp', uuid='b305d781-263f-4016-8422-301f61c11472', mount='/tmp'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-opt', uuid='5e7cbfb8-760e-4526-90d5-ab103ae626a5', mount='/opt'), # PartitionInfo(device='/dev/sda', partition='/dev/sda4', mapper='/dev/mapper/main-home', uuid='1b089fc0-f3a4-400b-955c-d3fa6b1e2a5f', mount='/home')], # '/dev/sdb': [PartitionInfo(device='/dev/sdb', partition=None, mapper=None, uuid=None, mount=None)]}) candidateDevs = [] formattedDevs = [] # determine candidate storage devices, which are any disks that do not have a mount point associated with # it in any way, (no partitions, mappings, etc. that are mounted) and is at least 100 gigabytes for device, entries in allDisks.items(): deviceMounts = list( set([par.mount for par in entries if par.mount is not None])) if (len(deviceMounts) == 0) and (GetDeviceSize(device) >= MINIMUM_CAPTURE_DEVICE_BYTES): candidateDevs.append(device) # sort candidate devices largest to smallest candidateDevs = sorted(candidateDevs, key=lambda x: GetDeviceSize(x), reverse=True) if debug: eprint( f"Device candidates: {[(x, sizeof_fmt(GetDeviceSize(x))) for x in candidateDevs]}" ) if len(candidateDevs) > 0: if args.encrypt: # create keyfile (will be on the encrypted system drive, and used to automatically unlock the encrypted capture drives) with open(CAPTURE_CRYPT_KEYFILE, 'wb') as f: f.write(os.urandom(4096)) os.chown(CAPTURE_CRYPT_KEYFILE, 0, 0) os.chmod(CAPTURE_CRYPT_KEYFILE, CAPTURE_CRYPT_KEYFILE_PERMS) # partition/format each candidate device for device in candidateDevs: # we only need at most two drives (one for pcap, one for zeek), or at least one if (len(formattedDevs) >= 2): break if (not args.interactive) or YesOrNo( f'Partition and format {device}{" (dry-run)" if args.dryrun else ""}?' ): if args.dryrun: eprint(f"Partitioning {device} (dry run only)...") eprint( f'\t/sbin/parted --script --align optimal {device} -- mklabel gpt \\\n\t\tunit mib mkpart primary 1 100%' ) ecode = 0 partedOut = [] else: # use parted to create a gpt partition table with a single partition consuming 100% of the disk minus one megabyte at the beginning if debug: eprint(f"Partitioning {device}...") ecode, partedOut = run_process( f'/sbin/parted --script --align optimal {device} -- mklabel gpt \\\n unit mib mkpart primary 1 100%', stdout=True, stderr=True, timeout=300) if debug: eprint(partedOut) if (ecode == 0): if debug: eprint(f"Success partitioning {device}") # get the list of partitions from the newly partitioned device (should be just one) _, fdiskOut = run_process(f'fdisk -l {device}') pars = [] parsList = False for line in fdiskOut: if debug: eprint(f"\t{line}") if (not parsList ) and fdisk_pars_begin_pattern.search(line): parsList = True elif parsList: match = fdisk_par_pattern.search(line) if match is not None: pars.append(match.group('device')) if len(pars) == 1: parDev = pars[0] parUuid = str(uuid.uuid4()) parMapperDev = None okToFormat = True if args.encrypt: okToFormat = False # remove this device from /etc/crypttab if os.path.isfile(CRYPTTAB_FILE): with fileinput.FileInput( CRYPTTAB_FILE, inplace=True, backup='.bak') as f: for line in f: line = line.rstrip("\n") if line.startswith( f"{CreateMapperName(parDev)}" ): if debug: eprint( f"removed {line} from {CRYPTTAB_FILE}" ) else: print(line) _, reloadOut = run_process( f"systemctl daemon-reload") # for good measure, run luksErase in case it was a previous luks volume if debug: eprint( f"Running crypsetup luksErase on {parDev}..." ) _, cryptOut = run_process( f"/sbin/cryptsetup --verbose --batch-mode luksErase {parDev}", stdout=True, stderr=True, timeout=600) if debug: for line in cryptOut: eprint(f"\t{line}") _, reloadOut = run_process( f"systemctl daemon-reload") # luks volume creation # format device as a luks volume if debug: eprint( f"Running crypsetup luksFormat on {device}..." ) ecode, cryptOut = run_process( f"/sbin/cryptsetup --verbose --batch-mode luksFormat {parDev} --uuid='{parUuid}' --key-file {CAPTURE_CRYPT_KEYFILE}", stdout=True, stderr=True, timeout=3600) if debug or (ecode != 0): for line in cryptOut: eprint(f"\t{line}") if (ecode == 0): # open the luks volume in /dev/mapper/ if debug: eprint( f"Running crypsetup luksOpen on {device}..." ) parMapperDev = CreateMapperDeviceName( parDev) ecode, cryptOut = run_process( f"/sbin/cryptsetup --verbose luksOpen {parDev} {CreateMapperName(parDev)} --key-file {CAPTURE_CRYPT_KEYFILE}", stdout=True, stderr=True, timeout=180) if debug or (ecode != 0): for line in cryptOut: eprint(f"\t{line}") if (ecode == 0): # we have everything we need for luks okToFormat = True else: eprint( f"Error {ecode} opening LUKS on {parDev}, giving up on {device}" ) else: eprint( f"Error {ecode} formatting LUKS on {parDev}, giving up on {device}" ) # format the partition as an XFS file system if okToFormat: if debug: eprint( f'Created {parDev}, assigning {parUuid}' ) if args.encrypt: formatCmd = f"/sbin/mkfs.xfs -f {parMapperDev}" else: formatCmd = f"/sbin/mkfs.xfs -f -m uuid='{parUuid}' {parDev}" if debug: eprint(f"Formatting: {formatCmd}") ecode, mkfsOut = run_process(formatCmd, stdout=True, stderr=True, timeout=3600) if debug: for line in mkfsOut: eprint(f"\t{line}") if (ecode == 0): eprint( f"Success formatting {parMapperDev if args.encrypt else parDev}" ) formattedDevs.append( PartitionInfo(device=device, partition=parDev, mapper=parMapperDev, uuid=parUuid, mount=None)) else: eprint( f"Error {ecode} formatting {formatPath}, giving up on {device}" ) else: eprint( f"Error partitioning {device}, unexpected partitions after running parted, giving up on {device}" ) elif (ecode != 0): eprint( f"Error {ecode} partitioning {device}, giving up on {device}" ) # now that we have formatted our device(s), decide where they're going to mount (these are already sorted) if len(formattedDevs) >= 2: formattedDevs[0].mount = os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR) formattedDevs[1].mount = os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR) elif len(formattedDevs) == 1: formattedDevs[0].mount = CAPTURE_MOUNT_ROOT_PATH if debug: eprint(formattedDevs) # mountpoints are probably not already mounted, but this will make sure run_process( f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR)}" ) run_process( f"umount {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR)}" ) run_process(f"umount {CAPTURE_MOUNT_ROOT_PATH}") _, reloadOut = run_process(f"systemctl daemon-reload") # clean out any previous fstab entries that might be interfering from previous configurations if Fstab.remove_by_mountpoint(os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR), path=FSTAB_FILE): if debug: eprint( f"Removed previous {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_PCAP_DIR)} mount from {FSTAB_FILE}" ) if Fstab.remove_by_mountpoint(os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR), path=FSTAB_FILE): if debug: eprint( f"Removed previous {os.path.join(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_MOUNT_ZEEK_DIR)} mount from {FSTAB_FILE}" ) if Fstab.remove_by_mountpoint(CAPTURE_MOUNT_ROOT_PATH, path=FSTAB_FILE): if debug: eprint( f"Removed previous {CAPTURE_MOUNT_ROOT_PATH} mount from {FSTAB_FILE}" ) # reload tab files with systemctl _, reloadOut = run_process(f"systemctl daemon-reload") # get the GID of the group of the user(s) that will be doing the capture try: ecode, guidGetOut = run_process( f"getent group {CAPTURE_GROUP_OWNER}", stdout=True, stderr=True) if (ecode == 0) and (len(guidGetOut) > 0): netdevGuid = int(guidGetOut[0].split(':')[2]) else: netdevGuid = -1 except: netdevGuid = -1 # rmdir any mount directories that might be interfering from previous configurations if os.path.isdir(CAPTURE_MOUNT_ROOT_PATH): for root, dirs, files in os.walk(CAPTURE_MOUNT_ROOT_PATH, topdown=False): for name in dirs: if debug: eprint(f"Removing {os.path.join(root, name)}") os.rmdir(os.path.join(root, name)) if debug: eprint(f"Removing {CAPTURE_MOUNT_ROOT_PATH}") os.rmdir(CAPTURE_MOUNT_ROOT_PATH) if debug: eprint(f"Creating {CAPTURE_MOUNT_ROOT_PATH}") os.makedirs(CAPTURE_MOUNT_ROOT_PATH, exist_ok=True) os.chown(CAPTURE_MOUNT_ROOT_PATH, -1, netdevGuid) os.chmod(CAPTURE_MOUNT_ROOT_PATH, CAPTURE_DIR_PERMS) # add crypttab entries if args.encrypt: with open(CRYPTTAB_FILE, 'a' if os.path.isfile(CRYPTTAB_FILE) else 'w') as f: for par in formattedDevs: crypttabLine = f"{CreateMapperName(par.partition)} UUID={par.uuid} {CAPTURE_CRYPT_KEYFILE} luks\n" f.write(crypttabLine) if debug: eprint(f'Added "{crypttabLine}" to {CRYPTTAB_FILE}') # recreate mount directories and add fstab entries for par in formattedDevs: if debug: eprint(f"Creating {par.mount}") os.makedirs(par.mount, exist_ok=True) if args.encrypt: entry = Fstab.add( device=f"{par.mapper}", mountpoint=par.mount, options= f"defaults,inode64,noatime,rw,auto,user,x-systemd.device-timeout=600s", fs_passno=2, filesystem='xfs', path=FSTAB_FILE) else: entry = Fstab.add( device=f"UUID={par.uuid}", mountpoint=par.mount, options= f"defaults,inode64,noatime,rw,auto,user,x-systemd.device-timeout=600s", fs_passno=2, filesystem='xfs', path=FSTAB_FILE) eprint(f'Added "{entry}" to {FSTAB_FILE} for {par.partition}') # reload tab files with systemctl _, reloadOut = run_process(f"systemctl daemon-reload") # mount the partitions and create a directory with user permissions for par in formattedDevs: ecode, mountOut = run_process(f"mount {par.mount}") if (ecode == 0): if debug: eprint(f'Mounted {par.partition} at {par.mount}') userDirs = [] if par.mount == CAPTURE_MOUNT_ROOT_PATH: # only one drive, so we're mounted at /capture, create user directories for CAPTURE_MOUNT_ZEEK_DIR and CAPTURE_MOUNT_PCAP_DIR userDirs.append( os.path.join(par.mount, CAPTURE_MOUNT_PCAP_DIR)) userDirs.append( os.path.join(par.mount, CAPTURE_MOUNT_ZEEK_DIR)) else: # we're mounted somewhere *underneath* /capture, so create a user-writeable subdirectory where we are userDirs.append(os.path.join(par.mount, 'capture')) # set permissions on user dirs pcapDir = None zeekDir = None for userDir in userDirs: os.makedirs(userDir, exist_ok=True) os.chown(userDir, CAPTURE_USER_UID, netdevGuid) os.chmod(userDir, CAPTURE_SUBDIR_PERMS) if debug: eprint( f'Created "{userDir}" for writing by capture user') if f"{os.path.sep}{CAPTURE_MOUNT_PCAP_DIR}{os.path.sep}" in userDir: pcapDir = userDir elif f"{os.path.sep}{CAPTURE_MOUNT_ZEEK_DIR}{os.path.sep}" in userDir: zeekDir = userDir # replace capture paths in-place in SENSOR_CAPTURE_CONFIG if os.path.isfile(SENSOR_CAPTURE_CONFIG): capture_re = re.compile( r"\b(?P<key>PCAP_PATH|ZEEK_LOG_PATH)\s*=\s*.*?$") with fileinput.FileInput(SENSOR_CAPTURE_CONFIG, inplace=True, backup='.bak') as f: for line in f: line = line.rstrip("\n") log_path_match = capture_re.search(line) if (log_path_match is not None): if (log_path_match.group('key') == 'PCAP_PATH') and (pcapDir is not None): print( capture_re.sub(r"\1=%s" % pcapDir, line)) elif (log_path_match.group('key') == 'ZEEK_LOG_PATH') and (zeekDir is not None): print( capture_re.sub(r"\1=%s" % zeekDir, line)) else: print(line) else: print(line) else: eprint(f"Error {ecode} mounting {par.partition}") else: eprint( f"Could not find any unmounted devices greater than 100GB, giving up" )
def test_fstab_get_uuid(self): fstab = Fstab(fstab=os.path.join(self.testdir, "data", "fstab")) self.assertEqual(fstab.uuid_for_mountpoint("/"), "UUID=fe63f598-1906-478e-acc7-f74740e78d1f")
class MainWindow(Gtk.Window): def __init__(self): self.fstab = Fstab(FSTAB_PATH) builder = Gtk.Builder() builder.add_from_file("/usr/lib/snowlinux/snowMount/snowMount.ui") window = builder.get_object("main_window") self.disk_treeview = builder.get_object("disk_treeview") self.disk_label = builder.get_object("disk_label") self.disk_label2 = builder.get_object("disk_label2") self.part_treeview = builder.get_object("part_treeview") self.part_store = Gtk.ListStore(str, str, str, str) self.part_treeview.set_model(self.part_store) renderer_text = Gtk.CellRendererText() renderer_filesystem_text = Gtk.CellRendererText() renderer_filesystem_text.set_property("editable", True) renderer_filesystem_text.connect("edited", self.onFileSystemEdited) renderer_mountpoint_text = Gtk.CellRendererText() renderer_mountpoint_text.set_property("editable", True) renderer_mountpoint_text.connect("edited", self.onMountpointEdited) renderer_mountoptions_text = Gtk.CellRendererText() renderer_mountoptions_text.set_property("editable", True) renderer_mountoptions_text.connect("edited", self.onMountoptionsEdited) column = Gtk.TreeViewColumn("Partition", renderer_text, text=0) self.part_treeview.append_column(column) column = Gtk.TreeViewColumn("Filesystem", renderer_filesystem_text, text=1) self.part_treeview.append_column(column) column = Gtk.TreeViewColumn("Mountpoint", renderer_mountpoint_text, text=2) self.part_treeview.append_column(column) column = Gtk.TreeViewColumn("Options", renderer_mountoptions_text, text=3) self.part_treeview.append_column(column) self.part_filesystem = builder.get_object("part_filesystem") self.part_size = builder.get_object("part_size") self.part_label = builder.get_object("part_label") self.disk_store = Gtk.ListStore(str) self.disk_treeview.set_model(self.disk_store) renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Disks", renderer, text=0) self.disk_treeview.append_column(column) self.createDiskStore() self.aboutdialog = builder.get_object("aboutdialog") self.aboutdialog.set_version(VERSION) self.aboutdialog.set_license(LICENSE) self.aboutdialog.set_copyright(COPYRIGHT) self.aboutdialog.set_comments("A tool to manage mountpoints and options of devices.") handlers = { "onDeleteWindow": Gtk.main_quit, "onButtonRefreshClicked": self.onButtonRefreshClicked, "onButtonSaveClicked": self.onButtonSaveClicked, "onButtonAboutClicked": self.onButtonAboutClicked, "onDiskCursorChanged": self.onDiskCursorChanged, "onPartCursorChanged": self.onPartCursorChanged, } builder.connect_signals(handlers) window.show_all() def createDiskStore(self): disks = drivereader.get_disks() for disk in disks: self.disk_store.append(["{} ({})".format(disks[disk].getModel(), disk)]) def onFileSystemEdited(self, widget, path, text): self.part_store[path][1] = text def onMountpointEdited(self, widget, path, text): self.part_store[path][2] = text def onMountoptionsEdited(self, widget, path, text): self.part_store[path][3] = text def updateFstab(self, path): device_path = path[0] filesystem = path[1] mountpoint = path[2] mountoptions = path[3] self.fstab.updateFstab(device_path, mountpoint, mountoptions, filesystem) def onButtonSaveClicked(self, button): model, path = self.current_part[0], self.current_part[1] self.updateFstab(model[path]) self.fstab.writeFstab() def onButtonRefreshClicked(self, button): self.disk_store.clear() self.part_store.clear() self.part_filesystem.set_text("") self.part_size.set_text("") self.part_label.set_text("") self.createDiskStore() def onButtonAboutClicked(self, button): self.aboutdialog.run() self.aboutdialog.hide() def onDiskCursorChanged(self, selection): self.part_store.clear() self.part_filesystem.set_text("") self.part_size.set_text("") self.part_label.set_text("") model, treeiter = selection.get_selected() if treeiter is not None: device_path = model[treeiter][0].split()[-1][1:-1] disk = drivereader.get_disk(device_path) self.disk_label.set_text("{} ({})".format(disk.getModel(), disk.getSize())) self.disk_label2.set_text(device_path) for part in disk.getPartitions(): self.part_store.append( [ part, self.fstab.getFilesystem(part), self.fstab.getMountpoint(part), self.fstab.getMountoptions(part), ] ) def onPartCursorChanged(self, selection): model, treeiter = selection.get_selected() if treeiter is not None: device_path = model[treeiter][0] part = drivereader.get_partition(device_path) self.part_filesystem.set_text(part.getFilesystem()) self.part_size.set_text(part.getSize()) self.part_label.set_text(part.getLabel()) self.current_part = (model, treeiter)
class AptBtrfsSnapshot(object): """ the high level object that interacts with the snapshot system """ def __init__(self, fstab="/etc/fstab", sandbox=None): self.fstab = Fstab(fstab) self.commands = LowLevelCommands() # if we haven't been given a testing ground to play in, mount the real # root volume self.test = sandbox is not None self.mp = sandbox if self.mp is None: uuid = self.fstab.uuid_for_mountpoint("/") mountpoint = tempfile.mkdtemp(prefix="apt-btrfs-snapshot-mp-") if not self.commands.mount(uuid, mountpoint): os.rmdir(mountpoint) raise Exception("Unable to mount root volume") self.mp = mountpoint snapshots.setup(self.mp) def __del__(self): """ unmount root volume if necessary """ # This will probably not get run if there are cyclic references. # check thoroughly because we get called even if __init__ fails if not self.test and self.mp is not None: res = self.commands.umount(self.mp) os.rmdir(self.mp) self.mp = None def _get_now_str(self): return datetime.datetime.now().replace(microsecond=0).isoformat( str('_')) def _parse_older_than_to_datetime(self, timefmt): if isinstance(timefmt, datetime.datetime): return timefmt now = datetime.datetime.now() if not timefmt.endswith("d"): raise Exception("Please specify time in days (e.g. 10d)") days = int(timefmt[:-1]) return now - datetime.timedelta(days) def _get_last_snapshot_time(self): last_snapshot = datetime.datetime.fromtimestamp(0.0) if self.test: last_snapshot_file = '/tmp/apt_last_snapshot' else: last_snapshot_file = '/run/apt_last_snapshot' if os.path.exists(last_snapshot_file): try: t = open(last_snapshot_file) last_snapshot = \ datetime.datetime.fromtimestamp(float(t.readline())) except: # If we fail to read the timestamp for some reason, just return # the default value silently pass finally: t.close() return last_snapshot def _save_last_snapshot_time(self): if self.test: last_snapshot_file = '/tmp/apt_last_snapshot' else: last_snapshot_file = '/run/apt_last_snapshot' f = open(last_snapshot_file, 'w') f.write(str(time.time())) f.close() def _get_status(self): parent = Snapshot("@").parent if parent is not None: date_parent = parent.date else: date_parent = None if self.test: testdir = os.path.dirname(os.path.abspath(__file__)) if not testdir.endswith("test"): testdir = os.path.join(testdir, "test") var_location = os.path.join(testdir, "data/var") history = DpkgHistory(since = date_parent, var_location = var_location) else: history = DpkgHistory(since = date_parent) return parent, history def _prettify_changes(self, history, i_indent="- ", s_indent=" "): if history == None or history == NO_HISTORY: return [i_indent + "No packages operations recorded"] output = [] for op in ("install", "auto-install", "upgrade", "remove", "purge"): if len(history[op]) > 0: output.append("%s%ss (%d):" % (i_indent, op, len(history[op]))) packages = [] for p, v in history[op]: packages.append(p) packages = ", ".join(packages) if sys.stdout.isatty(): # if we are in a terminal, wrap text to match its width rows, columns = os.popen('stty size', 'r').read().split() packages = textwrap.wrap(packages, width=int(columns), initial_indent=s_indent, subsequent_indent=s_indent, break_on_hyphens=False) output.extend(packages) return output def status(self): """ show current root's parent and recent changes """ return self.show("@") def show(self, snapshot, compact=False): """ show details pertaining to given snapshot """ snapshot = Snapshot(snapshot) if snapshot.name == "@": parent, changes = self._get_status() else: parent, changes = snapshot.parent, snapshot.changes fca = snapshots.first_common_ancestor("@", snapshot) mainline = (fca == snapshot) and 'Is' or "Isn't" mainline = "%s an ancestor of @" % mainline pretty_history = self._prettify_changes(changes) if parent == None: parent = "unknown" else: parent = parent.name if not compact: title = "Snapshot %s" % snapshot.name print(title) if snapshot.name != "@": print(mainline) print("Parent: %s" % parent) if parent == "unknown" and snapshot.name == "@": print("dpkg history shown for the last 30 days") print("dpkg history:") else: print("dpkg history for %s" % snapshot.name) print("\n".join(pretty_history)) return True def create(self, tag=""): """ create a new apt-snapshot of @, tagging it if a tag is given """ if 'APT_NO_SNAPSHOTS' in os.environ and tag == "": print("Shell variable APT_NO_SNAPSHOTS found, skipping creation") return True elif 'APT_NO_SNAPSHOTS' in os.environ and tag != "": print("Shell variable APT_NO_SNAPSHOTS found, but tag supplied, " "creating snapshot") last = self._get_last_snapshot_time() # If there is a recent snapshot and no tag supplied, skip creation if tag == "" \ and last > datetime.datetime.now() - datetime.timedelta(seconds=60): print("A recent snapshot already exists: %s" % last) return True # make snapshot snap_id = SNAP_PREFIX + self._get_now_str() + tag res = self.commands.btrfs_subvolume_snapshot( os.path.join(self.mp, "@"), os.path.join(self.mp, snap_id)) # set root's new parent Snapshot("@").parent = snap_id # find and store dpkg changes parent, history = self._get_status() Snapshot(snap_id).changes = history self._save_last_snapshot_time() return res def tag(self, snapshot, tag): """ Adds/replaces the tag for the given snapshot """ children = Snapshot(snapshot).children pos = len(SNAP_PREFIX) new_name = snapshot[:pos + 19] + tag old_snap = os.path.join(self.mp, snapshot) new_snap = os.path.join(self.mp, new_name) os.rename(old_snap, new_snap) tagged = Snapshot(new_name) for child in children: child.parent = tagged return True def list(self): # The function name will not clash with reserved keywords. It is only # accessible via self.list() print("Available snapshots:") print(" \n".join(snapshots.get_list())) return True def list_older_than(self, timefmt): older_than = self._parse_older_than_to_datetime(timefmt) print("Available snapshots older than '%s':" % timefmt) print(" \n".join(snapshots.get_list(older_than=older_than))) return True def _prompt_for_tag(self): print("You haven't specified a tag for the snapshot that will be created from the current state.") tag = raw_input("Please enter a tag: ") if tag: tag = "-" + tag return tag def set_default(self, snapshot, tag=""): """ backup @ and replace @ with a copy of given snapshot """ if not tag: tag = self._prompt_for_tag() snapshot = Snapshot(snapshot) new_root = os.path.join(self.mp, snapshot.name) if ( os.path.isdir(new_root) and snapshot.name.startswith(SNAP_PREFIX)): default_root = os.path.join(self.mp, "@") staging = os.path.join(self.mp, "@apt-btrfs-staging") if os.path.lexists(staging): raise Exception("Reserved directory @apt-btrfs-staging " "exists\nPlease remove from btrfs volume root before " "trying again") # find and store dpkg changes date, history = self._get_status() Snapshot("@").changes = history # snapshot the requested default so as not to remove it res = self.commands.btrfs_subvolume_snapshot(new_root, staging) if not res: raise Exception("Could not create snapshot") # make backup name backup = os.path.join(self.mp, SNAP_PREFIX + self._get_now_str()) + tag # if backup name is already in use, wait a sec and try again if os.path.exists(backup): time.sleep(1) backup = os.path.join(self.mp, SNAP_PREFIX + self._get_now_str()) # move everything into place os.rename(default_root, backup) os.rename(staging, default_root) # remove @/etc/apt-btrfs-changes & set root's new parent new_default = Snapshot("@") new_default.changes = None new_default.parent = snapshot.name print("Default changed to %s, please reboot for changes to take " "effect." % snapshot.name) else: print("You have selected an invalid snapshot. Please make sure " "that it exists, and that its name starts with " "\"%s\"" % SNAP_PREFIX) return True def rollback(self, number=1, tag=""): back_to = Snapshot("@") for i in range(number): back_to = back_to.parent if back_to == None: raise Exception("Can't rollback that far") return False return self.set_default(back_to, tag) def delete(self, snapshot): snapshot = Snapshot(snapshot) to_delete = os.path.join(self.mp, snapshot.name) res = True if ( os.path.isdir(to_delete) and snapshot.name.startswith(SNAP_PREFIX)): # correct parent links and combine change info parent = snapshot.parent children = snapshot.children old_history = snapshot.changes # clean-ups in the global vars of snapshots.list_of.remove(snapshot) if parent != None and parent.name in snapshots.children: snapshots.children[parent.name].remove(snapshot) for child in children: child.parent = parent # and do the same again in the global vars of snapshots # messy but necessary for delete_older_than to work snapshots.parents[child.name] = parent if parent != None: snapshots.children[parent.name].append(child) # necessary newer_history = child.changes if old_history == None: combined = newer_history elif newer_history == None: combined = None else: combined = old_history + newer_history child.changes = combined res = self.commands.btrfs_delete_snapshot(to_delete) else: print("You have selected an invalid snapshot. Please make sure " "that it exists, and that its name starts with " "\"%s\"" % SNAP_PREFIX) return res def delete_older_than(self, timefmt): older_than = self._parse_older_than_to_datetime(timefmt) res = True list_of = snapshots.get_list(older_than=older_than) list_of.sort(key = lambda x: x.date, reverse = True) for snap in list_of: if len(snap.children) < 2 and snap.tag == "": res &= self.delete(snap) return res def prune(self, snapshot): snapshot = Snapshot(snapshot) res = True if len(snapshot.children) != 0: raise Exception("Snapshot is not the end of a branch") while True: parent = snapshot.parent res &= self.delete(snapshot) snapshot = parent if snapshot == None or len(snapshot.children) != 0: break return res def tree(self): date_parent, history = self._get_status() tree = TreeView(history) tree.print() def recent(self, number, snapshot): print("%s and its predecessors. Showing %d snapshots.\n" % (snapshot, number)) snapshot = Snapshot(snapshot) for i in range(number): self.show(snapshot, compact=True) snapshot = snapshot.parent if snapshot == None or i == number - 1: break else: print() return True def clean(self, what="apt-cache"): snapshot_list = snapshots.get_list() for snapshot in snapshot_list: path = os.path.join(self.mp, snapshot.name) if what == "apt-cache": path = os.path.join(path, "var/cache/apt/archives") if not os.path.exists(path): continue dirlist = os.listdir(path) for f in dirlist: fpath = os.path.join(path, f) if f.endswith(".deb") and os.path.lexists(fpath): os.remove(fpath)