2013-08-12 8 views
6

Mam program, który otwiera plik i sprawdza jego długość.plain C: otwarcie katalogu z fopen()

FILE* fd = fopen(argv[1], "rb"); 
fseek(fd, 0, SEEK_END); 
size_t flen = ftell(fd); 
if (flen == ((size_t)-1)) { 
    printf("%s is a directory.\n", argv[1]); 
    fclose(fd); 
    exit(1); 
} 

Teraz, przynajmniej pod Linuksem, fopen() zwraca poprawny deskryptor pliku podczas otwierania katalogu. Powoduje to, że operacja wyszukiwania zwraca -1 (lub, jako size_t, jest niepodpisana, 0xFFFFFFFF w systemie 64-bitowym).

Niestety, warunek w powyższym kodzie (flen == ((size_t)-1)) nie przechwytuje tego przypadku, podobnie jak flen == 0xFFFFFFFF. printf() - Polecenia z %x ord %d jako ciąg formatu pokazują, że obie strony porównania powinny mieć tę samą wartość.

Dlaczego operator porównania zachowuje się w tak dziwny sposób, nawet jeśli obie strony są tego samego typu (size_t)? Używam gcc 4.8.1 jako kompilatora.

+0

Nie masz na myśli 'sizeof (size_t) -1'? –

+0

@ScottyBauer Nie, nie robi tego. –

+7

Również "PLIK fd" ma postać "PLIKU * fd". A 'ftell()' nie zwraca 'size_t', ale' long'. I -1 nie jest '0xFFFFFFFF' na 64-bitowym (przy założeniu uzupełnienia 2), ale' 0xFFFFFFFFFFFFFFFF'. –

Odpowiedz

3

Katalogi nie istnieją w standardzie C99 (lub C2011). Tak więc z definicji katalog jest albo specyficzny dla implementacji, albo niezdefiniowany.

fopen(3) może zawieść (dając wynik NULL). fseek(3) może również zawieść (zwracając -1). A następnie należy najlepiej sprawdzić errno(3) lub użyj perror(3)

ftell jest udokumentowana zwrócić long i -1L na niepowodzenie. W 64-bitowym systemie Linux jest to 0xffffffffffffffff.

kod

Powinieneś być zamiast

FILE* fd = fopen(argv[1], "rb"); 
if (!fd) 
    { perror(argv[1]); exit(EXIT_FAILURE); }; 
if (fseek(fd, 0, SEEK_END)<0) 
    { perror("fseek"); exit(EXIT_FAILURE); }; 
long flen = ftell(fd); 
if (flen == -1L) 
    { perror("ftell"); exit(EXIT_FAILURE); }; 

BTW Na Linux/Debian/Sid/AMD64 z libc-2.17 i jądra 3.10.6, że kody działa ok, gdy argv[1] jest /tmp; surprizingly, flen jest LONG_MAX tj 0x7fffffffffffffff

BTW, na Linuksie, katalogi są szczególnym przypadkiem plików. Użyj ścieżki stat(2) na ścieżce pliku (i fstat na file descriptor, prawdopodobnie uzyskanej z fileno(3) z niektórych FILE*), aby poznać więcej metadanych dotyczących jakiegoś pliku, w tym jego "typ" (przez jego tryb). Chcesz, aby opendir(3), działał na zawartości katalogu. Zobacz także inode(7).

+0

Dziękuję, rozwiązałem go teraz, zmieniając typ flen na 'int32_t', który działa dobrze w systemach 32- i 64-bitowych (użycie' long' nie działało w systemach 64-bitowych). – Heinrich

6

Od http://pubs.opengroup.org/onlinepubs/7908799/xsh/fopen.html:

The fopen() function will fail if: 
[EISDIR] The named file is a directory and mode requires write access. 

Przynajmniej na Linuksie, jeśli spróbujesz fopen("dirname", "wb") pojawi się błąd EISDIR. Próbowałem również z katalogiem z prawami dostępu d -------- i nadal otrzymuję EISDIR (a nie EACCES).

Powiązane problemy