将 Windows 库调用转换为 POSIX 以实现 Linux 兼容性
Convert Windows library call to POSIX for Linux compatibility
我目前有这段代码可以在 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
POSIX 拥有三个基本权限集:
- 用户: 文件的所有者,
- 组:文件的组(通常只是文件所有者所属的主要组,但并非总是如此),并且
- other (a.k.a. "world"):文件组之外的任何人也不是文件所有者
假设您想要 Windows-like 行为,您需要将它们全部设置为 read-only(即删除所有写入权限);这与 WINE 在 Linux 上所做的相同。这在 QB64 中并不简单,因为有多少 POSIX 留给了实现(例如 st_mode
出现在 struct stat
中),所以你想写一个 DECLARE LIBRARY
header 如果您希望它在 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&
可以 return 一个错误值),您可以创建一个与 makeReadOnlyPOSIX
:
#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
我提到这一点只是因为在 pre-compilation 指令之间不拆分逻辑更容易维护一些东西,更不用说没有重复了。
我目前有这段代码可以在 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
POSIX 拥有三个基本权限集:
- 用户: 文件的所有者,
- 组:文件的组(通常只是文件所有者所属的主要组,但并非总是如此),并且
- other (a.k.a. "world"):文件组之外的任何人也不是文件所有者
假设您想要 Windows-like 行为,您需要将它们全部设置为 read-only(即删除所有写入权限);这与 WINE 在 Linux 上所做的相同。这在 QB64 中并不简单,因为有多少 POSIX 留给了实现(例如 st_mode
出现在 struct stat
中),所以你想写一个 DECLARE LIBRARY
header 如果您希望它在 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&
可以 return 一个错误值),您可以创建一个与 makeReadOnlyPOSIX
:
#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
我提到这一点只是因为在 pre-compilation 指令之间不拆分逻辑更容易维护一些东西,更不用说没有重复了。