Skip to content

Commit

Permalink
Instead of creating Cygwin symlinks, use deep copy by default
Browse files Browse the repository at this point in the history
The new `winsymlinks` mode `deepcopy` (which is made the default) lets
calls to `symlink()` create (deep) copies of the source file/directory.

This is necessary because unlike Cygwin, MSYS2 does not try to be its
own little ecosystem that lives its life separate from regular Win32
programs: the latter have _no idea_ about Cygwin-emulated symbolic links
(i.e. system files whose contents start with `!<symlink>\xff\xfe` and
the remainder consists of the NUL-terminated, UTF-16LE-encoded symlink
target).

To support Cygwin-style symlinks, the new mode `sysfile` is introduced.

Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
Alexpux and dscho committed Aug 27, 2024
1 parent 6393488 commit 06ea20e
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 1 deletion.
4 changes: 4 additions & 0 deletions winsup/cygwin/environ.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ set_winsymlinks (const char *buf)
else if (ascii_strncasematch (buf, "native", 6))
allow_winsymlinks = ascii_strcasematch (buf + 6, "strict")
? WSYM_nativestrict : WSYM_native;
else if (ascii_strncasematch (buf, "deepcopy", 8))
allow_winsymlinks = WSYM_deepcopy;
else
allow_winsymlinks = WSYM_sysfile;
}

/* The structure below is used to set up an array which is used to
Expand Down
3 changes: 2 additions & 1 deletion winsup/cygwin/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ enum winsym_t
WSYM_nativestrict,
WSYM_nfs,
WSYM_sysfile,
WSYM_deepcopy
};

exit_states NO_COPY exit_state;
Expand All @@ -70,7 +71,7 @@ bool ignore_case_with_glob;
bool pipe_byte = true; /* Default to byte mode so that C# programs work. */
bool reset_com;
bool wincmdln;
winsym_t allow_winsymlinks = WSYM_default;
winsym_t allow_winsymlinks = WSYM_deepcopy;
bool disable_pcon;
bool winjitdebug = false;

Expand Down
154 changes: 154 additions & 0 deletions winsup/cygwin/path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,89 @@ conv_path_list (const char *src, char *dst, size_t size,

/********************** Symbolic Link Support **************************/

/*
Create a deep copy of src as dst, while avoiding descending in origpath.
*/
static int
recursiveCopy (char * src, char * dst, const char * origpath)
{
WIN32_FIND_DATA dHfile;
HANDLE dH = INVALID_HANDLE_VALUE;
BOOL findfiles;
int srcpos = strlen (src);
int dstpos = strlen (dst);
int res = -1;

debug_printf("recursiveCopy (%s, %s)", src, dst);

/* Create the destination directory */
if (!CreateDirectoryEx (src, dst, NULL))
{
debug_printf("CreateDirectoryEx(%s, %s, 0) failed", src, dst);
__seterrno ();
goto done;
}
/* Descend into the source directory */
if (srcpos + 2 >= MAX_PATH || dstpos + 1 >= MAX_PATH)
{
set_errno (ENAMETOOLONG);
goto done;
}
strcat (src, "\\*");
strcat (dst, "\\");
dH = FindFirstFile (src, &dHfile);
debug_printf("dHfile(1): %s", dHfile.cFileName);
findfiles = FindNextFile (dH, &dHfile);
debug_printf("dHfile(2): %s", dHfile.cFileName);
findfiles = FindNextFile (dH, &dHfile);
while (findfiles)
{
/* Append the directory item filename to both source and destination */
int filelen = strlen (dHfile.cFileName);
debug_printf("dHfile(3): %s", dHfile.cFileName);
if (srcpos + 1 + filelen >= MAX_PATH ||
dstpos + 1 + filelen >= MAX_PATH)
{
set_errno (ENAMETOOLONG);
goto done;
}
strcpy (&src[srcpos+1], dHfile.cFileName);
strcpy (&dst[dstpos+1], dHfile.cFileName);
debug_printf("%s -> %s", src, dst);
if (dHfile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
/* Recurse into the child directory */
debug_printf("%s <-> %s", src, origpath);
if (strcmp (src, origpath)) // avoids endless recursion
if (recursiveCopy (src, dst, origpath))
goto done;
}
else
{
/* Just copy the file */
if (!CopyFile (src, dst, FALSE))
{
__seterrno ();
goto done;
}
}
findfiles = FindNextFile (dH, &dHfile);
}
if (GetLastError() != ERROR_NO_MORE_FILES)
{
__seterrno ();
goto done;
}
res = 0;

done:

if (dH != INVALID_HANDLE_VALUE)
FindClose (dH);

return res;
}

/* Create a symlink from FROMPATH to TOPATH. */

extern "C" int
Expand Down Expand Up @@ -2219,6 +2302,77 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
}
else /* wsym_type == WSYM_sysfile */
{
if (wsym_type == WSYM_deepcopy)
{
path_conv src_path;
src_path.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
if (src_path.error)
{
set_errno (src_path.error);
__leave;
}
if (!src_path.isdevice () && !src_path.is_fs_special ())
{
/* MSYS copy file instead make symlink */

char * real_oldpath;
if (isabspath (oldpath))
strcpy (real_oldpath = tp.c_get (), oldpath);
else
/* Find the real source path, relative
to the directory of the destination */
{
/* Determine the character position of the last path component */
const char *newpath = win32_newpath.get_posix();
int pos = strlen (newpath);
while (--pos >= 0)
if (isdirsep (newpath[pos]))
break;
/* Append the source path to the directory
component of the destination */
if (pos+1+strlen(oldpath) >= MAX_PATH)
{
set_errno(ENAMETOOLONG);
__leave;
}
strcpy (real_oldpath = tp.c_get (), newpath);
strcpy (&real_oldpath[pos+1], oldpath);
}

/* As a MSYS limitation, the source path must exist. */
path_conv win32_oldpath;
win32_oldpath.check (real_oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
if (!win32_oldpath.exists ())
{
set_errno (ENOENT);
__leave;
}

char *w_newpath;
char *w_oldpath;
stpcpy (w_newpath = tp.c_get (), win32_newpath.get_win32());
stpcpy (w_oldpath = tp.c_get (), win32_oldpath.get_win32());
if (win32_oldpath.isdir())
{
char *origpath;
strcpy (origpath = tp.c_get (), w_oldpath);
res = recursiveCopy (w_oldpath, w_newpath, origpath);
}
else
{
if (!CopyFile (w_oldpath, w_newpath, FALSE))
{
__seterrno ();
}
else
{
res = 0;
}
}
__leave;
}
}

/* Default technique creating a symlink. */
buf = tp.t_get ();
cp = stpcpy (buf, SYMLINK_COOKIE);
Expand Down

0 comments on commit 06ea20e

Please sign in to comment.