350 lines
13 KiB
Python
350 lines
13 KiB
Python
import os
|
|
import re
|
|
import subprocess
|
|
import shutil
|
|
import sys
|
|
import platform
|
|
|
|
import ogGlobals
|
|
import SystemLib
|
|
import DiskLib
|
|
import FileSystemLib
|
|
import CacheLib
|
|
|
|
#/**
|
|
# ogCreateCache [int_ndisk] int_partsize
|
|
#@brief Define la caché local, por defecto en partición 4 del disco 1.
|
|
#@param int_ndisk numero de disco donde crear la cache, si no se indica es el 1 por defecto
|
|
#@param int_npart número de partición (opcional, 4 por defecto)
|
|
#@param int_partsize tamaño de la partición (en KB)
|
|
#@return (nada, por determinar)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@note Requisitos: sfdisk, parted, awk, sed
|
|
#@warning El tamaño de caché debe estar entre 50 MB y la mitad del disco.
|
|
#@warning La caché no puede solaparse con las particiones de datos.
|
|
#*/ ##
|
|
def ogCreateCache (ndsk=1, part=4, sizecache=0):
|
|
if not sizecache:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '')
|
|
return None
|
|
sizecache = int (sizecache)
|
|
|
|
DISK = DiskLib.ogDiskToDev (ndsk)
|
|
if not DISK: return None
|
|
|
|
# PATCH Para discos nvme la particion debe ser p1, p2, etc...en lugar de 1,2, sino falla sfdisk
|
|
NVME_PREFIX = ''
|
|
if 'nvme' in DISK:
|
|
NVME_PREFIX = 'p'
|
|
|
|
END = DiskLib.ogGetLastSector (ndsk)
|
|
SIZE = 2 * sizecache
|
|
# Inicio partición cache según el disco tenga sectores de 4k o menores
|
|
IOSIZE = 0
|
|
fdisk_out = subprocess.run (['fdisk', '-l', DISK], capture_output=True, text=True).stdout
|
|
for l in fdisk_out.splitlines():
|
|
items = l.split()
|
|
if len(items) < 4: continue
|
|
if 'I/O' == items[0]:
|
|
IOSIZE = int (items[3])
|
|
break
|
|
START = 0
|
|
if 4096 == IOSIZE:
|
|
END -= 8192
|
|
START = END - SIZE + 2048 - (END-SIZE)%2048
|
|
else:
|
|
START = END - SIZE + 1
|
|
|
|
ENDPREVPART = None
|
|
i = 1
|
|
while True:
|
|
prev_part = part - i
|
|
if prev_part <= 0: break
|
|
ENDPREVPART = DiskLib.ogGetLastSector (ndsk, prev_part)
|
|
if ENDPREVPART: break
|
|
i += 1
|
|
if not ENDPREVPART:
|
|
ENDPREVPART=0
|
|
#SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, ndsk)
|
|
#return None
|
|
# Error si tamaño no está entre límites permitidos o si se solapa con la partición anterior.
|
|
MINSIZE = 25000
|
|
MAXSIZE = END
|
|
if SIZE < MINSIZE or SIZE > MAXSIZE or START < ENDPREVPART:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, ndsk)
|
|
return None
|
|
|
|
# Desmontar todos los sistemas de archivos del disco.
|
|
FileSystemLib.ogUnmountAll (ndsk)
|
|
# Definir particiones y notificar al kernel.
|
|
# En el caso de ser disco GPT, de momento se borra la particion y se vuelve a crear,
|
|
# por lo que se pierden los datos.
|
|
pttype = DiskLib.ogGetPartitionTableType (ndsk)
|
|
if not pttype:
|
|
pttype = 'MSDOS'
|
|
DiskLib.ogCreatePartitionTable (ndsk, pttype)
|
|
|
|
get_ptt = DiskLib.ogGetPartitionTableType (ndsk)
|
|
if 'GPT' == get_ptt:
|
|
# Si la tabla de particiones no es valida, volver a generarla.
|
|
if subprocess.run (['sgdisk', '-p', DISK]).returncode:
|
|
subprocess.run (['gdisk', DISK], input='2\nw\nY\n', text=True)
|
|
# Si existe la cache se borra previamente
|
|
if ogFindCache(): ogDeleteCache()
|
|
# Capturamos el codigo de particion GPT para cache
|
|
# PATCH - Cuando es GPT, la particion con codigo CACHE (CA00) no existe y no puede crearse, se cambia por LINUX (8300)
|
|
ID = DiskLib.ogTypeToId ('LINUX', 'GPT')
|
|
subprocess.run (['sgdisk', DISK, f'-n{part}:{START}:{END}', f'-c{part}:CACHE', f'-t{part}:{ID}'])
|
|
elif 'MSDOS' == get_ptt:
|
|
# Si la tabla de particiones no es valida, volver a generarla.
|
|
if subprocess.run (['parted', '-s', DISK, 'print']).returncode:
|
|
subprocess.run (['fdisk', DISK], input='w\n', text=True)
|
|
# Definir particiones y notificar al kernel.
|
|
ID = DiskLib.ogTypeToId ('CACHE', 'MSDOS')
|
|
# Salvamos la configuración de las particiones e incluimos la cache.
|
|
tmp = subprocess.run (['sfdisk', '--dump', DISK], capture_output=True, text=True).stdout.splitlines()
|
|
tmp = [ x for x in tmp if f'{DISK}{part}' not in x ]
|
|
tmp.append (f'{DISK}{NVME_PREFIX}{part} : start= {START}, size= {SIZE}, Id={ID}')
|
|
# Ordenamos las líneas de los dispositivos
|
|
UNIT = [ x for x in tmp if 'unit' in x ][0]
|
|
tmp = sorted ([ x for x in tmp if re.match ('^/dev', x) ])
|
|
tmp = [UNIT, ''] + tmp
|
|
# Guardamos nueva configuración en el disco.
|
|
i = '\n'.join(tmp)
|
|
subprocess.run (['sfdisk', '--no-reread', DISK], input=i, text=True)
|
|
# Actualiza la tabla de particiones en el kernel.
|
|
DiskLib.ogUpdatePartitionTable()
|
|
|
|
|
|
|
|
#/**
|
|
# ogDeleteCache
|
|
#@brief Elimina la partición de caché local.
|
|
#@return (nada, por determinar)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@note Requisitos: fdisk, sgdisk, partprobe
|
|
#*/ ##
|
|
def ogDeleteCache():
|
|
cachepart = ogFindCache()
|
|
if not cachepart:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return None
|
|
|
|
ndisk, npart = cachepart.split()
|
|
disk = DiskLib.ogDiskToDev (ndisk)
|
|
|
|
# Desmontar todos los sistemas de archivos del disco.
|
|
FileSystemLib.ogUnmountAll (ndisk)
|
|
|
|
ptt = DiskLib.ogGetPartitionTableType (ndisk)
|
|
if 'GPT' == ptt:
|
|
# Si la tabla de particiones no es valida, volver a generarla.
|
|
if subprocess.run (['sgdisk', '-p', disk]).returncode:
|
|
subprocess.run (['gdisk', disk], input='2\nw\nY\n', text=True)
|
|
subprocess.run (['sgdisk', disk, f'-d{npart}'])
|
|
elif 'MSDOS' == ptt:
|
|
# Si la tabla de particiones no es valida, volver a generarla.
|
|
if subprocess.run (['parted', '-s', disk, 'print']).returncode:
|
|
subprocess.run (['fdisk', disk], input='w', text=True)
|
|
# Eliminar la partición de caché.
|
|
subprocess.run (['fdisk', disk], input=f'd\n{npart}\nw', text=True)
|
|
# Borrar etiqueta de la caché.
|
|
if os.path.exists ('/dev/disk/by-label/CACHE'):
|
|
os.unlink ('/dev/disk/by-label/CACHE')
|
|
#Actualiza la tabla de particiones en el kernel.
|
|
DiskLib.ogUpdatePartitionTable()
|
|
|
|
#/**
|
|
# ogFindCache
|
|
#@brief Detecta la partición caché local.
|
|
#@param No requiere parametros
|
|
#@return int_ndisk int_npart - devuelve el par nº de disco-nº de partición .
|
|
#@warning Si no hay cache no devuelve nada
|
|
#*/ ##
|
|
def ogFindCache():
|
|
# Obtener el dispositivo del sistema de archivos etiquetado como "CACHE".
|
|
PART = subprocess.run (['blkid', '-L', 'CACHE'], capture_output=True, text=True).stdout.strip()
|
|
|
|
# En discos nvme con particiones GPT la partición se detecta usando el tag PARTLABEL
|
|
if not PART:
|
|
out = subprocess.run (['blkid', '-t', 'PARTLABEL=CACHE'], capture_output=True, text=True).stdout.strip()
|
|
PART = out.split (':')[0]
|
|
|
|
# Si no se detecta, obtener particiones marcadas de tipo caché en discos MSDOS.
|
|
if not PART:
|
|
out = subprocess.run (['sfdisk', '-l'], capture_output=True, text=True).stdout.splitlines()
|
|
for l in out:
|
|
elems = l.split (maxsplit=5)
|
|
if 6 > len (elems): continue
|
|
if 'ca' in elems[5] or 'a7' in elems[5]:
|
|
PART=elems[0]
|
|
break
|
|
|
|
# Por último revisar todos los discos GPT y obtener las particiones etiquetadas como caché.
|
|
if not PART:
|
|
PART = ''
|
|
for d in DiskLib.ogDiskToDev():
|
|
out = subprocess.run (['sgdisk', '-p', d], capture_output=True, text=True).stdout.splitlines()
|
|
for l in out:
|
|
elems = l.split (maxsplit=6)
|
|
if 7 > len (elems): continue
|
|
if 'CACHE' in elems[6]:
|
|
p = 'p' if 'nvme' in d else ''
|
|
PART += f' {d}{p}{elems[0]}'
|
|
|
|
if not PART: return
|
|
return DiskLib.ogDevToDisk (PART.split()[0]) # usar la 1ª partición encontrada.
|
|
|
|
|
|
#/**
|
|
# ogFormatCache
|
|
#@brief Formatea el sistema de ficheros para la caché local.
|
|
#@return (por determinar)
|
|
#@warning Prueba con formato Reiser.
|
|
#@attention
|
|
#@note El sistema de archivos de la caché se queda montado.
|
|
#*/ ##
|
|
def ogFormatCache():
|
|
cachepart = ogFindCache()
|
|
if cachepart is None:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return
|
|
|
|
cachepart = cachepart.split()
|
|
disk = DiskLib.ogDiskToDev (cachepart[0], cachepart[1])
|
|
if not disk: return
|
|
|
|
ogUnmountCache()
|
|
options = "extent,large_file"
|
|
if re.match("^5", platform.release()):
|
|
options += ",uninit_bg,^metadata_csum,^64bit"
|
|
|
|
try:
|
|
subprocess.run(["mkfs.ext4", "-q", "-F", disk, "-L", "CACHE", "-O", options], check=True)
|
|
except subprocess.CalledProcessError as e:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, "CACHE")
|
|
return
|
|
|
|
# Crear estructura básica.
|
|
mntdir = ogMountCache()
|
|
j = '/'.join ([mntdir, ogGlobals.OGIMG]) ## os.path.join doesn't work: "If a segment […] is an absolute path, all previous segments are ignored"
|
|
#print (f'cucu mntdir ({mntdir}) OGIMG ({ogGlobals.OGIMG}) j ({j})')
|
|
os.makedirs (j, exist_ok=True)
|
|
|
|
# Incluir kernel e Initrd del ogLive
|
|
## como lo llamo sin especificar el path entero?
|
|
#subprocess.run (['scripts/updateBootCache.py']) ## TODO
|
|
|
|
|
|
#/**
|
|
# ogGetCacheSize
|
|
#@brief Devuelve el tamaño definido para la partición de caché.
|
|
#@return int_partsize tamaño de la partición (en KB)
|
|
#@exception OG_ERR_PARTITION No existe partición de caché.
|
|
#*/ ##
|
|
def ogGetCacheSize():
|
|
cachepart = ogFindCache()
|
|
if cachepart is None:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return None
|
|
|
|
disk, par = cachepart.split()
|
|
return DiskLib.ogGetPartitionSize (disk, par)
|
|
|
|
#/**
|
|
# ogGetCacheSpace
|
|
#@brief Devuelve el espacio de disco disponible para la partición de caché.
|
|
#@return int_size tamaño disponible (en KB)
|
|
#@note El espacio disponible es el que hay entre el límite superior de la partición 3 del disco 1 y el final de dicho disco, y no puede ser superior a la mitad de dicho disco.
|
|
#*/ ##
|
|
def ogGetCacheSpace():
|
|
cachepart = ogFindCache()
|
|
if not cachepart:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return None
|
|
cachepart = cachepart.split()
|
|
|
|
disk = DiskLib.ogDiskToDev (cachepart[0])
|
|
if not disk:
|
|
return None
|
|
|
|
sectors = 0
|
|
disk_bn = os.path.basename (disk)
|
|
with open ('/proc/partitions', 'r') as fd:
|
|
proc_parts = fd.read()
|
|
for l in proc_parts.splitlines():
|
|
items = l.split()
|
|
if len(items) < 4: continue
|
|
if items[3] == disk_bn:
|
|
sectors = 2 * int (items[2])
|
|
if not sectors: return None
|
|
|
|
sfdisk_out = subprocess.run (['sfdisk', '-g', disk], capture_output=True, text=True).stdout
|
|
cyls = int (sfdisk_out.split()[1])
|
|
sectors = sectors/cyls * cyls - 1
|
|
|
|
## the original code has a hard dependency on the existence of a third partition
|
|
## if the disk has sda1, sda2 and sda4, the code fails.
|
|
## this is an improved version
|
|
endpart3 = 0
|
|
for trypart in [3, 2, 1]:
|
|
sfdisk_out = subprocess.run (['sfdisk', '-uS', '-l', disk], capture_output=True, text=True).stdout
|
|
for l in sfdisk_out.splitlines():
|
|
items = l.split()
|
|
if len(items) < 6: continue
|
|
if f'{disk}{trypart}' == items[0]:
|
|
endpart3 = int (items[2])
|
|
break
|
|
if endpart3: break
|
|
if not endpart3: return None
|
|
|
|
# Mostrar espacio libre en KB (1 KB = 2 sectores)
|
|
if endpart3 > sectors // 2:
|
|
return (sectors - endpart3) // 2
|
|
else:
|
|
return sectors // 4
|
|
|
|
|
|
#/**
|
|
# ogMountCache
|
|
#@brief Monta la partición Cache y exporta la variable $OGCAC
|
|
#@param sin parametros
|
|
#@return path_mountpoint - Punto de montaje del sistema de archivos de cache.
|
|
#@warning Salidas de errores no determinada
|
|
#*/ ##
|
|
def ogMountCache():
|
|
c = ogFindCache()
|
|
if not c:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return None
|
|
|
|
c = c.split()
|
|
m = FileSystemLib.ogMountFs (c[0], c[1])
|
|
if not m:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return None
|
|
return m
|
|
|
|
|
|
#/**
|
|
# ogUnmountCache
|
|
#@brief Desmonta la particion Cache y elimina la variable $OGCAC
|
|
#@param sin parametros
|
|
#@return nada
|
|
#@warning Salidas de errores no determinada
|
|
#*/ ##
|
|
def ogUnmountCache():
|
|
cachepart = ogFindCache()
|
|
if cachepart is None:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE)
|
|
return
|
|
|
|
cachepart = cachepart.split()
|
|
if not FileSystemLib.ogIsMounted (cachepart[0], cachepart[1]): return True
|
|
FileSystemLib.ogUnmountFs (cachepart[0], cachepart[1])
|
|
|
|
# Eliminar el enlace simbólico de /mnt/ParticiónCache.
|
|
dev = DiskLib.ogDiskToDev (cachepart[0], cachepart[1])
|
|
dev = dev.replace ('dev', 'mnt')
|
|
if os.path.exists (dev): os.remove (dev)
|