diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2007-10-10 20:54:09 +0400 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2007-10-10 20:54:09 +0400 |
commit | 044b62c7676d56bcabea5956cae38771eb5b2179 (patch) | |
tree | f81c4546538d0c5c7d728f476098c339829a70b5 /winsup | |
parent | 4797f5bca35d9598ae6ac4a021ef37ba3f1a75e0 (diff) |
* path.cc (struct symlink_info): Change size of contents member to
be able to keep SYMLINK_MAX sized strings.
(symlink_worker): Rework for long path names. When writing windows
shortcuts, store pathname additionally "hidden" after the actual
shortcut data to workaround size limitations of the description field.
(symlink_info::check_shortcut): Drop file name parameter. Drop max
file size check. Use NT functions. Use appended full path if
available, description otherwise. Check symlink string length for
not exceeding SYMLINK_MAX. Don't close file here.
(symlink_info::check_sysfile): Drop file name parameter. Use NT
functions. Check symlink string length for not exceeding SYMLINK_MAX.
Don't close file here.
(symlink_info::check_reparse_point): Drop file name parameter. Drop
useless length checks. Allow SYMLINK_MAX length symlink strings.
Don't close file here.
(symlink_info::posixify): Allow SYMLINK_MAX length symlink strings.
(symlink_info::check): Turn around checking for symlink file attributes.
Use NT functions. Close symlink file here.
* include/limits.h (PATH_MAX): Define as 32760. Change comment.
(SYMLINK_MAX): Define as PATH_MAX - 1.
Diffstat (limited to 'winsup')
-rw-r--r-- | winsup/cygwin/ChangeLog | 23 | ||||
-rw-r--r-- | winsup/cygwin/include/limits.h | 7 | ||||
-rw-r--r-- | winsup/cygwin/path.cc | 501 |
3 files changed, 301 insertions, 230 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 169302d44..b47e7529e 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,5 +1,28 @@ 2007-10-10 Corinna Vinschen <corinna@vinschen.de> + * path.cc (struct symlink_info): Change size of contents member to + be able to keep SYMLINK_MAX sized strings. + (symlink_worker): Rework for long path names. When writing windows + shortcuts, store pathname additionally "hidden" after the actual + shortcut data to workaround size limitations of the description field. + (symlink_info::check_shortcut): Drop file name parameter. Drop max + file size check. Use NT functions. Use appended full path if + available, description otherwise. Check symlink string length for + not exceeding SYMLINK_MAX. Don't close file here. + (symlink_info::check_sysfile): Drop file name parameter. Use NT + functions. Check symlink string length for not exceeding SYMLINK_MAX. + Don't close file here. + (symlink_info::check_reparse_point): Drop file name parameter. Drop + useless length checks. Allow SYMLINK_MAX length symlink strings. + Don't close file here. + (symlink_info::posixify): Allow SYMLINK_MAX length symlink strings. + (symlink_info::check): Turn around checking for symlink file attributes. + Use NT functions. Close symlink file here. + * include/limits.h (PATH_MAX): Define as 32760. Change comment. + (SYMLINK_MAX): Define as PATH_MAX - 1. + +2007-10-10 Corinna Vinschen <corinna@vinschen.de> + * fhandler_socket.cc (fhandler_socket::bind): Open file for deletion, too. Don't write to file and especially don't close handle if file couldn't be created. Set delete disposition if writing failed, diff --git a/winsup/cygwin/include/limits.h b/winsup/cygwin/include/limits.h index f7ae460e8..6dcf9caa1 100644 --- a/winsup/cygwin/include/limits.h +++ b/winsup/cygwin/include/limits.h @@ -318,9 +318,10 @@ details. */ #undef NAME_MAX #define NAME_MAX 255 -/* Maximum length of a path */ +/* Maximum length of a path including trailing NUL. + (32767 - max. native NT path prefix) */ #undef PATH_MAX -#define PATH_MAX 260 +#define PATH_MAX 32760 /* # of bytes in a pipe buf. This is the max # of bytes which can be written to a pipe in one atomic operation. */ @@ -352,7 +353,7 @@ details. */ /* Maximum number of bytes in a symbolic link. */ #undef SYMLINK_MAX -#define SYMLINK_MAX PATH_MAX +#define SYMLINK_MAX (PATH_MAX - 1) /* Runtime Increasable Values */ diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 88add9a10..82be4df3e 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -89,7 +89,7 @@ static void backslashify (const char *, char *, int); struct symlink_info { - char contents[CYG_MAX_PATH + 4]; + char contents[SYMLINK_MAX + 1]; char *ext_here; int extn; unsigned pflags; @@ -106,9 +106,9 @@ struct symlink_info int set (char *path); bool parse_device (const char *); bool case_check (char *path); - int check_sysfile (const char *path, HANDLE h); - int check_shortcut (const char *path, HANDLE h); - int check_reparse_point (const char *path, HANDLE h); + int check_sysfile (HANDLE h); + int check_shortcut (HANDLE h); + int check_reparse_point (HANDLE h); int posixify (char *srcbuf); bool set_error (int); }; @@ -2874,16 +2874,17 @@ int symlink_worker (const char *oldpath, const char *newpath, bool use_winsym, bool isdevice) { - HANDLE h; int res = -1; - path_conv win32_path, win32_oldpath; - char from[CYG_MAX_PATH + 5]; - char cwd[CYG_MAX_PATH], *cp = NULL, c = 0; - char w32oldpath[CYG_MAX_PATH]; - char reloldpath[CYG_MAX_PATH] = { 0 }; - DWORD written; + size_t len; + path_conv win32_newpath, win32_oldpath; + char *buf, *cp; SECURITY_ATTRIBUTES sa = sec_none_nih; security_descriptor sd; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE fh; + FILE_BASIC_INFORMATION fbi; /* POSIX says that empty 'newpath' is invalid input while empty 'oldpath' is valid -- it's symlink resolver job to verify if @@ -2897,192 +2898,239 @@ symlink_worker (const char *oldpath, const char *newpath, bool use_winsym, goto done; } - if (strlen (oldpath) >= CYG_MAX_PATH) + if (strlen (oldpath) > SYMLINK_MAX) { set_errno (ENAMETOOLONG); goto done; } - win32_path.check (newpath, PC_SYM_NOFOLLOW, - transparent_exe ? stat_suffixes : NULL); - if (use_winsym && !win32_path.exists ()) + len = strlen (newpath); + /* Trailing dirsep is a no-no. */ + if (isdirsep (newpath[len - 1])) + { + set_errno (ENOENT); + goto done; + } + /* We need the normalized full path below. */ + win32_newpath.check (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes); + if (use_winsym && !win32_newpath.exists ()) { - strcpy (from, newpath); - strcat (from, ".lnk"); - win32_path.check (from, PC_SYM_NOFOLLOW); + char *newplnk = (char *) alloca (len + 5); + stpcpy (stpcpy (newplnk, newpath), ".lnk"); + win32_newpath.check (newplnk, PC_SYM_NOFOLLOW | PC_POSIX); } - if (win32_path.error) + if (win32_newpath.error) { - set_errno (win32_path.case_clash ? ECASECLASH : win32_path.error); + set_errno (win32_newpath.case_clash ? ECASECLASH : win32_newpath.error); goto done; } - syscall_printf ("symlink (%s, %s)", oldpath, win32_path.get_win32 ()); + syscall_printf ("symlink (%s, %S)", oldpath, + win32_newpath.get_nt_native_path ()); - if ((!isdevice && win32_path.exists ()) - || win32_path.is_auto_device ()) + if ((!isdevice && win32_newpath.exists ()) + || win32_newpath.is_auto_device ()) { set_errno (EEXIST); goto done; } - DWORD create_how; - if (!use_winsym) - create_how = CREATE_NEW; - else if (isdevice) + if (use_winsym) { - strcpy (w32oldpath, oldpath); - create_how = CREATE_ALWAYS; - SetFileAttributes (win32_path.get_win32 (), FILE_ATTRIBUTE_NORMAL); - } - else - { - if (!isabspath (oldpath)) - { - getcwd (cwd, CYG_MAX_PATH); - if ((cp = strrchr (from, '/')) || (cp = strrchr (from, '\\'))) - { - c = *cp; - *cp = '\0'; - chdir (from); - } - backslashify (oldpath, reloldpath, 0); - /* Creating an ITEMIDLIST requires an absolute path. So if we - create a shortcut file, we create relative and absolute Win32 - paths, the first for the relpath field and the latter for the - ITEMIDLIST field. */ - if (GetFileAttributes (reloldpath) == INVALID_FILE_ATTRIBUTES) - { - win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, - transparent_exe ? stat_suffixes : NULL); - if (win32_oldpath.error != ENOENT) - strcpy (use_winsym ? reloldpath : w32oldpath, - win32_oldpath.get_win32 ()); - } - else if (!use_winsym) - strcpy (w32oldpath, reloldpath); - if (use_winsym) - { - win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, - transparent_exe ? stat_suffixes : NULL); - strcpy (w32oldpath, win32_oldpath.get_win32 ()); - } - if (cp) - { - *cp = c; - chdir (cwd); - } - } - else - { - win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, - transparent_exe ? stat_suffixes : NULL); - strcpy (w32oldpath, win32_oldpath.get_win32 ()); - } - create_how = CREATE_NEW; - } + ITEMIDLIST *pidl = NULL; + size_t full_len = 0; + unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0; + char desc[MAX_PATH + 1], *relpath; - if (allow_ntsec && win32_path.has_acls ()) - set_security_attribute (S_IFLNK | STD_RBITS | STD_WBITS, - &sa, sd); - - h = CreateFile (win32_path.get_win32 (), GENERIC_WRITE, 0, &sa, create_how, - FILE_ATTRIBUTE_NORMAL, 0); - if (h == INVALID_HANDLE_VALUE) - __seterrno (); - else - { - bool success = false; - - if (use_winsym) - { - /* A path of 240 chars with 120 one character directories in it - can result in a 6K shortcut. */ - char *buf = (char *) alloca (8192); - win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf; - HRESULT hres; + if (!isdevice) + { + /* First create an IDLIST to learn how big our shortcut is + going to be. */ IShellFolder *psl; - WCHAR wc_path[CYG_MAX_PATH]; - ITEMIDLIST *pidl = NULL, *p; - unsigned short len; - - memset (shortcut_header, 0, sizeof *shortcut_header); - shortcut_header->size = sizeof *shortcut_header; - shortcut_header->magic = GUID_shortcut; - shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH); - shortcut_header->run = SW_NORMAL; - cp = buf + sizeof (win_shortcut_hdr); - /* Creating an IDLIST */ - hres = SHGetDesktopFolder (&psl); - if (SUCCEEDED (hres)) + + /* 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)) { - MultiByteToWideChar (CP_ACP, 0, w32oldpath, -1, wc_path, - CYG_MAX_PATH); - hres = psl->ParseDisplayName (NULL, NULL, wc_path, NULL, - &pidl, NULL); - if (SUCCEEDED (hres)) + len = strrchr (win32_newpath.normalized_path, '/') + - win32_newpath.normalized_path + 1; + char *absoldpath = (char *) alloca (len + strlen (oldpath) + 1); + stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len), + oldpath); + win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes); + } + else + win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); + if (SUCCEEDED (SHGetDesktopFolder (&psl))) + { + WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1]; + win32_oldpath.get_wide_win32_path (wc_path); + /* Amazing but true: Even though the ParseDisplayName method + takes a wide char path name, it does not understand the + Win32 prefix for long pathnames! So we have to tack off + the prefix and convert tyhe path to the "normal" syntax + for ParseDisplayName. I have no idea if it's able to take + long path names at all since I can't test it right now. */ + WCHAR *wc = wc_path + 4; + if (!wcscmp (wc, L"UNC\\")) + *(wc += 2) = L'\\'; + HRESULT res; + if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, NULL, + &pidl, NULL))) { - shortcut_header->flags |= WSH_FLAG_IDLIST; + ITEMIDLIST *p; + for (p = pidl; p->mkid.cb > 0; p = (ITEMIDLIST *)((char *) p + p->mkid.cb)) ; - len = (char *) p - (char *) pidl + 2; - *(unsigned short *)cp = len; - memcpy (cp += 2, pidl, len); - cp += len; - CoTaskMemFree (pidl); + pidl_len = (char *) p - (char *) pidl + 2; } psl->Release (); } - /* Creating a description */ - *(unsigned short *)cp = len = strlen (oldpath); - memcpy (cp += 2, oldpath, len); - cp += len; - /* Creating a relpath */ - if (reloldpath[0]) - { - *(unsigned short *)cp = len = strlen (reloldpath); - memcpy (cp += 2, reloldpath, len); - } - else - { - *(unsigned short *)cp = len = strlen (w32oldpath); - memcpy (cp += 2, w32oldpath, len); - } - cp += len; - success = WriteFile (h, buf, cp - buf, &written, NULL) - && written == (DWORD) (cp - buf); + } + /* Compute size of shortcut file. */ + full_len = sizeof (win_shortcut_hdr); + if (pidl_len) + full_len += sizeof (unsigned short) + pidl_len; + oldpath_len = strlen (oldpath); + /* Unfortunately the length of the description is restricted to a + length of MAX_PATH up to NT4, and to a length of 2000 bytes + since W2K. We don't want to add considerations for the different + lengths and even 2000 bytes is not enough for long path names. + So what we do here is to set the description to the POSIX path + only if the path is not longer than MAX_PATH characters. We + append the full path name after the regular shortcut data + (see below), which works fine with Windows Explorer as well + as older Cygwin versions (as long as the whole file isn't bigger + than 8K). The description field is only used for backward + compatibility to older Cygwin versions and those versions are + not capable of handling long path names anyway. */ + desc_len = stpcpy (desc, oldpath_len > MAX_PATH + ? "[path too long]" : oldpath) - desc; + full_len += sizeof (unsigned short) + desc_len; + /* Devices get the oldpath string unchanged as relative path. */ + if (isdevice) + { + relpath_len = oldpath_len; + stpcpy (relpath = (char *) alloca (relpath_len + 1), oldpath); } else { - /* This is the old technique creating a symlink. */ - char buf[sizeof (SYMLINK_COOKIE) + CYG_MAX_PATH + 10]; + relpath_len = strlen (win32_oldpath.get_win32 ()); + stpcpy (relpath = (char *) alloca (relpath_len + 1), + win32_oldpath.get_win32 ()); + } + full_len += sizeof (unsigned short) + relpath_len; + full_len += sizeof (unsigned short) + oldpath_len; + /* 1 byte more for trailing 0 written by stpcpy. */ + buf = (char *) alloca (full_len + 1); + + /* Create shortcut header */ + win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf; + memset (shortcut_header, 0, sizeof *shortcut_header); + shortcut_header->size = sizeof *shortcut_header; + shortcut_header->magic = GUID_shortcut; + shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH); + if (pidl) + shortcut_header->flags |= WSH_FLAG_IDLIST; + shortcut_header->run = SW_NORMAL; + cp = buf + sizeof (win_shortcut_hdr); + + /* Create IDLIST */ + if (pidl) + { + *(unsigned short *)cp = pidl_len; + memcpy (cp += 2, pidl, pidl_len); + cp += pidl_len; + CoTaskMemFree (pidl); + } - __small_sprintf (buf, "%s%s", SYMLINK_COOKIE, oldpath); - DWORD len = strlen (buf) + 1; + /* Create description */ + *(unsigned short *)cp = desc_len; + cp = stpcpy (cp += 2, desc); - /* Note that the terminating nul is written. */ - success = WriteFile (h, buf, len, &written, NULL) - || written != len; + /* Create relpath */ + *(unsigned short *)cp = relpath_len; + cp = stpcpy (cp += 2, relpath); - } - if (success) + /* Append the POSIX path after the regular shortcut data for + the long path support. */ + *(unsigned short *)cp = oldpath_len; + cp = stpcpy (cp += 2, oldpath); + } + else + { + /* This is the old technique creating a symlink. */ + unsigned short oldpath_len = strlen (oldpath); + buf = (char *) alloca (sizeof (SYMLINK_COOKIE) + oldpath_len + 1); + /* Note that the terminating nul is written. */ + cp = stpcpy (stpcpy (buf, SYMLINK_COOKIE), oldpath) + 1; + } + + if (isdevice && win32_newpath.exists ()) + { + status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, + win32_newpath.get_object_attr (attr, sa), + &io, 0, FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) { - CloseHandle (h); - DWORD attr = use_winsym ? FILE_ATTRIBUTE_READONLY - : FILE_ATTRIBUTE_SYSTEM; - SetFileAttributes (win32_path.get_win32 (), attr); - - res = 0; + __seterrno_from_nt_status (status); + goto done; } - else + fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart + = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL; + fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL; + status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi, + FileBasicInformation); + NtClose (fh); + if (!NT_SUCCESS (status)) { - __seterrno (); - CloseHandle (h); - DeleteFileA (win32_path.get_win32 ()); + __seterrno_from_nt_status (status); + goto done; } } + if (allow_ntsec && win32_newpath.has_acls ()) + set_security_attribute (S_IFLNK | STD_RBITS | STD_WBITS, + &sa, sd); + status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE, + win32_newpath.get_object_attr (attr, sa), + &io, NULL, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_VALID_FLAGS, + isdevice ? FILE_OVERWRITE_IF : FILE_CREATE, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_NON_DIRECTORY_FILE + | FILE_OPEN_FOR_BACKUP_INTENT, + NULL, 0); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + goto done; + } + status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, NULL, NULL); + if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf)) + { + fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart + = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL; + fbi.FileAttributes = use_winsym ? FILE_ATTRIBUTE_READONLY + : FILE_ATTRIBUTE_SYSTEM; + status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi, + FileBasicInformation); + if (!NT_SUCCESS (status)) + debug_printf ("Setting attributes failed, status = %p", status); + res = 0; + } + else + { + __seterrno_from_nt_status (status); + FILE_DISPOSITION_INFORMATION fdi = { TRUE }; + status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi, + FileDispositionInformation); + if (!NT_SUCCESS (status)) + debug_printf ("Setting delete dispostion failed, status = %p", status); + } + NtClose (fh); done: syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, oldpath, @@ -3104,80 +3152,98 @@ cmp_shortcut_header (win_shortcut_hdr *file_header) } int -symlink_info::check_shortcut (const char *path, HANDLE h) +symlink_info::check_shortcut (HANDLE h) { win_shortcut_hdr *file_header; char *buf, *cp; unsigned short len; int res = 0; - DWORD size, got = 0; + DWORD size; + IO_STATUS_BLOCK io; - if ((size = GetFileSize (h, NULL)) > 8192) /* Not a Cygwin symlink. */ - goto file_not_symlink; + size = GetFileSize (h, NULL); buf = (char *) alloca (size + 1); - if (!ReadFile (h, buf, size, &got, 0)) + if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, + &io, buf, size, NULL, NULL))) { set_error (EIO); goto close_it; } file_header = (win_shortcut_hdr *) buf; - if (got != size || !cmp_shortcut_header (file_header)) + if (io.Information != size || !cmp_shortcut_header (file_header)) goto file_not_symlink; cp = buf + sizeof (win_shortcut_hdr); if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */ cp += *(unsigned short *) cp + 2; - if ((len = *(unsigned short *) cp) == 0 || len >= CYG_MAX_PATH) + if (!(len = *(unsigned short *) cp)) goto file_not_symlink; cp += 2; - cp[len] = '\0'; /* Check if this is a device file - these start with the sequence :\\ */ if (strncmp (cp, ":\\", 2) == 0) - res = strlen (strcpy (contents, cp)); /* Don't try to mess with device files */ + res = strlen (strcpy (contents, cp)); /* Don't mess with device files */ else - res = posixify (cp); + { + /* Has appended full path? If so, use it instead of description. */ + unsigned short relpath_len = *(unsigned short *) (cp + len); + if (cp + len + 2 + relpath_len < buf + size) + { + cp += len + 2 + relpath_len; + len = *(unsigned short *) cp; + cp += 2; + } + if (len > SYMLINK_MAX) + goto file_not_symlink; + cp[len] = '\0'; + res = posixify (cp); + } if (res) /* It's a symlink. */ pflags = PATH_SYMLINK | PATH_LNK; goto close_it; file_not_symlink: /* Not a symlink, see if executable. */ - if (!(pflags & PATH_ALL_EXEC) && has_exec_chars ((const char *) &file_header, got)) + if (!(pflags & PATH_ALL_EXEC) && has_exec_chars ((const char *) &file_header, io.Information)) pflags |= PATH_EXEC; close_it: - CloseHandle (h); return res; } int -symlink_info::check_sysfile (const char *path, HANDLE h) +symlink_info::check_sysfile (HANDLE h) { char cookie_buf[sizeof (SYMLINK_COOKIE) - 1]; - char srcbuf[CYG_MAX_PATH]; - DWORD got; + char srcbuf[SYMLINK_MAX + 2]; + IO_STATUS_BLOCK io; int res = 0; - if (!ReadFile (h, cookie_buf, sizeof (cookie_buf), &got, 0)) + if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, &io, + cookie_buf, sizeof (cookie_buf), NULL, NULL))) { debug_printf ("ReadFile1 failed"); set_error (EIO); } - else if (got == sizeof (cookie_buf) + else if (io.Information == sizeof (cookie_buf) && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0) { /* It's a symlink. */ pflags = PATH_SYMLINK; - res = ReadFile (h, srcbuf, CYG_MAX_PATH, &got, 0); - if (!res) + if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, &io, + srcbuf, SYMLINK_MAX + 2, NULL, NULL))) { debug_printf ("ReadFile2 failed"); set_error (EIO); } + else if (io.Information > SYMLINK_MAX + 1) + { + debug_printf ("symlink string too long"); + set_error (EIO); + } else res = posixify (srcbuf); } - else if (got == sizeof (cookie_buf) + else if (io.Information == sizeof (cookie_buf) && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0) pflags |= PATH_SOCKET; else @@ -3185,44 +3251,32 @@ symlink_info::check_sysfile (const char *path, HANDLE h) /* Not a symlink, see if executable. */ if (pflags & PATH_ALL_EXEC) /* Nothing to do */; - else if (has_exec_chars (cookie_buf, got)) + else if (has_exec_chars (cookie_buf, io.Information)) pflags |= PATH_EXEC; else pflags |= PATH_NOTEXEC; } - syscall_printf ("%d = symlink.check_sysfile (%s, %s) (%p)", - res, path, contents, pflags); - - CloseHandle (h); return res; } int -symlink_info::check_reparse_point (const char *path, HANDLE h) +symlink_info::check_reparse_point (HANDLE h) { - int res = 0; PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE); DWORD size; - char srcbuf[CYG_MAX_PATH + 6]; + char srcbuf[SYMLINK_MAX + 7]; if (!DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, NULL)) { debug_printf ("DeviceIoControl(FSCTL_GET_REPARSE_POINT) failed, %E"); set_error (EIO); - goto close_it; + return 0; } if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - if (rp->SymbolicLinkReparseBuffer.SubstituteNameLength - > 2 * (CYG_MAX_PATH + 6)) - { - debug_printf ("Symlink name too long"); - set_error (EIO); - goto close_it; - } - sys_wcstombs (srcbuf, CYG_MAX_PATH, + sys_wcstombs (srcbuf, SYMLINK_MAX + 1, (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset), rp->SymbolicLinkReparseBuffer.SubstituteNameLength / 2); @@ -3234,26 +3288,16 @@ symlink_info::check_reparse_point (const char *path, HANDLE h) if (rp->SymbolicLinkReparseBuffer.PrintNameLength == 0) { /* Likely a volume mount point. Not treated as symlink. */ - goto close_it; - } - if (rp->MountPointReparseBuffer.SubstituteNameLength - > 2 * (CYG_MAX_PATH + 6)) - { - debug_printf ("Symlink name too long"); - set_error (EIO); - goto close_it; + return 0; } - sys_wcstombs (srcbuf, CYG_MAX_PATH, + sys_wcstombs (srcbuf, SYMLINK_MAX + 1, (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer + rp->MountPointReparseBuffer.SubstituteNameOffset), rp->MountPointReparseBuffer.SubstituteNameLength / 2); pflags = PATH_SYMLINK | PATH_REP; fileattr &= ~FILE_ATTRIBUTE_DIRECTORY; } - res = posixify (srcbuf); -close_it: - CloseHandle (h); - return res; + return posixify (srcbuf); } int @@ -3297,7 +3341,7 @@ symlink_info::posixify (char *srcbuf) slashify (srcbuf, contents, 0); else /* Paths starting with \ are current drive relative. */ { - char cvtbuf[CYG_MAX_PATH + 6]; + char cvtbuf[SYMLINK_MAX + 1]; strncpy (cvtbuf, cygheap->cwd.win32, 2); strcpy (cvtbuf + 2, srcbuf); @@ -3524,6 +3568,7 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) { FILE_BASIC_INFORMATION fbi; NTSTATUS status; + IO_STATUS_BLOCK io; error = 0; get_nt_native_path (suffix.path, upath); @@ -3561,7 +3606,6 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) UNICODE_STRING dirname, basename; OBJECT_ATTRIBUTES dattr; HANDLE dir; - IO_STATUS_BLOCK io; FILE_DIRECTORY_INFORMATION fdi; RtlSplitUnicodePath (&upath, &dirname, &basename); @@ -3624,9 +3668,10 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) == FILE_ATTRIBUTE_DIRECTORY) goto file_not_symlink; - /* Reparse points are potentially symlinks. */ - if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) - sym_check = 3; + /* Windows shortcuts are potentially treated as symlinks. */ + /* Valid Cygwin & U/WIN shortcuts are R/O. */ + if ((fileattr & FILE_ATTRIBUTE_READONLY) && suffix.lnk_match ()) + sym_check = 1; /* This is the old Cygwin method creating symlinks: */ /* A symlink will have the `system' file attribute. */ @@ -3634,29 +3679,29 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) else if (fileattr & FILE_ATTRIBUTE_SYSTEM) sym_check = 2; - /* Windows shortcuts are potentially treated as symlinks. */ - /* Valid Cygwin & U/WIN shortcuts are R/O. */ - else if ((fileattr & FILE_ATTRIBUTE_READONLY) && suffix.lnk_match ()) - sym_check = 1; + /* Reparse points are potentially symlinks. */ + else if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) + sym_check = 3; if (!sym_check) goto file_not_symlink; - /* Open the file. */ - - h = CreateFile (suffix.path, GENERIC_READ, FILE_SHARE_READ, - &sec_none_nih, OPEN_EXISTING, - sym_check == 3 ? FILE_FLAG_OPEN_REPARSE_POINT - | FILE_FLAG_BACKUP_SEMANTICS - : FILE_ATTRIBUTE_NORMAL, 0); res = -1; - if (h == INVALID_HANDLE_VALUE) + + /* Open the file. */ + status = NtOpenFile (&h, FILE_GENERIC_READ, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT + | (sym_check == 3 ? FILE_OPEN_REPARSE_POINT : 0)); + if (!NT_SUCCESS (status)) goto file_not_symlink; switch (sym_check) { case 1: - res = check_shortcut (suffix.path, h); + res = check_shortcut (h); + NtClose (h); if (!res) /* check more below */; else if (contents[0] == ':' && contents[1] == '\\' && parse_device (contents)) @@ -3671,12 +3716,14 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) fileattr = INVALID_FILE_ATTRIBUTES; continue; /* in case we're going to tack *another* .lnk on this filename. */ case 2: - res = check_sysfile (suffix.path, h); + res = check_sysfile (h); + NtClose (h); if (!res) goto file_not_symlink; break; case 3: - res = check_reparse_point (suffix.path, h); + res = check_reparse_point (h); + NtClose (h); if (!res) goto file_not_symlink; break; |