blob: 299bbb2006aeb0b3daa956d295e012d32f603a79 [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.
*/
#include <windows.h>
#include <nt.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <pathname.h>
#include <ntstatus.h>
#include <assert.h>
#include <stdio.h> // For debug messages
int vopen(const char *pathname, int flags, va_list ap) {
unsigned long int fileaccess;
switch(flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
case O_RDONLY:
fileaccess = GENERIC_READ;
break;
case O_WRONLY:
fileaccess = GENERIC_WRITE;
break;
case O_RDWR:
fileaccess = GENERIC_READ | GENERIC_WRITE;
break;
default:
errno = EINVAL;
return -1;
}
if(flags & O_APPEND) {
if(!(fileaccess & GENERIC_WRITE)) {
errno = EINVAL;
return -1;
}
fileaccess = FILE_APPEND_DATA;
}
if(!(flags & O_NONBLOCK)) fileaccess |= SYNCHRONIZE;
unsigned long int file_create_disposition;
switch(flags & (O_CREAT | O_EXCL | O_TRUNC)) {
case 0:
case O_EXCL: // O_EXCL without O_CREAT: ignored
// Open file if it exist, or fail if not
file_create_disposition = FILE_OPEN;
break;
case O_CREAT:
// Open file if it exist, or create it if not
file_create_disposition = FILE_OPEN_IF;
break;
case O_CREAT | O_EXCL:
case O_CREAT | O_TRUNC | O_EXCL:
// Create file if it not exist, otherwise fail
file_create_disposition = FILE_CREATE;
break;
case O_TRUNC:
case O_TRUNC | O_EXCL: // O_EXCL without O_CREAT: ignored
// Open and truncate file if it exist, or fail if not
file_create_disposition = FILE_OVERWRITE;
break;
case O_CREAT | O_TRUNC:
// Open and truncate file if it exist, or create it if not
file_create_disposition = FILE_OVERWRITE_IF;
break;
default:
/* this can't happen ... all cases are covered */
puts(__FILE__": Unexpected code path");
return -1;
}
unsigned long int fileshare = FILE_SHARE_READ | FILE_SHARE_DELETE;
unsigned long int fileattrib = 0;
if(flags & O_CREAT) {
int pmode = va_arg(ap, int);
if((pmode & S_IWRITE) != S_IWRITE) fileattrib |= FILE_ATTRIBUTE_READONLY;
}
if(!fileattrib) fileattrib = FILE_ATTRIBUTE_NORMAL;
unsigned long int file_create_options = 0;
if(!(flags & O_NONBLOCK)) {
if(flags & O_ASYNC) file_create_options |= FILE_SYNCHRONOUS_IO_ALERT;
else file_create_options |= FILE_SYNCHRONOUS_IO_NONALERT;
}
if(flags & O_DIRECTORY) {
if(flags & O_CREAT) {
// XXX
//errno = EISDIR;
errno = EINVAL;
return -1;
}
file_create_options |= FILE_DIRECTORY_FILE;
}
if(fileaccess & GENERIC_WRITE) file_create_options |= FILE_NON_DIRECTORY_FILE;
// If both FILE_DIRECTORY_FILE and FILE_NON_DIRECTORY_FILE are defined in file_create_options,
// the NtCreateFile will returns STATUS_INVALID_PARAMETER; that is caller's fault.
if(flags & O_RANDOM) file_create_options |= FILE_RANDOM_ACCESS;
else if(flags & O_SEQUENTIAL) file_create_options |= FILE_SEQUENTIAL_ONLY;
if(flags & O_TEMPORARY) file_create_options |= FILE_DELETE_ON_CLOSE;
if(flags & O_DIRECT) file_create_options |= FILE_NO_INTERMEDIATE_BUFFERING;
if(flags & O_NOFOLLOW) file_create_options |= FILE_OPEN_REPARSE_POINT;
int request_dir_in_path = 0;
UNICODE_STRING ntpathname;
if(!RtlCreateUnicodeStringFromAsciiz(&ntpathname, pathname)) {
errno = ENOMEM;
return -1;
}
if(ntpathname.Buffer[ntpathname.Length / sizeof(wchar_t) - 1] == L'/') {
if((flags & O_CREAT) || (fileaccess & GENERIC_WRITE)) {
RtlFreeUnicodeString(&ntpathname);
errno = EISDIR;
return -1;
}
request_dir_in_path = 1;
}
PATHNAME_UNIX2NT_UTF16_STRUCT(ntpathname);
OBJECT_ATTRIBUTES object_attrib = { sizeof(OBJECT_ATTRIBUTES), NULL, &ntpathname, OBJ_INHERIT, NULL, NULL };
void *r;
if(!(flags & O_CREAT) || !(flags & O_EXCL)) {
// If caller don't want to create a file, we will try to open this file in some special ways
if((flags & O_NOFOLLOW) && !request_dir_in_path) {
long int status = NtOpenSymbolicLinkObject(&r, fileaccess & ~SYNCHRONIZE, &object_attrib);
printf("nativelibc debug: open: status = 0x%lx\n", status);
if(status != STATUS_OBJECT_TYPE_MISMATCH &&
(!(flags & O_CREAT) || status != STATUS_OBJECT_NAME_NOT_FOUND)) {
RtlFreeUnicodeString(&ntpathname);
return __set_errno_from_ntstatus(status) ? -1 : (int)r;
}
}
UNICODE_STRING path;
if(request_dir_in_path && ntpathname.Length > sizeof(wchar_t)) {
path.Length = ntpathname.Length - sizeof(wchar_t);
path.MaximumLength = ntpathname.MaximumLength - sizeof(wchar_t);
path.Buffer = malloc(path.MaximumLength);
if(!path.Buffer) {
RtlFreeUnicodeString(&ntpathname);
errno = ENOMEM;
return -1;
}
memcpy(path.Buffer, ntpathname.Buffer, path.Length);
path.Buffer[path.Length / sizeof(wchar_t)] = 0;
object_attrib.ObjectName = &path;
}
// Try to open it as a directory object
long int status = NtOpenDirectoryObject(&r, fileaccess & ~SYNCHRONIZE, &object_attrib);
printf("nativelibc debug: open: status = 0x%lx, request_dir_in_path = %d\n", status, request_dir_in_path);
if(status != STATUS_OBJECT_TYPE_MISMATCH && (!(flags & O_CREAT) || status != STATUS_OBJECT_NAME_NOT_FOUND)) {
if(request_dir_in_path) RtlFreeUnicodeString(&path);
RtlFreeUnicodeString(&ntpathname);
if(fileaccess & GENERIC_WRITE) {
if(status >= 0) NtClose(r);
errno = EISDIR;
return -1;
}
return __set_errno_from_ntstatus(status) ? -1 : (int)r;
}
if(request_dir_in_path) {
RtlFreeUnicodeString(&path);
object_attrib.ObjectName = &ntpathname;
}
}
IO_STATUS_BLOCK io_status;
long int status = NtCreateFile(&r, fileaccess, &object_attrib, &io_status, NULL, fileattrib, fileshare, file_create_disposition, file_create_options, NULL, 0);
printf("nativelibc debug: open: status = 0x%lx\n", status);
if(status == STATUS_OBJECT_TYPE_MISMATCH && (flags & O_CREAT) && (flags & O_EXCL)) {
RtlFreeUnicodeString(&ntpathname);
errno = EEXIST;
return -1;
}
if(status == STATUS_OBJECT_NAME_INVALID && request_dir_in_path && !(flags & O_CREAT)) {
void *handle;
long int status = NtCreateFile(&handle, fileaccess, &object_attrib, &io_status, NULL, 0, fileshare, FILE_OPEN, FILE_DIRECTORY_FILE, NULL, 0);
printf("nativelibc debug: open: inner status = 0x%lx\n", status);
if(status == STATUS_NOT_A_DIRECTORY) {
RtlFreeUnicodeString(&ntpathname);
errno = ENOTDIR;
return -1;
}
if(status >= 0) NtClose(handle);
}
RtlFreeUnicodeString(&ntpathname);
return __set_errno_from_ntstatus(status) ? -1 : (int)r;
}
int open(const char *pathname, int flags, ...) {
va_list ap;
va_start(ap, flags);
int r = vopen(pathname, flags, ap);
va_end(ap);
return r;
}