From ea20f4081910213be44e2f38360099f961091c2c Mon Sep 17 00:00:00 2001 From: Thorvald Natvig Date: Mon, 10 Aug 2009 12:47:37 +0200 Subject: [PATCH 1/2] 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 From afb9111bacf3da37d87186057739b3e3fbf044b0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 11 Aug 2009 18:08:44 +0200 Subject: [PATCH 2/2] Fix style of multi-line comments In git.git, we want multi-line comments between single /* and */ in a line. Signed-off-by: Johannes Schindelin --- compat/mingw.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 20f370b73b..ed2664dc00 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -147,7 +147,8 @@ static inline time_t filetime_to_time_t(const FILETIME *ft) return (time_t)winTime; } -/* We keep the do_lstat code in a separate function to avoid recursion. +/* + * We keep the do_lstat code in a separate function to avoid recursion. * When a path ends with a slash, the stat will fail with ENOENT. In * this case, we strip the trailing slashes and stat again. */ @@ -187,7 +188,8 @@ static int do_lstat(const char *file_name, struct stat *buf) return -1; } -/* We provide our own lstat/fstat functions, since the provided +/* + * We provide our own lstat/fstat functions, since the provided * lstat/fstat functions are so slow. These stat functions are * tailored for Git's usage (read: fast), and are not meant to be * complete. Note that Git stat()s are redirected to mingw_lstat() @@ -201,7 +203,8 @@ int mingw_lstat(const char *file_name, struct stat *buf) if (!do_lstat(file_name, buf)) return 0; - /* if file_name ended in a '/', Windows returned ENOENT; + /* + * if file_name ended in a '/', Windows returned ENOENT; * try again without trailing slashes */ if (errno != ENOENT) @@ -368,7 +371,8 @@ int poll(struct pollfd *ufds, unsigned int nfds, int timeout) return errno = EINVAL, error("poll timeout not supported"); } - /* When there is only one fd to wait for, then we pretend that + /* + * When there is only one fd to wait for, then we pretend that * input is available and let the actual wait happen when the * caller invokes read(). */ @@ -408,7 +412,8 @@ repeat: ufds[i].revents = 0; } if (!pending) { - /* The only times that we spin here is when the process + /* + * The only times that we spin here is when the process * that is connected through the pipes is waiting for * its own input data to become available. But since * the process (pack-objects) is itself CPU intensive, @@ -663,7 +668,8 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (cons == INVALID_HANDLE_VALUE) { - /* There is no console associated with this process. + /* + * There is no console associated with this process. * Since the child is a console process, Windows * would normally create a console window. But * since we'll be redirecting std streams, we do @@ -674,7 +680,8 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env, */ flags = DETACHED_PROCESS; } else { - /* There is already a console. If we specified + /* + * There is already a console. If we specified * DETACHED_PROCESS here, too, Windows would * disassociate the child from the console. * The same is true for CREATE_NO_WINDOW. @@ -874,9 +881,7 @@ static int lookup_env(char **env, const char *name, size_t nmln) return -1; } -/* - * If name contains '=', then sets the variable, otherwise it unsets it - */ +/* If name contains '=', then sets the variable, otherwise it unsets it */ char **env_setenv(char **env, const char *name) { char *eq = strchrnul(name, '='); @@ -1195,7 +1200,8 @@ static int timer_interval; static int one_shot; static sig_handler_t timer_fn = SIG_DFL; -/* The timer works like this: +/* + * The timer works like this: * The thread, ticktack(), is a trivial routine that most of the time * only waits to receive the signal to terminate. The main thread tells * the thread to terminate by setting the timer_event to the signalled @@ -1454,8 +1460,10 @@ struct dirent *mingw_readdir(DIR *dir) DWORD lasterr = GetLastError(); FindClose((HANDLE)dir->dd_handle); dir->dd_handle = (long)INVALID_HANDLE_VALUE; - /* POSIX says you shouldn't set errno when readdir can't - find any more files; so, if another error we leave it set. */ + /* + * POSIX says you shouldn't set errno when readdir can't + * find any more files; so, if another error we leave it set. + */ if (lasterr != ERROR_NO_MORE_FILES) errno = err_win_to_posix(lasterr); return NULL;