"""Repair missing SONAME aliases only for provenance-verified task-owned vendor SSL copies."""
import json,hashlib,os,re,struct,sys,tempfile
from pathlib import Path

def soname(data):
    if data[:6]!=b'\x7fELF\x02\x01' or struct.unpack_from('<H',data,18)[0]!=62:
        raise ValueError('Expected x86_64 little-endian ELF64')
    phoff=struct.unpack_from('<Q',data,32)[0]
    size,count=struct.unpack_from('<HH',data,54)
    if size!=56 or phoff+count*size>len(data):raise ValueError('Invalid ELF program headers')
    ph=[struct.unpack_from('<IIQQQQQQ',data,phoff+i*size)for i in range(count)]
    dynamic=next((p for p in ph if p[0]==2),None)
    if not dynamic:raise ValueError('No ELF dynamic segment')
    if dynamic[2]+dynamic[5]>len(data):raise ValueError('Invalid ELF dynamic size')
    tags={}
    for pos in range(dynamic[2],dynamic[2]+dynamic[5],16):
        tag,value=struct.unpack_from('<QQ',data,pos)
        if tag==0:break
        tags[tag]=value
    if 5 not in tags:raise ValueError('ELF string table absent')
    if 14 not in tags:return None
    seg=next((p for p in ph if p[0]==1 and p[3]<=tags[5]<p[3]+p[5]),None)
    if seg is None:raise ValueError('ELF string segment absent')
    pos=seg[2]+tags[5]-seg[3]+tags[14]
    end=data.index(0,pos)
    return data[pos:end].decode('ascii')

def repair(root):
    requested=Path(root)
    if requested.is_symlink():raise ValueError('Runtime root must not be a symlink')
    root=requested.resolve()
    for rel in ['dm-client','dm-client/lib','dm-client/lib/dependencies']:
        d=root/rel
        if d.is_symlink() or not d.is_dir() or d.resolve()!=d:raise ValueError('Native destination directory is not a real bounded directory')
    evidence_path=root/'dm-client/SONAME-ALIASES.json'
    if evidence_path.is_symlink() or (evidence_path.exists() and not evidence_path.is_file()):raise ValueError('Evidence path conflict')
    records=json.loads((root/'dm-client/SOURCE.json').read_text())
    evidence=[];plans=[]
    for name in ['libcrypto.so','libssl.so']:
        target=root/'dm-client/lib/dependencies'/name
        if target.is_symlink() or not target.is_file():raise ValueError('Expected regular installed native library: '+name)
        content=target.read_bytes();digest=hashlib.sha256(content).hexdigest()
        record=next((x for x in records if x.get('destination')==str(target)),None)
        if not record or record.get('sha256')!=digest:raise ValueError('Native library provenance mismatch: '+name)
        src=Path(record['source']).resolve()
        if not str(src).startswith('/home/dmdba/dmdbms/bin/') or hashlib.sha256(src.read_bytes()).hexdigest()!=digest:
            raise ValueError('Native source changed or outside approved vendor bin: '+name)
        actual=soname(content) or name
        stem=name[:-3]
        if not re.fullmatch(re.escape(stem)+r'\.so(?:\.[0-9]+)*',actual):raise ValueError('Unexpected SONAME: '+actual)
        aliases=[target.parent/actual]
        if actual!=name:aliases.append(root/'dm-client/lib'/actual)
        for alias in aliases:
            if alias!=target and os.path.lexists(str(alias)):
                saved=next((x for x in records if x.get('destination')==str(alias)),None)
                same_copy=alias.is_file() and saved and saved.get('sha256')==digest and hashlib.sha256(alias.read_bytes()).hexdigest()==digest
                if not alias.is_file() or (alias.resolve()!=target.resolve() and not same_copy):raise ValueError('Alias conflict; refusing overwrite: '+actual)
            plans.append((target,alias,digest))
    for target,alias,digest in plans:
        if alias!=target and not os.path.lexists(str(alias)):alias.symlink_to(os.path.relpath(str(target),str(alias.parent)))
        evidence.append({'alias':str(alias),'target':os.path.relpath(str(target),str(alias.parent)),'sha256':digest})
    with tempfile.NamedTemporaryFile(mode='w',dir=str(evidence_path.parent),prefix='.soname-',delete=False)as f:
        json.dump(evidence,f,indent=2);f.write('\n');temporary=f.name
    os.chmod(temporary,0o644);os.replace(temporary,str(evidence_path))
    for x in evidence:print('已核对本机库并补齐名称：'+Path(x['alias']).name+' -> '+x['target'])
    return evidence

if __name__=='__main__':
    try:repair(sys.argv[1]if len(sys.argv)>1 else '/opt/zhct-php73')
    except Exception as e:raise SystemExit('停止：'+str(e))
