[PATCH] Symlink support for msysgit

112 views
Skip to first unread message

mic...@frog.wheelycreek.net

unread,
Jan 25, 2011, 1:12:33 AM1/25/11
to msy...@googlegroups.com

This is a combination of 3 patches that have been lost in merges.

 

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).

 

Original added by: Johannes Schindelin <johannes....@gmx.de>

 

Support for utf8 in symlinks.

 

Copied from work added by Johannes Schindelin <johannes....@gmx.de>

 

Replace dir separator in symlinks

 

It seems quite a few windows utilities cannot handle '../' in symlinks, so

we replace every / with a \.

 

Replace make_backslash_path with a thread-safe version.

 

original by: Thorvald Natvig <sli...@users.sourceforge.net>

 

Signed-off-by: Michael Geddes <mic...@frog.wheelycreek.net>

---

compat/mingw.c |   93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--

compat/mingw.h |    6 +--

2 files changed, 92 insertions(+), 7 deletions(-)

 

diff --git a/compat/mingw.c b/compat/mingw.c

index 7050a1e..b3f0003 100644

--- a/compat/mingw.c

+++ b/compat/mingw.c

@@ -1,6 +1,7 @@

#include "../git-compat-util.h"

#include "win32.h"

#include <conio.h>

+#include <winioctl.h>

#include <wchar.h>

#include "../strbuf.h"

#include "../cache.h"

@@ -423,6 +424,47 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)

               return (time_t)(filetime_to_hnsec(ft) / 10000000);

}

+#ifndef FSCTL_GET_REPARSE_POINT

+#define FSCTL_GET_REPARSE_POINT  0x000900a8

+#endif

+

+static int do_readlink(const wchar_t *path, wchar_t *buf, size_t bufsiz)

+{

+             HANDLE handle = CreateFileW(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);

+                                                             int i;

+                                                             len = (bufsiz < len) ? bufsiz : len;

+                                                             wcsncpy(buf, & b->SymbolicLinkReparseBuffer.PathBuffer[offset], len);

+                                                             for (i = 0; i < len; i++)

+                                                                             if (buf[i] == '\\')

+                                                                                             buf[i] = '/';

+                                                             buf[len] = 0;

+                                                             CloseHandle(handle);

+                                                             return len;

+                                             }

+                             }

+

+                             CloseHandle(handle);

+             }

+

+             errno = EINVAL;

+             return -1;

+}

+

+

