diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index b9f7e05452..5fb3f53ef5 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -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 diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc index d14ea593f6..0c21fa624e 100644 --- a/winsup/cygwin/globals.cc +++ b/winsup/cygwin/globals.cc @@ -57,6 +57,7 @@ enum winsym_t WSYM_nativestrict, WSYM_nfs, WSYM_sysfile, + WSYM_deepcopy }; exit_states NO_COPY exit_state; @@ -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; diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 84851a12f3..8615386aff 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -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 @@ -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);