Преобразование вызова библиотеки Windows в POSIX для совместимости с Linux - PullRequest
0 голосов
/ 10 мая 2018

В настоящее время у меня есть этот фрагмент кода, который работает в 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 голосов
/ 24 июня 2018

POSIX имеет три основных набора разрешений:

  • пользователь : владелец файла,
  • group : группа файла (часто только основная группа, к которой принадлежит владелец файла, но не всегда), и
  • прочее (a.k.a. "world"): любой, кто не принадлежит группе файла, но также не является владельцем файла

Предполагая, что вам нужно поведение, подобное 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 (см. Пример 2, POSIX C приблизительный эквивалент вашего кода).

Чтобы исправить проблему, просто получите и установите права доступа к файлу. Если в какой-то момент возникает проблема, вы можете либо проверить, существует ли файл, и напечатать сообщение об ошибке (или распечатать, что он не существует), либо просто напечатать сообщение об ошибке, независимо от того, существует файл или нет. Чтобы очистить код в Windows (поскольку GetFileAttributes& может возвращать значение ошибки), вы можете создать функцию C, которая делает то же самое, что и 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

Конечно, я предполагаю, что $IF ... THEN, $ELSE и т. Д. В QB64 работают подобно препроцессору 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

Я упоминаю об этом только потому, что легче поддерживать что-то, где логика не разделена между директивами перед компиляцией, не говоря уже об отсутствии дублирования.

...