2017-01-20 9 views
3

POSIX関数S_ISDIRが時折私に嘘をつきます。
明らかにそうでない場合、ディレクトリが存在することを私に伝えています。S_ISDIR()は、ディレクトリが存在しないときにディレクトリを示す理由は何ですか?

$./main 
-1 false 
-1 false 
-1 false 
-1 false 
-1 false 
-1 false 
$./main 
-1 false 
-1 false 
-1 true 
-1 true 
-1 true 
-1 true 
$./main 
-1 false 
-1 false 
-1 false 
-1 false 
-1 false 
-1 false 

:私は(多くの時間)は、このプログラムを実行する場合

#include <sys/stat.h> 
#include <dirent.h> 
#include <string> 
#include <iostream> 
#include <iomanip> 

bool Is_Directory(const char* path_to_file){ 
    struct stat fileInfo; 
    std::cout << lstat(path_to_file, &fileInfo) << " "; 
    return S_ISDIR(fileInfo.st_mode); 
} 

int main(){ 

    std::cout << std::boolalpha; 
    std::cout << Is_Directory("folder") << '\n'; 
    std::cout << Is_Directory("folder") << '\n'; 
    std::cout << Is_Directory("folder") << '\n'; 
    std::cout << Is_Directory("folder") << '\n'; 
    std::cout << Is_Directory("folder") << '\n'; 
    std::cout << Is_Directory("folder") << '\n'; 
} 

は、非常に迅速に、私は次の出力が表示されます。ここでは

は、問題を説明する小さなプログラムですディレクトリが存在しない場合でも、関数が突然 trueを返す方法を参照してください。

奇妙なことですが、プログラムを無限ループでチェックすると、ディレクトリが存在しないと言うことになります。プログラムを何度も繰り返し実行するだけで、問題が発生します。ここで

は、私がこれまで試したものです:

チェックコード: は、コードが間違っていないようです。

Macro: int S_ISDIR (mode_t m) 
This macro returns non-zero if the file is a directory. 

lstatのエラーコードは常に-1ですので、私は時折エラー移入スタットはないと思います。私はlstatにある以下のドキュメントを見 :

は、ドキュメントを読んで

lstat() is identical to stat(), except that if pathname is a symbolic 
     link, then it returns information about the link itself, not the file 
     that it refers to. 

を私はまさにこのことの意味を理解していないが、多分それは私の問題に関連しますか?
代わりに普通のstat()を使用することに決めましたが、私はまだ同じ問題を抱えています。

異なるコンパイラ: 私は警告とサニタイザで2種類のコンパイラを試しました。
g++およびclang++。どちらも同じ問題を呈します。

Cコンパイラでコンパイルする必要がありますか?
私はvanilla Cでプログラムを書き直しました(しかしg ++/clang ++でコンパイルしました)。

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

bool Is_Directory(const char* path_to_file){ 
    struct stat fileInfo; 
    printf("%d ",lstat(path_to_file, &fileInfo)); 
    return S_ISDIR(fileInfo.st_mode); 
} 

int main(){ 

    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
} 

突然のすべてが、問題がなくなっています。プログラムを何度も何度もやり直しますが、ディレクトリが存在しないことを常に正確に報告します。
私はC++コードに戻り、再度テストを実行します。確かに、時々間違った陽性。

システムヘッダですか?
私はC++ヘッダーをCバージョンに入れました。プログラムは問題なく動作します。

std :: coutですか?
std::coutが遅いので、私はその問題を見ているのです...または、まったく無関係かもしれません。たぶんstd::coutを使用していると間接的に問題の原因となっているバイナリに何かが残っています。または、std::coutは私のプログラムの環境に対して何かを世界的に行っていますか?
ここでは暗闇の中で撮影しています。

私は次のことを試してみました:

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

bool Is_Directory(const char* path_to_file){ 
    struct stat fileInfo; 
    printf("%d ",lstat(path_to_file, &fileInfo)); 
    return S_ISDIR(fileInfo.st_mode); 
} 

int main(){ 

    std::cout << "test" << std::endl; 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
    printf("%d\n",Is_Directory("folder")); 
} 

