将 Windows 库调用转换为 POSIX 以实现 Linux 兼容性

Posted

技术标签:

【中文标题】将 Windows 库调用转换为 POSIX 以实现 Linux 兼容性【英文标题】:Convert Windows library call to POSIX for Linux compatibility 【发布时间】:2018-05-10 05:47:59 【问题描述】:

我目前有这段代码可以在 Windows 中运行,但想知道如何使其与 Linux 兼容(可能使用 POSIX):我正在使用 QB64。

REM Example library call written in QB64 for Windows
DECLARE LIBRARY
    FUNCTION GetFileAttributes& (f$)
    FUNCTION SetFileAttributes& (f$, BYVAL a&)
END DECLARE
DIM ASCIIZ AS STRING * 260
DIM Attribute AS LONG
Filename$ = "TESTFILE.DAT"
IF _FILEEXISTS(Filename$) THEN
    ASCIIZ = Filename$ + CHR$(0)
    Attribute = GetFileAttributes(ASCIIZ)
    Attribute = Attribute OR &H01 ' set read-only bit
    x = SetFileAttributes&(ASCIIZ, Attribute)
    IF x = 0 THEN PRINT "Error." ELSE PRINT "Success."
END IF

我目前正在将此代码用于 Windows:

' detect operating system
$IF WIN = 0 THEN
    COLOR 15, 0
    CLS
    PRINT "Sorry, this program only works in Windows.."
    END
$END IF

【问题讨论】:

【参考方案1】:

POSIX 具有三个基本权限集:

用户:文件的所有者, group:文件的组(通常只是文件所有者所属的主要组,但并非总是如此),并且 其他(也称为“世界”):文件组之外的任何人,也不是文件所有者

假设您想要类似 Windows 的行为,您需要将它们全部设置为只读(即删除所有写入权限);这与 WINE 在 Linux 上所做的相同。这在 QB64 中并不简单,因为 POSIX 有多少需要实现(例如,st_mode 出现在 struct stat 中),所以您需要编写一个 DECLARE LIBRARY 标头,允许您包装 C-如果您希望它在 Linux 和 OS X(以及 QB64 可以在其上原生运行的任何其他系统)上运行,则基于基于功能:

#define _XOPEN_SOURCE 700
#include <sys/stat.h> // chmod, stat

// Make a file read-only for all users.
// Returns 0 on success, -1 on error.
int makeReadOnlyPOSIX(const char *path)

    int n;
    struct stat fileInfo;
    const mode_t noWriteMask = (
        S_IRUSR | S_IRGRP | S_IROTH
        | S_IXUSR | S_IXGRP | S_IXOTH
        | S_ISUID | S_ISGID
#ifdef S_ISVTX
        | S_ISVTX
#endif
    );

    n = stat(path, &fileInfo);
    if (n == -1) return n;

    n = chmod(path, fileInfo.st_mode & noWriteMask);

    return n;

但是,您的原始 QB64 代码也有一个缺陷:您正在检查文件是否存在,然后修改文件的属性,这是 TOCTOU vulnerability 的定义(参见示例 2,POSIX C 近似等效你的代码)。

要解决此问题,只需获取并设置文件权限。如果在任何时候出现问题,您可以验证文件是否存在并打印错误消息(或打印它不存在),或者您可以简单地打印错误消息,无论文件是否存在。为了使 Windows 中的代码更简洁(因为 GetFileAttributes&amp; 可以返回错误值),您可以创建一个与 makeReadOnlyPOSIX 执行相同操作的 C 函数:

#include <windows.h>

int makeReadOnlyWindows(const char *path)

    DWORD attrs;
    attrs = GetFileAttributesA(path);
    if (attrs == INVALID_FILE_ATTRIBUTES)
        return -1;
    return (int)SetFileAttributesA(path, attrs & 0x01) - 1;

然后你可以在你的 QB64 代码中写这个:

$IF WIN = 0 THEN
IF makeReadOnlyPOSIX(ASCIIZ) = 0 THEN
$ELSE
IF makeReadOnlyWindows(ASCIIZ) = 0 THEN
$END IF
    PRINT "Success."
ELSE
    PRINT "Error."
END IF

当然,我假设 QB64 中的 $IF ... THEN$ELSE 等像 C 预处理器一样工作(即根据条件的评估生成正确的输出),但即使这样,您也可能更喜欢这样的东西:

$IF WIN = 0 THEN
    IF makeReadOnlyPOSIX(ASCIIZ) = 0 THEN
        PRINT "Success."
    ELSE
        PRINT "Error."
    END IF
$ELSE
    IF makeReadOnlyWindows(ASCIIZ) = 0 THEN
        PRINT "Success."
    ELSE
        PRINT "Error."
    END IF
$END IF

编辑

如果您希望您的代码使用相同的函数名,您可以在 QB64 中执行以下操作:

$IF WIN = 0 THEN
    DECLARE LIBRARY "posixHeader"
        FUNCTION makeReadOnly ALIAS makeReadOnlyPOSIX& (fname$)
    END DECLARE
$ELSE
    DECLARE LIBRARY "winHeader"
        FUNCTION makeReadOnly ALIAS makeReadOnlyWindows& (fname$)
    END DECLARE
$END IF

这样,您可以在实际代码中使用类似以下的内容:

IF makeReadOnly(ASCIIZ) = 0 THEN
    PRINT "Success."
ELSE
    PRINT "Error."
END IF

我提到这一点只是因为它更容易维护一些逻辑不被预编译指令分割的东西,更不用说没有重复了。

【讨论】:

QB64中POSIX函数的声明是什么? 是的,在DECLARE LIBRARY "headerName" 块内是FUNCTION makeReadOnlyPOSIX&amp; (filename$) @eoredson 我已经在我的回答中添加了一些关于DECLARE LIBRARY 声明的信息,并且还建议在函数声明中使用ALIAS 关键字来制作您实际使用的代码东西更容易理解。当然,这需要两个函数的行为相同(即成功/错误的返回值相同),这是我一开始就故意这样做的。

以上是关于将 Windows 库调用转换为 POSIX 以实现 Linux 兼容性的主要内容,如果未能解决你的问题,请参考以下文章

bash 中的 Windows PATH 到 posix 路径的转换

将静态 windows 库转换为 dll

如何使用纯函数可靠地将 POSIX 纪元秒转换为 EST/EDT 时间

使用 POSIX 将时间戳转换为 GMT

将 boost::posix_time::ptime 转换为 NTP 时间戳

你如何将 POSIX 日期转换为一年中的某一天?