Getting file descriptor type in Windows

Background information


Windows has a C runtime library that partially implements posix. This library uses integer file descriptors that are convertable between HANDLEs. It’s functions are wrapppers around the Windows API.

Becuase of this, the _open function can open any type of file that CreateFile can open. This includes regular files, character devices (consoles, printers, serial ports) and block devices (disk drives). A HANDLE is a void pointer and a SOCKET is an unsigned integer pointer, but they can be both converted into a CRT file descriptor using _open_osfhandle.


The problem is that Windows has no decent function for determining the type of file referenced by a file descriptor or HANDLE. _stat only tells you if it’s a regular file or directory and GetFileType is not specific enough.

I made a function that queries the type of file referenced by a file descriptor:


#include <windows.h>

enum {
	FD_REG,		/* Regular file */
	FD_DIR,		/* Directory */
	FD_CHR,		/* Character device */
	FD_BLK,		/* Block device */
	FD_FIFO,	/* Pipe */
	FD_LNK,		/* Symbolic link */
	FD_SOCK		/* Socket */
};

int GetFdType(int fd) {
	HANDLE handle;
	BY_HANDLE_FILE_INFORMATION info;
	DWORD dw;
	
	/* Convert CRT file descriptor to HANDLE */
	handle = (HANDLE)_get_osfhandle(fd);
	if (handle == INVALID_HANDLE_VALUE) return 0;

	/* GetFileType vaguely tells us what kind of file it is */
	dw = GetFileType(handle);
	if (dw == FILE_TYPE_UNKNOWN) return 0;

	switch (dw) {
	/* Character device */
	case FILE_TYPE_CHAR:
		return FD_CHR;
	/* Regular file, Directory, Symbolic link, or block device */
	case FILE_TYPE_DISK:

		/* Block device if file attributes are undefined */
		if (GetFileInformationByHandle(handle, &info) == FALSE)
			return FD_BLK;

		/* File attributes can tell us what type of physical file it is */
		switch (info.dwFileAttributes) {
		case FILE_ATTRIBUTE_NORMAL: return FD_REG;
		case FILE_ATTRIBUTE_DIRECTORY: return FD_DIR;
		case FILE_ATTRIBUTE_REPARSE_POINT: return FD_LNK;
		}
		return 0;
	/* Socket or FIFO */
	case FILE_TYPE_PIPE:
		/* If we cannot query pipe info, it must be a socket */
		return GetNamedPipeInfo(handle, 0, 0, 0, 0) ?
			FD_FIFO : FD_SOCK;	
	}
	/* Should never reach here */
	return 0;	
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: