📄 tclwinfile.c
字号:
/* * tclWinFile.c -- * * This file contains temporary wrappers around UNIX file handling * functions. These wrappers map the UNIX functions to Win32 HANDLE-style * files, which can be manipulated through the Win32 console redirection * interfaces. * * Copyright (c) 1995-1998 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclWinFile.c,v 1.39 2002/07/20 01:01:41 vincentdarley Exp $ *///#define _WIN32_WINNT 0x0500#include "tclWinInt.h"#include <winioctl.h>#include <sys/stat.h>#include <shlobj.h>#include <lmaccess.h> /* For TclpGetUserHome(). *//* * Declarations for 'link' related information. This information * should come with VC++ 6.0, but is not in some older SDKs. * In any case it is not well documented. */#ifndef IO_REPARSE_TAG_RESERVED_ONE# define IO_REPARSE_TAG_RESERVED_ONE 0x000000001#endif#ifndef IO_REPARSE_TAG_RESERVED_RANGE# define IO_REPARSE_TAG_RESERVED_RANGE 0x000000001#endif#ifndef IO_REPARSE_TAG_VALID_VALUES# define IO_REPARSE_TAG_VALID_VALUES 0x0E000FFFF#endif#ifndef IO_REPARSE_TAG_HSM# define IO_REPARSE_TAG_HSM 0x0C0000004#endif#ifndef IO_REPARSE_TAG_NSS# define IO_REPARSE_TAG_NSS 0x080000005#endif#ifndef IO_REPARSE_TAG_NSSRECOVER# define IO_REPARSE_TAG_NSSRECOVER 0x080000006#endif#ifndef IO_REPARSE_TAG_SIS# define IO_REPARSE_TAG_SIS 0x080000007#endif#ifndef IO_REPARSE_TAG_DFS# define IO_REPARSE_TAG_DFS 0x080000008#endif#ifndef IO_REPARSE_TAG_RESERVED_ZERO# define IO_REPARSE_TAG_RESERVED_ZERO 0x00000000#endif#ifndef FILE_FLAG_OPEN_REPARSE_POINT# define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000#endif#ifndef IO_REPARSE_TAG_MOUNT_POINT# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003#endif#ifndef IsReparseTagValid# define IsReparseTagValid(x) (!((x)&~IO_REPARSE_TAG_VALID_VALUES)&&((x)>IO_REPARSE_TAG_RESERVED_RANGE))#endif#ifndef IO_REPARSE_TAG_SYMBOLIC_LINK# define IO_REPARSE_TAG_SYMBOLIC_LINK IO_REPARSE_TAG_RESERVED_ZERO#endif#ifndef FILE_SPECIAL_ACCESS# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)#endif#ifndef FSCTL_SET_REPARSE_POINT# define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) # define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) #endif/* * Maximum reparse buffer info size. The max user defined reparse * data is 16KB, plus there's a header. */#define MAX_REPARSE_SIZE 17000/* * Undocumented REPARSE_MOUNTPOINT_HEADER_SIZE structure definition. * This is found in winnt.h. * * IMPORTANT: caution when using this structure, since the actual * structures used will want to store a full path in the 'PathBuffer' * field, but there isn't room (there's only a single WCHAR!). Therefore * one must artificially create a larger space of memory and then cast it * to this type. We use the 'DUMMY_REPARSE_BUFFER' struct just below to * deal with this problem. */#define REPARSE_MOUNTPOINT_HEADER_SIZE 8#ifndef REPARSE_DATA_BUFFER_HEADER_SIZEtypedef struct _REPARSE_DATA_BUFFER { DWORD ReparseTag; WORD ReparseDataLength; WORD Reserved; union { struct { WORD SubstituteNameOffset; WORD SubstituteNameLength; WORD PrintNameOffset; WORD PrintNameLength; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { WORD SubstituteNameOffset; WORD SubstituteNameLength; WORD PrintNameOffset; WORD PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { BYTE DataBuffer[1]; } GenericReparseBuffer; };} REPARSE_DATA_BUFFER;#endiftypedef struct { REPARSE_DATA_BUFFER dummy; WCHAR dummyBuf[MAX_PATH*3];} DUMMY_REPARSE_BUFFER;/* Other typedefs required by this code */static time_t ToCTime(FILETIME fileTime);typedef NET_API_STATUS NET_API_FUNCTION NETUSERGETINFOPROC (LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr);typedef NET_API_STATUS NET_API_FUNCTION NETAPIBUFFERFREEPROC (LPVOID Buffer);typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC (LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr);/* * Declarations for local procedures defined in this file: */static int NativeAccess(CONST TCHAR *path, int mode);static int NativeStat(CONST TCHAR *path, Tcl_StatBuf *statPtr, int checkLinks);static int NativeIsExec(CONST TCHAR *path);static int NativeReadReparse(CONST TCHAR* LinkDirectory, REPARSE_DATA_BUFFER* buffer);static int NativeWriteReparse(CONST TCHAR* LinkDirectory, REPARSE_DATA_BUFFER* buffer);static int NativeMatchType(CONST char *name, int nameLen, CONST TCHAR* nativeName, Tcl_GlobTypeData *types);static int WinIsDrive(CONST char *name, int nameLen);static Tcl_Obj* WinReadLink(CONST TCHAR* LinkSource);static Tcl_Obj* WinReadLinkDirectory(CONST TCHAR* LinkDirectory);static int WinLink(CONST TCHAR* LinkSource, CONST TCHAR* LinkTarget, int linkAction);static int WinSymLinkDirectory(CONST TCHAR* LinkDirectory, CONST TCHAR* LinkTarget);/* *-------------------------------------------------------------------- * * WinLink * * Make a link from source to target. *-------------------------------------------------------------------- */static int WinLink(LinkSource, LinkTarget, linkAction) CONST TCHAR* LinkSource; CONST TCHAR* LinkTarget; int linkAction;{ WCHAR tempFileName[MAX_PATH]; TCHAR* tempFilePart; int attr; /* Get the full path referenced by the target */ if (!(*tclWinProcs->getFullPathNameProc)(LinkTarget, MAX_PATH, tempFileName, &tempFilePart)) { /* Invalid file */ TclWinConvertError(GetLastError()); return -1; } /* Make sure source file doesn't exist */ attr = (*tclWinProcs->getFileAttributesProc)(LinkSource); if (attr != 0xffffffff) { Tcl_SetErrno(EEXIST); return -1; } /* Get the full path referenced by the directory */ if (!(*tclWinProcs->getFullPathNameProc)(LinkSource, MAX_PATH, tempFileName, &tempFilePart)) { /* Invalid file */ TclWinConvertError(GetLastError()); return -1; } /* Check the target */ attr = (*tclWinProcs->getFileAttributesProc)(LinkTarget); if (attr == 0xffffffff) { /* The target doesn't exist */ TclWinConvertError(GetLastError()); return -1; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { /* It is a file */ if (tclWinProcs->createHardLinkProc == NULL) { Tcl_SetErrno(ENOTDIR); return -1; } if (linkAction & TCL_CREATE_HARD_LINK) { if (!(*tclWinProcs->createHardLinkProc)(LinkSource, LinkTarget, NULL)) { TclWinConvertError(GetLastError()); return -1; } return 0; } else if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { /* Can't symlink files */ Tcl_SetErrno(ENOTDIR); return -1; } else { Tcl_SetErrno(ENODEV); return -1; } } else { if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { return WinSymLinkDirectory(LinkSource, LinkTarget); } else if (linkAction & TCL_CREATE_HARD_LINK) { /* Can't hard link directories */ Tcl_SetErrno(EISDIR); return -1; } else { Tcl_SetErrno(ENODEV); return -1; } }}/* *-------------------------------------------------------------------- * * WinReadLink * * What does 'LinkSource' point to? We need the original 'pathPtr' * just so we can construct a path object in the correct filesystem. *-------------------------------------------------------------------- */static Tcl_Obj* WinReadLink(LinkSource) CONST TCHAR* LinkSource;{ WCHAR tempFileName[MAX_PATH]; TCHAR* tempFilePart; int attr; /* Get the full path referenced by the target */ if (!(*tclWinProcs->getFullPathNameProc)(LinkSource, MAX_PATH, tempFileName, &tempFilePart)) { /* Invalid file */ TclWinConvertError(GetLastError()); return NULL; } /* Make sure source file does exist */ attr = (*tclWinProcs->getFileAttributesProc)(LinkSource); if (attr == 0xffffffff) { /* The source doesn't exist */ TclWinConvertError(GetLastError()); return NULL; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { /* It is a file - this is not yet supported */ Tcl_SetErrno(ENOTDIR); return NULL; } else { return WinReadLinkDirectory(LinkSource); }}/* *-------------------------------------------------------------------- * * WinSymLinkDirectory * * This routine creates a NTFS junction, using the undocumented * FSCTL_SET_REPARSE_POINT structure Win2K uses for mount points * and junctions. * * Assumption that LinkTarget is a valid, existing directory. * * Returns zero on success. *-------------------------------------------------------------------- */static int WinSymLinkDirectory(LinkDirectory, LinkTarget) CONST TCHAR* LinkDirectory; CONST TCHAR* LinkTarget;{ DUMMY_REPARSE_BUFFER dummy; REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; int len; WCHAR nativeTarget[MAX_PATH]; WCHAR *loop; /* Make the native target name */ memcpy((VOID*)nativeTarget, (VOID*)L"\\??\\", 4*sizeof(WCHAR)); memcpy((VOID*)(nativeTarget + 4), (VOID*)LinkTarget, sizeof(WCHAR)*(1+wcslen((WCHAR*)LinkTarget))); len = wcslen(nativeTarget); /* * We must have backslashes only. This is VERY IMPORTANT. * If we have any forward slashes everything appears to work, * but the resulting symlink is useless! */ for (loop = nativeTarget; *loop != 0; loop++) { if (*loop == L'/') *loop = L'\\'; } if ((nativeTarget[len-1] == L'\\') && (nativeTarget[len-2] != L':')) { nativeTarget[len-1] = 0; } /* Build the reparse info */ memset(reparseBuffer, 0, sizeof(DUMMY_REPARSE_BUFFER)); reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength = wcslen(nativeTarget) * sizeof(WCHAR); reparseBuffer->Reserved = 0; reparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength = 0; reparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset = reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + sizeof(WCHAR); memcpy(reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, nativeTarget, sizeof(WCHAR) + reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength); reparseBuffer->ReparseDataLength = reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + 12; return NativeWriteReparse(LinkDirectory, reparseBuffer);}/* *-------------------------------------------------------------------- * * TclWinSymLinkCopyDirectory * * Copy a Windows NTFS junction. This function assumes that * LinkOriginal exists and is a valid junction point, and that * LinkCopy does not exist. * * Returns zero on success. *-------------------------------------------------------------------- */int TclWinSymLinkCopyDirectory(LinkOriginal, LinkCopy) CONST TCHAR* LinkOriginal; /* Existing junction - reparse point */ CONST TCHAR* LinkCopy; /* Will become a duplicate junction */{ DUMMY_REPARSE_BUFFER dummy; REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; if (NativeReadReparse(LinkOriginal, reparseBuffer)) { return -1; } return NativeWriteReparse(LinkCopy, reparseBuffer);}/* *-------------------------------------------------------------------- * * TclWinSymLinkDelete * * Delete a Windows NTFS junction. Once the junction information * is deleted, the filesystem object becomes an ordinary directory. * Unless 'linkOnly' is given, that directory is also removed. * * Assumption that LinkOriginal is a valid, existing junction. * * Returns zero on success. *-------------------------------------------------------------------- */int TclWinSymLinkDelete(LinkOriginal, linkOnly) CONST TCHAR* LinkOriginal; int linkOnly;{ /* It is a symbolic link -- remove it */ DUMMY_REPARSE_BUFFER dummy; REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; HANDLE hFile; int returnedLength; memset(reparseBuffer, 0, sizeof(DUMMY_REPARSE_BUFFER)); reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; hFile = (*tclWinProcs->createFileProc)(LinkOriginal, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile != INVALID_HANDLE_VALUE) { if (!DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, reparseBuffer, REPARSE_MOUNTPOINT_HEADER_SIZE, NULL, 0, &returnedLength, NULL)) { /* Error setting junction */ TclWinConvertError(GetLastError()); CloseHandle(hFile); } else { CloseHandle(hFile); if (!linkOnly) { (*tclWinProcs->removeDirectoryProc)(LinkOriginal); } return 0; } } return -1;}/* *-------------------------------------------------------------------- * * WinReadLinkDirectory * * This routine reads a NTFS junction, using the undocumented * FSCTL_GET_REPARSE_POINT structure Win2K uses for mount points * and junctions. * * Assumption that LinkDirectory is a valid, existing directory. * * Returns a Tcl_Obj with refCount of 1 (i.e. owned by the caller). *-------------------------------------------------------------------- */static Tcl_Obj* WinReadLinkDirectory(LinkDirectory) CONST TCHAR* LinkDirectory;{ int attr; DUMMY_REPARSE_BUFFER dummy; REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; attr = (*tclWinProcs->getFileAttributesProc)(LinkDirectory); if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) { Tcl_SetErrno(EINVAL); return NULL; } if (NativeReadReparse(LinkDirectory, reparseBuffer)) { return NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -