From ea20f4081910213be44e2f38360099f961091c2c Mon Sep 17 00:00:00 2001 From: Thorvald Natvig Date: Mon, 10 Aug 2009 12:47:37 +0200 Subject: [PATCH] Symlink support Add symlink support; with UAC, you may need administrator's privileges to create symlinks, but you can at least read them. As reparse points on Windows differentiate between file and directory links, we just assume that file links are meant for the time being; it might be very hard to determine the type before the target exists, but it is thinkable to change the type on-the-fly. A bridge to cross when we arrive there (read: something for somebody to fix who actually has that problem). Signed-off-by: Johannes Schindelin --- compat/mingw.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++- compat/mingw.h | 6 ++--- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 5cda95d084..20f370b73b 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1,6 +1,7 @@ #include "../git-compat-util.h" #include "win32.h" #include +#include #include "../strbuf.h" unsigned int _CRT_fmode = _O_BINARY; @@ -166,6 +167,21 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + WIN32_FIND_DATAA findbuf; + HANDLE handle = FindFirstFileA(file_name, &findbuf); + if (handle != INVALID_HANDLE_VALUE) { + if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { + char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + buf->st_mode = S_IREAD | S_IFLNK; + if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + buf->st_mode |= S_IWRITE; + buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + } + FindClose(handle); + } + } return 0; } return -1; @@ -1334,6 +1350,58 @@ int link(const char *oldpath, const char *newpath) return 0; } +int symlink(const char *oldpath, const char *newpath) +{ + typedef BOOL WINAPI (*symlink_fn)(const char*, const char*, DWORD); + static symlink_fn create_symbolic_link = NULL; + if (!create_symbolic_link) { + create_symbolic_link = (symlink_fn) GetProcAddress( + GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkA"); + if (!create_symbolic_link) + create_symbolic_link = (symlink_fn)-1; + } + if (create_symbolic_link == (symlink_fn)-1) { + errno = ENOSYS; + return -1; + } + + if (!create_symbolic_link(newpath, oldpath, 0)) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + return 0; +} + +int readlink(const char *path, char *buf, size_t bufsiz) +{ + HANDLE handle = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + + if (handle != INVALID_HANDLE_VALUE) { + unsigned char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + DWORD dummy = 0; + if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dummy, NULL)) { + REPARSE_DATA_BUFFER *b = (REPARSE_DATA_BUFFER *) buffer; + if (b->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + int len = b->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + int offset = b->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); + snprintf(buf, bufsiz, "%*ls", len, & b->SymbolicLinkReparseBuffer.PathBuffer[offset]); + CloseHandle(handle); + return len; + } + } + + CloseHandle(handle); + } + + errno = EINVAL; + return -1; +} + char *getpass(const char *prompt) { struct strbuf buf = STRBUF_INIT; @@ -1399,7 +1467,10 @@ struct dirent *mingw_readdir(DIR *dir) /* Set file type, based on WIN32_FIND_DATA */ mdir->dd_dir.d_type = 0; - if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + if ((buf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (buf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + mdir->dd_dir.d_type |= DT_LNK; + else if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mdir->dd_dir.d_type |= DT_DIR; else mdir->dd_dir.d_type |= DT_REG; diff --git a/compat/mingw.h b/compat/mingw.h index c76af61617..4f9f7f4887 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -66,10 +66,6 @@ struct itimerval { * trivial stubs */ -static inline int readlink(const char *path, char *buf, size_t bufsiz) -{ errno = ENOSYS; return -1; } -static inline int symlink(const char *oldpath, const char *newpath) -{ errno = ENOSYS; return -1; } static inline int fchmod(int fildes, mode_t mode) { errno = ENOSYS; return -1; } static inline int fork(void) @@ -138,6 +134,8 @@ struct passwd *getpwuid(int uid); int setitimer(int type, struct itimerval *in, struct itimerval *out); int sigaction(int sig, struct sigaction *in, struct sigaction *out); int link(const char *oldpath, const char *newpath); +int symlink(const char *oldpath, const char *newpath); +int readlink(const char *path, char *buf, size_t bufsiz); /* * replacements of existing functions