なるほど!

$./main 
test 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
$./main 
test 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
$./main 
test 
-1 1 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
$./main 
test 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
$./main 
test 
-1 1 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
$./main 
test 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 
-1 0 

時には最初のチェックでtrueを返すことがあります。
S_ISDIRのようなもので、S_ISDIRが呼び出された後には、S_ISDIRの次の呼び出しが混乱しないように、std::coutのようなものです。

調査ソース:
私は/usr/include/sysS_ISDIRのソースコードを発見した:

/* Test macros for file types. */ 
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask)) 
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) 

S_ISDIRは、ヘルパーが、何もないように思われ、ディレクトリが存在するかどうか、すでにから決定されていますstat()。 (繰り返しますが、私はstatlstatの両方を試してみた。私は人々が私のコード例と同じように S_ISDIRを使用しているオンラインの他の例を見つけた。私はそうは思いませんfstatを使用すると仮定アム)。

また、コードをチェックして印刷する無限ループにコードを入れても、std::coutと表示されません。

$./main 
-1 false 
-1 false 
-1 true 
-1 true 
-1 true 
-1 true 

オペレーティングシステムを/:あなたは私の元の出力を見れば、それは行っているので、問題はプログラムの開始時に発生するが、私はそれがいずれかの真実は思えない推測と信じて私につながりますハードドライブ/システムライブラリ/コンパイラ
私のコンピュータに問題がありますか? いいえ、私はUbuntu 16.04.1 LTSです。私は行って、これを別のマシンCentOS 6.5に入れて、より古いバージョンのg++で試しました。同じ結果。
私のコードは悪いです。

は、問題を切り分け:

私は問題を単純化してきました。
このプログラムにはがあります。エラーを返します。

#include <sys/stat.h> 
#include <iostream> 
bool Is_Directory(const char* path_to_file){ 
    struct stat fileInfo; 
    stat(path_to_file, &fileInfo); 
    return S_ISDIR(fileInfo.st_mode); 
} 
int main(){ 
    std::cout << std::endl; 
    return Is_Directory("folder"); 
} 

このプログラム意志決してリターンエラー。

#include <sys/stat.h> 
#include <iostream> 
bool Is_Directory(const char* path_to_file){ 
    struct stat fileInfo; 
    stat(path_to_file, &fileInfo); 
    return S_ISDIR(fileInfo.st_mode); 
} 
int main(){ 
    return Is_Directory("folder"); 
} 

なぜバッファをフラッシュすると、ディレクトリが存在することがありますか?
実際には、バッファをフラッシュするだけで問題はなくなります。

このプログラム意志決してリターンエラー。

#include <sys/stat.h> 
#include <iostream> 
bool Is_Directory(const char* path_to_file){ 
    struct stat fileInfo; 
    stat(path_to_file, &fileInfo); 
    return S_ISDIR(fileInfo.st_mode); 
} 
int main(){ 
    std::cout.flush(); 
    return Is_Directory("folder"); 
} 

これはおそらく何もフラッシュしていないためです。

少なくとも1文字をフラッシュしている限り、私たちは再び問題を抱えています。ここ
は本当のMCVEです:

#include <sys/stat.h> 
#include <iostream> 
int main(){ 
    std::cout << std::endl; 
    struct stat fileInfo; 
    stat("f", &fileInfo); 
    return S_ISDIR(fileInfo.st_mode); 
} 

ここでも、無限ループが動作しません。 (それが最初の試行での幸運を取得仮定)
このプログラムはなり決してリターン:プロセスと同様にフラッシュを再起動したときに

#include <sys/stat.h> 
#include <iostream> 
int main(){ 
    while (true){ 
     std::cout << std::endl; 
     struct stat fileInfo; 
     stat("f", &fileInfo); 
     if(S_ISDIR(fileInfo.st_mode)) return 0; 
    } 
} 

ので、問題が発生しますか?
私はアセンブリをダンプしましたが、それは私にはあまり意味がありません。

g++ -std=c++1z -g -c a.cpp 
objdump -d -M intel -S a.o > a.s 

