diff options
Diffstat (limited to 'win/tclWinFile.c')
-rw-r--r-- | win/tclWinFile.c | 212 |
1 files changed, 188 insertions, 24 deletions
diff --git a/win/tclWinFile.c b/win/tclWinFile.c index 622be3c..6195c18 100644 --- a/win/tclWinFile.c +++ b/win/tclWinFile.c @@ -11,7 +11,7 @@ * 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.30 2002/06/12 19:16:01 hobbs Exp $ + * RCS: @(#) $Id: tclWinFile.c,v 1.31 2002/06/13 09:40:02 vincentdarley Exp $ */ //#define _WIN32_WINNT 0x0500 @@ -22,14 +22,14 @@ #include <shlobj.h> #include <lmaccess.h> /* For TclpGetUserHome(). */ -extern int ConvertFileNameFormat(Tcl_Interp *interp, - int objIndex, Tcl_Obj *fileName, int longShort, - Tcl_Obj **attributePtrPtr); +extern int ConvertFileNameFormat(Tcl_Interp *interp, + int objIndex, Tcl_Obj *fileName, int longShort, + Tcl_Obj **attributePtrPtr); /* - * Declarations for 'link' related information (which may or may - * not be in the windows headers, and some of which is not very - * well documented). + * 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 @@ -90,6 +90,13 @@ extern int ConvertFileNameFormat(Tcl_Interp *interp, /* * 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 @@ -120,6 +127,11 @@ typedef struct _REPARSE_DATA_BUFFER { } REPARSE_DATA_BUFFER; #endif +typedef 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); @@ -149,12 +161,86 @@ static int NativeMatchType(CONST char *name, int nameLen, 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 linkType); +static int WinSymLinkDirectory(CONST TCHAR* LinkDirectory, + CONST TCHAR* LinkTarget); extern Tcl_Filesystem nativeFilesystem; /* *-------------------------------------------------------------------- * + * WinLink + * + * Make a link from source to target. + *-------------------------------------------------------------------- + */ +static int +WinLink(LinkSource, LinkTarget, linkType) + CONST TCHAR* LinkSource; + CONST TCHAR* LinkTarget; + int linkType; +{ + 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 (linkType == 1) { + /* Can't symlink files */ + return -1; + } + if (!(*tclWinProcs->createHardLinkProc)(LinkSource, LinkTarget, NULL)) { + TclWinConvertError(GetLastError()); + return -1; + } + return 0; + } else { + if (linkType == 2) { + /* Can't hard link directories */ + return -1; + } + return WinSymLinkDirectory(LinkSource, LinkTarget); + } +} + +/* + *-------------------------------------------------------------------- + * * WinReadLink * * What does 'LinkSource' point to? We need the original 'pathPtr' @@ -195,6 +281,67 @@ WinReadLink(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 @@ -209,12 +356,13 @@ 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; - REPARSE_DATA_BUFFER reparseBuffer; - if (NativeReadReparse(LinkOriginal, &reparseBuffer)) { + if (NativeReadReparse(LinkOriginal, reparseBuffer)) { return -1; } - return NativeWriteReparse(LinkCopy, &reparseBuffer); + return NativeWriteReparse(LinkCopy, reparseBuffer); } /* @@ -237,16 +385,17 @@ TclWinSymLinkDelete(LinkOriginal, linkOnly) int linkOnly; { /* It is a symbolic link -- remove it */ + DUMMY_REPARSE_BUFFER dummy; + REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; HANDLE hFile; - REPARSE_DATA_BUFFER buffer; int returnedLength; - memset(&buffer, 0, sizeof( buffer )); - buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + 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, &buffer, + if (!DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, reparseBuffer, REPARSE_MOUNTPOINT_HEADER_SIZE, NULL, 0, &returnedLength, NULL)) { /* Error setting junction */ @@ -282,18 +431,19 @@ WinReadLinkDirectory(LinkDirectory) CONST TCHAR* LinkDirectory; { int attr; - REPARSE_DATA_BUFFER reparseBuffer; + 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)) { + if (NativeReadReparse(LinkDirectory, reparseBuffer)) { return NULL; } - switch (reparseBuffer.ReparseTag) { + switch (reparseBuffer->ReparseTag) { case 0x80000000|IO_REPARSE_TAG_SYMBOLIC_LINK: case IO_REPARSE_TAG_SYMBOLIC_LINK: case IO_REPARSE_TAG_MOUNT_POINT: { @@ -301,12 +451,12 @@ WinReadLinkDirectory(LinkDirectory) ClientData clientData; Tcl_Obj *retVal; - len = reparseBuffer.SymbolicLinkReparseBuffer.SubstituteNameLength + len = reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + sizeof(WCHAR); clientData = (ClientData)ckalloc(len); memcpy((VOID*)clientData, - (VOID*)reparseBuffer.SymbolicLinkReparseBuffer.PathBuffer, - len); + (VOID*)reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, + len); retVal = Tcl_FSNewNativePath(&nativeFilesystem, clientData); Tcl_IncrRefCount(retVal); @@ -347,8 +497,8 @@ NativeReadReparse(LinkDirectory, buffer) } /* Get the link */ if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, - 0, buffer, - sizeof(REPARSE_DATA_BUFFER), &returnedLength, NULL)) { + 0, buffer, sizeof(DUMMY_REPARSE_BUFFER), + &returnedLength, NULL)) { /* Error setting junction */ TclWinConvertError(GetLastError()); CloseHandle(hFile); @@ -1705,12 +1855,26 @@ TclpObjLstat(pathPtr, statPtr) #ifdef S_IFLNK Tcl_Obj* -TclpObjLink(pathPtr, toPtr) +TclpObjLink(pathPtr, toPtr, linkType) Tcl_Obj *pathPtr; Tcl_Obj *toPtr; + int linkType; { if (toPtr != NULL) { - return NULL; + int res; + TCHAR* LinkTarget = (TCHAR*)Tcl_FSGetNativePath(toPtr); + TCHAR* LinkSource = (TCHAR*)Tcl_FSGetNativePath(pathPtr); + if (LinkSource == NULL || LinkTarget == NULL) { + return NULL; + } + /* We don't recognise these codes */ + if (linkType < 0 || linkType > 2) return NULL; + res = WinLink(LinkSource, LinkTarget, linkType); + if (res == 0) { + return toPtr; + } else { + return NULL; + } } else { TCHAR* LinkSource = (TCHAR*)Tcl_FSGetNativePath(pathPtr); if (LinkSource == NULL) { |