#/** #@file FileSystemLib.py #@brief Librería o clase FileSystem #@class FileSystem #@brief Funciones para gestión de sistemas de archivos. #@warning License: GNU GPLv3+ #*/ import subprocess import sys import os.path import ogGlobals import SystemLib import DiskLib import CacheLib import FileSystemLib #/** # ogCheckFs int_ndisk int_nfilesys #@brief Comprueba el estado de un sistema de archivos. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@exception OG_ERR_PARTITION Partición desconocida o no accesible. #@note Requisitos: *fsck* #@warning No se comprueban sistemas de archivos montados o bloqueados. #@todo Definir salidas. #*/ ## def ogCheckFs (disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return data = { 'EXT2': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, 'EXT3': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, 'EXT4': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, 'CACHE': { 'prog': 'e2fsck', 'params': '-y', 'codes': (1, 2), }, 'BTRFS': { 'prog': 'btrfsck', 'codes': (1), }, 'REISERFS': { 'prog': 'fsck.reiserfs', 'codes': (1, 2), 'input': 'Yes' }, 'REISER4': { 'prog': 'fsck.reiser4', 'params': '-ay', }, 'JFS': { 'prog': 'fsck.jfs', 'codes': (1, 2), }, 'XFS': { 'prog': 'xfs_repair', }, 'F2FS': { 'prog': 'fsck.f2fs', }, 'NTFS': { 'prog': 'ntfsfix', }, 'EXFAT': { 'prog': 'fsck.exfat', }, 'FAT32': { 'prog': 'dosfsck', 'params': '-a', 'codes': (1), }, 'FAT16': { 'prog': 'dosfsck', 'params': '-a', 'codes': (1), }, 'FAT12': { 'prog': 'dosfsck', 'params': '-a', 'codes': (1), }, 'HFS': { 'prog': 'fsck.hfs', 'params': '-f', }, 'HFSPLUS': { 'prog': 'fsck.hfs', 'params': '-f', }, 'UFS': { 'prog': 'fsck.ufs', }, 'ZFS': { 'prog': 'fsck.zfs', }, } type = ogGetFsType (disk, par) if type in data: prog = data[type]['prog'] params = data[type]['params'] if 'params' in data[type] else '' codes = data[type]['codes'] if 'codes' in data[type] else [] input = data[type]['input'] if 'input' in data[type] else None else: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk}, {par}, {type}') return None ogUnmount (disk, par) if ogIsMounted (disk, par): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') return None if ogIsLocked (disk, par): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{disk} {par}') return None ogLock (disk, par) rc = subprocess.run ([prog] + params.split() + [PART], input=input, text=True).returncode if 0 == rc or rc in codes: errcode = 0 elif 127 == rc: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog) errcode = ogGlobals.OG_ERR_NOTEXEC else: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') errcode = ogGlobals.OG_ERR_PARTITION ogUnlock (disk, par) return errcode #/** # ogExtendFs int_ndisk int_nfilesys #@brief Extiende un sistema de archivos al tamaño de su partición. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@exception OG_ERR_PARTITION Partición desconocida o no accesible. #@note Requisitos: *resize* #*/ ## def ogExtendFs (disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return data = { 'EXT2': { 'prog': 'resize2fs', 'params': '-f', }, 'EXT3': { 'prog': 'resize2fs', 'params': '-f', }, 'EXT4': { 'prog': 'resize2fs', 'params': '-f', }, 'BTRFS': { 'prog': 'btrfs', 'params': 'filesystem resize max', 'domount': True }, 'REISERFS': { 'prog': 'resize_reiserfs', 'params': '-f', }, 'REISER4': { 'prog': 'resize_reiserfs', 'params': '-f', }, 'NTFS': { 'prog': 'ntfsresize', 'params': '-f', 'input': 'y' }, 'F2FS': { 'unsupported': True }, 'JFS': { 'unsupported': True }, 'NILFS2': { 'unsupported': True }, # try "nilfs-resize" 'XFS': { 'unsupported': True }, 'EXFAT': { 'unsupported': True }, 'FAT32': { 'unsupported': True }, # try "fatresize" 'FAT16': { 'unsupported': True }, # try "fatresize" 'HFS': { 'unsupported': True }, 'HFSPLUS': { 'unsupported': True }, 'UFS': { 'unsupported': True }, } type = ogGetFsType (disk, par) if type in data: prog = data[type]['prog'] if 'prog' in data[type] else None params = data[type]['params'] if 'params' in data[type] else None domount = data[type]['domount'] if 'domount' in data[type] else False input = data[type]['input'] if 'input' in data[type] else None else: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par} {type}') return if not prog: return if domount: PART = ogMount (disk, par) if not PART: return else: ogUnmount (disk, par) if ogIsMounted (disk, par): SystemLib.ogRaiseError([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') return # Error si el sistema de archivos está bloqueado. if ogIsLocked (disk, par): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{disk} {par}') return # Redimensionar en modo uso exclusivo. ogLock (disk, par) try: if input: rc = subprocess.run ([prog] + params.split() + [PART], input=input, text=True).returncode else: rc = subprocess.run ([prog] + params.split() + [PART]).returncode except FileNotFoundError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog) rc = ogGlobals.OG_ERR_NOTEXEC except: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') rc = ogGlobals.OG_ERR_PARTITION ogUnlock (disk, par) return not rc ## reverse to indicate success #/** # ogFormat int_ndisk int_nfilesys | CACHE #@see ogFormatFs ogFormatCache #*/ ## def ogFormat (disk, par=None, fs=None, label=None): if disk.lower() == "cache": return CacheLib.ogFormatCache() else: return ogFormatFs (disk, par, fs=fs, label=label) #/** # ogFormatFs int_ndisk int_nfilesys [type_fstype] [str_label] #@brief Formatea un sistema de ficheros según el tipo de su partición. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@param type_fstype mnemónico de sistema de ficheros a formatear (opcional al reformatear) #@param str_label etiqueta de volumen (opcional) #@return (por determinar) #@exception OG_ERR_FORMAT Formato de ejecución incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@exception OG_ERR_PARTITION Partición no accesible o desconocida. #@note Requisitos: mkfs* #@warning No formatea particiones montadas ni bloqueadas. #@todo Definir salidas. #*/ ## def ogFormatFs (disk, par, fs=None, label=None): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return if ogIsMounted (disk, par): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_DONTFORMAT, f'{ogGlobals.lang.MSG_MOUNT}: {disk} {par}') return None if ogIsLocked (disk, par): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f"{disk} {par}") return None if not fs: fs = ogGetFsType (disk, par) if not fs: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{disk} {par} ...") return None data = { 'EXT2': { 'PROG': 'mkfs.ext2', 'PARAMS': '-F' }, 'EXT3': { 'PROG': 'mkfs.ext3', 'PARAMS': '-F' }, 'EXT4': { 'PROG': 'mkfs.ext4', 'PARAMS': '-F' }, 'BTRFS': { 'PROG': 'mkfs.btrfs', 'PARAMS': '-f' }, 'REISERFS': { 'PROG': 'mkfs.reiserfs', 'PARAMS': '-f', 'LABELPARAM': '-l' }, 'REISER4': { 'PROG': 'mkfs.reiser4', 'PARAMS': '-f', 'INPUT': 'y\n' }, 'XFS': { 'PROG': 'mkfs.xfs', 'PARAMS': '-f' }, 'JFS': { 'PROG': 'mkfs.jfs', 'INPUT': 'y\n' }, 'F2FS': { 'PROG': 'mkfs.f2fs', 'LABELPARAM': '-l' }, 'NILFS2': { 'PROG': 'mkfs.nilfs2', 'PARAMS': '-f' }, 'LINUX-SWAP': { 'PROG': 'mkswap' }, 'NTFS': { 'PROG': 'mkntfs', 'PARAMS': '-f' }, 'EXFAT': { 'PROG': 'mkfs.exfat', 'LABELPARAM': '-n' }, 'FAT32': { 'PROG': 'mkdosfs', 'PARAMS': '-F 32', 'LABELPARAM': '-n' }, 'FAT16': { 'PROG': 'mkdosfs', 'PARAMS': '-F 16', 'LABELPARAM': '-n' }, 'FAT12': { 'PROG': 'mkdosfs', 'PARAMS': '-F 12', 'LABELPARAM': '-n' }, 'HFS': { 'PROG': 'mkfs.hfs' }, 'HFSPLUS': { 'PROG': 'mkfs.hfsplus', 'LABELPARAM': '-v' }, 'UFS': { 'PROG': 'mkfs.ufs', 'PARAMS': '-O 2' }, } if fs not in data: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk} {par} {fs}") return d = data[fs] prog = d['PROG'] params = d['PARAMS'] if 'PARAMS' in d else '' labelparam = d['LABELPARAM'] if 'LABELPARAM' in d else '' input = d['INPUT'] if 'INPUT' in d else '' if label == "CACHE": SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{ogGlobals.lang.MSG_RESERVEDVALUE}: CACHE") return if label: params = f"{params} {labelparam or '-L'} {label}" ogLock (disk, par) subprocess.run (['umount', PART]) try: if input: errcode = subprocess.run ([prog] + params.split (' ') + [PART]).returncode else: errcode = subprocess.run ([prog] + params.split (' ') + [PART], input=input, text=True).returncode except FileNotFoundError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog) errcode = ogGlobals.OG_ERR_NOTEXEC except: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk} {par}") errcode = ogGlobals.OG_ERR_PARTITION ogUnlock (disk, par) return errcode #/** # ogGetFsSize int_ndisk int_npartition [str_unit] #@brief Muestra el tamanio del sistema de archivos indicado, permite definir la unidad de medida, por defecto GB #@param int_ndisk nº de orden del disco #@param int_npartition nº de orden de la partición #@param str_unit unidad (opcional, por defecto: KB) #@return float_size - Tamaño del sistema de archivos #@note str_unit = { KB, MB, GB, TB } #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo. #*/ ## def ogGetFsSize (disk, par, unit='KB'): factor = 1 if unit.upper() == "MB": factor = 1024 elif unit.upper() == "GB": factor = 1024 * 1024 elif unit.upper() == "TB": factor = 1024 * 1024 * 1024 elif unit.upper() != "KB": SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_FORMAT, f"{unit} != {{ KB, MB, GB, TB }}" ) return # Obtener el tamaño del sistema de archivo (si no está formateado; tamaño = 0). mnt = FileSystemLib.ogMount (disk, par) if mnt: result = subprocess.run(["df", "-BK", mnt], capture_output=True, text=True) val = result.stdout.split("\n")[1].split()[1] val = val.replace ('K', '') sz = int (val) / factor else: sz = 0 return int (sz) #/** # ogGetFsType int_ndisk int_nfilesys #@brief Devuelve el mnemonico con el tipo de sistema de archivos. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Mnemonico #@note Mnemonico: { EXT2, EXT3, EXT4, BTRFS, REISERFS, XFS, JFS, FAT12, FAT16, FAT32, NTFS, LINUX-SWAP, LINUX-LVM, LINUX-RAID, HFS, HFSPLUS, CACHE } #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #*/ ## def ogGetFsType(disk, part): PART = DiskLib.ogDiskToDev(disk, part) if not PART: return TYPE = None if PART.startswith("/"): out = subprocess.run(["blkid", "-o", "export", PART], capture_output=True, text=True).stdout.splitlines() for line in out: if line.startswith("TYPE="): TYPE = line.split("=")[1].upper() break else: try: subprocess.run(["zfs", "mount", PART]) except FileNotFoundError: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_NOTEXEC, 'zfs' ) return out = subprocess.run(["mount"], capture_output=True, text=True).stdout.splitlines() for line in out: if line.startswith(PART): TYPE = line.split()[4].upper() break if not TYPE: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_NOTFOUND, f'{disk} {part}' ) return # Componer valores correctos. if TYPE == "EXT4": if f"{disk} {part}" == CacheLib.ogFindCache(): if ogIsFormated(disk, part): TYPE = "CACHE" elif TYPE == "VFAT": result = subprocess.run(["blkid", "-po", "export", PART], capture_output=True, text=True) for line in result.stdout.split("\n"): if line.startswith("VERSION="): TYPE = line.split("=")[1].upper() break elif TYPE == "SWAP": TYPE = "LINUX-SWAP" elif TYPE.startswith("LVM"): TYPE = "LINUX-LVM" elif "RAID" in TYPE: TYPE = "LINUX-RAID" elif TYPE == "ZFS_MEMBER": TYPE = "ZVOL" elif "_MEMBER" in TYPE: TYPE = TYPE.replace("_MEMBER", "") return TYPE #/** # ogGetMountPoint int_ndisk int_nfilesys #@brief Devuelve el punto de montaje de un sistema de archivos. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Punto de montaje #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@note Requisitos: \c mount* \c awk #*/ ## def ogGetMountPoint(disk, par): PART = DiskLib.ogDiskToDev(disk, par) if not PART: return return subprocess.run(["findmnt", "-n", "-o", "TARGET", PART], capture_output=True, text=True).stdout.strip() #/** # ogIsFormated int_ndisk int_nfilesys #@brief Comprueba si un sistema de archivos está formateado. #@param int_ndisk nº de orden del disco o volumen. #@param int_nfilesys nº de orden del sistema de archivos #@return Código de salida: True - formateado, False - sin formato o error. #*/ ## def ogIsFormated(disk, part): PART = DiskLib.ogDiskToDev (disk, part) if not PART: return # Revisar tipo de sistema de archivos. if PART.startswith("/"): out = subprocess.run(["blkid", "-s", "TYPE", PART], capture_output=True, text=True).stdout.strip() if 'swap' in out: return False if '_member' in out: return False return bool(out) else: out = subprocess.run(["zfs", "list", "-Hp", "-o", "canmount", PART], capture_output=True, text=True).stdout.strip() return out == "on" #/** # ogIsLocked int_ndisk int_npartition #@see ogIsPartitionLocked #*/ def ogIsLocked(disk, par): return ogIsPartitionLocked(disk, par) #/** # ogIsPartitionLocked int_ndisk int_npartition #@brief Comprueba si una partición o su disco están bloqueados por una operación de uso exclusivo. #@param int_ndisk nº de orden del disco #@param int_npartition nº de orden de la partición #@return Código de salida: 0 - bloqueado, 1 - sin bloquear o error. #@note Los ficheros de bloqueo se localizan en \c /var/lock/dev, siendo \c dev el dispositivo de la partición o de su disco, sustituyendo el carácter "/" por "-". #*/ ## def ogIsPartitionLocked(disk, par): DISK = DiskLib.ogDiskToDev(disk) PART = DiskLib.ogDiskToDev(disk, par) if not PART: return LOCKDISK = f"/var/lock/lock{DISK.replace('/', '-')}" LOCKPART = f"/var/lock/lock{PART.replace('/', '-')}" rc = os.path.isfile(LOCKDISK) or os.path.isfile(LOCKPART) return rc #/** # ogIsMounted int_ndisk int_nfilesys #@brief Comprueba si un sistema de archivos está montado. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Código de salida: 0 - montado, 1 - sin montar o error. #*/ ## def ogIsMounted (disk, par): return bool (ogGetMountPoint (disk, par)) #/** # ogIsReadonly int_ndisk int_nfilesys #@brief Comprueba si un sistema de archivos está montado solo de lectura. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Código de salida: 0 - montado solo de lectura, 1 - con escritura o no montado. #@version 1.1.0 - Primera versión para OpenGnsys. #@author Ramon Gomez, ETSII Universidad de Sevilla #@date 2016-01-20 #*/ ## def ogIsReadonly(disk, par): PART = DiskLib.ogDiskToDev(disk, par) if not PART: return result = subprocess.run(["findmnt", "-n", "-o", "OPTIONS", PART], capture_output=True, text=True) options = result.stdout.strip().split(",") return "ro" in options #/** # ogIsWritable int_ndisk int_nfilesys #@brief Comprueba si un sistema de archivos está montado de lectura y escritura. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Código de salida: 0 - lectura y escritura, 1 - solo lectura o no montado. #*/ ## def ogIsWritable (disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return result = subprocess.run(["findmnt", "-n", "-o", "OPTIONS", PART], capture_output=True, text=True) options = result.stdout.strip().split(",") return "rw" in options #/** # ogLock int_ndisk int_npartition #@see ogLockPartition #*/ def ogLock(disk, par): return ogLockPartition(disk, par) #/** # ogLockPartition int_ndisk int_npartition #@brief Genera un fichero de bloqueo para una partición en uso exlusivo. #@param int_ndisk nº de orden del disco #@param int_npartition nº de orden de la partición #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@note El fichero de bloqueo se localiza en \c /var/lock/part, siendo \c part el dispositivo de la partición, sustituyendo el carácter "/" por "-". #*/ ## def ogLockPartition (disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}" open(LOCKFILE, 'w').close() return True #/** # ogMount int_ndisk int_nfilesys #@see ogMountFs ogMountCache ogMountCdrom #*/ ## def ogMount(*args): if 1 == len (args): if 'cache' == args[0].lower(): return DiskLib.ogMountCache() elif 'cdrom' == args[0].lower(): return ogMountCdrom() elif 2 == len (args): return ogMountFs(args[0], args[1]) #/** # ogMountFirstFs int_ndisk #@brief Monta el primer sistema de archivos disponible en el disco. #@param int_ndisk nº de orden del disco #@return Punto de montaje del primer sistema de archivos detectado #*/ ## def ogMountFirstFs(int_ndisk): # Obtener número de particiones del disco. NPARTS = DiskLib.ogGetPartitionsNumber(int_ndisk) for PART in range(1, NPARTS + 1): MNTDIR = ogMount(int_ndisk, PART) if MNTDIR: return MNTDIR SystemLib.ogRaiseError( "session", ogGlobals.OG_ERR_NOTFOUND, f"{int_ndisk}" ) return ogGlobals.OG_ERR_NOTFOUND #/** # ogMountFs int_ndisk int_nfilesys #@brief Monta un sistema de archivos. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Punto de montaje #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@exception OG_ERR_PARTITION Tipo de particion desconocido o no se puede montar. #*/ ## def ogMountFs (disk, par): dev = DiskLib.ogDiskToDev (disk, par) if not dev: return mntdir = ogGetMountPoint (disk, par) if mntdir: return mntdir if ogIsLocked (disk, par): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_PARTITION}, {disk} {par}') return # El camino de un dispositivo normal comienza por el carácter "/". if dev.startswith ('/'): # Crear punto de montaje o enlace simbólico para caché local. mntdir = dev.replace ('/dev', '/mnt') if f"{disk} {par}" == CacheLib.ogFindCache(): os.makedirs(ogGlobals.OGCAC, exist_ok=True) try: os.symlink(ogGlobals.OGCAC, mntdir) except FileExistsError: pass else: os.makedirs(mntdir, exist_ok=True) # Montar sistema de archivos. try: rc = subprocess.run(['mount', dev, mntdir], check=True).returncode except subprocess.CalledProcessError: try: rc = subprocess.run(['mount', dev, mntdir, '-o', 'force,remove_hiberfile'], check=True).returncode except subprocess.CalledProcessError: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_PARTITION, f"{disk}, {par}" ) return if 0 == rc: pass elif 14 == rc: try: subprocess.run (['ntfsfix', '-d', par], check=True) except subprocess.CalledProcessError: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_PARTITION, f"{disk, par}" ) #return else: try: subprocess.run (['mount', par, mntdir, '-o', 'ro'], check=True) except subprocess.CalledProcessError: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_PARTITION, f"{disk, par}" ) #return # Aviso de montaje de solo lectura. if ogIsReadonly(disk, par): SystemLib.ogEcho("warning", f'ogMountFs: {ogGlobals.lang.MSG_MOUNTREADONLY}: "{disk}, {par}"') else: # Montar sistema de archivos ZFS (un ZPOOL no comienza por "/"). subprocess.run(['zfs', 'mount', dev]) return mntdir #/** # ogMountCdrom #@brief Monta dispositivo óptico por defecto #@return Punto de montaje #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_PARTITION Tipo de particion desconocido o no se puede montar. #@version #@author #@date #*/ ## def ogMountCdrom(): DEV = '/dev/cdrom' # Por defecto outlines = subprocess.run (['mount'], capture_output=True, text=True).stdout.split ('\n') mntdir = '' for l in outlines: items = l.split (' ') if DEV == items[0]: mntdir = items[2] break if not mntdir: mntdir = DEV.replace ('/dev', '/mnt') os.makedirs (mntdir, exist_ok=True) try: subprocess.run (['mount', '-t', 'iso9660', DEV, mntdir], check=True) except subprocess.CalledProcessError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'cdrom') return None return mntdir #/** # ogReduceFs int_ndisk int_nfilesys #@brief Reduce el tamaño del sistema de archivos, sin tener en cuenta el espacio libre. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return int_tamañoKB - tamaño en KB #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@exception OG_ERR_PARTITION Partición desconocida o no accesible. #@warning En Windows, se borran los ficheros de hiberanción y de paginación. #@warning El sistema de archivos se amplía al mínimo + 10%. #@note Requisitos: *resize* #*/ ## def ogReduceFs (disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return # Redimensionar según el tipo de partición. type = ogGetFsType (disk, par) if type in ['EXT2', 'EXT3', 'EXT4']: ogUnmount (disk, par) rc = subprocess.run (['resize2fs', '-fpM', PART], capture_output=True, text=True).returncode if rc: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk},{par}") return None elif 'BTRFS' == type: mntdir = ogMount (disk, par) # Calcular tamaño ocupado + 10%, redondeado + 1 (incluyendo letra de unidad). btrfs_lines = subprocess.run (['btrfs', 'filesystem', 'show', mntdir], capture_output=True, text=True).stdout.splitlines() for l in btrfs_lines: if 'devid' not in l: continue ## 'devid 2 size 8.89GiB used 1.00GiB path /dev/sda4' devid_str, devid, size_str, size, used_str, used, path_str, path = l.split() if PART != os.path.realpath (path): continue (sz, unit) = re.search ('^([^A-Z]+)([A-Z])', used).groups() sz = float (sz) * 1.1 + 1 size = f'{str(sz)}{unit}' subprocess.run (['btrfs', 'filesystem', 'resize', size, mntdir], capture_output=True, text=True) break elif type in ['REISERFS', 'REISER4']: mntdir = ogMount (disk, par) df_lines = subprocess.run (['df', '-k', mntdir], capture_output=True, text=True).stdout.splitlines() for l in df_lines: if 'Filesystem' in l: continue fs, blocks, used, avail, use_pct, mntpt = l.split() size = str (int (used) * 1.1) ogUnmount (disk, par) subprocess.run (['resize_reiserfs', f'-s{size}K', PART], input='y\n', capture_output=True, text=True) break elif type == 'NTFS': ogUnmount (disk, par) nr_lines = subprocess.run (['ntfsresize', '-fi', PART], capture_output=True, text=True).stdout.splitlines() maxsize = None size = None for l in nr_lines: if 'device size' in l: maxsize = float (l.split()[3]) if 'resize at' in l: size = l.split()[4] size = int ((int (size) * 1.1 / 1024 + 1) * 1024) if not maxsize and not size: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk},{par}') return None import time extrasize = 0 retval = 1 while retval != 0 and size+extrasize < maxsize: nr = subprocess.run (['ntfsresize', '-fns', str(size), PART], capture_output=True, text=True) for l in nr.stdout.splitlines(): if 'Needed relocations' not in l: continue extrasize = int ((int (l.split()[3])*1.1/1024+1)*1024) break retval = nr.returncode size += extrasize if size < maxsize: rc = subprocess.run (['ntfsresize', '-fs', str(size), PART], input='y\n', capture_output=True, text=True).returncode if rc: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk},{par}") return None elif type in ['FAT32', 'FAT16', 'F2FS', 'JFS', 'NILFS2', 'XFS', 'EXFAT', 'HFS', 'HFSPLUS', 'UFS']: pass else: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk},{par}") return None return ogGetFsSize (disk, par) #/** # ogUnlock int_ndisk int_npartition #@see ogUnlockPartition #*/ ## def ogUnlock (disk, par): return ogUnlockPartition (disk, par) #/** # ogUnlockPartition int_ndisk int_npartition #@brief Elimina el fichero de bloqueo para una particion. #@param int_ndisk nº de orden del disco #@param int_npartition nº de orden de la partición #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@note El fichero de bloqueo se localiza en \c /var/lock/part, siendo \c part el dispositivo de la partición, sustituyendo el carácter "/" por "-". #*/ ## def ogUnlockPartition (disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}" os.remove(LOCKFILE) #/** # ogUnmount int_ndisk int_npartition #@see ogUnmountFs #*/ ## def ogUnmount (disk, par): return ogUnmountFs (disk, par) #/** # ogUnmountFs int_ndisk int_nfilesys #@brief Desmonta un sistema de archivos. #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@return Nada #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@warning La partición no está previamente montada o no se puede desmontar. #*/ ## def ogUnmountFs(disk, par): PART = DiskLib.ogDiskToDev (disk, par) if not PART: return MNTDIR = ogGetMountPoint (disk, par) # Si está montada, desmontarla. if MNTDIR: # Error si la particion está bloqueada. if ogIsLocked (disk, par): SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_LOCKED, f"{ogGlobals.lang.MSG_PARTITION}, {disk} {par}" ) return # Desmontar y borrar punto de montaje. try: subprocess.run(["umount", PART], check=True) except subprocess.CalledProcessError: SystemLib.ogEcho("warning", f'ogUnmountFs: {ogGlobals.lang.MSG_DONTUNMOUNT}: "{disk}, {par}"') try: os.rmdir(MNTDIR) except: try: os.remove(MNTDIR) except: pass return True else: SystemLib.ogEcho ([], "warning", f'{ogGlobals.lang.MSG_DONTMOUNT}: "{disk},{par}"') return True #/** # ogUnmountAll int_ndisk #@brief Desmonta todos los sistema de archivos de un disco, excepto el caché local. #@param int_ndisk nº de orden del disco #@return Nada #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo. #@warning No se desmonta la partición marcada como caché local. #*/ ## def ogUnmountAll(int_ndisk): if len(sys.argv) != 3: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_FORMAT, "Not enough arguments" ) return # Obtener partición y punto de montaje. DISK = DiskLib.ogDiskToDev(int_ndisk) for PART in range(1, DiskLib.ogGetPartitionsNumber(int_ndisk) + 1): if ogGetFsType(int_ndisk, PART) != "CACHE": ogUnmount(int_ndisk, PART) #/** # ogUnsetDirtyBit int_ndisk int_npart #@brief Inhabilita el Dirty Bit del sistema de ficheros NTFS para evitar un CHKDSK en el primer arranque #@param int_ndisk nº de orden del disco #@param int_npart nº de orden de partición #@return Nada #@exception OG_ERR_FORMAT Formato incorrecto. #*/ ## def ogUnsetDirtyBit(int_ndisk, int_nfilesys): # Error si no se reciben 2 parámetros. if len(sys.argv) != 3: SystemLib.ogRaiseError ( [], ogGlobals.OG_ERR_FORMAT, "Not enough arguments" ) return # Obtener partición y punto de montaje. PART = DiskLib.ogDiskToDev(int_ndisk, int_nfilesys) if not PART: return # Realizar acciones específicas según el tipo de sistema de archivos. TYPE = ogGetFsType(int_ndisk, int_nfilesys) if TYPE == "NTFS": ogUnmount(int_ndisk, int_nfilesys) subprocess.run(["ntfsfix", "-d", PART], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) else: pass # Add more specific actions for other file systems if needed. #/** # ogGetFreeSize int_disco int_partition str_SizeOutput #@brief muestra informacion del tamaño total, datos y libre. #@param int_ndisk nº de orden del disco #@param int_npart nº de orden de partición #@param str_unitSize unidad mostrada #@return int_size:int_data:int_free #@TODO Componer corretcamente esta función. #@exception OG_ERR_FORMAT Formato incorrecto. #*/ ## def ogGetFreeSize(disk, part, unit='KB'): PART = DiskLib.ogDiskToDev (disk, part) if not PART: return unit2factor = { 'kb': 1.024 / 1, 'mb': 1.024 / 1000, 'gb': 1.024 / 1000000, } if unit.lower() not in unit2factor: kk factor = unit2factor[unit.lower()] particion = FileSystemLib.ogMount (disk, part) if not particion: kk df = subprocess.run (['df'], capture_output=True, text=True).stdout df_part = list (filter (lambda l: particion in l, df.splitlines())) if not len (df_part): kk _, size, used, free, pct, mntpt = df_part[0].split() return float (free) * factor