/* 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.

@@ -1767,9 +1809,8 @@ sig_handler_t mingw_signal(int sig, sig_handler_t handler)

               return old;

}

-static const char *make_backslash_path(const char *path)

+static const char *make_backslash_path(const char *path, char *buf)

{

-              static char buf[PATH_MAX + 1];

               char *c;

                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)

@@ -1784,7 +1825,8 @@ static const char *make_backslash_path(const char *path)

 void mingw_open_html(const char *unixpath)

{

-              const char *htmlpath = make_backslash_path(unixpath);

+             char buf[PATH_MAX + 1];

+             const char *htmlpath = make_backslash_path(unixpath, buf);

               typedef HINSTANCE (WINAPI *T)(HWND, const char *,

                                               const char *, const char *, const char *, INT);

               T ShellExecute;

@@ -1834,6 +1876,51 @@ int link(const char *oldpath, const char *newpath)

               return 0;

}

+int symlink(const char *oldpath, const char *newpath)

+{

+             typedef BOOL WINAPI (*symlink_fn)(const wchar_t*, const wchar_t*, DWORD);

+             static symlink_fn create_symbolic_link = NULL;

+             char buf[PATH_MAX + 1];

+             wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];

+

+             if (utftowcs(woldpath, make_backslash_path(oldpath, buf), MAX_PATH) < 0)

+                             return -1;

+             if (utftowcs(wnewpath, newpath, MAX_PATH) < 0)

+                             return -1;

+

+             if (!create_symbolic_link) {

+                             create_symbolic_link = (symlink_fn) GetProcAddress(

+                                                             GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkW");

+                             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(wnewpath, woldpath, 0)) {

+                             errno = err_win_to_posix(GetLastError());

+                             return -1;

+             }

+             return 0;

+}

+

+int do_readlink(const wchar_t *path, wchar_t *buf, size_t bufsiz);

+

+int readlink(const char *path, char *buf, size_t bufsiz)

+{

+             wchar_t wpath[MAX_PATH], wbuffer[MAX_PATH];

+             int result;

+             if (utftowcs(wpath, path, MAX_PATH) < 0)

+                             return -1;

+             result = do_readlink(wpath, wbuffer, MAX_PATH);

+             if (result >= 0) {

+                             return wcstoutf(buf, wbuffer, bufsiz);

+             }

+             return -1;

+}

+

char *getpass(const char *prompt)

{

               struct strbuf buf = STRBUF_INIT;

diff --git a/compat/mingw.h b/compat/mingw.h

index afb7faf..5bb9008 100644

--- a/compat/mingw.h

+++ b/compat/mingw.h

@@ -79,10 +79,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 pid_t fork(void)

@@ -158,6 +154,8 @@ struct passwd *getpwuid(uid_t 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

--

1.7.3.4.3927.g47aaf

 

Michael

unread,
Feb 3, 2011, 12:19:35 AM2/3/11
to msy...@googlegroups.com
On Tuesday, January 25, 2011 2:12:33 PM UTC+8, Michael wrote:

This is a combination of 3 patches that have been lost in merges.

.

Hm.. I think 'LookOut' managed to insert a few lines in my initial post.  I can repost that patch easily enough and turn off html. 

I've been following up on this, and having an 'exciting' time getting the tests to recognise symlinks.  To start with, the msys commands don't want to play nicely.  

So far 

* ln -s   : doesn't make ntfs symlinks - had to replace it with a function that calls mklink.
* test -h : doesn't work for ntfs symlinks - had to replace it with a function that uses dir /a+l 
      (At least dir has the courtesy of failing if the file is not found with the correct attributes)
* rm   : doesn't work on a dangling (ntfs) symlink.
* ls  : doesn't work on a dangling (ntfs) smlink

I've at least got these basic symlink tests working: t0055 t2005 t2007 t2102 

diffs of symlinks seem to fail so guess that's the next point of call.

The disheartening thing about this work, is that without msys commands dealing with symlinks properly, I'm not confident of being able to do a whole lot with it.

Thoughts? Comments?

On the up-side, I'm getting more familiar with git and its internals, and should be up for some other testing/patching in the near future.

//.ichael 

Cesar Strauss

unread,
Feb 3, 2011, 6:32:04 PM2/3/11
to msy...@googlegroups.com, Michael
On 2/3/2011 3:19 AM, Michael wrote:

> * ln -s : doesn't make ntfs symlinks - had to replace it with a function
> that calls mklink.
> * test -h : doesn't work for ntfs symlinks - had to replace it with a
> function that uses dir /a+l
> (At least dir has the courtesy of failing if the file is not found with
> the correct attributes)
> * rm : doesn't work on a dangling (ntfs) symlink.
> * ls : doesn't work on a dangling (ntfs) smlink
>

There is work in progress to bring the ability to create and deal with
native symlinks on MSYS.

See patch #3046195 "Native symlinks" on the MinGW patch tracker:
http://sourceforge.net/tracker/index.php?func=detail&aid=3046195&group_id=2435&atid=302435

Regards,
Cesar

Johannes Schindelin

unread,
Feb 17, 2011, 8:45:05 AM2/17/11
to Michael, msy...@googlegroups.com
Hi,

On Wed, 2 Feb 2011, Michael wrote:

> On Tuesday, January 25, 2011 2:12:33 PM UTC+8, Michael wrote:
> >
> > This is a combination of 3 patches that have been lost in merges.

Sorry, I might have missed it (and the last 2 weeks of hackathon craziness
did not help my memory), so please indulge with me: did you set up a fork
on github or repo.or.cz yet?

Ciao,
Dscho

Michael Geddes

unread,
Feb 17, 2011, 6:38:22 PM2/17/11
to msy...@googlegroups.com, Johannes Schindelin
Thanks Johannes for the 'push' to submit this.

http://github.com/frogonwheels/git
branch: unicode_symlink

I've merged with the latest /devel

//.

Reply all
Reply to author
Forward
0 new messages