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

时间:2018-05-10 05:47:59

标签: posix qb64

我目前有这段代码可以在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 个答案:

答案 0 :(得分:1)

POSIX具有三个基本权限集:

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

假设您想要类似Windows的行为,则需要将它们全部设置为只读(即,删除所有写权限);这与WINE在Linux上所做的相同。在QB64中,这并非一帆风顺,因为有多少POSIX尚待实现(例如st_modestruct stat中出现的位置),因此您需要编写一个DECLARE LIBRARY标头如果您希望基于C的功能在Linux和OS X(以及QB64可以在本机上运行的任何其他系统)上运行,则可以包装基于C的功能:

#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,POSIXC。大约与您的代码等效。)

要解决此问题,只需获取并设置文件许可权即可。如果在任何时候出现问题,您都可以验证文件是否存在并打印错误消息(或打印不存在的错误消息),或者可以简单地打印错误消息,而不管文件是否存在。为了使Windows中的代码更简洁(因为GetFileAttributes&可以返回错误值),您可以创建一个与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

之所以提到这一点,是因为维护逻辑不会在预编译指令之间分割的东西更容易,更不用说缺少重复了。