Commit 6dad7178 authored by Eli Zaretskii's avatar Eli Zaretskii

Support symlinks on latest versions of MS-Windows.

 src/w32.c: Include winioctl.h and aclapi.h.
 (is_symlink, chase_symlinks, enable_privilege, restore_privilege)
 (revert_to_self): Forward declarations of static functions.
 <static BOOL g_b_init_get_security_info>:
 <g_b_init_create_symbolic_link>: New static flags.
 (globals_of_w32): Initialize them to zero.
 (GetSecurityInfo_Proc, CreateSymbolicLink_Proc): New typedefs.
 (map_w32_filename): Improve commentary.  Simplify switch.
 (SYMBOLIC_LINK_FLAG_DIRECTORY): Define if not defined in system
 headers (most versions of MinGW w32api don't).
 (get_security_info, create_symbolic_link)
 (get_file_security_desc_by_handle, is_symlink, chase_symlinks):
 New functions.
 (sys_access, sys_chmod): Call 'chase_symlinks' to resolve symlinks
 in the argument file name.
 (sys_access): Call unc_volume_file_attributes only if
 GetFileAttributes fails with network-related error codes.
 (sys_rename): Diagnose renaming of a symlink when the user doesn't
 have the required privileges.
 (get_file_security_desc_by_name): Renamed from
 get_file_security_desc.
 (stat_worker): New function, with most of the guts of 'stat', and
 with addition of handling of symlinks and support for 'lstat'.  If
 possible, get file's attributes and security information by
 handle, not by name.  Produce S_IFLNK bit for symlinks, when
 called from 'lstat'.
 (stat, lstat): New functions, call 'stat_worker'.
 (symlink, readlink, careadlinkat): Rewritten to create and resolve
 symlinks when the underlying filesystem supports them.

 lib/src/ntlib.c (lstat): New function, calls 'stat'.

 nt/inc/sys/stat.h (S_IFLNK): Define.
 (S_ISLNK): A non-trivial definition.
 (lstat): Prototype instead of a macro that redirects to 'stat'.

 lisp/files.el (file-truename): Don't skip symlink-chasing part on
 windows-nt.  Incorporate the resolution of 8+3 short aliases on
 Windows into the loop that recursively chases symlinks.  Compare
 directory and its parent case-insensitively on MS-Windows and
 MS-DOS.

 etc/NEWS: Announce the symlink support on MS-Windows.
parent 09486324
......@@ -596,6 +596,8 @@ is detected.
Emacs now supports mouse highlight, help-echo (in the echo area), and
mouse-autoselect-window.
** On MS-Windows Vista and later Emacs now supports symbolic links.
* Installation Changes in Emacs 24.1
......
2012-08-03 Eli Zaretskii <eliz@gnu.org>
* ntlib.c (lstat): New function, calls 'stat'.
2012-08-02 Paul Eggert <eggert@cs.ucla.edu>
Use C99-style 'extern inline' if available.
......
......@@ -374,3 +374,9 @@ stat (const char * path, struct stat * buf)
return 0;
}
int
lstat (const char * path, struct stat * buf)
{
return stat (path, buf);
}
2012-08-03 Eli Zaretskii <eliz@gnu.org>
* files.el (file-truename): Don't skip symlink-chasing part on
windows-nt. Incorporate the resolution of 8+3 short aliases on
Windows into the loop that recursively chases symlinks. Compare
directory and its parent case-insensitively on MS-Windows and
MS-DOS.
2012-08-03 Chong Yidong <cyd@gnu.org>
* menu-bar.el (menu-bar-tools-menu): Remove PCL-CVS.
......
......@@ -1079,9 +1079,7 @@ containing it, until no links are left at any level.
(delq (rassq 'ange-ftp-completion-hook-function tem) tem)))))
(or prev-dirs (setq prev-dirs (list nil)))
;; andrewi@harlequin.co.uk - none of the following code (except for
;; invoking the file-name handler) currently applies on Windows
;; (ie. there are no native symlinks), but there is an issue with
;; andrewi@harlequin.co.uk - on Windows, there is an issue with
;; case differences being ignored by the OS, and short "8.3 DOS"
;; name aliases existing for all files. (The short names are not
;; reported by directory-files, but can be used to refer to files.)
......@@ -1091,31 +1089,15 @@ containing it, until no links are left at any level.
;; it is stored on disk (expanding short name aliases with the full
;; name in the process).
(if (eq system-type 'windows-nt)
(let ((handler (find-file-name-handler filename 'file-truename)))
;; For file name that has a special handler, call handler.
;; This is so that ange-ftp can save time by doing a no-op.
(if handler
(setq filename (funcall handler 'file-truename filename))
;; If filename contains a wildcard, newname will be the old name.
(unless (string-match "[[*?]" filename)
;; If filename exists, use the long name. If it doesn't exist,
;; drill down until we find a directory that exists, and use
;; the long name of that, with the extra non-existent path
;; components concatenated.
(let ((longname (w32-long-file-name filename))
missing rest)
(if longname
(setq filename longname)
;; Include the preceding directory separator in the missing
;; part so subsequent recursion on the rest works.
(setq missing (concat "/" (file-name-nondirectory filename)))
(let ((length (length missing)))
(setq rest
(if (> length (length filename))
""
(substring filename 0 (- length)))))
(setq filename (concat (file-truename rest) missing))))))
(setq done t)))
(unless (string-match "[[*?]" filename)
;; If filename exists, use its long name. If it doesn't
;; exist, the recursion below on the directory of filename
;; will drill down until we find a directory that exists,
;; and use the long name of that, with the extra
;; non-existent path components concatenated.
(let ((longname (w32-long-file-name filename)))
(if longname
(setq filename longname)))))
;; If this file directly leads to a link, process that iteratively
;; so that we don't use lots of stack.
......@@ -1135,6 +1117,8 @@ containing it, until no links are left at any level.
(setq dirfile (directory-file-name dir))
;; If these are equal, we have the (or a) root directory.
(or (string= dir dirfile)
(and (memq system-type '(windows-nt ms-dos cygwin))
(eq (compare-strings dir 0 nil dirfile 0 nil t) t))
;; If this is the same dir we last got the truename for,
;; save time--don't recalculate.
(if (assoc dir (car prev-dirs))
......
2012-08-03 Eli Zaretskii <eliz@gnu.org>
* inc/sys/stat.h (S_IFLNK): Define.
(S_ISLNK): A non-trivial definition.
(lstat): Prototype instead of a macro that redirects to 'stat'.
2012-08-02 Paul Eggert <eggert@cs.ucla.edu>
Use C99-style 'extern inline' if available.
......
......@@ -33,13 +33,14 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <sys/types.h>
#include <time.h>
#define S_IFMT 0xF000
#define S_IFMT 0xF800
#define S_IFREG 0x8000
#define S_IFDIR 0x4000
#define S_IFBLK 0x3000
#define S_IFCHR 0x2000
#define S_IFIFO 0x1000
#define S_IFLNK 0x0800
#define S_IREAD 0x0100
#define S_IWRITE 0x0080
......@@ -55,6 +56,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
/* These don't exist on Windows, but lib/filemode.c wants them. */
#define S_ISUID 0
......@@ -68,7 +70,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#define S_IXOTH (S_IXUSR >> 6)
#define S_ISSOCK(m) 0
#define S_ISLNK(m) 0
#define S_ISCTG(p) 0
#define S_ISDOOR(m) 0
#define S_ISMPB(m) 0
......@@ -103,9 +104,7 @@ struct stat {
_CRTIMP int __cdecl __MINGW_NOTHROW fstat (int, struct stat*);
_CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int);
_CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*);
/* fileio.c and dired.c want lstat. */
#define lstat stat
_CRTIMP int __cdecl __MINGW_NOTHROW lstat (const char*, struct stat*);
#endif /* INC_SYS_STAT_H_ */
2012-08-03 Eli Zaretskii <eliz@gnu.org>
Support symlinks on latest versions of MS-Windows.
* w32.c: Include winioctl.h and aclapi.h.
(is_symlink, chase_symlinks, enable_privilege, restore_privilege)
(revert_to_self): Forward declarations of static functions.
<static BOOL g_b_init_get_security_info>:
<g_b_init_create_symbolic_link>: New static flags.
(globals_of_w32): Initialize them to zero.
(GetSecurityInfo_Proc, CreateSymbolicLink_Proc): New typedefs.
(map_w32_filename): Improve commentary. Simplify switch.
(SYMBOLIC_LINK_FLAG_DIRECTORY): Define if not defined in system
headers (most versions of MinGW w32api don't).
(get_security_info, create_symbolic_link)
(get_file_security_desc_by_handle, is_symlink, chase_symlinks):
New functions.
(sys_access, sys_chmod): Call 'chase_symlinks' to resolve symlinks
in the argument file name.
(sys_access): Call unc_volume_file_attributes only if
GetFileAttributes fails with network-related error codes.
(sys_rename): Diagnose renaming of a symlink when the user doesn't
have the required privileges.
(get_file_security_desc_by_name): Renamed from
get_file_security_desc.
(stat_worker): New function, with most of the guts of 'stat', and
with addition of handling of symlinks and support for 'lstat'. If
possible, get file's attributes and security information by
handle, not by name. Produce S_IFLNK bit for symlinks, when
called from 'lstat'.
(stat, lstat): New functions, call 'stat_worker'.
(symlink, readlink, careadlinkat): Rewritten to create and resolve
symlinks when the underlying filesystem supports them.
2012-08-02 Paul Eggert <eggert@cs.ucla.edu>
Fix macroexp crash on Windows with debugging (Bug#12118).
......
......@@ -116,6 +116,42 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX {
} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
#endif
#include <winioctl.h>
#include <aclapi.h>
#ifdef _MSC_VER
/* MSVC doesn't provide the definition of REPARSE_DATA_BUFFER, except
on ntifs.h, which cannot be included because it triggers conflicts
with other Windows API headers. So we define it here by hand. */
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif
/* TCP connection support. */
#include <sys/socket.h>
#undef socket
......@@ -156,6 +192,11 @@ Lisp_Object QCloaded_from;
void globals_of_w32 (void);
static DWORD get_rid (PSID);
static int is_symlink (const char *);
static char * chase_symlinks (const char *);
static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *);
static int restore_privilege (TOKEN_PRIVILEGES *);
static BOOL WINAPI revert_to_self (void);
/* Initialization states.
......@@ -173,6 +214,7 @@ static BOOL g_b_init_get_token_information;
static BOOL g_b_init_lookup_account_sid;
static BOOL g_b_init_get_sid_sub_authority;
static BOOL g_b_init_get_sid_sub_authority_count;
static BOOL g_b_init_get_security_info;
static BOOL g_b_init_get_file_security;
static BOOL g_b_init_get_security_descriptor_owner;
static BOOL g_b_init_get_security_descriptor_group;
......@@ -192,6 +234,7 @@ static BOOL g_b_init_equal_sid;
static BOOL g_b_init_copy_sid;
static BOOL g_b_init_get_native_system_info;
static BOOL g_b_init_get_system_times;
static BOOL g_b_init_create_symbolic_link;
/*
BEGIN: Wrapper functions around OpenProcessToken
......@@ -238,6 +281,15 @@ typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
DWORD n);
typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
PSID pSid);
typedef DWORD (WINAPI * GetSecurityInfo_Proc) (
HANDLE handle,
SE_OBJECT_TYPE ObjectType,
SECURITY_INFORMATION SecurityInfo,
PSID *ppsidOwner,
PSID *ppsidGroup,
PACL *ppDacl,
PACL *ppSacl,
PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
typedef BOOL (WINAPI * GetFileSecurity_Proc) (
LPCTSTR lpFileName,
SECURITY_INFORMATION RequestedInformation,
......@@ -298,6 +350,10 @@ typedef BOOL (WINAPI * GetSystemTimes_Proc) (
LPFILETIME lpIdleTime,
LPFILETIME lpKernelTime,
LPFILETIME lpUserTime);
typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
LPTSTR lpSymlinkFileName,
LPTSTR lpTargetFileName,
DWORD dwFlags);
/* ** A utility function ** */
static BOOL
......@@ -499,6 +555,39 @@ get_sid_sub_authority_count (PSID pSid)
return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
}
static DWORD WINAPI
get_security_info (HANDLE handle,
SE_OBJECT_TYPE ObjectType,
SECURITY_INFORMATION SecurityInfo,
PSID *ppsidOwner,
PSID *ppsidGroup,
PACL *ppDacl,
PACL *ppSacl,
PSECURITY_DESCRIPTOR *ppSecurityDescriptor)
{
static GetSecurityInfo_Proc s_pfn_Get_Security_Info = NULL;
HMODULE hm_advapi32 = NULL;
if (is_windows_9x () == TRUE)
{
return FALSE;
}
if (g_b_init_get_security_info == 0)
{
g_b_init_get_security_info = 1;
hm_advapi32 = LoadLibrary ("Advapi32.dll");
s_pfn_Get_Security_Info =
(GetSecurityInfo_Proc) GetProcAddress (
hm_advapi32, "GetSecurityInfo");
}
if (s_pfn_Get_Security_Info == NULL)
{
return FALSE;
}
return (s_pfn_Get_Security_Info (handle, ObjectType, SecurityInfo,
ppsidOwner, ppsidGroup, ppDacl, ppSacl,
ppSecurityDescriptor));
}
static BOOL WINAPI
get_file_security (LPCTSTR lpFileName,
SECURITY_INFORMATION RequestedInformation,
......@@ -726,6 +815,57 @@ get_system_times (LPFILETIME lpIdleTime,
return FALSE;
return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
}
static BOOLEAN WINAPI
create_symbolic_link (LPTSTR lpSymlinkFilename,
LPTSTR lpTargetFileName,
DWORD dwFlags)
{
static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL;
BOOLEAN retval;
if (is_windows_9x () == TRUE)
{
errno = ENOSYS;
return 0;
}
if (g_b_init_create_symbolic_link == 0)
{
g_b_init_create_symbolic_link = 1;
#ifdef _UNICODE
s_pfn_Create_Symbolic_Link =
(CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
"CreateSymbolicLinkW");
#else
s_pfn_Create_Symbolic_Link =
(CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
"CreateSymbolicLinkA");
#endif
}
if (s_pfn_Create_Symbolic_Link == NULL)
{
errno = ENOSYS;
return 0;
}
retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
dwFlags);
/* If we were denied creation of the symlink, try again after
enabling the SeCreateSymbolicLinkPrivilege for our process. */
if (!retval)
{
TOKEN_PRIVILEGES priv_current;
if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current))
{
retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
dwFlags);
restore_privilege (&priv_current);
revert_to_self ();
}
}
return retval;
}
/* Equivalent of strerror for W32 error codes. */
char *
......@@ -1535,6 +1675,8 @@ init_environment (char ** argv)
read-only filesystem, like CD-ROM or a write-protected floppy.
The only way to be really sure is to actually create a file and
see if it succeeds. But I think that's too much to ask. */
/* MSVCRT's _access crashes with D_OK. */
if (tmp && sys_access (tmp, D_OK) == 0)
{
char * var = alloca (strlen (tmp) + 8);
......@@ -1774,6 +1916,8 @@ init_environment (char ** argv)
}
/* Remember the initial working directory for getwd. */
/* FIXME: Do we need to resolve possible symlinks in startup_dir?
Does it matter anywhere in Emacs? */
if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
abort ();
......@@ -1793,6 +1937,8 @@ init_environment (char ** argv)
init_user_info ();
}
/* Called from expand-file-name when default-directory is not a string. */
char *
emacs_root_dir (void)
{
......@@ -2187,8 +2333,15 @@ GetCachedVolumeInformation (char * root_dir)
return info;
}
/* Get information on the volume where name is held; set path pointer to
start of pathname in name (past UNC header\volume header if present). */
/* Get information on the volume where NAME is held; set path pointer to
start of pathname in NAME (past UNC header\volume header if present),
if pPath is non-NULL.
Note: if NAME includes symlinks, the information is for the volume
of the symlink, not of its target. That's because, even though
GetVolumeInformation returns information about the symlink target
of its argument, we only pass the root directory to
GetVolumeInformation, not the full NAME. */
static int
get_volume_info (const char * name, const char ** pPath)
{
......@@ -2199,7 +2352,7 @@ get_volume_info (const char * name, const char ** pPath)
if (name == NULL)
return FALSE;
/* find the root name of the volume if given */
/* Find the root name of the volume if given. */
if (isalpha (name[0]) && name[1] == ':')
{
rootname = temp;
......@@ -2239,7 +2392,8 @@ get_volume_info (const char * name, const char ** pPath)
}
/* Determine if volume is FAT format (ie. only supports short 8.3
names); also set path pointer to start of pathname in name. */
names); also set path pointer to start of pathname in name, if
pPath is non-NULL. */
static int
is_fat_volume (const char * name, const char ** pPath)
{
......@@ -2248,7 +2402,8 @@ is_fat_volume (const char * name, const char ** pPath)
return FALSE;
}
/* Map filename to a valid 8.3 name if necessary. */
/* Map filename to a valid 8.3 name if necessary.
The result is a pointer to a static buffer, so CAVEAT EMPTOR! */
const char *
map_w32_filename (const char * name, const char ** pPath)
{
......@@ -2257,6 +2412,7 @@ map_w32_filename (const char * name, const char ** pPath)
char c;
char * path;
const char * save_name = name;
int is_fat = 0;
if (strlen (name) >= MAX_PATH)
{
......@@ -2278,15 +2434,10 @@ map_w32_filename (const char * name, const char ** pPath)
{
switch ( c )
{
case ':':
case '\\':
case '/':
*str++ = '\\';
extn = 0; /* reset extension flags */
dots = 2; /* max 2 dots */
left = 8; /* max length 8 for main part */
break;
case ':':
*str++ = ':';
*str++ = (c == ':' ? ':' : '\\');
extn = 0; /* reset extension flags */
dots = 2; /* max 2 dots */
left = 8; /* max length 8 for main part */
......@@ -2395,6 +2546,9 @@ opendir (char *filename)
if (wnet_enum_handle != INVALID_HANDLE_VALUE)
return NULL;
/* Note: We don't support traversal of UNC volumes via symlinks.
Doing so would mean punishing 99.99% of use cases by resolving
all the possible symlinks in FILENAME, recursively. */
if (is_unc_volume (filename))
{
wnet_enum_handle = open_unc_volume (filename);
......@@ -2411,6 +2565,9 @@ opendir (char *filename)
strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
dir_pathname[MAXPATHLEN] = '\0';
/* Note: We don't support symlinks to file names on FAT volumes.
Doing so would mean punishing 99.99% of use cases by resolving
all the possible symlinks in FILENAME, recursively. */
dir_is_fat = is_fat_volume (filename, NULL);
return dirp;
......@@ -2457,6 +2614,9 @@ readdir (DIR *dirp)
strcat (filename, "\\");
strcat (filename, "*");
/* Note: No need to resolve symlinks in FILENAME, because
FindFirst opens the directory that is the target of a
symlink. */
dir_find_handle = FindFirstFile (filename, &dir_find_data);
if (dir_find_handle == INVALID_HANDLE_VALUE)
......@@ -2650,21 +2810,34 @@ sys_access (const char * path, int mode)
/* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
newer versions blow up when passed D_OK. */
path = map_w32_filename (path, NULL);
if (is_unc_volume (path))
{
attributes = unc_volume_file_attributes (path);
if (attributes == -1) {
errno = EACCES;
return -1;
}
}
else if ((attributes = GetFileAttributes (path)) == -1)
/* If the last element of PATH is a symlink, we need to resolve it
to get the attributes of its target file. Note: any symlinks in
PATH elements other than the last one are transparently resolved
by GetFileAttributes below. */
if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
path = chase_symlinks (path);
if ((attributes = GetFileAttributes (path)) == -1)
{
DWORD w32err = GetLastError ();
switch (w32err)
{
case ERROR_INVALID_NAME:
case ERROR_BAD_PATHNAME:
if (is_unc_volume (path))
{
attributes = unc_volume_file_attributes (path);
if (attributes == -1)
{
errno = EACCES;
return -1;
}
break;
}
/* FALLTHROUGH */
case ERROR_FILE_NOT_FOUND:
case ERROR_BAD_NETPATH:
errno = ENOENT;
break;
default:
......@@ -2700,7 +2873,8 @@ sys_chdir (const char * path)
int
sys_chmod (const char * path, int mode)
{
return _chmod (map_w32_filename (path, NULL), mode);
path = chase_symlinks (map_w32_filename (path, NULL));
return _chmod (path, mode);
}
int
......@@ -2980,6 +3154,7 @@ sys_rename (const char * oldname, const char * newname)
if (result < 0)
{
DWORD w32err = GetLastError ();
if (errno == EACCES
&& newname_dev != oldname_dev)
......@@ -2992,7 +3167,7 @@ sys_rename (const char * oldname, const char * newname)
DWORD attributes;
if ((attributes = GetFileAttributes (temp)) != -1
&& attributes & FILE_ATTRIBUTE_DIRECTORY)
&& (attributes & FILE_ATTRIBUTE_DIRECTORY))
errno = EXDEV;
}
else if (errno == EEXIST)
......@@ -3003,6 +3178,14 @@ sys_rename (const char * oldname, const char * newname)
return result;
result = rename (temp, newname);
}
else if (w32err == ERROR_PRIVILEGE_NOT_HELD
&& is_symlink (temp))
{
/* This is Windows prohibiting the user from creating a
symlink in another place, since that requires
privileges. */
errno = EPERM;
}
}
return result;
......@@ -3133,7 +3316,23 @@ generate_inode_val (const char * name)
#endif
static PSECURITY_DESCRIPTOR
get_file_security_desc (const char *fname)
get_file_security_desc_by_handle (HANDLE h)
{
PSECURITY_DESCRIPTOR psd = NULL;
DWORD err;
SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
err = get_security_info (h, SE_FILE_OBJECT, si,
NULL, NULL, NULL, NULL, &psd);
if (err != ERROR_SUCCESS)
return NULL;
return psd;
}
static PSECURITY_DESCRIPTOR
get_file_security_desc_by_name (const char *fname)
{
PSECURITY_DESCRIPTOR psd = NULL;
DWORD sd_len, err;
......@@ -3349,18 +3548,24 @@ is_slow_fs (const char *name)
/* MSVC stat function can't cope with UNC names and has other bugs, so
replace it with our own. This also allows us to calculate consistent
inode values without hacks in the main Emacs code. */
int
stat (const char * path, struct stat * buf)
inode values and owner/group without hacks in the main Emacs code. */
static int
stat_worker (const char * path, struct stat * buf, int follow_symlinks)
{
char *name, *r;
char *name, *save_name, *r;
WIN32_FIND_DATA wfd;
HANDLE fh;
unsigned __int64 fake_inode;
unsigned __int64 fake_inode = 0;
int permission;
int len;
int rootdir = FALSE;
PSECURITY_DESCRIPTOR psd = NULL;
int is_a_symlink = 0;
DWORD file_flags = FILE_FLAG_BACKUP_SEMANTICS;
DWORD access_rights = 0;
DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
FILETIME ctime, atime, wtime;
if (path == NULL || buf == NULL)
{
......@@ -3368,7 +3573,7 @@ stat (const char * path, struct stat * buf)
return -1;
}
name = (char *) map_w32_filename (path, &path);
save_name = name = (char *) map_w32_filename (path, &path);
/* Must be valid filename, no wild cards or other invalid
characters. We use _mbspbrk to support multibyte strings that
might look to strpbrk as if they included literal *, ?, and other
......@@ -3380,99 +3585,67 @@ stat (const char * path, struct stat * buf)
return -1;
}
/* If name is "c:/.." or "/.." then stat "c:/" or "/". */
r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0')
{
r[1] = r[2] = '\0';
}
/* Remove trailing directory separator, unless name is the root
directory of a drive or UNC volume in which case ensure there
is a trailing separator. */
len = strlen (name);
rootdir = (path >= name + len - 1
&& (IS_DIRECTORY_SEP (*path) || *path == 0));
name = strcpy (alloca (len + 2), name);
if (is_unc_volume (name))
{
DWORD attrs = unc_volume_file_attributes (name);
if (attrs == -1)
return -1;
memset (&wfd, 0, sizeof (wfd));
wfd.dwFileAttributes = attrs;
wfd.ftCreationTime = utc_base_ft;
wfd.ftLastAccessTime = utc_base_ft;
wfd.ftLastWriteTime = utc_base_ft;
strcpy (wfd.cFileName, name);
}
else if (rootdir)
{
if (!IS_DIRECTORY_SEP (name[len-1]))
strcat (name, "\\");
if (GetDriveType (name) < 2)
{
errno = ENOENT;
return -1;
}
memset (&wfd, 0, sizeof (wfd));
wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
wfd.ftCreationTime = utc_base_ft;
wfd.ftLastAccessTime = utc_base_ft;
wfd.ftLastWriteTime = utc_base_ft;
strcpy (wfd.cFileName, name);
}