Metadata-Version: 2.4
Name: dlp-io
Version: 0.1.0
Summary: io-compatible file reads through an approved Python helper on Windows DLP hosts
Project-URL: Repository, https://gitea.docker.antior.cn/antior/DataPacker
Classifier: Development Status :: 3 - Alpha
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# dlp-io

`dlp-io` 为 Windows DLP 透明加密环境提供接近 `io.open` 的 Python 文件 API。非白名单进程负责业务逻辑和写入；文件读取由已获 DLP 白名单授权的 Python helper 完成，再通过经过认证的 Windows Named Pipe 返回明文字节。

当前版本为 `dlp-io==0.1.0`，distribution 名是 `dlp-io`，import 名是 `dlp_io`。

## 安装

Windows PowerShell：

```powershell
py -m pip install --index-url https://gitea.docker.antior.cn/api/packages/antior/pypi/simple dlp-io==0.1.0
```

调用方需要安装 `dlp-io`。白名单 Python 无需安装该包：helper 源码由调用方以 UTF-8 通过 stdin 注入，且只依赖 Python 标准库，不会在磁盘创建临时 `.py` 文件。

运行环境必须是 Windows，并且机器上已有一个真正具备 DLP 明文读取权限的 Python。解释器查找顺序为：

1. `DLP_IO_PYTHON`
2. `DATAPACKER_DLP_PYTHON`（兼容 DataPacker）
3. `python`
4. `py -3`

生产环境优先把 `DLP_IO_PYTHON` 设置为审批过的 Python 绝对路径。

## io 风格用法

把原来的 `io.open` 或内置 `open` 改成 `dlp_io.open` 即可。纯读取走 helper；写入、追加和独占创建仍使用调用进程的原生文件 API。

```python
from pathlib import Path

import dlp_io


source = Path(r"D:\Protected\input.txt")
output = Path(r"D:\Output\result.txt")

with dlp_io.open(source, "r", encoding="utf-8", newline="") as reader:
    text = reader.read()

with dlp_io.open(output, "w", encoding="utf-8", newline="\n") as writer:
    writer.write(text.upper())

dlp_io.shutdown()
```

`dlp_io.open()` 保留 `io.open()` 的参数名和位置习惯，包括 `buffering`、`encoding`、`errors`、`newline`、`closefd` 和 `opener`。

模式路由规则：

- 路径型 `r`、`rt`、`rb`：通过 helper 读取。
- 路径型 `w`、`a`、`x`：使用原生 `open` 写入。
- 整数文件描述符或自定义 `opener`：使用原生 `open`。
- 路径型 `+` 更新模式：显式抛出 `io.UnsupportedOperation`。
- 路径读取配合 `closefd=False`：与标准 API 一样抛出 `ValueError`。
- `rb` 配合 `buffering=0`：返回 raw stream；默认返回 buffered reader。
- 文本模式的默认 encoding 与当前 Python `io.open` 一致；跨机器文件请显式传 `encoding="utf-8"`。

## 配置和生命周期

默认 session 在第一次读取时懒启动，并被后续读取复用：

```python
import dlp_io

dlp_io.configure(
    python_executable=r"C:\ApprovedPython\python.exe",
    startup_timeout=30,
)

try:
    with dlp_io.open(r"D:\Protected\payload.bin", "rb") as stream:
        header = stream.read(64)
finally:
    dlp_io.shutdown()
```

必须在默认 session 启动前调用 `configure()`。需要更换解释器时，先 `shutdown()`，再重新配置。`shutdown()` 和 session 的 `close()` 都是幂等操作。

## 显式 session

库或服务代码可以显式持有 session，避免全局状态：

```python
import dlp_io

with dlp_io.DlpSession.start(
    python_cmd=[r"C:\ApprovedPython\python.exe"],
    timeout=30,
) as session:
    with session.open(r"D:\Protected\archive.dat", "rb") as stream:
        stream.seek(-32, 2)
        trailer = stream.read()
```

一个 session 同时只允许一个活动读取 stream。请关闭当前 stream 后再打开下一个；并行读取应由调用方创建彼此独立的 session。

## 兼容既有代码的全局 patch

只有当第三方代码无法逐个改为 `dlp_io.open()` 时，才临时 patch `builtins.open` 和 `io.open`：

```python
import dlp_io

dlp_io.configure(python_executable=r"C:\ApprovedPython\python.exe")
dlp_io.install_open_patch()
try:
    run_legacy_application()
finally:
    dlp_io.uninstall_open_patch()
    dlp_io.shutdown()
```

在打包入口中，应先 import 完业务模块和本地资源，再安装 patch。patch 只路由路径型纯读模式，重复安装和卸载是幂等的。它不会拦截 `os.open`，也无法拦截 C 扩展内部绕过 Python `open` 的读取。

如果应用自己管理 session，可以把 `session.open_raw` 传给 `install_reader_patch()`。

## 错误和安全边界

主要异常均继承 `DlpIoError`：

- `DlpConfigurationError`：平台、配置或参数不成立。
- `DlpHelperStartError`：白名单 Python 启动、源码注入或握手失败。
- `DlpProtocolError`：协议版本、控制消息或响应结构无效。
- `DlpTransportError`：helper 在显式 EOF 前断开，或文件传输被截断。
- `DlpSessionBusyError`：同一个 session 已有活动 stream。

helper 启动或读取失败时，库会显式报错并且不回退到当前 EXE 直接读取。DLP 直读可能返回合法长度的密文；静默回退会把数据损坏伪装成成功。

版本 1 会在 EOF 和 seek 重连时检测文件 size 的截断或增长，但不提供文件快照，也不能识别读取期间发生的同尺寸内容替换；调用方应避免并发修改输入文件。

每次 session 使用随机 32-byte authkey，AF_PIPE 连接执行 challenge-response 认证；协议控制消息和数据帧都有明确大小边界，零长度数据帧是唯一 EOF。

## 卸载

```powershell
py -m pip uninstall dlp-io
```