a.o:  file format elf64-x86-64 


Disassembly of section .text: 

0000000000000000 <main>: 
#include <sys/stat.h> 
#include <iostream> 
int main(){ 
    0: 55      push rbp 
    1: 48 89 e5    mov rbp,rsp 
    4: 48 81 ec a0 00 00 00 sub rsp,0xa0 
    b: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 
    12: 00 00 
    14: 48 89 45 f8    mov QWORD PTR [rbp-0x8],rax 
    18: 31 c0     xor eax,eax 
    std::cout << std::endl; 
    1a: be 00 00 00 00   mov esi,0x0 
    1f: bf 00 00 00 00   mov edi,0x0 
    24: e8 00 00 00 00   call 29 <main+0x29> 
    struct stat fileInfo; 
    stat("f", &fileInfo); 
    29: 48 8d 85 60 ff ff ff lea rax,[rbp-0xa0] 
    30: 48 89 c6    mov rsi,rax 
    33: bf 00 00 00 00   mov edi,0x0 
    38: e8 00 00 00 00   call 3d <main+0x3d> 
    return S_ISDIR(fileInfo.st_mode); 
    3d: 8b 85 78 ff ff ff  mov eax,DWORD PTR [rbp-0x88] 
    43: 25 00 f0 00 00   and eax,0xf000 
    48: 3d 00 40 00 00   cmp eax,0x4000 
    4d: 0f 94 c0    sete al 
    50: 0f b6 c0    movzx eax,al 
    53: 48 8b 55 f8    mov rdx,QWORD PTR [rbp-0x8] 
    57: 64 48 33 14 25 28 00 xor rdx,QWORD PTR fs:0x28 
    5e: 00 00 
    60: 74 05     je  67 <main+0x67> 
    62: e8 00 00 00 00   call 67 <main+0x67> 
    67: c9      leave 
    68: c3      ret  

0000000000000069 <_Z41__static_initialization_and_destruction_0ii>: 
    69: 55      push rbp 
    6a: 48 89 e5    mov rbp,rsp 
    6d: 48 83 ec 10    sub rsp,0x10 
    71: 89 7d fc    mov DWORD PTR [rbp-0x4],edi 
    74: 89 75 f8    mov DWORD PTR [rbp-0x8],esi 
    77: 83 7d fc 01    cmp DWORD PTR [rbp-0x4],0x1 
    7b: 75 27     jne a4 <_Z41__static_initialization_and_destruction_0ii+0x3b> 
    7d: 81 7d f8 ff ff 00 00 cmp DWORD PTR [rbp-0x8],0xffff 
    84: 75 1e     jne a4 <_Z41__static_initialization_and_destruction_0ii+0x3b> 
    extern wostream wclog; /// Linked to standard error (buffered) 
#endif 
    //@} 

    // For construction of filebuffers for cout, cin, cerr, clog et. al. 
    static ios_base::Init __ioinit; 
    86: bf 00 00 00 00   mov edi,0x0 
    8b: e8 00 00 00 00   call 90 <_Z41__static_initialization_and_destruction_0ii+0x27> 
    90: ba 00 00 00 00   mov edx,0x0 
    95: be 00 00 00 00   mov esi,0x0 
    9a: bf 00 00 00 00   mov edi,0x0 
    9f: e8 00 00 00 00   call a4 <_Z41__static_initialization_and_destruction_0ii+0x3b> 
    a4: 90      nop 
    a5: c9      leave 
    a6: c3      ret  

00000000000000a7 <_GLOBAL__sub_I_main>: 
    a7: 55      push rbp 
    a8: 48 89 e5    mov rbp,rsp 
    ab: be ff ff 00 00   mov esi,0xffff 
    b0: bf 01 00 00 00   mov edi,0x1 
    b5: e8 af ff ff ff   call 69 <_Z41__static_initialization_and_destruction_0ii> 
    ba: 5d      pop rbp 
    bb: c3      ret  

私はstatソースコードに従ってみましたが、やや失われました。
C++ソースコードのほうが少し面白かったです。

template<typename _CharT, typename _Traits> 
    basic_ostream<_CharT, _Traits>& 
    basic_ostream<_CharT, _Traits>:: 
    flush() 
    { 
     // _GLIBCXX_RESOLVE_LIB_DEFECTS 
     // DR 60. What is a formatted input function? 
     // basic_ostream::flush() is *not* an unformatted output function. 
     ios_base::iostate __err = ios_base::goodbit; 
     __try 
    { 
     if (this->rdbuf() && this->rdbuf()->pubsync() == -1) 
     __err |= ios_base::badbit; 
    } 
     __catch(__cxxabiv1::__forced_unwind&) 
    { 
     this->_M_setstate(ios_base::badbit);  
     __throw_exception_again; 
    } 
     __catch(...) 
    { this->_M_setstate(ios_base::badbit); } 
     if (__err) 
    this->setstate(__err); 
     return *this; 
    } 

pubsync()を呼び出すように思わ/ext/stdio_sync_filebuf.hsync()方法に私を導く:ここ/bits/ostream.tccからフラッシュ機能がある

 sync() 
     { return std::fflush(_M_file); } 

     virtual std::streampos 
     seekoff(std::streamoff __off, std::ios_base::seekdir __dir, 
      std::ios_base::openmode = std::ios_base::in | std::ios_base::out) 
     { 
    std::streampos __ret(std::streamoff(-1)); 
    int __whence; 
    if (__dir == std::ios_base::beg) 
     __whence = SEEK_SET; 
    else if (__dir == std::ios_base::cur) 
     __whence = SEEK_CUR; 
    else 
     __whence = SEEK_END; 
#ifdef _GLIBCXX_USE_LFS 
    if (!fseeko64(_M_file, __off, __whence)) 
     __ret = std::streampos(ftello64(_M_file)); 
#else 
    if (!fseek(_M_file, __off, __whence)) 
     __ret = std::streampos(std::ftell(_M_file)); 
#endif 
    return __ret; 
     } 

     virtual std::streampos 
     seekpos(std::streampos __pos, 
      std::ios_base::openmode __mode = 
      std::ios_base::in | std::ios_base::out) 
     { return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); } 
    };  sync() 
     { return std::fflush(_M_file); } 

     virtual std::streampos 
     seekoff(std::streamoff __off, std::ios_base::seekdir __dir, 
      std::ios_base::openmode = std::ios_base::in | std::ios_base::out) 
     { 
    std::streampos __ret(std::streamoff(-1)); 
    int __whence; 
    if (__dir == std::ios_base::beg) 
     __whence = SEEK_SET; 
    else if (__dir == std::ios_base::cur) 
     __whence = SEEK_CUR; 
    else 
     __whence = SEEK_END; 
#ifdef _GLIBCXX_USE_LFS 
    if (!fseeko64(_M_file, __off, __whence)) 
     __ret = std::streampos(ftello64(_M_file)); 
#else 
    if (!fseek(_M_file, __off, __whence)) 
     __ret = std::streampos(std::ftell(_M_file)); 
#endif 
    return __ret; 
     } 

     virtual std::streampos 
     seekpos(std::streampos __pos, 
      std::ios_base::openmode __mode = 
      std::ios_base::in | std::ios_base::out) 
     { return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); } 
    }; 

