| /* A part of the Native C Library for Windows NT |
| Copyright 2007-2015 PC GO Ld. |
| Copyright 2015-2016 Rivoreo |
| |
| 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/statvfs.h> |
| #include <windows.h> |
| #include <nt.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <ntstatus.h> |
| #include <pathname.h> |
| |
| #include <stdio.h> |
| |
| static int truncate_to_device(UNICODE_STRING *path) { |
| void *handle; |
| size_t len = path->Length / sizeof(wchar_t) - 1; |
| if(len && path->Buffer[len] == '\\') path->Buffer[len--] = 0; |
| //while(path->Buffer[--len] != L'/') if(!len) return -1; |
| do { |
| if(!len-- || !len) return -1; |
| } while(path->Buffer[len] != L'\\'); |
| UNICODE_STRING up_path = { len * sizeof(wchar_t), (len + 1) * sizeof(wchar_t), malloc((len + 1) * sizeof(wchar_t)) }; |
| if(!up_path.Buffer) return -1; |
| memcpy(up_path.Buffer, path->Buffer, len * sizeof(wchar_t)); |
| up_path.Buffer[len] = 0; |
| OBJECT_ATTRIBUTES object_attrib = { sizeof(OBJECT_ATTRIBUTES), NULL, &up_path, 0, NULL, NULL }; |
| long int status = NtOpenDirectoryObject(&handle, DIRECTORY_QUERY, &object_attrib); |
| free(up_path.Buffer); |
| if(status == STATUS_OBJECT_TYPE_MISMATCH) { |
| path->Length = len * sizeof(wchar_t); |
| path->MaximumLength = (len + 1) * sizeof(wchar_t); |
| path->Buffer[len] = 0; |
| return truncate_to_device(path); |
| } |
| if(status < 0) return -1; |
| NtClose(handle); |
| return 0; |
| } |
| |
| int fstatvfs(int fd, struct statvfs *buffer) { |
| if(!buffer) { |
| errno = EFAULT; |
| return -1; |
| } |
| |
| IO_STATUS_BLOCK io_status; |
| //FILE_FS_SIZE_INFORMATION ffssi; |
| FILE_FS_FULL_SIZE_INFORMATION ffsfsi; |
| //long int status = NtQueryVolumeInformationFile((void *)fd, &io_status, &ffssi, sizeof ffssi, FileFsSizeInformation); |
| //printf("nativelibc debug: fstatfs: status = 0x%lx\n", status); |
| long int status = NtQueryVolumeInformationFile((void *)fd, &io_status, &ffsfsi, sizeof ffsfsi, FileFsFullSizeInformation); |
| if(status < 0) { |
| if(status != STATUS_OBJECT_TYPE_MISMATCH) { |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| buffer->f_bsize = 0; |
| buffer->f_frsize = 0; |
| buffer->f_blocks = 0; |
| buffer->f_bfree = 0; |
| buffer->f_bavail = 0; |
| } else { |
| printf("nativelibc debug: BytesPerSector = %lu, SectorsPerAllocationUnit = %lu\n", ffsfsi.BytesPerSector, ffsfsi.SectorsPerAllocationUnit); |
| printf("nativelibc debug: ActualAvailableAllocationUnits = %lld, CallerAvailableAllocationUnits = %lld\n", ffsfsi.ActualAvailableAllocationUnits.QuadPart, ffsfsi.CallerAvailableAllocationUnits.QuadPart); |
| buffer->f_bsize = ffsfsi.SectorsPerAllocationUnit * ffsfsi.BytesPerSector; |
| buffer->f_frsize = ffsfsi.BytesPerSector; |
| buffer->f_blocks = ffsfsi.TotalAllocationUnits.QuadPart * ffsfsi.SectorsPerAllocationUnit; |
| buffer->f_bfree = ffsfsi.ActualAvailableAllocationUnits.QuadPart * ffsfsi.SectorsPerAllocationUnit; |
| buffer->f_bavail = ffsfsi.CallerAvailableAllocationUnits.QuadPart * ffsfsi.SectorsPerAllocationUnit; |
| } |
| buffer->f_files = 0; |
| buffer->f_ffree = 0; |
| buffer->f_favail = 0; |
| buffer->f_fsid = 0; |
| buffer->f_flag = 0; |
| buffer->f_namemax = 0; |
| *buffer->f_fstypename = 0; |
| |
| size_t oni_size = sizeof(OBJECT_NAME_INFORMATION) + MNAMELEN * 2; |
| OBJECT_NAME_INFORMATION *oni = malloc(oni_size); |
| if(!oni) return -1; |
| status = NtQueryObject((void *)fd, ObjectNameInformation, oni, oni_size, NULL); |
| //printf("nativelibc debug: fstatfs: status = 0x%lx\n", status); |
| if(status < 0) *buffer->f_mntfromname = 0; |
| else { |
| if(truncate_to_device(&oni->Name) < 0) strcpy(buffer->f_mntfromname, "obfs"); |
| else { |
| PATHNAME_NT2UNIX_UTF16_STRUCT(oni->Name); |
| if(wcstombs(buffer->f_mntfromname, oni->Name.Buffer, oni->Name.MaximumLength) == (size_t)-1) { |
| *buffer->f_mntfromname = 0; |
| } |
| } |
| } |
| free(oni); |
| |
| return 0; |
| } |
| |
| int statvfs(const char *path, struct statvfs *buffer) { |
| if(!path || !buffer) { |
| errno = EFAULT; |
| return -1; |
| } |
| if(!*path) { |
| errno = ENOENT; |
| return -1; |
| } |
| |
| int fd = open(path, O_RDONLY); |
| if(fd == -1) return -1; |
| |
| int r = fstatvfs(fd, buffer); |
| int e = errno; |
| close(fd); |
| errno = e; |
| return r; |
| } |