web-dev-qa-db-ja.com

デバイスのstat :: st_sizeが0であるのに、lseekがデバイスサイズを正しく定義するのはなぜですか?

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
14

悪魔は詳細にあります...まず第一に、Unixデザインの基本原則があります:すべてがファイルです、うまく 説明ここ

2つ目は、stat(2)呼び出しにより、inodeがファイルシステムに保存されているdevice-サイズがゼロの特殊ファイルlstat(2)と考える)。ファイルシステムが存在するブロックデバイスがある場合、statfs(2)またはgetfsstat(2)またはstatvfs(2)をファイルシステム/デバイスに依存しない方法で使用して、その情報を取得します。

特殊ファイル(通常は/ devにある)の処理は常にシステム固有であり、マニュアルページはセクション4にあります。したがって、デバイスを直接操作する場合は、そこで詳細を読む必要があります。たとえば、Linuxでは、man 4 hdはプログラムでIDEブロックデバイスを操作する方法を示します。一方、man 4 sdはscsiディスクなどを操作する方法を示します。

3番目に、システムコールの機能に矛盾がないこと[〜#〜] nor [〜#〜]の制限は想定されていません。

これがお役に立てば幸いです。

12
Ahmed Masud

これから nix Stack Exchange 質問:

デバイスファイル自体はファイルではありません。これらは、Unixライクなオペレーティングシステムでデバイスを使用するためのI/Oインターフェイスです。これらはディスク上のスペースを使用しませんが、statコマンドによって報告されるように、iノードを使用します。

$ 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ファイル。

_/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_

このフィールドは、ファイルのサイズ(通常のファイルまたはシンボリックリンクの場合)をバイト単位で示します。シンボリックリンクのサイズは、終端のnullバイトを含まない、リンクに含まれるパス名の長さです。

6
Andrew Henle

Linuxでは、文書化された、オープン可能なrawディスクデバイスのサイズを取得する方法は、BLKGETSIZE ioctlを使用することです。 sd(4) のマンページを参照してください。

これは、デバイスのサイズをsectorsで返すことに注意してください。バイト単位のサイズの場合、BLKSSZGET ioctlによって返された値を乗算する必要があると思うかもしれませんが、 ソースコードを正しく読んでいる場合 は、実際にBLKSSZGETが何を返しても、512を掛けます。

0
zwol