| /* 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 <sys/stat.h> |
| #include <windows.h> |
| #include <nt.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <wchar.h> |
| |
| #include <stdio.h> |
| |
| static int is_in_do(int fd) { |
| size_t oni_size = sizeof(OBJECT_NAME_INFORMATION) + 256 * 2; |
| OBJECT_NAME_INFORMATION *oni = malloc(oni_size); |
| if(!oni) return 0; |
| long int status = NtQueryObject((void *)fd, ObjectNameInformation, oni, oni_size, NULL); |
| if(status < 0) { |
| free(oni); |
| return 0; |
| } |
| /* |
| printf("nativelibc debug: object name: "); |
| NtDisplayString(&oni->Name); |
| putchar('\n'); |
| { |
| void *handle; |
| size_t len = oni->Name.Length / sizeof(wchar_t) - 1; |
| if(len && oni->Name.Buffer[len] != '\\') { |
| oni->Name.Buffer[++len] = L'\\'; |
| oni->Name.Buffer[++len] = 0; |
| oni->Name.Length = len * sizeof(wchar_t); |
| oni->Name.MaximumLength = (len + 1) * sizeof(wchar_t); |
| } |
| printf("nativelibc debug: object name: "); |
| NtDisplayString(&oni->Name); |
| putchar('\n'); |
| OBJECT_ATTRIBUTES object_attrib = { sizeof(OBJECT_ATTRIBUTES), NULL, &oni->Name, 0, NULL, NULL }; |
| status = NtOpenDirectoryObject(&handle, DIRECTORY_QUERY, &object_attrib); |
| printf("nativelibc debug: is_in_do: status = 0x%lx\n", status); |
| if(status >= 0) NtClose(handle); |
| } |
| */ |
| |
| // Truncate to upper directory |
| size_t len = oni->Name.Length / sizeof(wchar_t) - 1; |
| if(len && oni->Name.Buffer[len] == '\\') oni->Name.Buffer[len--] = 0; |
| do { |
| if(!len-- || !len) return 0; |
| } while(oni->Name.Buffer[len] != L'\\'); |
| oni->Name.Length = len * sizeof(wchar_t); |
| oni->Name.MaximumLength = (len + 1) * sizeof(wchar_t); |
| oni->Name.Buffer[len] = 0; |
| //printf("nativelibc debug: truncated path: "); |
| //NtDisplayString(&oni->Name); |
| //putchar('\n'); |
| |
| void *handle; |
| OBJECT_ATTRIBUTES object_attrib = { sizeof(OBJECT_ATTRIBUTES), NULL, &oni->Name, 0, NULL, NULL }; |
| status = NtOpenDirectoryObject(&handle, DIRECTORY_QUERY, &object_attrib); |
| //printf("nativelibc debug: is_in_do: status = 0x%lx\n", status); |
| free(oni); |
| if(status < 0) return 0; |
| NtClose(handle); |
| return 1; |
| } |
| |
| static int fstat_f(int fd, struct stat *st) { |
| FILE_FS_DEVICE_INFORMATION ffsdi; |
| FILE_BASIC_INFORMATION fbi; |
| IO_STATUS_BLOCK io_status; |
| st->st_mode = S_IREAD | S_IEXEC; |
| long int status = NtQueryVolumeInformationFile((void *)fd, &io_status, &ffsdi, sizeof ffsdi, FileFsDeviceInformation); |
| //printf("nativelibc debug: fstat: NtQueryVolumeInformationFile status = 0x%lx\n", status); |
| if(status < 0) { |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| printf("nativelibc debug: fstat: ffsdi.DeviceType = 0x%lx, ffsdi.Characteristics = 0x%lx\n", ffsdi.DeviceType, ffsdi.Characteristics); |
| switch(ffsdi.DeviceType) { |
| case FILE_DEVICE_CD_ROM: |
| case FILE_DEVICE_DFS: |
| case FILE_DEVICE_DISK: |
| case FILE_DEVICE_VIRTUAL_DISK: |
| status = NtQueryInformationFile((void *)fd, &io_status, &fbi, sizeof fbi, FileBasicInformation); |
| //printf("nativelibc debug: fstat: status = 0x%lx\n", status); |
| if(status < 0) { |
| st->st_mode |= S_IFBLK; |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = ffsdi.Characteristics; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| } else { |
| // File in file system |
| unsigned long int t; |
| st->st_ctime = RtlTimeToSecondsSince1970(&fbi.ChangeTime, &t) ? t : 0; |
| st->st_mtime = RtlTimeToSecondsSince1970(&fbi.LastWriteTime, &t) ? t : 0; |
| st->st_atime = RtlTimeToSecondsSince1970(&fbi.LastAccessTime, &t) ? t : 0; |
| if(!(fbi.FileAttributes & FILE_ATTRIBUTE_READONLY)) st->st_mode |= S_IWRITE; |
| |
| FILE_STANDARD_INFORMATION fsi; |
| status = NtQueryInformationFile((void *)fd, &io_status, &fsi, sizeof fsi, FileStandardInformation); |
| //printf("nativelibc debug: fstat: status = 0x%lx\n", status); |
| if(status < 0) { |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| st->st_blocks = fsi.AllocationSize.QuadPart / 512; |
| st->st_size = fsi.EndOfFile.QuadPart; |
| st->st_rdev = 0; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = fsi.NumberOfLinks; |
| st->st_mode |= fsi.Directory ? S_IFDIR : S_IFREG; |
| st->st_dev = 0; |
| |
| FILE_INTERNAL_INFORMATION fii; |
| status = NtQueryInformationFile((void *)fd, &io_status, &fii, sizeof fii, FileInternalInformation); |
| //printf("nativelibc debug: fstat: status = 0x%lx\n", status); |
| st->st_ino = status < 0 ? 0 : fii.IndexNumber.QuadPart; |
| } |
| break; |
| |
| case FILE_DEVICE_NAMED_PIPE: |
| st->st_mode |= is_in_do(fd) ? S_IFBLK : S_IFIFO; |
| |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = 0; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| |
| break; |
| |
| case FILE_DEVICE_NETWORK_FILE_SYSTEM: |
| st->st_mode |= S_IFBLK; |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = ffsdi.Characteristics; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| break; |
| |
| default: |
| st->st_mode |= S_IFCHR; |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = ffsdi.Characteristics; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| break; |
| } |
| return 0; |
| #if 0 |
| long int status1 = NtQueryInformationFile((void *)fd, &io_status, &fbi, sizeof fbi, FileBasicInformation); |
| printf("nativelibc debug: fstat: status1 = 0x%lx\n", status1); |
| if(status1 >= 0) { |
| unsigned long int t; |
| st->st_ctime = RtlTimeToSecondsSince1970(&fbi.ChangeTime, &t) ? t : 0; |
| st->st_mtime = RtlTimeToSecondsSince1970(&fbi.LastWriteTime, &t) ? t : 0; |
| st->st_atime = RtlTimeToSecondsSince1970(&fbi.LastAccessTime, &t) ? t : 0; |
| if(!(fbi.FileAttributes & FILE_ATTRIBUTE_READONLY)) st->st_mode |= S_IWRITE; |
| //printf("nativelibc debug: fstat: fbi.FileAttributes = 0x%lx\n", fbi.FileAttributes); |
| //if(st->st_ctime) printf("ctime = %lu\n", (unsigned long int)st->st_ctime); |
| } |
| long int status2 = NtQueryInformationFile((void *)fd, &io_status, &fsi, sizeof fsi, FileStandardInformation); |
| printf("nativelibc debug: fstat: status2 = 0x%lx\n", status2); |
| if(status2 < 0) { |
| if(status1 < 0) { |
| __set_errno_from_ntstatus(status2); |
| return -1; |
| } |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = 0; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| } else { |
| if(status1 < 0) { |
| st->st_ctime = 0; |
| st->st_mtime = 0; |
| st->st_atime = 0; |
| } |
| st->st_blocks = fsi.AllocationSize.QuadPart / 512; |
| st->st_size = fsi.EndOfFile.QuadPart; |
| st->st_rdev = 0; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = fsi.NumberOfLinks; |
| st->st_mode |= fsi.Directory ? S_IFDIR : S_IFREG; |
| } |
| st->st_dev = 0; |
| long int status3 = NtQueryInformationFile((void *)fd, &io_status, &fii, sizeof fii, FileInternalInformation); |
| //printf("nativelibc debug: fstat: status3 = 0x%lx\n", status3); |
| if(status3 < 0) st->st_ino = 0; |
| else st->st_ino = fii.IndexNumber.QuadPart; |
| return 0; |
| #endif |
| } |
| |
| static int fstat_do(int fd, struct stat *st) { |
| OBJECT_BASIC_INFORMATION obi; |
| long int status = NtQueryObject((void *)fd, ObjectBasicInformation, &obi, sizeof obi, NULL); |
| if(status < 0) { |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| unsigned long int t; |
| st->st_mode = 0777 | S_IFDIR; |
| st->st_ctime = RtlTimeToSecondsSince1970(&obi.CreateTime, &t) ? t : 0; |
| st->st_mtime = 0; |
| st->st_atime = 0; |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = 0; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| st->st_ino = 0; |
| return 0; |
| } |
| |
| static int fstat_sl(int fd, struct stat *st) { |
| OBJECT_BASIC_INFORMATION obi; |
| long int status = NtQueryObject((void *)fd, ObjectBasicInformation, &obi, sizeof obi, NULL); |
| if(status < 0) { |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| unsigned long int t; |
| st->st_mode = 0777 | S_IFLNK; |
| st->st_ctime = RtlTimeToSecondsSince1970(&obi.CreateTime, &t) ? t : 0; |
| st->st_mtime = 0; |
| st->st_atime = 0; |
| st->st_blocks = 0; |
| st->st_size = 0; |
| st->st_rdev = 0; |
| st->st_gid = 0; |
| st->st_uid = 0; |
| st->st_nlink = 1; |
| st->st_ino = 0; |
| return 0; |
| } |
| |
| int fstat(int fd, struct stat *st) { |
| //printf("function: fstat(%d, %p)\n", fd, st); |
| if(!st) { |
| errno = EFAULT; |
| return -1; |
| } |
| |
| size_t oti_size = sizeof(OBJECT_TYPE_INFORMATION) + 256; |
| OBJECT_TYPE_INFORMATION *oti = malloc(oti_size); |
| if(!oti) return -1; |
| long int status = NtQueryObject((void *)fd, ObjectTypeInformation, oti, oti_size, NULL); |
| //printf("nativelibc debug: fstat: status = 0x%lx\n", status); |
| if(status < 0) { |
| free(oti); |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| //printf("TypeName: "); |
| //NtDisplayString(&oti->TypeName); |
| //putchar('\n'); |
| //printf("PoolType: %lu\n", oti->PoolType); |
| |
| int r = -1; |
| if(wcscmp(oti->TypeName.Buffer, L"File") == 0) r = fstat_f(fd, st); |
| else if(wcscmp(oti->TypeName.Buffer, L"Directory") == 0) r = fstat_do(fd, st); |
| else if(wcscmp(oti->TypeName.Buffer, L"SymbolicLink") == 0) r = fstat_sl(fd, st); |
| else errno = ENOSYS; |
| |
| free(oti); |
| return r; |
| } |
| |
| int stat(const char *path, struct stat *st) { |
| if(!path || !st) { |
| errno = EFAULT; |
| return -1; |
| } |
| if(!*path) { |
| errno = ENOENT; |
| return -1; |
| } |
| |
| int fd = open(path, O_RDONLY); |
| if(fd == -1) return -1; |
| |
| int r = fstat(fd, st); |
| int e = errno; |
| close(fd); |
| errno = e; |
| return r; |
| } |
| |
| int lstat(const char *path, struct stat *st) { |
| if(!path || !st) { |
| errno = EFAULT; |
| return -1; |
| } |
| if(!*path) { |
| errno = ENOENT; |
| return -1; |
| } |
| |
| int fd = open(path, O_RDONLY | O_NOFOLLOW); |
| if(fd == -1) return -1; |
| |
| int r = fstat(fd, st); |
| int e = errno; |
| close(fd); |
| errno = e; |
| return r; |
| } |