* 伪造 sysfs 视图。
*
* 本应用域允许 open /dev/bus/usb 节点,但禁止枚举 /sys/bus/usb/devices,
* 而 libusb/fastboot/adb 的 usb_linux 后端都靠枚举 sysfs 发现设备。
* 这里把该前缀重写到 ArkTS 侧生成的替身树(FakeSysAdapter),
* 并把 statfs("/sys") 伪装成 SYSFS_MAGIC——libusb 用这个魔数决定用不用 sysfs 后端,
* 不伪装它会整体退回 usbfs(于是 speed=UNKNOWN、port_number=0、adb 拼出 usb:0-0)。
*/
#include "compat.h"
int ohb_rewrite(const char *path, char *out, size_t outsz) {
if (!path) {
return 0;
}
size_t plen = strlen(OHB_SYS_PREFIX);
if (strncmp(path, OHB_SYS_PREFIX, plen) != 0) {
return 0;
}
if (path[plen] != '\0' && path[plen] != '/') {
return 0;
}
snprintf(out, outsz, "%s%s", OHB_FAKE_DEVICES, path + plen);
return 1;
}
DIR *opendir(const char *path) {
ohb_real_init();
char np[512];
if (ohb_rewrite(path, np, sizeof(np))) {
ohb_trace("opendir %s -> %s", path, np);
return g_real_opendir(np);
}
if (path && strstr(path, "usb") != NULL) {
ohb_trace("opendir %s (passthrough)", path);
}
return g_real_opendir(path);
}
int stat(const char *path, struct stat *buf) {
ohb_real_init();
char np[512];
if (ohb_rewrite(path, np, sizeof(np))) {
return g_real_stat(np, buf);
}
return g_real_stat(path, buf);
}
int lstat(const char *path, struct stat *buf) {
ohb_real_init();
char np[512];
if (ohb_rewrite(path, np, sizeof(np))) {
return g_real_lstat(np, buf);
}
return g_real_lstat(path, buf);
}
int access(const char *path, int mode) {
ohb_real_init();
char np[512];
if (ohb_rewrite(path, np, sizeof(np))) {
return g_real_access(np, mode);
}
return g_real_access(path, mode);
}
FILE *fopen(const char *path, const char *mode) {
ohb_real_init();
char np[512];
if (ohb_rewrite(path, np, sizeof(np))) {
return g_real_fopen(np, mode);
}
return g_real_fopen(path, mode);
}
* 真实的 /sys/bus/usb/devices/<bus>-<port> 是符号链接,部分调用方靠 readlink
* 推路径。伪造条目是普通目录,这里造一个指向"重写前缀下同一路径"的目标,
* 后续对该路径的读取仍会命中伪造树。
*/
ssize_t readlink(const char *path, char *buf, size_t bufsiz) {
ohb_real_init();
char np[512];
* adb 取 basename 拼成 "usb:1-3") */
unsigned int maj = 0, min = 0;
if (path && sscanf(path, "/sys/dev/char/%u:%u", &maj, &min) == 2) {
int bus = 0, addr = 0;
if (ohb_acq_lookup_rdev(maj, min, &bus, &addr)) {
snprintf(np, sizeof(np), "%s/%d-%d", OHB_FAKE_DEVICES, bus, addr);
size_t n = strlen(np);
if (n > bufsiz) {
n = bufsiz;
}
memcpy(buf, np, n);
return (ssize_t)n;
}
}
if (ohb_rewrite(path, np, sizeof(np))) {
size_t n = strlen(np);
if (bufsiz == 0) {
return -1;
}
if (n > bufsiz) {
n = bufsiz;
}
memcpy(buf, np, n);
return (ssize_t)n;
}
return g_real_readlink(path, buf, bufsiz);
}
ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
ohb_real_init();
char np[512];
if (dirfd == AT_FDCWD && ohb_rewrite(path, np, sizeof(np))) {
return readlink(path, buf, bufsiz);
}
return g_real_readlinkat(dirfd, path, buf, bufsiz);
}
char *realpath(const char *path, char *resolved) {
ohb_real_init();
char np[512];
if (ohb_rewrite(path, np, sizeof(np))) {
size_t n = strlen(np);
if (!resolved) {
resolved = malloc(n + 1);
if (!resolved) {
return NULL;
}
}
memcpy(resolved, np, n + 1);
return resolved;
}
return g_real_realpath(path, resolved);
}
* 判定内核能力,而鸿蒙的 release 是 "HongMeng Kernel 1.13.0",解析失败 → op_init
* 返回 LIBUSB_ERROR_OTHER → libusb_init 失败 → 上层看到 "ctxUSB is nullptr,
* recompile please"(hdc 的 USB 通道整体不可用)。这里只改 sysname/release,
* 其余字段保持真实值。 */
int uname(struct utsname *buf) {
ohb_real_init();
if (!g_real_uname) {
errno = ENOSYS;
return -1;
}
int r = g_real_uname(buf);
if (r == 0 && buf) {
snprintf(buf->sysname, sizeof(buf->sysname), "%s", "Linux");
snprintf(buf->release, sizeof(buf->release), "%s", OHB_FAKE_KERNEL_RELEASE);
}
return r;
}
* USBDEVFS_REAPURBNDELAY 去取完成事件。真实 usbfs 里内核会在 URB 完成时把 fd
* 标成可读;而我们的 URB 完成事件放在**用户态队列**里(urb.c 的 g_done),
* 真实 fd 永远不会变得可读 —— libusb 因此从不来 reap:
* SUBMITURB → 干等 → 超时 → DISCARDURB → 完成事件没人取 → event loop 挂死。
* hdc 实测就是卡在这一步(WatchUsbNodeChange 连续 30s+ busy,之后再无任何 ioctl)。
*
* 对策:把我们的 usbfs fd 在 poll 里当作"有工作就绪":
* - 队列里有活:立刻标 POLLIN,libusb 马上回来 reap;
* - 队列空:把超时压到 10ms,让它周期性回来推进挂起的 IN URB 和处理定时器。
* 只对登记过的 fake fd 生效,其它 fd 原样透传。 */
int poll(struct pollfd *fds, nfds_t nfds, int timeout) {
ohb_real_init();
if (!g_real_poll || !fds || nfds == 0) {
return g_real_poll ? g_real_poll(fds, nfds, timeout) : -1;
}
{
static int s_off = -1;
if (s_off < 0) {
const char *v = getenv("OHB_NO_POLL");
s_off = (v && v[0] == '1') ? 1 : 0;
}
if (s_off) {
return g_real_poll(fds, nfds, timeout);
}
}
int has_fake = 0;
int has_work = 0;
for (nfds_t i = 0; i < nfds; i++) {
if (fds[i].fd < 0 || !ohb_dev_known(fds[i].fd)) {
continue;
}
has_fake = 1;
if (ohb_urb_has_work(fds[i].fd)) {
has_work = 1;
}
}
if (!has_fake) {
return g_real_poll(fds, nfds, timeout);
}
int wait = has_work ? 0 : (timeout < 0 || timeout > 10 ? 10 : timeout);
int r = g_real_poll(fds, nfds, wait);
if (has_work) {
for (nfds_t i = 0; i < nfds; i++) {
if (fds[i].fd >= 0 && ohb_dev_known(fds[i].fd)) {
fds[i].revents |= POLLIN;
if (r <= 0) {
r = 1;
}
}
}
}
return r;
}
*
* 理由:设备在调试态下对字符串描述符的应答并不可靠——实测桥接回来的 langid 是
* `30 00 00 00`(不是合法的 `04 03 09 04`),libusb 直接判
* "invalid language ID string descriptor",hdc 的 CheckDescriptor 随即失败、
* DetectMyNeed 返回 -1,于是 `hdc list targets` 永远为空。
* 而这些东西伪造树里本来就有(manufacturer / product / serial,且配置描述符里
* iManufacturer=1 / iProduct=2 / iSerialNumber=3 与之一一对应)。
* fastboot 当初也是靠本地应答过掉的,这里把同一招做成通用的。
*
* index 0 = langid 列表(固定 en-US);1/2/3 = 厂商/产品/序列号。
* 返回写入的字节数;0 表示不处理(交回桥接)。 */
int ohb_string_desc_local(int bus, int addr, unsigned int index, unsigned char *buf, int len) {
if (!buf || len <= 0) {
return 0;
}
unsigned char tmp[256];
unsigned char text[128];
if (index == 0) {
static const unsigned char langid[] = { 4, 3, 0x09, 0x04 };
int n = (int)sizeof(langid);
if (n > len) {
n = len;
}
memcpy(buf, langid, (size_t)n);
return n;
}
const char *name = index == 1 ? "manufacturer" : (index == 2 ? "product" : (index == 3 ? "serial" : NULL));
if (!name) {
return 0;
}
char path[512];
snprintf(path, sizeof(path), "%s/%d-%d/%s", OHB_FAKE_DEVICES, bus, addr, name);
FILE *fp = g_real_fopen ? g_real_fopen(path, "r") : NULL;
if (!fp) {
return 0;
}
size_t got = fread(text, 1, sizeof(text), fp);
fclose(fp);
while (got > 0 && (text[got - 1] == '\n' || text[got - 1] == '\r' || text[got - 1] == ' ')) {
got--;
}
if (got == 0) {
return 0;
}
int n = 2;
for (size_t i = 0; i < got && n + 2 <= (int)sizeof(tmp); i++) {
tmp[n++] = text[i];
tmp[n++] = 0;
}
if (n > 2) {
tmp[0] = (unsigned char)n;
tmp[1] = 3;
}
if (n > len) {
n = len;
}
memcpy(buf, tmp, (size_t)n);
return n;
}
static int is_sys_root(const char *path) {
return path && (strcmp(path, "/sys") == 0 || strcmp(path, "/sys/") == 0);
}
* 判定后端;应用域里这一步可能失败。只改 f_type,其余字段保持真实值。 */
int statfs(const char *path, struct statfs *buf) {
ohb_real_init();
int r = g_real_statfs ? g_real_statfs(path, buf) : -1;
if (is_sys_root(path)) {
if (r != 0 && buf) {
memset(buf, 0, sizeof(*buf));
buf->f_bsize = 4096;
r = 0;
}
if (buf) {
buf->f_type = OHB_SYSFS_MAGIC;
}
return 0;
}
return r;
}