2025, Nov 16 18:02

Как определить целевую версию Windows для MSI, MSU, CAB и MSP: почему метаданные не работают

Разбираем, почему по метаданным файловой системы нельзя понять, на какую версию Windows 10/11 нацелены пакеты MSI, MSU, CAB и MSP, и какие подходы работают.

Когда нужно понять, на какую версию Windows рассчитан пакет обновления — Windows 10 или Windows 11, рука тянется просто пробежать по метаданным файла и на этом остановиться. Это особенно часто всплывает при автоматизации проверок загрузок из каталога Windows Update, когда хочется обойтись без хрупкого разбора имён файлов. Возникает вопрос: содержится ли ответ в метаданных файловой системы для .msu, .msi, .cab или .msp? Короткий ответ: нет.

Постановка задачи

Цель — определить целевую версию Windows для файлов обновлений, скачанных из каталога Windows Update, не опираясь на их имена. Первая мысль — прочитать некую «версию», которую Python может получить из метаданных файловой системы.

Пример неудачного подхода

Следующий фрагмент Python показывает типичный вариант. Он смотрит на метаданные и базовые атрибуты файла, пытаясь вывести целевую версию ОС. Ожидаемо, это ничего полезного не даёт, поскольку предназначенная версия Windows не кодируется на уровне файловой системы.

from pathlib import Path
from typing import Optional

# Попытка угадать целевую версию ОС по метаданным файловой системы.
# Это не работает для .msu, .msi, .cab, .msp.
def infer_target_platform_from_fs(path_str: str) -> Optional[str]:
    obj = Path(path_str)
    if not obj.exists():
        raise FileNotFoundError(f"Missing file: {obj}")

    # Базовые атрибуты файловой системы
    info = obj.stat()
    size = info.st_size
    modified_ts = info.st_mtime
    suffix = obj.suffix.lower()

    # Ни один из этих атрибутов не содержит целевую версию Windows
    # Возвращаем None, чтобы отразить отсутствие надёжного признака
    return None

# Показательные вызовы; все вернут None
print(infer_target_platform_from_fs("example.cab"))
print(infer_target_platform_from_fs("example.msi"))
print(infer_target_platform_from_fs("example.msu"))
print(infer_target_platform_from_fs("example.msp"))

Почему это не работает

Из метаданных файловой системы? Нет.

В этом и проблема. Файлы CAB, MSI, MSU и MSP не раскрывают целевую версию Windows через метаданные файловой системы. CAB — это архив, по духу похожий на ZIP, который объединяет несколько сжатых файлов. Нацеливание на ОС, если оно вообще присутствует, находится внутри полезной нагрузки. Один CAB даже может содержать наборы файлов для разных версий ОС, так что для архива в целом единого ответа может не быть.

MSI — это не просто контейнеры; это базы данных, описывающие, как установить или обновить продукт. В отдельных случаях можно вывести предварительные требования, заглянув в соответствующую таблицу, но в общем случае это ненадёжно, потому что MSI могут включать или ссылаться на пользовательские сценарии или DLL, реализующие разовые проверки предпосылок. Такая логика не проявляется в базовых метаданных и не извлекается простым запросом.

И для MSU и MSP нет причин ожидать, что целевая версия Windows появится в метаданных файловой системы.

Практический путь

Если нужно узнать, для какой версии Windows предназначено обновление, не опирайтесь на метаданные файловой системы. Для CAB придётся посмотреть содержимое архива и найти соответствующие сведения внутри файлов. Инструменты вроде extract.exe умеют распаковывать cabinet, и определяющие данные могут лежать в одном из извлечённых файлов. Для MSI потребуется обратиться к базе данных MSI и, где это предусмотрено, определить там предпосылки, помня, что фактические проверки могут быть реализованы в пользовательском коде. Для MSU и MSP также не стоит ожидать, что атрибуты файловой системы дадут ответ.

Код ниже отражает этот подход. Он не пытается «выдать» результат из метаданных и прямо указывает, что ответ находится в содержимом пакета или логике установщика.

from pathlib import Path

class TargetDetectionUnavailable(Exception):
    pass

# Диспетчер, который отказывается выводить нацеливание на ОС из метаданных файловой системы
# и направляет вызывающего к анализу содержимого.
def determine_target_os(pkg_path: str) -> str:
    p = Path(pkg_path)
    if not p.exists():
        raise FileNotFoundError(f"Missing file: {p}")

    ext = p.suffix.lower()

    if ext == ".cab":
        raise TargetDetectionUnavailable(
            "CAB archives do not carry OS targeting in filesystem metadata; "
            "inspect files inside the cabinet to find relevant information."
        )

    if ext == ".msi":
        raise TargetDetectionUnavailable(
            "MSI targeting is defined in the MSI database and possibly custom scripts or DLLs; "
            "do not rely on filesystem metadata."
        )

    if ext in (".msu", ".msp"):
        raise TargetDetectionUnavailable(
            "Do not expect filesystem metadata to reveal intended Windows version for MSU/MSP; "
            "analyze the package content if needed."
        )

    raise TargetDetectionUnavailable(
        "Unknown package type; no filesystem metadata can provide the target OS."
    )

Почему это важно

Автоматизация, исходящая из предположения, что целевую версию Windows можно вывести из метки времени, размера или другого базового атрибута, будет тихо ошибаться. CAB-файлы могут смешивать содержимое для Windows 10 и Windows 11, так что даже «ярлык» на уровне пакета окажется вводящим в заблуждение. MSI могут ограничивать установку логикой, которая не отображается ни в одном очевидном поле. Рассматривать имена файлов как безусловную истину столь же рискованно: соглашения об именах ничем не гарантированы.

Выводы

Не рассчитывайте на метаданные файловой системы, чтобы понять, ориентирован ли .msu, .msi, .cab или .msp на Windows 10 или Windows 11. Для кабинетов, если нужна уверенность, изучайте полезную нагрузку. В случае MSI ответ спрятан в базе данных и может дополнительно обеспечиваться пользовательскими сценариями или DLL. Для MSU и MSP не опирайтесь на атрибуты, которые показывает файловая система. Стройте инструменты вокруг анализа содержимого или внешних авторитетных источников и учитывайте, что один пакет способен охватывать несколько версий Windows.