summaryrefslogtreecommitdiffstats
path: root/src/utils/fs.py
diff options
context:
space:
mode:
authorJose M. Guisado <jguisado@soleta.eu>2023-04-25 14:03:29 +0200
committerJose M. Guisado <jguisado@soleta.eu>2023-05-02 17:31:08 +0200
commitdd999bfe34e7f812d4ce61828f262ee06b5d4fa4 (patch)
tree587d17a5f5226f53892e0413bdd36c0e5c2f1c1d /src/utils/fs.py
parent22dce48d3ec27d0db8f6a069744d44454382bcc4 (diff)
utils: rewrite ogReduceFs
Drop subprocess call to bash function ogReduceFs. Use a native python solution with subprocess calls to the required underlying tools. Use get_filesystem_type to get the filesystem from a partition and call the corresponding supported filesystem shrink function. Filesystem specific functions are declared "_reduce_{filesystem}" and should not be imported elsewhere. In case of NTFS filesystems, the output of 'ntfsresize' is processed directly. This is dirty, but we can expect no changes to the output strings if we read the following comment in the nftsresize.c source code: https://github.com/tuxera/ntfs-3g/blob/edge/ntfsprogs/ntfsresize.c#L12 ntfsresize requires to do previous dry-run executions to confirm that the resizing is possible. If a dry-run fails but a 10% increase in size is still smaller than original filesystem then retry the operation until dry-run reports sucess or the size increase is bigger than original. If resizing to a smaller ntfs filesystem is not possible then ogReduceFs will do nothing.
Diffstat (limited to 'src/utils/fs.py')
-rw-r--r--src/utils/fs.py61
1 files changed, 52 insertions, 9 deletions
diff --git a/src/utils/fs.py b/src/utils/fs.py
index a7058a7..bffdb8c 100644
--- a/src/utils/fs.py
+++ b/src/utils/fs.py
@@ -82,15 +82,21 @@ def get_usedperc(mountpoint):
def ogReduceFs(disk, part):
"""
- Bash function 'ogReduceFs' wrapper
- """
- proc = subprocess.run(f'ogReduceFs {disk} {part}',
- shell=True, stdout=PIPE,
- encoding='utf-8')
- if proc.returncode != 0:
- logging.warn(f'ogReduceFS exited with non zero code: {proc.returncode}')
- subprocess.run(f'ogUnmount {disk} {part}',
- shell=True)
+ Shrink filesystem of a partition. Supports ext4 and ntfs partitions.
+ Unsupported filesystem or invalid paths don't raise an exception,
+ instead this method logs a warning message and does nothing.
+ """
+ partdev = get_partition_device(disk, part)
+ fstype = get_filesystem_type(partdev)
+
+ umount(partdev)
+ if fstype == 'ext4':
+ _reduce_resize2fs(partdev)
+ elif fstype == 'ntfs':
+ _reduce_ntfsresize(partdev)
+ else:
+ logging.warn(f'Unable to shrink filesystem at {partdev}. '
+ f'Unsupported filesystem "{fstype}".')
def ogExtendFs(disk, part):
@@ -177,3 +183,40 @@ def get_filesystem_type(partdev):
if proc.returncode != 0:
raise RuntimeError(f'Error getting filesystem from {partdev}')
return proc.stdout.strip()
+
+
+def _reduce_resize2fs(partdev):
+ cmd = shlex.split(f'resize2fs -fpM {partdev}')
+ with open('/tmp/command.log', 'ab', 0) as logfile:
+ subprocess.run(cmd, stdout=logfile, stderr=STDOUT)
+
+
+def _reduce_ntfsresize(partdev):
+ cmd_info = shlex.split(f'ntfsresize -Pfi {partdev}')
+ proc_info = subprocess.run(cmd_info, stdout=subprocess.PIPE, encoding='utf-8')
+ out_info = proc_info.stdout.strip()
+
+ # Process ntfsresize output directly.
+ # The first split operation leaves the wanted data at the second element of
+ # the split ([1]). Finally do a second split with ' ' to get the data but
+ # nothing else following it.
+ #
+ # In addition, increase by 10%+1K the reported shrink location on which the
+ # filesystem can be resized according to ntfsresize.
+ size = int(out_info.split('device size: ')[1].split(' ')[0])
+ new_size = int(int(out_info.split('resize at ')[1].split(' ')[0])*1.1+1024)
+
+ # Dry-run loop to test if resizing is actually possible. This is required by ntfsresize.
+ returncode = 1
+ while new_size < size and returncode != 0:
+ cmd_resize_dryrun = shlex.split(f'ntfsresize -Pfns {new_size:.0f} {partdev}')
+ proc_resize_dryrun = subprocess.run(cmd_resize_dryrun, stdout=subprocess.PIPE, encoding='utf-8')
+ returncode = proc_resize_dryrun.returncode
+ out_resize_dryrun = proc_resize_dryrun.stdout.strip()
+ extra_size = int(out_resize_dryrun.split('Needed relocations : ')[1].split(' ')[0])*1.1+1024
+ new_size += int(extra_size)
+
+ if new_size < size:
+ cmd_resize = shlex.split(f'ntfsresize -fs {new_size:.0f} {partdev}')
+ with open('/tmp/command.log', 'ab', 0) as logfile:
+ subprocess.run(cmd_resize, input='y', stderr=STDOUT, encoding='utf-8')