homework-jianmu/deps/MsvcLibX/src/lstat.c

433 lines
17 KiB
C

/*****************************************************************************\
* *
* Filename lstat.c *
* *
* Description: WIN32 port of standard C library's lstat() *
* Also contains unlink() and rmdir(), which do use lstat. *
* *
* Notes: TO DO: Make 3 versions for Windows: ANSI, WSTR, UTF8 *
* *
* History: *
* 2014-02-06 JFL Created this module. *
* 2014-02-12 JFL Added code to filter reparse points, and keep only *
* real junctions and symlinks. *
* 2014-02-13 JFL Moved dirent2stat() from dirent.c, as there must actually *
* be 4 WIN32 versions, for the four versions of struct stat.*
* 2014-02-28 JFL Added support for UTF-8 pathnames. *
* 2014-03-24 JFL Renamed "statx.h" as the standard <sys/stat.h>. *
* 2014-06-30 JFL Added support for 32K Unicode paths. *
* *
* Copyright 2016 Hewlett Packard Enterprise Development LP *
* Licensed under the Apache 2.0 license - www.apache.org/licenses/LICENSE-2.0 *
\*****************************************************************************/
#define _CRT_SECURE_NO_WARNINGS 1 /* Avoid Visual C++ security warnings */
#define _UTF8_SOURCE /* Generate the UTF-8 version of routines */
/* Microsoft C libraries include files */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/* MsvcLibX library extensions */
#include "msvclibx.h"
#include <sys/msvcStat.h>
#include "msvcDirent.h"
#include "msvcUnistd.h" /* For ResolveLinks() definition */
#include "msvcDebugm.h"
#include "msvcLimits.h"
#if defined(_MSDOS)
/* Make sure it's only defined it in one of the lstatxxx versions */
#if !defined(_USE_32BIT_TIME_T) && (_FILE_OFFSET_BITS == 32)
int dirent2stat(_dirent *pDirent, struct _stat *pStat) {
memset(pStat, 0, sizeof(struct stat));
/* Set times */
pStat->st_mtime = Filetime2Timet(pDirent->d_date, pDirent->d_time);
/* Size */
pStat->st_size = pDirent->d_filesize;
/* Standard attributes */
pStat->st_mode |= (pDirent->d_type << 12); /* Set the 4-bit type field */
pStat->st_mode |= _S_IREAD | _S_IWRITE | _S_IEXEC; /* Assume it's fully accessible */
if (pDirent->d_attribs & _A_RDONLY) pStat->st_mode &= ~_S_IWRITE;
/* DOS-specific attributes */
if (pDirent->d_attribs & _A_HIDDEN) pStat->st_mode |= S_HIDDEN;
if (pDirent->d_attribs & _A_ARCH) pStat->st_mode |= S_ARCHIVE;
if (pDirent->d_attribs & _A_SYSTEM) pStat->st_mode |= S_SYSTEM;
return 0;
}
#endif /* defined(_USE_32BIT_TIME_T) && (_FILE_OFFSET_BITS == 32) */
#endif /* defined(_MSDOS) */
#ifdef _WIN32
/* ------------ Display the *stat* macro values at compile time ------------ */
#pragma message(MACRODEF(_MSVC_stat))
#pragma message(MACRODEF(_MSVC_fstat))
#pragma message(MACRODEF(_MSVC_lstat))
#pragma message(MACRODEF(_MSVC_stat64))
#if _MSVCLIBX_STAT_DEFINED
#pragma message(MACRODEF(_LIBX_stat))
#pragma message(MACRODEF(_LIBX_stat64))
#endif
#pragma message(MACRODEF(stat))
#pragma message(MACRODEF(fstat))
#pragma message(MACRODEF(lstat))
#if 0
#pragma message(MACRODEF(_lstat))
#pragma message(MACRODEF(_lstati64))
#if _MSVCLIBX_STAT_DEFINED
#pragma message(MACRODEF(_lstat_ns))
#pragma message(MACRODEF(_lstati64_ns))
#endif
#endif
#if defined(_LARGEFILE_SOURCE64)
#pragma message(MACRODEF(stat64))
#pragma message(MACRODEF(fstat64))
#pragma message(MACRODEF(lstat64))
#endif
#include <windows.h>
/*---------------------------------------------------------------------------*\
* *
| Function: lstat |
| |
| Description: Common definition of all _lstatXY() functions |
| |
| Parameters: const char *path The symlink name |
| struct stat *buf Output buffer |
| |
| Returns: 0 = Success, -1 = Failure |
| |
| Notes: See statx.h for a description of how the stat and lstat |
| macros work. |
| |
| History: |
| 2014-02-06 JFL Created this routine |
| 2014-02-28 JFL Added support for UTF-8 pathnames. |
* *
\*---------------------------------------------------------------------------*/
int lstat(const char *path, struct stat *pStat) {
BOOL bDone;
DWORD dwAttr;
WIN32_FILE_ATTRIBUTE_DATA fileData;
unsigned __int64 qwSize;
int bIsJunction = FALSE;
int bIsMountPoint = FALSE;
DWORD dwTag = 0;
DEBUG_CODE(
char szTime[100];
)
WCHAR wszName[UNICODE_PATH_MAX];
int n;
DEBUG_ENTER((STRINGIZE(lstat) "(\"%s\", 0x%p);\n", path, pStat));
#if USE_MSVC_STAT
dwAttr = GetFileAttributes(path);
DEBUG_PRINTF(("GetFileAttributes() = 0x%lX\n", dwAttr));
if (dwAttr == INVALID_FILE_ATTRIBUTES) {
errno = ENOENT;
RETURN_INT_COMMENT(-1, ("File does not exist\n"));
}
if (!(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)) {
int iErr = stat(path, pStat);
RETURN_INT(iErr);
}
#endif
/* Convert the pathname to a unicode string, with the proper extension prefixes if it's longer than 260 bytes */
n = MultiByteToWidePath(CP_UTF8, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */
path, /* lpMultiByteStr, */
wszName, /* lpWideCharStr, */
UNICODE_PATH_MAX /* cchWideChar, */
);
if (!n) {
errno = Win32ErrorToErrno();
RETURN_INT_COMMENT(-1, ("errno=%d - %s\n", errno, strerror(errno)));
}
bDone = GetFileAttributesExW(wszName, GetFileExInfoStandard, &fileData);
if (!bDone) {
errno = Win32ErrorToErrno();
RETURN_INT_COMMENT(-1, ("GetFileAttributesEx(); // Failed\n"));
}
XDEBUG_PRINTF(("GetFileAttributesEx(); // Success\n"));
dwAttr = fileData.dwFileAttributes;
XDEBUG_PRINTF(("dwFileAttributes = 0x%lX\n", dwAttr));
DEBUG_CODE_IF_ON(Filetime2String(&fileData.ftLastWriteTime, szTime, sizeof(szTime)););
XDEBUG_PRINTF(("ftLastWriteTime = %s\n", szTime));
qwSize = ((unsigned __int64)fileData.nFileSizeHigh << 32) | fileData.nFileSizeLow;
XDEBUG_PRINTF(("nFileSize = %I64d\n", qwSize));
ZeroMemory(pStat, sizeof(struct stat));
/* Set times */
#if _MSVCLIBX_STAT_DEFINED
Filetime2Timespec(&fileData.ftCreationTime, &(pStat->st_ctim)); /* Windows = Create time; Unix = Permissions change time */
Filetime2Timespec(&fileData.ftLastWriteTime, &(pStat->st_mtim));
Filetime2Timespec(&fileData.ftLastAccessTime, &(pStat->st_atim));
#else
Filetime2Timet(&fileData.ftCreationTime, &(pStat->st_ctime)); /* Windows = Create time; Unix = Permissions change time */
Filetime2Timet(&fileData.ftLastWriteTime, &(pStat->st_mtime));
Filetime2Timet(&fileData.ftLastAccessTime, &(pStat->st_atime));
#endif
/* Size */
/* NOTE: There is loss of data here if the file size is > 2GB, and off_t is 32-bits */
pStat->st_size = (off_t)qwSize;
#if (_STAT_FILE_SIZE < 64)
#define _MAX_FILE_SIZE 0x7FFFFFFFL
if (qwSize > _MAX_FILE_SIZE) pStat->st_size = (off_t)_MAX_FILE_SIZE;
#endif
/* Standard attributes */
/* File type */
check_attr_again:
if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) {
/* JUNCTIONs and SYMLINKDs both have the FILE_ATTRIBUTE_DIRECTORY flag also set.
// Test the FILE_ATTRIBUTE_REPARSE_POINT flag first, to make sure they're seen as symbolic links.
//
// All symlinks are reparse points, but not all reparse points are symlinks. */
dwTag = GetReparseTagU(path);
switch (dwTag) {
case IO_REPARSE_TAG_MOUNT_POINT: /* NTFS junction or mount point */
{ /* We must read the link to distinguish junctions from mount points. */
WCHAR wbuf[UNICODE_PATH_MAX];
ssize_t n;
bIsMountPoint = TRUE;
n = readlinkW(wszName, wbuf, UNICODE_PATH_MAX);
/* Junction targets are absolute pathnames, starting with a drive letter. Ex: C: */
/* readlink() fails if the reparse point does not target a valid pathname */
if (n < 0) goto this_is_not_a_symlink; /* This is not a junction. */
bIsJunction = TRUE; /* Else this is a junction. Fall through to the symlink case. */
}
case IO_REPARSE_TAG_SYMLINK: /* NTFS symbolic link */
pStat->st_mode |= S_IFLNK; /* Symbolic link */
break;
default: /* Anything else is definitely not like a Unix symlink */
this_is_not_a_symlink:
dwAttr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
goto check_attr_again;
}
} else if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
pStat->st_mode |= S_IFDIR; /* Subdirectory */
else if (dwAttr & FILE_ATTRIBUTE_DEVICE)
pStat->st_mode |= S_IFCHR; /* Device (we don't know if character or block) */
else
pStat->st_mode |= S_IFREG; /* A normal file by default */
/* pStat->st_mode |= (pDirent->d_type << 12); /* Set the 4-bit type field */
pStat->st_mode |= _S_IREAD | _S_IWRITE | _S_IEXEC; /* Assume it's fully accessible */
if (dwAttr & FILE_ATTRIBUTE_READONLY) pStat->st_mode &= ~_S_IWRITE;
/* DOS-specific attributes */
if (dwAttr & FILE_ATTRIBUTE_HIDDEN) pStat->st_mode |= S_HIDDEN;
if (dwAttr & FILE_ATTRIBUTE_ARCHIVE) pStat->st_mode |= S_ARCHIVE;
if (dwAttr & FILE_ATTRIBUTE_SYSTEM) pStat->st_mode |= S_SYSTEM;
/* Windows-specific attributes */
if (dwAttr & FILE_ATTRIBUTE_COMPRESSED) pStat->st_mode |= S_COMPRESSED;
if (dwAttr & FILE_ATTRIBUTE_ENCRYPTED) pStat->st_mode |= S_ENCRYPTED;
if (dwAttr & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) pStat->st_mode |= S_NOT_CONTENT_INDEXED;
if (dwAttr & FILE_ATTRIBUTE_OFFLINE) pStat->st_mode |= S_OFFLINE;
if (dwAttr & FILE_ATTRIBUTE_SPARSE_FILE) pStat->st_mode |= S_SPARSE_FILE;
if (bIsMountPoint) pStat->st_mode |= S_MOUNT_POINT; /* Will allow to distinguish junctions from mount points */
/* if (dwAttr & FILE_ATTRIBUTE_TEMPORARY) pStat->st_mode |= S_TEMPORARY; */
/* if (dwAttr & FILE_ATTRIBUTE_VIRTUAL) pStat->st_mode |= S_VIRTUAL; */
#if _MSVCLIBX_STAT_DEFINED
pStat->st_Win32Attrs = dwAttr;
pStat->st_ReparseTag = dwTag;
#endif
RETURN_INT_COMMENT(0, ("%s mode = 0x%04X size = %I64d bytes\n", szTime, pStat->st_mode, qwSize));
}
#if !USE_MSVC_STAT
int stat(const char *path, struct stat *pStat) {
char buf[UTF8_PATH_MAX];
int iErr;
DEBUG_ENTER((STRINGIZE(stat) "(\"%s\", 0x%p);\n", path, pStat));
iErr = ResolveLinksU(path, buf, sizeof(buf));
if (!iErr) lstat(buf, pStat);
RETURN_INT(iErr);
}
#endif /* !USE_MSVC_STAT */
int dirent2stat(_dirent *pDirent, struct stat *pStat) {
memset(pStat, 0, sizeof(struct stat));
/* Set times */
#if _MSVCLIBX_STAT_DEFINED
Filetime2Timespec(&pDirent->d_CreationTime, &(pStat->st_ctim)); /* Windows = Create time; Unix = Permissions change time */
Filetime2Timespec(&pDirent->d_LastWriteTime, &(pStat->st_mtim));
Filetime2Timespec(&pDirent->d_LastAccessTime, &(pStat->st_atim));
#else
Filetime2Timet(&pDirent->d_CreationTime, &(pStat->st_ctime)); /* Windows = Create time; Unix = Permissions change time */
Filetime2Timet(&pDirent->d_LastWriteTime, &(pStat->st_mtime));
Filetime2Timet(&pDirent->d_LastAccessTime, &(pStat->st_atime));
#endif
/* Size */
/* NOTE: There is loss of data here if the file size is > 2GB, and off_t is 32-bits */
pStat->st_size = (off_t)(pDirent->d_filesize);
#if (_STAT_FILE_SIZE < 64)
#define _MAX_FILE_SIZE 0x7FFFFFFFL
if (pDirent->d_filesize > _MAX_FILE_SIZE) pStat->st_size = (off_t)_MAX_FILE_SIZE;
#endif
/* Standard attributes */
pStat->st_mode |= (pDirent->d_type << 12); /* Set the 4-bit type field */
pStat->st_mode |= _S_IREAD | _S_IWRITE | _S_IEXEC; /* Assume it's fully accessible */
if (pDirent->d_attribs & FILE_ATTRIBUTE_READONLY) pStat->st_mode &= ~_S_IWRITE;
/* DOS-specific attributes */
if (pDirent->d_attribs & FILE_ATTRIBUTE_HIDDEN) pStat->st_mode |= S_HIDDEN;
if (pDirent->d_attribs & FILE_ATTRIBUTE_ARCHIVE) pStat->st_mode |= S_ARCHIVE;
if (pDirent->d_attribs & FILE_ATTRIBUTE_SYSTEM) pStat->st_mode |= S_SYSTEM;
/* Windows-specific attributes */
if (pDirent->d_attribs & FILE_ATTRIBUTE_COMPRESSED) pStat->st_mode |= S_COMPRESSED;
if (pDirent->d_attribs & FILE_ATTRIBUTE_ENCRYPTED) pStat->st_mode |= S_ENCRYPTED;
if (pDirent->d_attribs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) pStat->st_mode |= S_NOT_CONTENT_INDEXED;
if (pDirent->d_attribs & FILE_ATTRIBUTE_OFFLINE) pStat->st_mode |= S_OFFLINE;
if (pDirent->d_attribs & FILE_ATTRIBUTE_SPARSE_FILE) pStat->st_mode |= S_SPARSE_FILE;
/* Special case of junction and mount points */
if (pDirent->d_ReparseTag) pStat->st_mode |= S_MOUNT_POINT;
/* if (pDirent->d_attribs & FILE_ATTRIBUTE_TEMPORARY) pStat->st_mode |= S_TEMPORARY; */
/* if (pDirent->d_attribs & FILE_ATTRIBUTE_VIRTUAL) pStat->st_mode |= S_VIRTUAL; */
#if _MSVCLIBX_STAT_DEFINED
pStat->st_Win32Attrs = pDirent->d_attribs;
pStat->st_ReparseTag = pDirent->d_ReparseTag;
#endif
return 0;
}
/*---------------------------------------------------------------------------*\
* *
| Function: unlink |
| |
| Description: Remove a file or a symbolic link |
| |
| Parameters: const char *path The file or symlink name |
| |
| Returns: 0 = Success, -1 = Failure |
| |
| Notes: |
| |
| History: |
| 2014-02-17 JFL Created this routine |
| 2014-02-28 JFL Added support for UTF-8 pathnames. |
* *
\*---------------------------------------------------------------------------*/
int unlink(const char *path) {
int iErr;
BOOL bDone;
struct stat st;
WCHAR wszName[UNICODE_PATH_MAX];
int n;
DEBUG_ENTER(("unlink(\"%s\");\n", path));
iErr = lstat(path, &st);
if (iErr) RETURN_INT(iErr);
if ((!S_ISREG(st.st_mode)) && (!S_ISLNK(st.st_mode))) {
errno = ENOENT;
RETURN_INT_COMMENT(-1, ("Pathname exists, but is not a file or a link\n"));
}
/* Convert the pathname to a unicode string, with the proper extension prefixes if it's longer than 260 bytes */
n = MultiByteToWidePath(CP_UTF8, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */
path, /* lpMultiByteStr, */
wszName, /* lpWideCharStr, */
UNICODE_PATH_MAX /* cchWideChar, */
);
if (!n) {
errno = Win32ErrorToErrno();
RETURN_INT_COMMENT(-1, ("errno=%d - %s\n", errno, strerror(errno)));
}
#if _MSVCLIBX_STAT_DEFINED
if (S_ISLNK(st.st_mode) && (st.st_Win32Attrs & FILE_ATTRIBUTE_DIRECTORY)) {
/* This link is a junction or a symlinkd */
bDone = RemoveDirectoryW(wszName);
} else
#endif
bDone = DeleteFileW(wszName);
if (bDone) {
RETURN_INT_COMMENT(0, ("Success\n"));
} else {
errno = Win32ErrorToErrno();
RETURN_INT_COMMENT(-1, ("Failed\n"));
}
}
/*---------------------------------------------------------------------------*\
* *
| Function: rmdir |
| |
| Description: Remove a directory |
| |
| Parameters: const char *path The directory name |
| |
| Returns: 0 = Success, -1 = Failure |
| |
| Notes: |
| |
| History: |
| 2014-03-05 JFL Created this routine with support for UTF-8 pathnames. |
* *
\*---------------------------------------------------------------------------*/
int rmdir(const char *path) {
int iErr;
BOOL bDone;
struct stat st;
WCHAR wszName[UNICODE_PATH_MAX];
int n;
DEBUG_ENTER(("rmdir(\"%s\");\n", path));
iErr = lstat(path, &st);
if (iErr) RETURN_INT(iErr);
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
RETURN_INT_COMMENT(-1, ("Pathname exists, but is not a directory\n"));
}
/* Convert the pathname to a unicode string, with the proper extension prefixes if it's longer than 260 bytes */
n = MultiByteToWidePath(CP_UTF8, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */
path, /* lpMultiByteStr, */
wszName, /* lpWideCharStr, */
UNICODE_PATH_MAX /* cchWideChar, */
);
if (!n) {
errno = Win32ErrorToErrno();
RETURN_INT_COMMENT(-1, ("errno=%d - %s\n", errno, strerror(errno)));
}
bDone = RemoveDirectoryW(wszName);
if (bDone) {
RETURN_INT_COMMENT(0, ("Success\n"));
} else {
errno = Win32ErrorToErrno();
RETURN_INT_COMMENT(-1, ("Failed\n"));
}
}
#endif /* _WIN32 */