| /* 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; |
| } |