Почему stat :: st_size 0 для устройств, но в то же время lseek правильно определяет размер устройства? - PullRequest
13 голосов
/ 14 марта 2019

Я заметил, что когда я запрашиваю размер устройства, используя open + lseek, все в порядке, но когда я stat устройство, я получаю ноль вместо реального размера устройства.Устройство чистое без какой-либо файловой системы, и первые байты устройства начинаются с некоторого текста, такого как «1234567890ABC».Что не так?

Код:

#include <sys/stat.h>
#include <dirent.h>

bool
GetFileSize(const char* pPath, uint64_t& Size)
{
    pPath = "/home/sw/.bashrc";
    pPath = "/dev/sda";

    struct stat buffer;
    if (stat(pPath, &buffer))
    {
        printf("Failed to stat file. Error: %s. FilePath: %s\n", strerror(errno), pPath);
        return false;
    }

    printf("File size by stat: %" PRIu64 " WTF?\n", buffer.st_size);

    //
    // Note: It's strange, but stat::st_size from the stat call is zero for devices
    //

    int File = open(pPath, O_RDONLY);
    if (File < 0)
    {
        printf("Failed to open file. Error: %s. FilePath: %s\n", strerror(errno), pPath);
        return false;
    }

    long off = lseek(File, 0, SEEK_END);
    if (off == (off_t)-1)
    {
        printf("Failed to get file size. Error: %s. FilePath: %s\n", strerror(errno), pPath);
        close(File);
        return false;
    }
    close(File);

    printf("File size by lseek: %" PRIu64 "\n", off);
    fflush(stdout);

    Size = off;
    return true;
}

Вывод:

File size by stat: 0 Huh?
File size by lseek: 34359738368

Если я использую stat для обычного файла, то все в порядке (закомментируйте строкус "/ dev / sda"):

File size by stat: 4019 Huh?
File size by lseek: 4019

Ответы [ 5 ]

12 голосов
/ 14 марта 2019

Дьявол кроется в деталях ... Для начала, есть фундаментальный принцип дизайна Unix: все это файл , приятно объяснено здесь .

Во-вторых, вызов stat (2) дает вам inode статистику, хранящуюся в файловой системе, о специальном файле устройства , который имеет нулевой размер (воспринимайте его как lstat(2)).Если у вас есть блочное устройство с файловой системой, вы получаете информацию о нем, используя statfs(2) или getfsstat(2) или statvfs(2) независимо от файловой системы / устройства.

Работа со специальными файлами (обычнорасположение в / dev) всегда зависело от системы, а страницы руководства находятся в разделе 4. Поэтому, если вы хотите напрямую манипулировать устройством, вам следует ознакомиться с его спецификой.Например, в Linux man 4 hd покажет вам, как программно взаимодействовать с блочными устройствами IDE.Принимая во внимание, что man 4 sd покажет вам, как взаимодействовать с дисками SCSI и т. Д.

В-третьих, системные вызовы не должны быть несовместимыми по своей функциональности NOR их ограничения.

Надеюсь, это помогло.

9 голосов
/ 14 марта 2019

из этого Unix Stack Exchange вопрос:

Файлы устройств сами по себе не являются файлами.Это интерфейс ввода / вывода для использования устройств в Unix-подобных операционных системах.Они не используют места на диске, однако они все еще используют инод, как сообщается командой stat:

$ stat /dev/sda
      File: /dev/sda
      Size: 0               Blocks: 0          IO Block: 4096   block special file
Device: 6h/6d   Inode: 14628       Links: 1     Device type: 8,0

Это решает часть stat.

тот факт, чтовы можете искать в этом "файле" не связан.На самом деле это не файл, но вы можете open прочитать его.Вы тоже можете к этому стремиться.Он позволяет читать диск на самом низком уровне, поэтому поиск необходим (поэтому он работает и почему он не возвращает новую позицию, как любой «настоящий» файл?).

Согласно этот другой ответ UnixSE , вы можете узнать размер устройства, прочитав этот /dev/sda/size файл.

6 голосов
/ 14 марта 2019

Длина «устройства», такого как /dev/sda, не указана POSIX struct stat:

off_t st_size       For regular files, the file size in bytes. 

                    For symbolic links, the length in bytes of the 
                    pathname contained in the symbolic link. 

                    For a shared memory object, the length in bytes. 

                    For a typed memory object, the length in bytes. 

                    For other file types, the use of this field is 
                    unspecified. 

Таким образом, POSIX не требует "размера" дискового устройства.

Linux также не указывает, что stat() должен возвращать размер дискового устройства:

st_size

В этом поле указывается размер файла (если он обычный файл или символическая ссылка) в байтах. Размер символической ссылки длина пути, который он содержит, без окончания нулевой байт.

0 голосов
/ 14 марта 2019

lseek является основой для C fseek, поэтому он имеет аналогичную семантику, соответствующую fseek - и совершенно не связан с другими областями Unix API.Что касается провенанса, вы ожидаете, что lseek будет действовать как получение файлового дескриптора fseek, а fseek - это интерфейс C-библиотеки, который появился без привязки к Unix.

stat специфичен только для Unix и делает свое дело.Разумная разница, если вы думаете о происхождении.Конечно, проблема в том, что API C имеют очень слабые модели типов, потому что C на один шаг не позволяет обеспечить истинную безопасность типов.

Почему это важно?Потому что, по сути, seekable_size и file_object_size - это две принципиально разные концепции, которые требуют разных типов - даже стандартная библиотека C ++ ошибается.

Но в то время как в C ++ и с современными компиляторами это теперь совершенно унаследованный недостаток, в C действительно нет способа эффективно объединить целые числа в несовместимые типы без снижения производительности и читабельности кода.И, таким образом, вы получите что-то вроде offs_t или long, используемое для совершенно несовместимых понятий.И это является источником путаницы: только то, что вы получаете число, связанное с размером, из функции, связанной с файлом, не означает, что число будет иметь то же значение.А значения обычно фиксируются в типах ... Единственное значение, которое присуще long, по своей сути - это «эй, я число, вы можете делать со мной числовые вещи»…: (

0 голосов
/ 14 марта 2019

В Linux документированный способ получения размера устройства с необработанным диском, который вы можете открыть, - это BLKGETSIZE ioctl. См. Справочную страницу sd(4).

Обратите внимание, что это возвращает размер устройства в секторах . Вы можете подумать, что для размера в байтах вам нужно умножить на значение, возвращаемое BLKSSZGET ioctl, но , если я правильно читаю исходный код , вам действительно нужно умножить на 512, нет независимо от того, что BLKSSZGET возвращает.

...