931 lines
35 KiB
Python
931 lines
35 KiB
Python
#/**
|
|
#@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
|