私の知る限り、C++が作業を行う農業れますstd::fflush

いくつかのより多くのテストを行った後、私は fflush()<iostream>から展示の問題が、<stdio.h>からfflush()がないことを発見しました。

私はfflush()から後方にトレースしようとしましたが、私はソースコード境界に当たったと思います。

This function is a possible cancellation point and therefore not 
    marked with __THROW. */ 
extern int fflush (FILE *__stream); 
__END_NAMESPACE_STD 

#ifdef __USE_MISC 
/* Faster versions when locking is not required. 

    This function is not part of POSIX and therefore no official 
    cancellation point. But due to similarity with an POSIX interface 
    or due to the implementation it is a cancellation point and 
    therefore not marked with __THROW. */ 
extern int fflush_unlocked (FILE *__stream); 
#endif 

これは私がリンクしているものである必要がありますか?

//exhibits the problem 
#include <sys/stat.h> 
#include <iostream> 
int main(){ 
    printf("\n");fflush(stdout); 
    struct stat fileInfo; 
    stat("f", &fileInfo); 
    return S_ISDIR(fileInfo.st_mode); 
} 

g++ -std=c++11 -o main a.cpp 
ldd main 
linux-vdso.so.1 => (0x00007ffdc878e000) 
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1300c00000) 
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1300837000) 
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f130052d000) 
/lib64/ld-linux-x86-64.so.2 (0x000055bace4bc000) 
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1300316000) 

