OpenHarmony 移植 ProFTPD 服务器:构建 FTP服务端实践总结
本文结合当前仓库
FtpNextServer,说明如何将开源 FTP 服务器 ProFTPD 从 Linux 平台移植为可在 OpenHarmony 5.0+ 设备上运行的 HAP 应用。核心结论:通过 OHOS 交叉编译工具链将 ProFTPD 及其依赖库(OpenSSL、libssh2、zlib)编译为 arm64-v8a 架构的原生库与可执行文件,再由 NAPI 桥接层启动 ProFTPD 子进程,实现 ArkUI 界面与 FTP 服务进程的分离运行。
移植成功后的运行效果(FTP客户端连接登录):
移植后的开源地址:https://gitcode.com/OpenHarmonyPCDeveloper/FtpNextServer
一、为什么鸿蒙上的 FTP 服务端和传统 Linux 不一样
| 维度 | 传统 Linux FTP 服务端 | 鸿蒙上的 FTP 服务端(本方案) |
|---|---|---|
| 运行环境 | root 或普通用户,完整的 Linux 用户空间 | 应用沙箱,无 root 权限,seccomp 过滤 |
| C 库 | glibc | musl libc |
| 系统调用 | chroot/setuid/setgid/wtmp 均可使用 | 全部被沙箱拦截,需源码绕过 |
| 端口权限 | 可绑定 21/22 等特权端口 | 无 root 权限,实际使用 60021/60022/60023 |
| 进程模型 | 独立守护进程 | fork()+execvp() 作为应用子进程运行 |
| 文件路径 | /etc/, /var/run/, /home/ 等标准路径 |
全部限制在应用沙箱 /data/storage/el2/base/haps/entry/files/ 内 |
因此,移植的关键不在于把代码编译过,而在于:让一个假定自己运行在标准 Unix 用户空间的 FTP 服务器,在 OHOS 的沙箱限制下正常工作。
二、待移植的 ProFTPD 介绍
ProFTPD 是一款类 Unix 平台上的开源 FTP 服务器软件,具有以下特点:
- 支持 FTP、FTPS(FTP over TLS)、SFTP(SSH File Transfer Protocol)三种协议
- 采用模块化架构,通过动态加载模块(mod_tls、mod_sftp、mod_vroot 等)扩展功能
- 使用 autoconf 构建体系,可配置性强
- 单配置文件管理,类似 Apache 的配置风格
ProFTPD 开源地址:http://www.proftpd.org/
本方案选用 ProFTPD 1.3.8 版本,并为其打上 OHOS 适配补丁,使其能在 OpenHarmony 应用沙箱中稳定运行。
三、本仓库中的代码布局
-
鸿蒙工程根目录:
FtpNextServer/(DevEco Studio 打开的工程)。 -
ArkTS 侧:
entry/src/main/ets/,包含服务控制、配置页面、用户管理等界面。 -
原生 C/C++ 侧(NAPI 桥接):
entry/src/main/cpp/ ├── napi_init.cpp ← NAPI 总入口,注册导出给 ArkTS 的函数 └── FLog.h ← 日志工具 -
三方库源码:
native_c/目录下存放所有 C 源码native_c/ ├── zlib-1.3.1/ ← 压缩库 ├── openssl-3.0.0/ ← TLS/SSL 与加密库 ├── libssh2-1.10.0/ ← SFTP 协议库 ├── gettext-0.26/ ← 国际化库 ├── proftpd-1.3.8-ohos/ ← 已打 OHOS 补丁的 ProFTPD 源码 │ └── OHOS配置文件/ ← proftpd_ohos.conf、ftpd.passwd、SSH 密钥 └── out/ ← 预编译产物 ├── bin/ proftpd, ftpasswd, openssl 等可执行文件 ├── lib/ libcrypto.so.3, libssl.so.3, libssh2.so.1, libz.so.1 ├── etc/ proftpd_ohos.conf └── ssl/ ftpd.pem(自签名 TLS 证书)
ArkTS 中调用原生库的方式(节选):
// entry/src/main/ets/pages/Index.ets
import { startServer, stopServer, cryptPasswd } from 'libentry.so';
// 启动 FTP 服务
startServer(this.binPath, (logMsg: string) => {
// 实时接收 ProFTPD 子进程的日志输出
this.logLines += logMsg + '\n';
}, () => {
// 服务启动完成回调
this.isRunning = true;
});
// 停止服务
stopServer(this.binPath, () => {
this.isRunning = false;
});
// 生成密码哈希
cryptPasswd(password, salt, (hash: string) => {
// 写入 ftpd.passwd 文件
});
NAPI 模块在原生侧的注册(节选):
// entry/src/main/cpp/napi_init.cpp
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"startServer", nullptr, StartServer, nullptr, nullptr, nullptr, napi_default, nullptr},
{"stopServer", nullptr, StopServer, nullptr, nullptr, nullptr, napi_default, nullptr},
{"cryptPasswd", nullptr, CryptPasswd, nullptr, nullptr, nullptr, napi_default, nullptr}};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry", // ← 对应 ArkTS 侧 'libentry.so'
.nm_priv = ((void *)0),
.reserved = {0},
};
nm_modname = "entry" 即对应 ArkTS 侧 'libentry.so' 这个导入字符串,CMake 产物名 libentry.so、nm_modname、import 路径三者必须对齐。
四、移植总流程
-
准备交叉编译环境
- OpenHarmony SDK(含 native 工具链,clang/LLVM for
aarch64-linux-ohos) - 配置环境变量脚本
native_c/prepare_clang.sh
- OpenHarmony SDK(含 native 工具链,clang/LLVM for
-
按依赖顺序编译三方库
zlib-1.3.1 → openssl-3.0.0 → libssh2-1.10.0 ↘ proftpd-1.3.8 gettext-0.26 ──────────────────────↗四个库各自编译方式不同,核心是让构建系统使用
--target=aarch64-linux-ohos参数。 -
编译 ProFTPD 主程序
- autoconf 配置:
./configure --host=aarch64-linux,指定交叉编译后的 OpenSSL/zlib/libssh2 头文件与库路径 - 启用模块:
--with-modules=mod_tls:mod_sftp:mod_vroot - 产出:
proftpd可执行文件及ftpasswd管理工具
- autoconf 配置:
-
修改 ProFTPD 源码适配 OHOS 沙箱
src/fsio.c:禁用 chroot,路径映射到沙箱目录src/privs.c:跳过 setuid/setgidsrc/wtmp.c:跳过 wtmp 日志- 配置文件路径全部指向沙箱私有目录
-
开发 NAPI 桥接层
- ArkUI/TS 通过 NAPI 调用 C++ 代码
- C++ 代码
fork()+execvp()启动 ProFTPD 子进程 - 子进程日志通过 pipe +
napi_threadsafe_function回传 TS 层
-
编译 HAP → 签名 → 安装到鸿蒙设备
- 使用 DevEco Studio 打包
- 将 ProFTPD 可执行文件、配置文件、依赖库部署到设备对应路径
五、CMake/Configure 侧关键改动(编译 .so 与可执行文件)
5.1 交叉编译工具链配置
在 native_c/prepare_clang.sh 中统一指定:
export OHOS_SDK=/opt/ohos-sdk
export CC="${OHOS_SDK}/native/llvm/bin/clang --target=aarch64-linux-ohos"
export CXX="${OHOS_SDK}/native/llvm/bin/clang++ --target=aarch64-linux-ohos"
export AR="${OHOS_SDK}/native/llvm/bin/llvm-ar"
export LD="${OHOS_SDK}/native/llvm/bin/ld.lld"
export CFLAGS="-fPIC -D__MUSL__=1 --target=aarch64-linux-ohos"
export LDFLAGS="--target=aarch64-linux-ohos -fuse-ld=lld"
aarch64-linux-ohos 是 OHOS 特有的目标三元组,-D__MUSL__=1 是因为 OHOS 使用 musl 而非 glibc。
5.2 四个三方库的编译
四个库按依赖顺序依次编译,方式各不相同:
- zlib:autoconf 体系,
./configure --archs="-arch aarch64",通过环境变量 CC/CFLAGS 传入 OHOS 工具链 - OpenSSL:
./config linux-aarch64 shared zlib,利用环境变量中指定的 CC 与 CFLAGS 完成交叉编译 - libssh2:CMake 体系,
cmake -DOPENSSL_ROOT_DIR=../../out指向已编译好的 OpenSSL 产物(必须指向交叉编译后的版本,否则会链接到宿主机的 x86_64 符号表导致 crash) - gettext:autoconf 体系,
./configure --host=aarch64-linux后通过 sed 修补 libtool 加入--target=aarch64-linux-ohos
5.3 ProFTPD 的 configure 配置
./configure \
--host=aarch64-linux \
--build=x86_64-linux \
--enable-static --disable-shared \
--disable-ipv6 \
--with-modules=mod_tls:mod_sftp:mod_vroot \
--with-includes=/path/to/out/include \
--with-libraries=/path/to/out/lib \
CC="clang --target=aarch64-linux-ohos" \
CFLAGS="-fPIC -D__MUSL__=1" \
LDFLAGS="-L/path/to/out/lib" \
LIBS="-lcrypto -lssl -lz -lssh2"
5.4 ProFTPD 的 OHOS 适配
ProFTPD 假定自己在标准 Unix 环境运行,OHOS 应用沙箱限制不同,需要五处修改:
- fsio.c:
chroot()被拦截,跳过并将根路径/映射到沙箱目录 - privs.c:setuid/setgid 无权限,
#if 0禁掉 - wtmp.c:OHOS 无
/var/log/wtmp,直接 return 0 - 配置文件:路径指向沙箱私有目录,端口改用 60021(FTP)/60022(SFTP)/60023(FTPS)
- 用户配置:使用
ohosuser/ohosgroup+RequireValidShell off
5.5 entry/src/main/cpp/CMakeLists.txt
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(SuperredFTPServer)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
link_directories(${NATIVERENDER_ROOT_PATH}/../../../libs/${OHOS_ARCH})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so)
六、与 DevEco 工程对接时要注意什么
-
可执行文件通过 HNP 部署
ProFTPD 二进制以 HNP形式分发,打包命令:hnpcli pack -i .\hnp_proftpd -o .\hnp\arm64-v8a -n proftpd -v 1.5在
module.json5中声明hnpPackages引入,napi_init.cpp中execvp()使用的路径需与此一致。
注意:HNP 包已存在时,若版本号不变直接重新打包可能会失败,需先删除旧包或更新版本号再执行打包命令。 -
端口配置
真机测试时确保proftpd_ohos.conf中配置的端口(60021/60022/60023)未被占用。可使用netstat命令检查。 -
WiFi 网络拦截
设备连接的 WiFi 路由器可能开启 AP 隔离或端口过滤,导致同一局域网下的客户端无法连接 FTP 服务。需确保路由器允许局域网设备互访,或在路由器管理后台放行对应端口。
七、开发过程中遇到的问题
- 执行 HNP 包中的二进制需使用绝对路径
通过execvp()调用 HNP 包内的二进制文件时,必须使用绝对路径(如/data/app/proftpd.org/proftpd_1.5/bin/proftpd),不能依赖相对路径或环境变量。
八、小结
- 传统 ProFTPD:autoconf 配置 →
make→ 独立守护进程,运行在标准 Linux 用户空间。 - 鸿蒙上的 ProFTPD:同一套 ProFTPD 源码 → OHOS 交叉编译工具链编译 → arm64 ELF 可执行文件 + 依赖 .so → NAPI 桥接层拉起子进程 → HAP 应用内运行。
当前仓库已包含:
- 全部三方的 OHOS 交叉编译产物(
native_c/out/) - 打上 OHOS 适配补丁的 ProFTPD 源码(
native_c/proftpd-1.3.8-ohos/) - NAPI 桥接层 C++ 代码(
entry/src/main/cpp/) - OHOS 配置文件模板(
native_c/proftpd-1.3.8-ohos/OHOS配置文件/)
参考链接
更多推荐



所有评论(0)