diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eb29af..7034738 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.26.0] - 2025-07-14 + +### Fixed + +- Overhaul partitioning + ## [0.25.2] - 2025-07-14 ### Fixed diff --git a/ogclient/interfaceAdm/Configurar.py b/ogclient/interfaceAdm/Configurar.py index f49eef8..72f43ba 100755 --- a/ogclient/interfaceAdm/Configurar.py +++ b/ogclient/interfaceAdm/Configurar.py @@ -2,7 +2,6 @@ import os import sys -import subprocess import ogGlobals import SystemLib @@ -34,12 +33,12 @@ prog = os.path.basename(__name__) # # El parámetro $2 es el que aporta toda la información y el $1 se queda obsoleto # Formato de entrada: -# dis=Número de disco -# != caracter de separación +# dis=Número de disco +# != caracter de separación # # Y un numero indeterminado de cadenas del tipo siguuenteseparadas por el caracter '$': -# par=Número de particion*cod=Código de partición*sfi=Sistema de ficheros*tam=Tamaño de la partición*ope=Operación -# @= caracter de separación +# par=Número de particion*cod=Código de partición*sfi=Sistema de ficheros*tam=Tamaño de la partición*ope=Operación +# @= caracter de separación #____________________________________________________________________ # Captura de parámetros (se ignora el 1er parámetro y se eliminan espacios y tabuladores). @@ -51,12 +50,13 @@ param = sys.argv[2] tbprm = param.split ('!') pparam = tbprm[0] # General disk parameters sparam = tbprm[1] # Partitioning and formatting parameters +is_there_cache = 'CACHE' in sparam # Toma valores de disco y caché, separados por "*". # Los valores están en las variables $dis: disco, $che: existe cache (1, 0), $tch: Tamaño de la cache. -tbprm = pparam.split ('*') +pparams = pparam.split ('*') dis = tch = None -for item in tbprm: +for item in pparams: if '=' not in item: continue k, v = item.split ('=', 1) @@ -74,10 +74,13 @@ if dis is None: tbp = [] # Valores de configuración (parámetros para ogCreatePartitions) tbf = {} # Tabla de formateo -tbprm = sparam.split('%') +sparams = sparam.split('%') maxp=0 -for item in tbprm: +sum_tam = 0 +do_sum_tam = True +cache_seen = extended_seen = efi_seen = False +for item in sparams: if not item: continue ## por si nos pasan un '%' al final de todo # Leer datos de la partición, separados por "*". par = cpt = sfi = tam = None @@ -93,7 +96,7 @@ for item in tbprm: if 'par' == k: par = int (v) elif 'cpt' == k: cpt = v elif 'sfi' == k: sfi = v - elif 'tam' == k: tam = v + elif 'tam' == k: tam = int (v) elif 'ope' == k: ope = int (v) missing_params = [] @@ -106,10 +109,29 @@ for item in tbprm: sys.exit (1) # Componer datos de particionado. + if 'EFI' == cpt: + if efi_seen: + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_FORMAT, f'se ha solicitado más de una partición de ESP') + sys.exit (1) + efi_seen = True if 'CACHE' == cpt: + if cache_seen: + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_FORMAT, f'se ha solicitado más de una partición de cache') + sys.exit (1) + cache_seen = True tch = tam else: tbp.append (f'{cpt}:{tam}') + if do_sum_tam: + sum_tam += tam + if 'EXTENDED' == cpt: + if extended_seen: + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_FORMAT, f'se ha solicitado más de una partición extendida') + sys.exit (1) + extended_seen = True + extended_is_at = par + do_sum_tam = False ## don't sum sizes anymore + if ope: # Si se activa operación de formatear, componer datos de formateo. if cpt not in ['EMPTY', 'EXTENDED', 'LINUX-LVM', 'LVM', 'ZPOOL']: @@ -118,7 +140,88 @@ for item in tbprm: if par > maxp: maxp = par if tch is None: - tch = '0' + tch = 0 + + + + +cur_ptt = DiskLib.ogGetPartitionTableType (dis) +ptt = 'GPT' if InventoryLib.ogIsEfiActive() else 'MSDOS' + +if not cache_seen and not tbp: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'No se ha solicitado ninguna partición') + sys.exit (1) + +if 'GPT' == ptt and extended_seen: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'En GPT no se pueden usar particiones extendidas') + sys.exit (1) + +## error si nos piden más de 4 y ninguna es extendida +if 'MSDOS' == ptt and not extended_seen: + requested_partitions = len (tbp) + if cache_seen: requested_partitions += 1 + if requested_partitions > 4: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'Se han solicitado más de 4 particiones y ninguna es extendida') + sys.exit (1) + +if efi_seen: + if 'EFI' != tbp[0].split (':')[0]: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'la partición ESP debe estar declarada en primera posición') + sys.exit (1) +else: + if 'GPT' == ptt and 1 == dis: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'En GPT debe haber una partición ESP') + sys.exit (1) + +## si no nos definen partición de cache y el disco tiene una, hay que borrarla +if not cache_seen: + c = CacheLib.ogFindCache() + if c: + cache_disk, cache_part = c.split() + if int (cache_disk) == int (dis): + CacheLib.ogUnmountCache() + CacheLib.ogDeleteCache() + +## la extendida solo puede estar en la (si hay cache) o en la 4 (si no lo hay) +if extended_seen: + extended_should_be_at = 3 if cache_seen else 4 + if extended_is_at != extended_should_be_at: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'La partición extendida no puede ser la "{extended_is_at}" sino que debe ser la "{extended_should_be_at}"') + sys.exit (1) + +recreate_partition_table = False +if not cur_ptt: + SystemLib.ogEcho (['session', 'log'], None, f'No partition table--will create a "{ptt}" one') + recreate_partition_table = True +if cur_ptt and ptt != cur_ptt: + SystemLib.ogEcho (['session', 'log'], None, f'Current partition table type "{cur_ptt}" is wrong for this system--will replace it for a "{ptt}" one') + recreate_partition_table = True + +## size check: check that cache fits in the space left by the previously existing partitions +if not recreate_partition_table and not CacheLib.ogCheckNewCacheSize (dis, tch): + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_CACHE, f'nueva partición de caché no cabe en el hueco actual') + ## BUG el "hueco actual" me podría dar igual, si luego resulta que también estoy definiendo otras particiones y ya sí hay sitio para todo + sys.exit (1) + +## size check: check that the newly defined partitions fit in the disk +disk_sectors = DiskLib.ogGetLastSector (dis) +IOSIZE = DiskLib.ogGetIoSize (dis) +if not IOSIZE: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_FORMAT, f'Failed to get disk sector size') + sys.exit (1) +if 512 == IOSIZE: + sum_tam_sectors = sum_tam*2 + cache_sectors = tch*2 +else: + sum_tam_sectors = (sum_tam+3)//4 ## sumamos 3 para que la división entera "redondee al alza" + cache_sectors = (tch+3)//4 +## esta comprobacion puede dejar pasar situaciones que más tarde dan error +## la ventana es bastante estrecha, y sumando aquí simplemente un 1 por 1000, ya la cerramos del todo +sum_tam_sectors = int (sum_tam_sectors * 1.001) +space_left_by_cache = disk_sectors - cache_sectors +if sum_tam_sectors > space_left_by_cache: + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_CACHE, f'las particiones no caben en el disco') + sys.exit (1) #____________________________________________________ # @@ -133,53 +236,46 @@ SystemLib.ogEcho (['session', 'log'], None, f'[10] {ogGlobals.lang.MSG_HELP_ogUn FileSystemLib.ogUnmountAll (dis) CacheLib.ogUnmountCache() -# Elimina la tabla de particiones -cur_ptt = DiskLib.ogGetPartitionTableType (dis) -ptt = 'GPT' if InventoryLib.ogIsEfiActive() else 'MSDOS' -if not cur_ptt or ptt != cur_ptt: +if recreate_partition_table: DiskLib.ogDeletePartitionTable (dis) SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogUpdatePartitionTable']) DiskLib.ogCreatePartitionTable (dis, ptt) # Inicia la cache. -if 'CACHE' in sparam: +if is_there_cache: SystemLib.ogEcho (['session', 'log'], None, f'[30] {ogGlobals.lang.MSG_HELP_ogCreateCache}') - SystemLib.ogEcho (['session', 'log'], None, f' initCache.py {tch}') - rc = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGSCRIPTS}/initCache.py', tch]) + SystemLib.ogEcho (['session', 'log'], None, f' initCache.py {dis} {tch}') + rc = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGSCRIPTS}/initCache.py', str (dis), str (tch)]) if not rc: SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_CACHE, f'initCache.py failed') sys.exit (1) # Definir particionado. -SystemLib.ogEcho (['session', 'log'], None, f'[50] {ogGlobals.lang.MSG_HELP_ogCreatePartitions}') -SystemLib.ogEcho (['session', 'log'], None, f' ogCreatePartitions {dis} {' '.join (tbp)}') -res = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogCreatePartitions', str(dis)] + tbp) -if not res: - SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_GENERIC, f'ogCreatePartitions {dis} {' '.join (tbp)}') - sys.exit (1) -SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogUpdatePartitionTable']) +if tbp: + SystemLib.ogEcho (['session', 'log'], None, f'[50] {ogGlobals.lang.MSG_HELP_ogCreatePartitions}') + SystemLib.ogEcho (['session', 'log'], None, f' ogCreatePartitions {dis} {' '.join (tbp)}') + res = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogCreatePartitions', str(dis)] + tbp) + if not res: + SystemLib.ogRaiseError (['log', 'session'], ogGlobals.OG_ERR_FORMAT, f'ogCreatePartitions {dis} {' '.join (tbp)}') + sys.exit (1) + SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogUpdatePartitionTable']) -# Formatear particiones -SystemLib.ogEcho (['session', 'log'], None, f'[70] {ogGlobals.lang.MSG_HELP_ogFormat}') retval = 0 -for p in range (1, maxp+1): - if p not in tbf: continue - if 'CACHE' == tbf[p]: - if CACHESIZE == tch: # Si el tamaño es distinto ya se ha formateado. - SystemLib.ogEcho (['session', 'log'], None, ' ogFormatCache') - retval = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogFormatCache']) - else: - SystemLib.ogEcho (['session', 'log'], None, f' ogFormatFs {dis} {p} {tbf[p]}') - retval = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogFormatFs', str(dis), str(p), tbf[p]]) - if not retval: - SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_GENERIC, f'ogFormatFs {dis} {p} {tbf[p]}') - sys.exit (1) -# Registro de fin de ejecución +if tbf: + # Formatear particiones + SystemLib.ogEcho (['session', 'log'], None, f'[70] {ogGlobals.lang.MSG_HELP_ogFormat}') + for p in range (1, maxp+1): + if p not in tbf: continue + if 'CACHE' == tbf[p]: + if CACHESIZE == tch: # Si el tamaño es distinto ya se ha formateado. + SystemLib.ogEcho (['session', 'log'], None, ' ogFormatCache') + retval = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogFormatCache']) + else: + SystemLib.ogEcho (['session', 'log'], None, f' ogFormatFs {dis} {p} {tbf[p]}') + retval = SystemLib.ogExecAndLog ('command', [f'{ogGlobals.OGPYFUNCS}/ogFormatFs', str(dis), str(p), tbf[p]]) + if not retval: + SystemLib.ogRaiseError (['session', 'log'], ogGlobals.OG_ERR_GENERIC, f'ogFormatFs {dis} {p} {tbf[p]}') + sys.exit (1) + SystemLib.ogEcho (['session', 'log'], None, f'{ogGlobals.lang.MSG_INTERFACE_END} {retval}') - -#___________________________________________________________________ -# -# Retorno -#___________________________________________________________________ - sys.exit (0) diff --git a/ogclient/lib/python3/BootLib.py b/ogclient/lib/python3/BootLib.py index 28c7d6e..3d3b7b9 100644 --- a/ogclient/lib/python3/BootLib.py +++ b/ogclient/lib/python3/BootLib.py @@ -195,7 +195,7 @@ def ogBoot (disk, par, nvramperm=False, params=''): winboot = os.environ.get ('winboot', '') if 'kexec' == winboot: # Modo de arranque en caliente (con kexec). - cp_cmd = f'cp {ogGlobals.OGLIB}/grub4dos/* {mntdir}' + cp_cmd = f'cp {ogGlobals.OGLIB}/grub4dos/* {mntdir}' ## shutil sucks a bit for copying both files and dirs subprocess.run (cp_cmd, shell=True) disk0 = int(disk)-1 par0 = int(par)-1 diff --git a/ogclient/lib/python3/CacheLib.py b/ogclient/lib/python3/CacheLib.py index 8edde91..ab0ff90 100644 --- a/ogclient/lib/python3/CacheLib.py +++ b/ogclient/lib/python3/CacheLib.py @@ -37,16 +37,15 @@ def ogCreateCache (ndsk=1, part=4, sizecache=0): NVME_PREFIX = 'p' END = DiskLib.ogGetLastSector (ndsk) + if not END: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'Failed to get last sector of disk') + return None 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 + IOSIZE = DiskLib.ogGetIoSize (ndsk) + if not IOSIZE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'Failed to get disk sector size') + return None START = 0 if 4096 == IOSIZE: END -= 8192 @@ -70,7 +69,7 @@ def ogCreateCache (ndsk=1, part=4, sizecache=0): MINSIZE = 25000 MAXSIZE = END if SIZE < MINSIZE or SIZE > MAXSIZE or START < ENDPREVPART: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'size ({SIZE}) < minsize ({MINSIZE}) or size > maxsize ({MAXSIZE}) or start ({START}) < endprevpart ({ENDPREVPART})') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'size ({SIZE}) < minsize ({MINSIZE}) or size ({SIZE}) > maxsize ({MAXSIZE}) or start ({START}) < endprevpart ({ENDPREVPART})') return None # Desmontar todos los sistemas de archivos del disco. @@ -90,27 +89,32 @@ def ogCreateCache (ndsk=1, part=4, sizecache=0): subprocess.run (['gdisk', DISK], input='2\nw\nY\n', capture_output=True, 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}']) + + ID = DiskLib.ogTypeToId ('LINUX', 'GPT_GUID') + sfdisk_line = f'{DISK}{NVME_PREFIX}{part} : start= {START}, size= {SIZE}, type={ID}, name=CACHE' elif 'MSDOS' == get_ptt: # Si la tabla de particiones no es valida, volver a generarla. - if subprocess.run (['parted', '-s', DISK, 'print']).returncode: ## if True, then parted failed - subprocess.run (['fdisk', DISK], input='w\n', text=True) - # Definir particiones y notificar al kernel. + if subprocess.run (['parted', '-s', DISK, 'print'], capture_output=True).returncode: ## if True, then parted failed + subprocess.run (['fdisk', DISK], input='w\n', text=True, capture_output=True) + 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) + sfdisk_line = f'{DISK}{NVME_PREFIX}{part} : start= {START}, size= {SIZE}, Id={ID}' + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'No hay tabla de particiones en el disco') + return False + + # 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 ] ## grep -v la_de_cache + tmp.append (sfdisk_line) + # 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() return True @@ -139,15 +143,15 @@ def ogDeleteCache(): 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: ## if True, then sgdisk failed - subprocess.run (['gdisk', disk], input='2\nw\nY\n', text=True) + if subprocess.run (['sgdisk', '-p', disk], capture_output=True).returncode: ## if True, then sgdisk failed + subprocess.run (['gdisk', disk], input='2\nw\nY\n', text=True, capture_output=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: ## if True, then parted failed - subprocess.run (['fdisk', disk], input='w', text=True) + if subprocess.run (['parted', '-s', disk, 'print'], capture_output=True).returncode: ## if True, then parted failed + subprocess.run (['fdisk', disk], input='w', text=True, capture_output=True) # Eliminar la partición de caché. - subprocess.run (['fdisk', disk], input=f'd\n{npart}\nw', text=True) + subprocess.run (['fdisk', disk], input=f'd\n{npart}\nw', text=True, capture_output=True) # Borrar etiqueta de la caché. if os.path.exists ('/dev/disk/by-label/CACHE'): os.unlink ('/dev/disk/by-label/CACHE') @@ -358,3 +362,46 @@ def ogUnmountCache(): def initCache (*args): p = subprocess.run ([f'{ogGlobals.OGSCRIPTS}/initCache.py'] + list(args)) return not p.returncode ## negate shell return code + + +#/** +# ogCheckNewCacheSize +#@brief Comprueba si un cache de X tamaño cabe en el hueco que dejan las demás particiones +#@param Tamaño de la nueva hipotética cache +#@return Boolean, True si la nueva cache cabría, False si no +#*/ ## +def ogCheckNewCacheSize (disk, kB): + DISK = DiskLib.ogDiskToDev (disk) + if not DISK: return None + + IOSIZE = DiskLib.ogGetIoSize (disk) + if not IOSIZE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'Failed to get disk sector size') + return None + last_sector = DiskLib.ogGetLastSector (disk) + + end_of_last_partition = 0 + cachepart = ogFindCache() + if cachepart: + cache_disk, cache_part = cachepart.split() + cache_dev = DiskLib.ogDiskToDev (cache_disk, cache_part) + else: + cache_dev = None + fdisk_l_out = subprocess.run (['fdisk', '-l', DISK], capture_output=True, text=True).stdout + for l in fdisk_l_out.splitlines(): + if not re.match ('^/dev', l): ## no empieza por /dev, nos la saltamos + continue + if cache_dev and re.match (f'^{cache_dev}', l): ## es la de cache, nos la saltamos + continue + last_sector_of_partition = int (l.split()[2]) + if last_sector_of_partition > end_of_last_partition: + end_of_last_partition = last_sector_of_partition + + if 512 == IOSIZE: + cache_sectors = kB*2 + else: ## 4096 + cache_sectors = (kB+3)//4 ## sumamos 3 para que la división entera "redondee al alza" + if end_of_last_partition + cache_sectors > last_sector: + SystemLib.ogEcho (['session', 'log'], None, f'end_of_last_partition ({end_of_last_partition}) + cache_sectors ({cache_sectors}) > last_sector ({last_sector}), check failed') + return False + return True diff --git a/ogclient/lib/python3/DiskLib.py b/ogclient/lib/python3/DiskLib.py index 24138a7..be7366b 100644 --- a/ogclient/lib/python3/DiskLib.py +++ b/ogclient/lib/python3/DiskLib.py @@ -38,6 +38,32 @@ def parted(*args): return "Error: 'parted' command not found" +#/** +# ogGetIoSize int_ndisk +#@brief Devuelve el tamaño de sector de un disco +#@param int_ndisk nº de orden del disco +#@return Tamaño de sector +#*/ ## +def ogGetIoSize (disk): + DISK = ogDiskToDev (disk) + if not DISK: return None + + IOSIZE = None + fdisk_out = subprocess.run (['fdisk', '-l', DISK], capture_output=True, text=True).stdout + for l in fdisk_out.splitlines(): + if 'I/O' not in l: continue + items = l.split() + if len(items) < 4: continue + IOSIZE = items[3] + break + + if IOSIZE is None: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'Could not extract disk sector size from fdisk output') + return None + + return int (IOSIZE) + + #/** # ogCreatePartitions int_ndisk str_parttype:int_partsize ... #@brief Define el conjunto de particiones de un disco. @@ -61,12 +87,11 @@ def ogCreatePartitions (disk, parts): PTTYPE = ogGetPartitionTableType (disk) if not PTTYPE: PTTYPE = 'MSDOS' # Por defecto para discos vacíos. - - if 'GPT' == PTTYPE: - return ogCreateGptPartitions (disk, parts) - elif 'MSDOS' != PTTYPE: + if PTTYPE not in ['MSDOS', 'GPT']: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, PTTYPE) return None + if 'MSDOS' == PTTYPE: CACHE_ID = ogTypeToId ('CACHE', 'MSDOS') + else: CACHE_ID = ogTypeToId ('CACHE', 'GPT_GUID') # Se calcula el ultimo sector del disco (total de sectores usables) SECTORS = ogGetLastSector (disk) @@ -78,24 +103,21 @@ def ogCreatePartitions (disk, parts): cache_disk, cache_part = CACHEPART.split() if int (ND) == int (cache_disk): CACHESIZE = int (CacheLib.ogGetCacheSize()) * 2 - # Sector de inicio (la partición 1 empieza en el sector 63). - IODISCO = ogDiskToDev (disk) - IOSIZE = 0 - fdisk_out = subprocess.run (['fdisk', '-l', IODISCO], capture_output=True, text=True).stdout - for l in fdisk_out.splitlines(): - if 'I/O' not in l: continue - items = l.split() - if len(items) < 4: continue - IOSIZE = items[3] - break - if '4096' == IOSIZE: + # Sector de inicio (la partición 1 empieza en el sector 63). + IOSIZE = ogGetIoSize (ND) + if not IOSIZE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'Failed to get disk sector size') + return None + + if 4096 == IOSIZE: START = 4096 SECTORS -= 8192 if CACHESIZE: SECTORS = SECTORS - CACHESIZE + 2048 - (SECTORS - CACHESIZE) % 2048 - 1 - else: - START = 63 + else: ## 512 + if 'MSDOS' == PTTYPE: START = 63 + else: START = 2048 if CACHESIZE: SECTORS -= CACHESIZE @@ -103,71 +125,108 @@ def ogCreatePartitions (disk, parts): sfdisk_input = 'unit: sectors\n\n' NVME_PREFIX = 'p' if 'nvme' in DISK else '' + EXTSTART = EXTSIZE = 0 for p in parts: - # Conservar los datos de la partición de caché. - if f'{ND} {PART}' == CACHEPART and CACHESIZE: - sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, Id=ca\n' - PART += 1 - continue # Leer formato de cada parámetro - Tipo:Tamaño TYPE, SIZE = p.split (':') if TYPE is None or 'None' == TYPE: TYPE='EMPTY' + + # Obtener identificador de tipo de partición válido. + if 'GPT' == PTTYPE: + ID = ogTypeToId (TYPE, 'GPT_GUID') + else: + ID = ogTypeToId (TYPE, PTTYPE) + + # Conservar los datos de la partición de caché. + if f'{ND} {PART}' == CACHEPART and CACHESIZE: + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, type={CACHE_ID}\n' + PART += 1 + try: SIZE = int (SIZE) except ValueError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, SIZE) return None - # Obtener identificador de tipo de partición válido. - ID = ogTypeToId (TYPE, 'MSDOS') if 'CACHE' == TYPE or not ID: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) return None - # Comprobar tamaño numérico y convertir en sectores de 512 B. - SIZE *= 2 + + # Comprobar tamaño numérico y convertir en sectores + ORIG_SIZE = SIZE ## para reportar en un error + if 512 == IOSIZE: SIZE *= 2 + else: SIZE = (SIZE+3)//4 ## sumamos 3 para que la división entera "redondee al alza" + # Comprobar si la partición es extendida. - EXTSTART = EXTSIZE = 0 - if 5 == ID: - if PART > 4: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') + if '5' == ID: + if 'GPT' == PTTYPE: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'EXTENDED') return None + + ## la extendida debe ser: + ## - si no estamos creando cache: /dev/sda4 + ## - si sí estamos creando cache: /dev/sda3 + ## de lo contrario, crea eg. esto: + ## Device Boot Start End Sectors Size Id Type + ## /dev/sda1 63 222444728 222444666 106,1G 82 Linux swap / Solaris + ## /dev/sda2 222444729 244889394 22444666 10,7G 5 Extended <--------- + ## /dev/sda3 244889395 251334060 6444666 3,1G 83 Linux <--------- + ## /dev/sda4 314103633 314572766 469134 229,1M ca unknown + ## /dev/sda5 222446777 224891442 2444666 1,2G 83 Linux + ## /dev/sda6 224891443 229558330 4666888 2,2G 83 Linux + ## la sda2 es extendida, y las lógicas deberían ser sda5, sda6, sda7 + ## pero esto mete una sda3 como lógica, y luego va el cache que es primaria... WTF + error_in_extended = False + if CACHEPART and PART != 3: error_in_extended = True + if not CACHEPART and PART != 4: error_in_extended = True + if PART > 4: error_in_extended = True + if error_in_extended: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'No se puede definir la partición extendida en la partición "{PART}"') + return None + # El inicio de la primera partición logica es el de la extendida más 4x512 EXTSTART = START+2048 EXTSIZE = SIZE-2048 # Incluir particiones lógicas dentro de la partición extendida. if 5 == PART: - if not EXTSTART: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') - return None - START = EXTSTART - SECTORS = EXTSTART+EXTSIZE + if 'GPT' == PTTYPE: + pass ## sin problema, la partición 5 es simplemente una más + else: + if not EXTSTART: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'la partición 5 es lógica pero no hay ninguna EXTENDED definida antes') + return None + START = EXTSTART + SECTORS = EXTSTART+EXTSIZE # Generar datos para la partición. # En el caso de que la partición sea EMPTY no se crea nada if 'EMPTY' != TYPE: - sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={START}, size={SIZE}, Id={ID}\n' + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={START}, size={SIZE}, type={ID}\n' START += SIZE - # Error si se supera el nº total de sectores. - if '4096' == IOSIZE and PART > 4: + if 'MSDOS' == PTTYPE and 4096 == IOSIZE and PART > 4: START += 2048 + + # Error si se supera el nº total de sectores. if START > SECTORS: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{START//2} > {SECTORS//2}') + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'partición "{TYPE}:{ORIG_SIZE}" no cabe') return None PART += 1 # Si no se indican las 4 particiones primarias, definirlas como vacías, conservando la partición de caché. while PART <= 4: if f'{ND} {PART}' == CACHEPART and CACHESIZE: - sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, Id=ca\n' + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, type={CACHE_ID}\n' else: - sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start=0, size=0, Id=0\n' + sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start=0, size=0, type=0\n' PART += 1 + # Si se define partición extendida sin lógicas, crear particion 5 vacía. if 5 == PART and EXTSTART: - sfdisk_input += f'{DISK}5 : start={EXTSTART}, SIZE={EXTSIZE}, Id=0\n' + ID = ogTypeToId ('WINDOWS', PTTYPE) + sfdisk_input += f'{DISK}5 : start={EXTSTART}, SIZE={EXTSIZE}, type={ID}\n' # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. FileSystemLib.ogUnmountAll (ND) @@ -183,113 +242,6 @@ def ogCreatePartitions (disk, parts): return not p.returncode -#/** -# ogCreateGptPartitions int_ndisk str_parttype:int_partsize ... -#@brief Define el conjunto de particiones de un disco GPT -#@param int_ndisk nº de orden del disco -#@param str_parttype mnemónico del tipo de partición -#@param int_partsize tamaño de la partición (en KB) -#@return (nada, por determinar) -#@exception OG_ERR_FORMAT formato incorrecto. -#@exception OG_ERR_NOTFOUND disco o partición no detectado (no es un dispositivo). -#@exception OG_ERR_PARTITION error en partición o en tabla de particiones. -#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize -#@attention Pueden definirse particiones vacías de tipo \c EMPTY -#@attention No puede definirse partición de caché y no se modifica si existe. -#@note Requisitos: sfdisk, parted, partprobe, awk -#@todo Definir atributos (arranque, oculta) y tamaños en MB, GB, etc. -#*/ ## -def ogCreateGptPartitions (disk, parts): - ND = disk - DISK = ogDiskToDev (ND) - if not DISK: return None - - # Se calcula el ultimo sector del disco (total de sectores usables) - SECTORS = ogGetLastSector (disk) - - # Se recalcula el nº de sectores del disco si existe partición de caché. - CACHESIZE = 0 - CACHEPART = CacheLib.ogFindCache() - if CACHEPART: - cache_disk, cache_part = CACHEPART.split() - if ND == cache_disk: - CACHESIZE = int (CacheLib.ogGetCacheSize()) * 2 - if CACHESIZE: - SECTORS -= CACHESIZE - - # Si el disco es GPT empieza en el sector 2048 por defecto, pero podria cambiarse - ALIGN = int (subprocess.run (['sgdisk', '-D', DISK], capture_output=True, text=True).stdout) - START = ALIGN - PART = 1 - print (f'START ({START}) SECTORS ({SECTORS}) PART ({PART})') - - # Leer parámetros con definición de particionado. - DELOPTIONS = [] - OPTIONS = [] - for p in parts: - # Conservar los datos de la partición de caché. - if f'{ND} {PART}' == CACHEPART and CACHESIZE: - PART += 1 - - # Leer formato de cada parámetro - Tipo:Tamaño - TYPE, SIZE = p.split (':') - try: - SIZE = int (SIZE) - except ValueError: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, SIZE) - return None - - # Error si la partición es extendida (no válida en discos GPT). - if 'EXTENDED' == TYPE: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'EXTENDED') - return None - - # Comprobar si existe la particion actual, capturamos su tamaño para ver si cambio o no - PARTSIZE = ogGetPartitionSize (ND, PART) - # En sgdisk no se pueden redimensionar las particiones, es necesario borrarlas y volver a crealas - if PARTSIZE: - DELOPTIONS.append (f'-d{PART}') - - # Creamos la particion - # Obtener identificador de tipo de partición válido. - ID = ogTypeToId (TYPE, 'GPT') - if 'CACHE' == TYPE or not ID: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) - return None - # Comprobar tamaño numérico y convertir en sectores de 512 B. - SIZE *= 2 - # SIZE debe ser múltiplo de ALIGN, si no gdisk lo mueve automáticamente. - SIZE = (SIZE // ALIGN) * ALIGN - - # Generar datos para la partición. - # En el caso de que la partición sea EMPTY no se crea nada - if 'EMPTY' != TYPE: - OPTIONS += [f'-n{PART}:{START}:+{SIZE}', f'-t{PART}:{ID}'] - - START += SIZE - - # Error si se supera el nº total de sectores. - if START > SECTORS: - SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{START//2} > {SECTORS//2}') - return None - - PART += 1 - - # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. - FileSystemLib.ogUnmountAll (ND) - if CACHESIZE: CacheLib.ogUnmountCache() - - # Si la tabla de particiones no es valida, volver a generarla. - ogCreatePartitionTable (ND, 'GPT') - - # Definir particiones y notificar al kernel. - p = subprocess.run (['sgdisk'] + DELOPTIONS + OPTIONS + [DISK], capture_output=True, text=True) - print (f'sgdisk rc ({p.returncode}) stdout ({p.stdout}) stderr ({p.stderr})') - subprocess.run (['partprobe', DISK]) - if CACHESIZE: CacheLib.ogMountCache() - return not p.returncode - - #/** # ogCreatePartitionTable int_ndisk [str_tabletype] #@brief Genera una tabla de particiones en caso de que no sea valida, si es valida no hace nada. @@ -858,7 +810,7 @@ def ogGetPartitionSize (disk, par): #@return Devuelve el numero paritiones del disco duro indicado #@warning Salidas de errores no determinada #@attention Requisitos: parted -#@note Notas sin especificar +#@note Notas sin especificar #*/ ## def ogGetPartitionsNumber (disk): DISK = ogDiskToDev (disk) @@ -1277,6 +1229,23 @@ def ogSetPartitionType (disk, par, t): #ogTypeToId ('LINUX', 'MSDOS') => "83" def ogTypeToId (type, pttype='MSDOS'): data = { + 'GPT_GUID': { + ## https://en.wikipedia.org/wiki/GUID_Partition_Table?useskin=vector + 'EMPTY': '0', + 'WINDOWS': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', + 'NTFS': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', + 'FAT32': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', + 'FAT16': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', + 'FAT12': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', + 'WIN-RESERV': 'E3C9E316-0B5C-4DB8-817D-F92DF00215AE', + 'LINUX-SWAP': '0657FD6D-A4AB-43C4-84E5-0933C84B4F4F', + 'LINUX': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + 'EXT2': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + 'EXT3': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + 'EXT4': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + 'CACHE': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + 'EFI': 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B', + }, 'GPT': { 'EMPTY': '0', 'WINDOWS': '0700', diff --git a/ogclient/lib/python3/ProtocolLib.py b/ogclient/lib/python3/ProtocolLib.py index 1b41ba8..59ce192 100644 --- a/ogclient/lib/python3/ProtocolLib.py +++ b/ogclient/lib/python3/ProtocolLib.py @@ -224,7 +224,6 @@ def ogUcastSendFile (disk=None, par=None, container=None, file=None, sess=None): return path2 = FileLib.ogGetPath (file=source) - print (f'nati path2 ({path2})') if not path2: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') return @@ -483,7 +482,6 @@ def ogMcastSendFile (disk=None, par=None, container=None, file=None, sess=None): return path2 = FileLib.ogGetPath (file=source) - print (f'nati path2 ({path2})') if not path2: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'device or file {dev_err} not found') return diff --git a/ogclient/scripts/bootLinux b/ogclient/scripts/bootLinux deleted file mode 120000 index d19be90..0000000 --- a/ogclient/scripts/bootLinux +++ /dev/null @@ -1 +0,0 @@ -bootOs \ No newline at end of file diff --git a/ogclient/scripts/bootWindows b/ogclient/scripts/bootWindows deleted file mode 120000 index d19be90..0000000 --- a/ogclient/scripts/bootWindows +++ /dev/null @@ -1 +0,0 @@ -bootOs \ No newline at end of file diff --git a/ogclient/scripts/initCache.py b/ogclient/scripts/initCache.py index f564924..6371ceb 100755 --- a/ogclient/scripts/initCache.py +++ b/ogclient/scripts/initCache.py @@ -50,6 +50,8 @@ def main (NDISK, NPART, SIZE, MOUNT): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_CACHE, 'failed to create cache') return False DiskLib.ogUpdatePartitionTable() + else: + print("[10] Partición de caché solicitada es del mismo tamaño que la existente, ignorar.") # Si caché no montada y no formateada o cambia el tamaño: formatear. cache = CacheLib.ogFindCache()