//works correctly 
#include <sys/stat.h> 
#include <stdio.h> 
int main(){ 
    printf("\n");fflush(stdout); 
    struct stat fileInfo; 
    stat("f", &fileInfo); 
    return S_ISDIR(fileInfo.st_mode); 
} 

g++ -std=c++11 -o main a.cpp 
ldd main 
linux-vdso.so.1 => (0x00007ffd57f7c000) 
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f482dc6c000) 
/lib64/ld-linux-x86-64.so.2 (0x000055828633a000) 

私はlibstdc++.so.6S_ISDIRを使用するのに適していないが、libc.so.6があると仮定?別にS_ISDIRを使用するコードを作成して、それをC++コードとリンクする必要がありますか?このような問題を早期に発見するにはどうしたらいいでしょうか?私はまだ何が起こっているのか分からない。私は間違ったライブラリをリンクしたため、間違ったメモリを踏みにじる/観察していますか?これを解決するにはどうしたらいいですか?

+1

システムコールが成功した場合にのみ、 'lstat()'によって返されるモードを分析することができます。失敗した場合、 '-1'を返します(値は不定ですが、おそらく' fileInfo'のデータは変更されていません)。 'fileInfo.st_mode'で取得するのは、' lstat() 'が失敗したためにガベージです。' S_ISDIR() 'が真偽でtrueまたはfalseを返すことができます。 –

+2

'lstat'が-1を返した場合、初期化されていない' struct stat'に対して 'S_ISDIR'を実行しています。 –

+0

_ "lstatのエラーコードは常に-1なので、時折エラーが発生しているとは思われません。" –

答えて

3

データセットは、lstat()でのみ解析できます。システムコールが成功した場合のみです。失敗した場合は-1を返します(値は不定ですが、おそらくfileInfoのデータは変更されていません)。 fileInfo.st_modeで取得するものは、lstat()が失敗してゴミになっています.S_ISDIR()についてはtrueまたはfalseを返すことができます。

このように、最初の例では、毎回lstat()が失敗することが示されているため、struct statの分析は無駄です。それは決まった値に設定されておらず、結果はOKです。

同じ引数がすべてのサンプルコードに当てはまると私は信じている。


stat()lstat()の差が設けられて名前がシンボリックリンクである場合、stat()システムコールは、一方が存在すると仮定すると(シンボリックリンクの遠端でファイル・システム・オブジェクトを参照することであり、それが失敗した場合symlinkは存在しないオブジェクトを指しています)、lstat()システムコールはシンボリックリンク自体を参照しています。名前がシンボリックリンクでない場合、2つの呼び出しは同じ情報を返します。

+1

確かに。バッファリングをストリーミングしたり、バッファをフラッシュしたり、UBのように初期化されていないメモリに単に影響を与えているものを流すことの効果。私はOPのために少し悪いと感じる。なぜなら、この問題に取り組んできた非常に徹底的な研究/デバッグであり、マニュアルを誤読していること、エラーリターンコードの慣習を忘れていること、そして最初の前提'-1'は" ok "条件です)は保持されません。 –

+1

さて、少なくとも彼はプログラミングの最初の*ルールを学んだ... https://blog.codinghorror.com/the-first-rule-of-programming-its-always-your-fault/ –

+0

@LightnessRacesinOrbitええ、私はhttp://www.virtsync.com/c-error-codes-include-errnoを見て、リストに-1が見当たらなかったので、間違いなくエラーがないと思ってしまった。レール。ありがとう。 –

関連する問題