mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
556a294363
We were too zealous about security before by only setting the owner bits and that would cause issues for projects like redbean that check "other" bits to determine if it's safe to serve a file. Since that doesn't exist on Windows, it's better to have things work than not work. So what we'll do instead is return modes like 0664 for files and 0775 for directories.
207 lines
7.8 KiB
C
207 lines
7.8 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/internal.h"
|
|
#include "libc/calls/struct/stat.h"
|
|
#include "libc/calls/struct/stat.internal.h"
|
|
#include "libc/calls/syscall_support-nt.internal.h"
|
|
#include "libc/fmt/wintime.internal.h"
|
|
#include "libc/intrin/atomic.h"
|
|
#include "libc/intrin/bsr.h"
|
|
#include "libc/intrin/fds.h"
|
|
#include "libc/intrin/strace.h"
|
|
#include "libc/macros.h"
|
|
#include "libc/mem/alloca.h"
|
|
#include "libc/nt/enum/fileflagandattributes.h"
|
|
#include "libc/nt/enum/fileinfobyhandleclass.h"
|
|
#include "libc/nt/enum/filetype.h"
|
|
#include "libc/nt/enum/fsctl.h"
|
|
#include "libc/nt/files.h"
|
|
#include "libc/nt/runtime.h"
|
|
#include "libc/nt/struct/byhandlefileinformation.h"
|
|
#include "libc/nt/struct/filecompressioninfo.h"
|
|
#include "libc/nt/struct/reparsedatabuffer.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/str/utf16.h"
|
|
#include "libc/sysv/consts/s.h"
|
|
#include "libc/sysv/errfuns.h"
|
|
|
|
static textwindows long GetSizeOfReparsePoint(int64_t fh) {
|
|
uint32_t mem =
|
|
sizeof(struct NtReparseDataBuffer) + PATH_MAX * sizeof(char16_t);
|
|
void *buf = alloca(mem);
|
|
uint32_t dwBytesReturned;
|
|
struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)buf;
|
|
if (!DeviceIoControl(fh, kNtFsctlGetReparsePoint, 0, 0, rdb, mem,
|
|
&dwBytesReturned, 0)) {
|
|
return -1;
|
|
}
|
|
const char16_t *p =
|
|
(const char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer +
|
|
rdb->SymbolicLinkReparseBuffer.PrintNameOffset);
|
|
uint32_t n =
|
|
rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t);
|
|
uint32_t i = 0;
|
|
uint32_t z = 0;
|
|
while (i < n) {
|
|
wint_t x = p[i++] & 0xffff;
|
|
if (!IsUcs2(x)) {
|
|
if (i < n) {
|
|
wint_t y = p[i++] & 0xffff;
|
|
x = MergeUtf16(x, y);
|
|
} else {
|
|
x = 0xfffd;
|
|
}
|
|
}
|
|
if (x >= 0200) {
|
|
z += bsrl(tpenc(x)) >> 3;
|
|
}
|
|
++z;
|
|
}
|
|
return z;
|
|
}
|
|
|
|
static textwindows int sys_fstat_nt_socket(int kind, struct stat *st) {
|
|
bzero(st, sizeof(*st));
|
|
st->st_blksize = 512;
|
|
st->st_mode = S_IFSOCK | 0666;
|
|
st->st_dev = 0x44444444;
|
|
st->st_ino = kind;
|
|
return 0;
|
|
}
|
|
|
|
textwindows int sys_fstat_nt_special(int kind, struct stat *st) {
|
|
bzero(st, sizeof(*st));
|
|
st->st_blksize = 512;
|
|
st->st_mode = S_IFCHR | 0666;
|
|
st->st_dev = 0x77777777;
|
|
st->st_ino = kind;
|
|
return 0;
|
|
}
|
|
|
|
textwindows int sys_fstat_nt(int fd, struct stat *st) {
|
|
if (fd + 0u >= g_fds.n)
|
|
return ebadf();
|
|
switch (g_fds.p[fd].kind) {
|
|
case kFdEmpty:
|
|
return ebadf();
|
|
case kFdConsole:
|
|
case kFdDevNull:
|
|
case kFdDevRandom:
|
|
return sys_fstat_nt_special(g_fds.p[fd].kind, st);
|
|
case kFdSocket:
|
|
return sys_fstat_nt_socket(g_fds.p[fd].kind, st);
|
|
default:
|
|
return sys_fstat_nt_handle(g_fds.p[fd].handle, 0, st);
|
|
}
|
|
}
|
|
|
|
textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
|
|
struct stat *out_st) {
|
|
struct stat st = {0};
|
|
|
|
// Always set st_blksize to avoid divide by zero issues.
|
|
// The Linux kernel sets this for /dev/tty and similar too.
|
|
st.st_blksize = 4096;
|
|
st.st_gid = st.st_uid = sys_getuid_nt();
|
|
|
|
switch (GetFileType(handle)) {
|
|
case kNtFileTypeUnknown:
|
|
break;
|
|
case kNtFileTypeChar:
|
|
st.st_mode = S_IFCHR | 0664;
|
|
st.st_dev = 0x66666666;
|
|
st.st_ino = handle;
|
|
break;
|
|
case kNtFileTypePipe:
|
|
st.st_mode = S_IFIFO | 0664;
|
|
st.st_dev = 0x55555555;
|
|
st.st_ino = handle;
|
|
break;
|
|
case kNtFileTypeDisk: {
|
|
struct NtByHandleFileInformation wst;
|
|
if (GetFileInformationByHandle(handle, &wst)) {
|
|
st.st_mode = 0444;
|
|
if ((wst.dwFileAttributes & kNtFileAttributeDirectory) ||
|
|
IsWindowsExecutable(handle, path))
|
|
st.st_mode |= 0111;
|
|
st.st_flags = wst.dwFileAttributes;
|
|
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly))
|
|
st.st_mode |= 0220;
|
|
if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
|
|
st.st_mode |= S_IFLNK;
|
|
} else if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
|
|
st.st_mode |= S_IFDIR;
|
|
} else {
|
|
st.st_mode |= S_IFREG;
|
|
}
|
|
st.st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
|
|
st.st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
|
|
st.st_birthtim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
|
// compute time of last status change
|
|
if (timespec_cmp(st.st_atim, st.st_mtim) > 0) {
|
|
st.st_ctim = st.st_atim;
|
|
} else {
|
|
st.st_ctim = st.st_mtim;
|
|
}
|
|
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
|
st.st_dev = wst.dwVolumeSerialNumber;
|
|
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;
|
|
st.st_nlink = wst.nNumberOfLinks;
|
|
if (S_ISLNK(st.st_mode)) {
|
|
if (!st.st_size) {
|
|
long size = GetSizeOfReparsePoint(handle);
|
|
if (size == -1)
|
|
return -1;
|
|
st.st_size = size;
|
|
}
|
|
} else {
|
|
// st_size = uncompressed size
|
|
// st_blocks*512 = physical size
|
|
uint64_t physicalsize;
|
|
struct NtFileCompressionInfo fci;
|
|
if (!(wst.dwFileAttributes &
|
|
(kNtFileAttributeDirectory | kNtFileAttributeReparsePoint)) &&
|
|
GetFileInformationByHandleEx(handle, kNtFileCompressionInfo, &fci,
|
|
sizeof(fci))) {
|
|
physicalsize = fci.CompressedFileSize;
|
|
} else {
|
|
physicalsize = st.st_size;
|
|
}
|
|
st.st_blocks = ROUNDUP(physicalsize, st.st_blksize) / 512;
|
|
}
|
|
} else if (GetVolumeInformationByHandle(
|
|
handle, 0, 0, &wst.dwVolumeSerialNumber, 0, 0, 0, 0)) {
|
|
st.st_dev = wst.dwVolumeSerialNumber;
|
|
st.st_mode = S_IFDIR | 0555;
|
|
} else {
|
|
// both GetFileInformationByHandle and
|
|
// GetVolumeInformationByHandle failed
|
|
return __winerr();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
memcpy(out_st, &st, sizeof(st));
|
|
return 0;
|
|
}
|