blob: 6623ed99cdb43fc35c079eb4ca0f0b2949d0ed3f [file] [log] [blame] [raw]
/* A part of the Native C Library for Windows NT
Copyright 2007-2015 PC GO Ld.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*/
#define _DIRENT_IMPLEMENTATION
#include <dirent.h>
#include <unistd.h>
#include <windows.h>
#include <nt.h>
#include <errno.h>
#include <fcntl.h>
#include <wchar.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ntstatus.h>
static unsigned char file_attribute_to_dirent_type(unsigned long int attrib) {
if(attrib & FILE_ATTRIBUTE_DIRECTORY) return DT_DIR;
return DT_REG;
}
static unsigned char object_type_name_to_dirent_type(const UNICODE_STRING *type_name) {
//printf("nativelibc debug: object_type_name_to_dirent_type: type_name: ");
//NtDisplayString((UNICODE_STRING *)type_name);
//putchar('\n');
if(wcscmp(type_name->Buffer, L"Device") == 0) return DT_CHR; // XXX
if(wcscmp(type_name->Buffer, L"Directory") == 0) return DT_DIR;
if(wcscmp(type_name->Buffer, L"File") == 0) return DT_REG;
if(wcscmp(type_name->Buffer, L"SymbolicLink") == 0) return DT_LNK;
return DT_UNKNOWN;
}
DIR *opendir(const char *name) {
//printf("function: opendir(%p<%s>)\n", name, name);
DIR *r = malloc(sizeof(DIR));
if(!r) return NULL;
int fd = open(name, O_RDONLY | O_DIRECTORY);
if(fd == -1) {
free(r);
return NULL;
}
r->handle = (void *)fd;
size_t oti_size = sizeof(OBJECT_TYPE_INFORMATION) + 64;
OBJECT_TYPE_INFORMATION *oti = malloc(oti_size);
if(!oti) {
free(r);
return NULL;
}
long int status = NtQueryObject(r->handle, ObjectTypeInformation, oti, oti_size, NULL);
//printf("nativelibc debug: opendir: status = 0x%lx\n", status);
if(status < 0) {
free(r);
__set_errno_from_ntstatus(status);
return NULL;
}
if(wcscmp(oti->TypeName.Buffer, L"Directory") == 0) r->type = _DIRTYPE_OBJECT;
else if(wcscmp(oti->TypeName.Buffer, L"File") == 0) r->type = _DIRTYPE_FILE;
else {
// This is not expected, because the open function is already open a directory successfully
puts("nativelibc warning: opendir: unexpected object type!");
free(oti);
free(r);
errno = ENOTDIR;
return NULL;
}
free(oti);
r->context = 0;
r->dir.d_ino = 0;
r->dir.d_reclen = sizeof r->dir;
*r->dir.d_name = 0;
return r;
}
struct dirent *readdir(DIR *dirp) {
//printf("function: readdir(%p)\n", dirp);
if(!dirp) {
errno = EBADF;
return NULL;
}
if(dirp->type == _DIRTYPE_OBJECT) {
size_t odi_size = sizeof(OBJECT_DIRECTORY_INFORMATION) + 512;
OBJECT_DIRECTORY_INFORMATION *odi = malloc(odi_size);
if(!odi) {
errno = ENOMEM;
return NULL;
}
long int status = NtQueryDirectoryObject(dirp->handle, odi, odi_size, 1, 0, &dirp->context, NULL);
//printf("nativelibc debug: readdir: status = 0x%lx\n", status);
if(status == STATUS_NO_MORE_ENTRIES) {
free(odi);
return NULL;
}
if(status < 0) {
free(odi);
__set_errno_from_ntstatus(status);
return NULL;
}
size_t len = odi->Name.MaximumLength;
if(len > 255) {
dirp->dir.d_name[255] = 0;
len = 255;
}
wcstombs(dirp->dir.d_name, odi->Name.Buffer, len);
dirp->dir.d_type = object_type_name_to_dirent_type(&odi->TypeName);
free(odi);
} else if(dirp->type == _DIRTYPE_FILE) {
IO_STATUS_BLOCK io_status;
//size_t fifdi_size = sizeof(FILE_ID_FULL_DIRECTORY_INFORMATION) + 512;
//FILE_ID_FULL_DIRECTORY_INFORMATION *fifdi = malloc(fifdi_size);
size_t fdi_size = sizeof(FILE_DIRECTORY_INFORMATION) + 512;
FILE_DIRECTORY_INFORMATION *fdi = malloc(fdi_size);
if(!fdi) {
errno = ENOMEM;
return NULL;
}
long int status = NtQueryDirectoryFile(dirp->handle, NULL, NULL, NULL, &io_status, fdi, fdi_size, FileDirectoryInformation, 1, NULL, dirp->context == 0);
//printf("nativelibc debug: readdir: status = 0x%lx\n", status);
if(status == STATUS_NO_MORE_ENTRIES) {
free(fdi);
return NULL;
}
if(status < 0) {
free(fdi);
__set_errno_from_ntstatus(status);
return NULL;
}
dirp->context++;
size_t len = fdi->FileNameLength;
if(len > 255) len = 255;
fdi->FileName[len / sizeof(wchar_t)] = 0;
wcstombs(dirp->dir.d_name, fdi->FileName, len);
//dirp->dir.d_name[cc < 0 ? 0 : len] = 0;
dirp->dir.d_type = file_attribute_to_dirent_type(fdi->FileAttributes);
//dirp->dir.d_ino = fifdi->FileId.QuadPart;
free(fdi);
} else {
errno = EBADF;
return NULL;
}
//dirp->index++;
return &dirp->dir;
}
int closedir(DIR *dirp) {
//printf("function: closedir(%p)\n", dirp);
if(!dirp) {
errno = EBADF;
return -1;
}
NtClose(dirp->handle);
//free(dirp->data);
return 0;
}
void rewinddir(DIR *dirp) {
if(!dirp) return;
dirp->context = 0;
}
long int telldir(DIR *dirp) {
if(!dirp) {
errno = EBADF;
return -1;
}
return dirp->context;
}
void seekdir(DIR *dirp, long int offset) {
if(!dirp) return;
if(offset < 0) return;
switch(dirp->type) {
case _DIRTYPE_OBJECT:
dirp->context = offset;
return;
case _DIRTYPE_FILE:
rewinddir(dirp);
while(dirp->context < offset && readdir(dirp));
return;
}
}