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: 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 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 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().split() if cachepart is None: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, ogGlobals.lang.MSG_NOCACHE) return 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') os.remove (dev)