Skip to content

Commit

Permalink
fixup! Instead of creating Cygwin symlinks, use deep copy by default
Browse files Browse the repository at this point in the history
Factor out deepcopy symlink to its own worker function, like wsl,
native, and nfs.  Move it up into the beginning switch with them, so the
fallback behavior is more obvious.  See also msys2#113/msys2#114.
  • Loading branch information
jeremyd2019 committed Feb 1, 2025
1 parent 965fb2d commit 94ae14f
Showing 1 changed file with 90 additions and 84 deletions.
174 changes: 90 additions & 84 deletions winsup/cygwin/path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2144,6 +2144,88 @@ symlink_wsl (const char *oldpath, path_conv &win32_newpath)
return 0;
}

int
symlink_deepcopy (const char *oldpath, path_conv &win32_newpath)
{
tmp_pathbuf tp;
path_conv win32_oldpath;

/* The symlink target is relative to the directory in which the
symlink gets created, not relative to the cwd. Therefore we
have to mangle the path quite a bit before calling path_conv.*/
if (isabspath (oldpath))
win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
else
{
size_t len = strrchr (win32_newpath.get_posix (), '/')
- win32_newpath.get_posix () + 1;
char *absoldpath = tp.t_get ();
stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len),
oldpath);
win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
}
if (win32_oldpath.error)
{
set_errno (win32_oldpath.error);
return -1;
}
if (win32_oldpath.isspecial ())
return -2;

/* MSYS copy file instead make symlink */
/* As a MSYS limitation, the source path must exist. */
if (!win32_oldpath.exists ())
{
set_errno (ENOENT);
return -1;
}

PUNICODE_STRING w_oldpath = win32_oldpath.get_nt_native_path ();
PUNICODE_STRING w_newpath = win32_newpath.get_nt_native_path ();
if (w_oldpath->Buffer[1] == L'?')
w_oldpath->Buffer[1] = L'\\';
if (w_newpath->Buffer[1] == L'?')
w_newpath->Buffer[1] = L'\\';
if (win32_oldpath.isdir ())
{
/* we need a larger UNICODE_STRING MaximumLength than
get_nt_native_path allocates for the recursive copy */
UNICODE_STRING u_oldpath, u_newpath;
RtlCopyUnicodeString (tp.u_get (&u_oldpath), w_oldpath);
RtlCopyUnicodeString (tp.u_get (&u_newpath), w_newpath);
return recursiveCopy (&u_oldpath, &u_newpath,
u_oldpath.Length, u_newpath.Length);
}
else
{
bool isdirlink = false;
if (win32_oldpath.issymlink () &&
win32_oldpath.is_known_reparse_point ())
{
/* Is there a better way to know this? */
DWORD attr = getfileattr (win32_oldpath.get_win32 (),
!!win32_oldpath.objcaseinsensitive ());
if (attr == INVALID_FILE_ATTRIBUTES)
{
__seterrno ();
return -1;
}
isdirlink = attr & FILE_ATTRIBUTE_DIRECTORY;
}
if (!CopyFileExW (w_oldpath->Buffer, w_newpath->Buffer, NULL, NULL, NULL,
COPY_FILE_COPY_SYMLINK|
(isdirlink ? COPY_FILE_DIRECTORY : 0)))
{
__seterrno ();
return -1;
}
else
{
return 0;
}
}
}

int
symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
{
Expand Down Expand Up @@ -2212,6 +2294,13 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
case WSYM_nfs:
res = symlink_nfs (oldpath, win32_newpath);
__leave;
case WSYM_deepcopy:
res = symlink_deepcopy (oldpath, win32_newpath);
if (!res || res == -1)
__leave;
/* fall back to default symlink type */
wsym_type = WSYM_default;
goto handle_default;
case WSYM_native:
case WSYM_nativestrict:
res = symlink_native (oldpath, win32_newpath);
Expand All @@ -2228,6 +2317,7 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
wsym_type = WSYM_default;
fallthrough;
case WSYM_default:
handle_default:
if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
{
res = symlink_wsl (oldpath, win32_newpath);
Expand Down Expand Up @@ -2380,90 +2470,6 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
}
else /* wsym_type == WSYM_sysfile */
{
if (wsym_type == WSYM_deepcopy)
{
path_conv win32_oldpath;
/* The symlink target is relative to the directory in which the
symlink gets created, not relative to the cwd. Therefore we
have to mangle the path quite a bit before calling path_conv.*/
if (isabspath (oldpath))
win32_oldpath.check (oldpath,
PC_SYM_NOFOLLOW,
stat_suffixes);
else
{
len = strrchr (win32_newpath.get_posix (), '/')
- win32_newpath.get_posix () + 1;
char *absoldpath = tp.t_get ();
stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (),
len),
oldpath);
win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW,
stat_suffixes);
}
if (win32_oldpath.error)
{
set_errno (win32_oldpath.error);
__leave;
}
if (!win32_oldpath.isspecial ())
{
/* MSYS copy file instead make symlink */
/* As a MSYS limitation, the source path must exist. */
if (!win32_oldpath.exists ())
{
set_errno (ENOENT);
__leave;
}

PUNICODE_STRING w_oldpath = win32_oldpath.get_nt_native_path ();
PUNICODE_STRING w_newpath = win32_newpath.get_nt_native_path ();
if (w_oldpath->Buffer[1] == L'?')
w_oldpath->Buffer[1] = L'\\';
if (w_newpath->Buffer[1] == L'?')
w_newpath->Buffer[1] = L'\\';
if (win32_oldpath.isdir ())
{
/* we need a larger UNICODE_STRING MaximumLength than
get_nt_native_path allocates for the recursive copy */
UNICODE_STRING u_oldpath, u_newpath;
RtlCopyUnicodeString (tp.u_get (&u_oldpath), w_oldpath);
RtlCopyUnicodeString (tp.u_get (&u_newpath), w_newpath);
res = recursiveCopy (&u_oldpath, &u_newpath,
u_oldpath.Length, u_newpath.Length);
}
else
{
bool isdirlink = false;
if (win32_oldpath.issymlink () &&
win32_oldpath.is_known_reparse_point ())
{
/* Is there a better way to know this? */
DWORD attr = getfileattr (win32_oldpath.get_win32 (),
!!win32_oldpath.objcaseinsensitive ());
if (attr == INVALID_FILE_ATTRIBUTES)
{
__seterrno ();
__leave;
}
isdirlink = attr & FILE_ATTRIBUTE_DIRECTORY;
}
if (!CopyFileExW (w_oldpath->Buffer, w_newpath->Buffer,
NULL, NULL, NULL,
COPY_FILE_COPY_SYMLINK|
(isdirlink ? COPY_FILE_DIRECTORY : 0)))
{
__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 94ae14f

Please sign in to comment.