diff options
| author | Eli Zaretskii | 2012-08-03 13:23:30 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2012-08-03 13:23:30 +0300 |
| commit | 6dad71783c018d3ccabcede7db6ea206de5b3255 (patch) | |
| tree | a30427f359ae98e4f4f7cc1483cee77371e5ed42 | |
| parent | 0948632492830f08805f2c1250a9ababb6baa95a (diff) | |
| download | emacs-6dad71783c018d3ccabcede7db6ea206de5b3255.tar.gz emacs-6dad71783c018d3ccabcede7db6ea206de5b3255.zip | |
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.
| -rw-r--r-- | etc/NEWS | 2 | ||||
| -rw-r--r-- | lib-src/ChangeLog | 4 | ||||
| -rw-r--r-- | lib-src/ntlib.c | 6 | ||||
| -rw-r--r-- | lisp/ChangeLog | 8 | ||||
| -rw-r--r-- | lisp/files.el | 40 | ||||
| -rw-r--r-- | nt/ChangeLog | 6 | ||||
| -rw-r--r-- | nt/inc/sys/stat.h | 9 | ||||
| -rw-r--r-- | src/ChangeLog | 33 | ||||
| -rw-r--r-- | src/w32.c | 1082 |
9 files changed, 1002 insertions, 188 deletions
| @@ -596,6 +596,8 @@ is detected. | |||
| 596 | Emacs now supports mouse highlight, help-echo (in the echo area), and | 596 | Emacs now supports mouse highlight, help-echo (in the echo area), and |
| 597 | mouse-autoselect-window. | 597 | mouse-autoselect-window. |
| 598 | 598 | ||
| 599 | ** On MS-Windows Vista and later Emacs now supports symbolic links. | ||
| 600 | |||
| 599 | 601 | ||
| 600 | * Installation Changes in Emacs 24.1 | 602 | * Installation Changes in Emacs 24.1 |
| 601 | 603 | ||
diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index 955f8cd0330..c98d377f199 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2012-08-03 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * ntlib.c (lstat): New function, calls 'stat'. | ||
| 4 | |||
| 1 | 2012-08-02 Paul Eggert <eggert@cs.ucla.edu> | 5 | 2012-08-02 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 6 | ||
| 3 | Use C99-style 'extern inline' if available. | 7 | Use C99-style 'extern inline' if available. |
diff --git a/lib-src/ntlib.c b/lib-src/ntlib.c index d3b001c157c..7b41941ab14 100644 --- a/lib-src/ntlib.c +++ b/lib-src/ntlib.c | |||
| @@ -374,3 +374,9 @@ stat (const char * path, struct stat * buf) | |||
| 374 | return 0; | 374 | return 0; |
| 375 | } | 375 | } |
| 376 | 376 | ||
| 377 | int | ||
| 378 | lstat (const char * path, struct stat * buf) | ||
| 379 | { | ||
| 380 | return stat (path, buf); | ||
| 381 | } | ||
| 382 | |||
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 4bd509ce277..d36cc4574fa 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,11 @@ | |||
| 1 | 2012-08-03 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * files.el (file-truename): Don't skip symlink-chasing part on | ||
| 4 | windows-nt. Incorporate the resolution of 8+3 short aliases on | ||
| 5 | Windows into the loop that recursively chases symlinks. Compare | ||
| 6 | directory and its parent case-insensitively on MS-Windows and | ||
| 7 | MS-DOS. | ||
| 8 | |||
| 1 | 2012-08-03 Chong Yidong <cyd@gnu.org> | 9 | 2012-08-03 Chong Yidong <cyd@gnu.org> |
| 2 | 10 | ||
| 3 | * menu-bar.el (menu-bar-tools-menu): Remove PCL-CVS. | 11 | * menu-bar.el (menu-bar-tools-menu): Remove PCL-CVS. |
diff --git a/lisp/files.el b/lisp/files.el index 7fc7ccc8553..0c895669542 100644 --- a/lisp/files.el +++ b/lisp/files.el | |||
| @@ -1079,9 +1079,7 @@ containing it, until no links are left at any level. | |||
| 1079 | (delq (rassq 'ange-ftp-completion-hook-function tem) tem))))) | 1079 | (delq (rassq 'ange-ftp-completion-hook-function tem) tem))))) |
| 1080 | (or prev-dirs (setq prev-dirs (list nil))) | 1080 | (or prev-dirs (setq prev-dirs (list nil))) |
| 1081 | 1081 | ||
| 1082 | ;; andrewi@harlequin.co.uk - none of the following code (except for | 1082 | ;; andrewi@harlequin.co.uk - on Windows, there is an issue with |
| 1083 | ;; invoking the file-name handler) currently applies on Windows | ||
| 1084 | ;; (ie. there are no native symlinks), but there is an issue with | ||
| 1085 | ;; case differences being ignored by the OS, and short "8.3 DOS" | 1083 | ;; case differences being ignored by the OS, and short "8.3 DOS" |
| 1086 | ;; name aliases existing for all files. (The short names are not | 1084 | ;; name aliases existing for all files. (The short names are not |
| 1087 | ;; reported by directory-files, but can be used to refer to files.) | 1085 | ;; 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. | |||
| 1091 | ;; it is stored on disk (expanding short name aliases with the full | 1089 | ;; it is stored on disk (expanding short name aliases with the full |
| 1092 | ;; name in the process). | 1090 | ;; name in the process). |
| 1093 | (if (eq system-type 'windows-nt) | 1091 | (if (eq system-type 'windows-nt) |
| 1094 | (let ((handler (find-file-name-handler filename 'file-truename))) | 1092 | (unless (string-match "[[*?]" filename) |
| 1095 | ;; For file name that has a special handler, call handler. | 1093 | ;; If filename exists, use its long name. If it doesn't |
| 1096 | ;; This is so that ange-ftp can save time by doing a no-op. | 1094 | ;; exist, the recursion below on the directory of filename |
| 1097 | (if handler | 1095 | ;; will drill down until we find a directory that exists, |
| 1098 | (setq filename (funcall handler 'file-truename filename)) | 1096 | ;; and use the long name of that, with the extra |
| 1099 | ;; If filename contains a wildcard, newname will be the old name. | 1097 | ;; non-existent path components concatenated. |
| 1100 | (unless (string-match "[[*?]" filename) | 1098 | (let ((longname (w32-long-file-name filename))) |
| 1101 | ;; If filename exists, use the long name. If it doesn't exist, | 1099 | (if longname |
| 1102 | ;; drill down until we find a directory that exists, and use | 1100 | (setq filename longname))))) |
| 1103 | ;; the long name of that, with the extra non-existent path | ||
| 1104 | ;; components concatenated. | ||
| 1105 | (let ((longname (w32-long-file-name filename)) | ||
| 1106 | missing rest) | ||
| 1107 | (if longname | ||
| 1108 | (setq filename longname) | ||
| 1109 | ;; Include the preceding directory separator in the missing | ||
| 1110 | ;; part so subsequent recursion on the rest works. | ||
| 1111 | (setq missing (concat "/" (file-name-nondirectory filename))) | ||
| 1112 | (let ((length (length missing))) | ||
| 1113 | (setq rest | ||
| 1114 | (if (> length (length filename)) | ||
| 1115 | "" | ||
| 1116 | (substring filename 0 (- length))))) | ||
| 1117 | (setq filename (concat (file-truename rest) missing)))))) | ||
| 1118 | (setq done t))) | ||
| 1119 | 1101 | ||
| 1120 | ;; If this file directly leads to a link, process that iteratively | 1102 | ;; If this file directly leads to a link, process that iteratively |
| 1121 | ;; so that we don't use lots of stack. | 1103 | ;; so that we don't use lots of stack. |
| @@ -1135,6 +1117,8 @@ containing it, until no links are left at any level. | |||
| 1135 | (setq dirfile (directory-file-name dir)) | 1117 | (setq dirfile (directory-file-name dir)) |
| 1136 | ;; If these are equal, we have the (or a) root directory. | 1118 | ;; If these are equal, we have the (or a) root directory. |
| 1137 | (or (string= dir dirfile) | 1119 | (or (string= dir dirfile) |
| 1120 | (and (memq system-type '(windows-nt ms-dos cygwin)) | ||
| 1121 | (eq (compare-strings dir 0 nil dirfile 0 nil t) t)) | ||
| 1138 | ;; If this is the same dir we last got the truename for, | 1122 | ;; If this is the same dir we last got the truename for, |
| 1139 | ;; save time--don't recalculate. | 1123 | ;; save time--don't recalculate. |
| 1140 | (if (assoc dir (car prev-dirs)) | 1124 | (if (assoc dir (car prev-dirs)) |
diff --git a/nt/ChangeLog b/nt/ChangeLog index 8b9163c9f78..d8b9b14a3fb 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog | |||
| @@ -1,3 +1,9 @@ | |||
| 1 | 2012-08-03 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * inc/sys/stat.h (S_IFLNK): Define. | ||
| 4 | (S_ISLNK): A non-trivial definition. | ||
| 5 | (lstat): Prototype instead of a macro that redirects to 'stat'. | ||
| 6 | |||
| 1 | 2012-08-02 Paul Eggert <eggert@cs.ucla.edu> | 7 | 2012-08-02 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 8 | ||
| 3 | Use C99-style 'extern inline' if available. | 9 | Use C99-style 'extern inline' if available. |
diff --git a/nt/inc/sys/stat.h b/nt/inc/sys/stat.h index 57fabff4b0c..b673b80a0e3 100644 --- a/nt/inc/sys/stat.h +++ b/nt/inc/sys/stat.h | |||
| @@ -33,13 +33,14 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 33 | #include <sys/types.h> | 33 | #include <sys/types.h> |
| 34 | #include <time.h> | 34 | #include <time.h> |
| 35 | 35 | ||
| 36 | #define S_IFMT 0xF000 | 36 | #define S_IFMT 0xF800 |
| 37 | 37 | ||
| 38 | #define S_IFREG 0x8000 | 38 | #define S_IFREG 0x8000 |
| 39 | #define S_IFDIR 0x4000 | 39 | #define S_IFDIR 0x4000 |
| 40 | #define S_IFBLK 0x3000 | 40 | #define S_IFBLK 0x3000 |
| 41 | #define S_IFCHR 0x2000 | 41 | #define S_IFCHR 0x2000 |
| 42 | #define S_IFIFO 0x1000 | 42 | #define S_IFIFO 0x1000 |
| 43 | #define S_IFLNK 0x0800 | ||
| 43 | 44 | ||
| 44 | #define S_IREAD 0x0100 | 45 | #define S_IREAD 0x0100 |
| 45 | #define S_IWRITE 0x0080 | 46 | #define S_IWRITE 0x0080 |
| @@ -55,6 +56,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 55 | #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) | 56 | #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) |
| 56 | #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) | 57 | #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) |
| 57 | #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) | 58 | #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) |
| 59 | #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) | ||
| 58 | 60 | ||
| 59 | /* These don't exist on Windows, but lib/filemode.c wants them. */ | 61 | /* These don't exist on Windows, but lib/filemode.c wants them. */ |
| 60 | #define S_ISUID 0 | 62 | #define S_ISUID 0 |
| @@ -68,7 +70,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 68 | #define S_IXOTH (S_IXUSR >> 6) | 70 | #define S_IXOTH (S_IXUSR >> 6) |
| 69 | 71 | ||
| 70 | #define S_ISSOCK(m) 0 | 72 | #define S_ISSOCK(m) 0 |
| 71 | #define S_ISLNK(m) 0 | ||
| 72 | #define S_ISCTG(p) 0 | 73 | #define S_ISCTG(p) 0 |
| 73 | #define S_ISDOOR(m) 0 | 74 | #define S_ISDOOR(m) 0 |
| 74 | #define S_ISMPB(m) 0 | 75 | #define S_ISMPB(m) 0 |
| @@ -103,9 +104,7 @@ struct stat { | |||
| 103 | _CRTIMP int __cdecl __MINGW_NOTHROW fstat (int, struct stat*); | 104 | _CRTIMP int __cdecl __MINGW_NOTHROW fstat (int, struct stat*); |
| 104 | _CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int); | 105 | _CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int); |
| 105 | _CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*); | 106 | _CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*); |
| 106 | 107 | _CRTIMP int __cdecl __MINGW_NOTHROW lstat (const char*, struct stat*); | |
| 107 | /* fileio.c and dired.c want lstat. */ | ||
| 108 | #define lstat stat | ||
| 109 | 108 | ||
| 110 | #endif /* INC_SYS_STAT_H_ */ | 109 | #endif /* INC_SYS_STAT_H_ */ |
| 111 | 110 | ||
diff --git a/src/ChangeLog b/src/ChangeLog index 45b24436519..3ba80f69749 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,36 @@ | |||
| 1 | 2012-08-03 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | Support symlinks on latest versions of MS-Windows. | ||
| 4 | * w32.c: Include winioctl.h and aclapi.h. | ||
| 5 | (is_symlink, chase_symlinks, enable_privilege, restore_privilege) | ||
| 6 | (revert_to_self): Forward declarations of static functions. | ||
| 7 | <static BOOL g_b_init_get_security_info>: | ||
| 8 | <g_b_init_create_symbolic_link>: New static flags. | ||
| 9 | (globals_of_w32): Initialize them to zero. | ||
| 10 | (GetSecurityInfo_Proc, CreateSymbolicLink_Proc): New typedefs. | ||
| 11 | (map_w32_filename): Improve commentary. Simplify switch. | ||
| 12 | (SYMBOLIC_LINK_FLAG_DIRECTORY): Define if not defined in system | ||
| 13 | headers (most versions of MinGW w32api don't). | ||
| 14 | (get_security_info, create_symbolic_link) | ||
| 15 | (get_file_security_desc_by_handle, is_symlink, chase_symlinks): | ||
| 16 | New functions. | ||
| 17 | (sys_access, sys_chmod): Call 'chase_symlinks' to resolve symlinks | ||
| 18 | in the argument file name. | ||
| 19 | (sys_access): Call unc_volume_file_attributes only if | ||
| 20 | GetFileAttributes fails with network-related error codes. | ||
| 21 | (sys_rename): Diagnose renaming of a symlink when the user doesn't | ||
| 22 | have the required privileges. | ||
| 23 | (get_file_security_desc_by_name): Renamed from | ||
| 24 | get_file_security_desc. | ||
| 25 | (stat_worker): New function, with most of the guts of 'stat', and | ||
| 26 | with addition of handling of symlinks and support for 'lstat'. If | ||
| 27 | possible, get file's attributes and security information by | ||
| 28 | handle, not by name. Produce S_IFLNK bit for symlinks, when | ||
| 29 | called from 'lstat'. | ||
| 30 | (stat, lstat): New functions, call 'stat_worker'. | ||
| 31 | (symlink, readlink, careadlinkat): Rewritten to create and resolve | ||
| 32 | symlinks when the underlying filesystem supports them. | ||
| 33 | |||
| 1 | 2012-08-02 Paul Eggert <eggert@cs.ucla.edu> | 34 | 2012-08-02 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 35 | ||
| 3 | Fix macroexp crash on Windows with debugging (Bug#12118). | 36 | Fix macroexp crash on Windows with debugging (Bug#12118). |
| @@ -116,6 +116,42 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX { | |||
| 116 | } PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX; | 116 | } PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX; |
| 117 | #endif | 117 | #endif |
| 118 | 118 | ||
| 119 | #include <winioctl.h> | ||
| 120 | #include <aclapi.h> | ||
| 121 | |||
| 122 | #ifdef _MSC_VER | ||
| 123 | /* MSVC doesn't provide the definition of REPARSE_DATA_BUFFER, except | ||
| 124 | on ntifs.h, which cannot be included because it triggers conflicts | ||
| 125 | with other Windows API headers. So we define it here by hand. */ | ||
| 126 | |||
| 127 | typedef struct _REPARSE_DATA_BUFFER { | ||
| 128 | ULONG ReparseTag; | ||
| 129 | USHORT ReparseDataLength; | ||
| 130 | USHORT Reserved; | ||
| 131 | union { | ||
| 132 | struct { | ||
| 133 | USHORT SubstituteNameOffset; | ||
| 134 | USHORT SubstituteNameLength; | ||
| 135 | USHORT PrintNameOffset; | ||
| 136 | USHORT PrintNameLength; | ||
| 137 | ULONG Flags; | ||
| 138 | WCHAR PathBuffer[1]; | ||
| 139 | } SymbolicLinkReparseBuffer; | ||
| 140 | struct { | ||
| 141 | USHORT SubstituteNameOffset; | ||
| 142 | USHORT SubstituteNameLength; | ||
| 143 | USHORT PrintNameOffset; | ||
| 144 | USHORT PrintNameLength; | ||
| 145 | WCHAR PathBuffer[1]; | ||
| 146 | } MountPointReparseBuffer; | ||
| 147 | struct { | ||
| 148 | UCHAR DataBuffer[1]; | ||
| 149 | } GenericReparseBuffer; | ||
| 150 | } DUMMYUNIONNAME; | ||
| 151 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; | ||
| 152 | |||
| 153 | #endif | ||
| 154 | |||
| 119 | /* TCP connection support. */ | 155 | /* TCP connection support. */ |
| 120 | #include <sys/socket.h> | 156 | #include <sys/socket.h> |
| 121 | #undef socket | 157 | #undef socket |
| @@ -156,6 +192,11 @@ Lisp_Object QCloaded_from; | |||
| 156 | 192 | ||
| 157 | void globals_of_w32 (void); | 193 | void globals_of_w32 (void); |
| 158 | static DWORD get_rid (PSID); | 194 | static DWORD get_rid (PSID); |
| 195 | static int is_symlink (const char *); | ||
| 196 | static char * chase_symlinks (const char *); | ||
| 197 | static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *); | ||
| 198 | static int restore_privilege (TOKEN_PRIVILEGES *); | ||
| 199 | static BOOL WINAPI revert_to_self (void); | ||
| 159 | 200 | ||
| 160 | 201 | ||
| 161 | /* Initialization states. | 202 | /* Initialization states. |
| @@ -173,6 +214,7 @@ static BOOL g_b_init_get_token_information; | |||
| 173 | static BOOL g_b_init_lookup_account_sid; | 214 | static BOOL g_b_init_lookup_account_sid; |
| 174 | static BOOL g_b_init_get_sid_sub_authority; | 215 | static BOOL g_b_init_get_sid_sub_authority; |
| 175 | static BOOL g_b_init_get_sid_sub_authority_count; | 216 | static BOOL g_b_init_get_sid_sub_authority_count; |
| 217 | static BOOL g_b_init_get_security_info; | ||
| 176 | static BOOL g_b_init_get_file_security; | 218 | static BOOL g_b_init_get_file_security; |
| 177 | static BOOL g_b_init_get_security_descriptor_owner; | 219 | static BOOL g_b_init_get_security_descriptor_owner; |
| 178 | static BOOL g_b_init_get_security_descriptor_group; | 220 | static BOOL g_b_init_get_security_descriptor_group; |
| @@ -192,6 +234,7 @@ static BOOL g_b_init_equal_sid; | |||
| 192 | static BOOL g_b_init_copy_sid; | 234 | static BOOL g_b_init_copy_sid; |
| 193 | static BOOL g_b_init_get_native_system_info; | 235 | static BOOL g_b_init_get_native_system_info; |
| 194 | static BOOL g_b_init_get_system_times; | 236 | static BOOL g_b_init_get_system_times; |
| 237 | static BOOL g_b_init_create_symbolic_link; | ||
| 195 | 238 | ||
| 196 | /* | 239 | /* |
| 197 | BEGIN: Wrapper functions around OpenProcessToken | 240 | BEGIN: Wrapper functions around OpenProcessToken |
| @@ -238,6 +281,15 @@ typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) ( | |||
| 238 | DWORD n); | 281 | DWORD n); |
| 239 | typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) ( | 282 | typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) ( |
| 240 | PSID pSid); | 283 | PSID pSid); |
| 284 | typedef DWORD (WINAPI * GetSecurityInfo_Proc) ( | ||
| 285 | HANDLE handle, | ||
| 286 | SE_OBJECT_TYPE ObjectType, | ||
| 287 | SECURITY_INFORMATION SecurityInfo, | ||
| 288 | PSID *ppsidOwner, | ||
| 289 | PSID *ppsidGroup, | ||
| 290 | PACL *ppDacl, | ||
| 291 | PACL *ppSacl, | ||
| 292 | PSECURITY_DESCRIPTOR *ppSecurityDescriptor); | ||
| 241 | typedef BOOL (WINAPI * GetFileSecurity_Proc) ( | 293 | typedef BOOL (WINAPI * GetFileSecurity_Proc) ( |
| 242 | LPCTSTR lpFileName, | 294 | LPCTSTR lpFileName, |
| 243 | SECURITY_INFORMATION RequestedInformation, | 295 | SECURITY_INFORMATION RequestedInformation, |
| @@ -298,6 +350,10 @@ typedef BOOL (WINAPI * GetSystemTimes_Proc) ( | |||
| 298 | LPFILETIME lpIdleTime, | 350 | LPFILETIME lpIdleTime, |
| 299 | LPFILETIME lpKernelTime, | 351 | LPFILETIME lpKernelTime, |
| 300 | LPFILETIME lpUserTime); | 352 | LPFILETIME lpUserTime); |
| 353 | typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) ( | ||
| 354 | LPTSTR lpSymlinkFileName, | ||
| 355 | LPTSTR lpTargetFileName, | ||
| 356 | DWORD dwFlags); | ||
| 301 | 357 | ||
| 302 | /* ** A utility function ** */ | 358 | /* ** A utility function ** */ |
| 303 | static BOOL | 359 | static BOOL |
| @@ -499,6 +555,39 @@ get_sid_sub_authority_count (PSID pSid) | |||
| 499 | return (s_pfn_Get_Sid_Sub_Authority_Count (pSid)); | 555 | return (s_pfn_Get_Sid_Sub_Authority_Count (pSid)); |
| 500 | } | 556 | } |
| 501 | 557 | ||
| 558 | static DWORD WINAPI | ||
| 559 | get_security_info (HANDLE handle, | ||
| 560 | SE_OBJECT_TYPE ObjectType, | ||
| 561 | SECURITY_INFORMATION SecurityInfo, | ||
| 562 | PSID *ppsidOwner, | ||
| 563 | PSID *ppsidGroup, | ||
| 564 | PACL *ppDacl, | ||
| 565 | PACL *ppSacl, | ||
| 566 | PSECURITY_DESCRIPTOR *ppSecurityDescriptor) | ||
| 567 | { | ||
| 568 | static GetSecurityInfo_Proc s_pfn_Get_Security_Info = NULL; | ||
| 569 | HMODULE hm_advapi32 = NULL; | ||
| 570 | if (is_windows_9x () == TRUE) | ||
| 571 | { | ||
| 572 | return FALSE; | ||
| 573 | } | ||
| 574 | if (g_b_init_get_security_info == 0) | ||
| 575 | { | ||
| 576 | g_b_init_get_security_info = 1; | ||
| 577 | hm_advapi32 = LoadLibrary ("Advapi32.dll"); | ||
| 578 | s_pfn_Get_Security_Info = | ||
| 579 | (GetSecurityInfo_Proc) GetProcAddress ( | ||
| 580 | hm_advapi32, "GetSecurityInfo"); | ||
| 581 | } | ||
| 582 | if (s_pfn_Get_Security_Info == NULL) | ||
| 583 | { | ||
| 584 | return FALSE; | ||
| 585 | } | ||
| 586 | return (s_pfn_Get_Security_Info (handle, ObjectType, SecurityInfo, | ||
| 587 | ppsidOwner, ppsidGroup, ppDacl, ppSacl, | ||
| 588 | ppSecurityDescriptor)); | ||
| 589 | } | ||
| 590 | |||
| 502 | static BOOL WINAPI | 591 | static BOOL WINAPI |
| 503 | get_file_security (LPCTSTR lpFileName, | 592 | get_file_security (LPCTSTR lpFileName, |
| 504 | SECURITY_INFORMATION RequestedInformation, | 593 | SECURITY_INFORMATION RequestedInformation, |
| @@ -726,6 +815,57 @@ get_system_times (LPFILETIME lpIdleTime, | |||
| 726 | return FALSE; | 815 | return FALSE; |
| 727 | return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime)); | 816 | return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime)); |
| 728 | } | 817 | } |
| 818 | |||
| 819 | static BOOLEAN WINAPI | ||
| 820 | create_symbolic_link (LPTSTR lpSymlinkFilename, | ||
| 821 | LPTSTR lpTargetFileName, | ||
| 822 | DWORD dwFlags) | ||
| 823 | { | ||
| 824 | static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL; | ||
| 825 | BOOLEAN retval; | ||
| 826 | |||
| 827 | if (is_windows_9x () == TRUE) | ||
| 828 | { | ||
| 829 | errno = ENOSYS; | ||
| 830 | return 0; | ||
| 831 | } | ||
| 832 | if (g_b_init_create_symbolic_link == 0) | ||
| 833 | { | ||
| 834 | g_b_init_create_symbolic_link = 1; | ||
| 835 | #ifdef _UNICODE | ||
| 836 | s_pfn_Create_Symbolic_Link = | ||
| 837 | (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), | ||
| 838 | "CreateSymbolicLinkW"); | ||
| 839 | #else | ||
| 840 | s_pfn_Create_Symbolic_Link = | ||
| 841 | (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), | ||
| 842 | "CreateSymbolicLinkA"); | ||
| 843 | #endif | ||
| 844 | } | ||
| 845 | if (s_pfn_Create_Symbolic_Link == NULL) | ||
| 846 | { | ||
| 847 | errno = ENOSYS; | ||
| 848 | return 0; | ||
| 849 | } | ||
| 850 | |||
| 851 | retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName, | ||
| 852 | dwFlags); | ||
| 853 | /* If we were denied creation of the symlink, try again after | ||
| 854 | enabling the SeCreateSymbolicLinkPrivilege for our process. */ | ||
| 855 | if (!retval) | ||
| 856 | { | ||
| 857 | TOKEN_PRIVILEGES priv_current; | ||
| 858 | |||
| 859 | if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current)) | ||
| 860 | { | ||
| 861 | retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName, | ||
| 862 | dwFlags); | ||
| 863 | restore_privilege (&priv_current); | ||
| 864 | revert_to_self (); | ||
| 865 | } | ||
| 866 | } | ||
| 867 | return retval; | ||
| 868 | } | ||
| 729 | 869 | ||
| 730 | /* Equivalent of strerror for W32 error codes. */ | 870 | /* Equivalent of strerror for W32 error codes. */ |
| 731 | char * | 871 | char * |
| @@ -1535,6 +1675,8 @@ init_environment (char ** argv) | |||
| 1535 | read-only filesystem, like CD-ROM or a write-protected floppy. | 1675 | read-only filesystem, like CD-ROM or a write-protected floppy. |
| 1536 | The only way to be really sure is to actually create a file and | 1676 | The only way to be really sure is to actually create a file and |
| 1537 | see if it succeeds. But I think that's too much to ask. */ | 1677 | see if it succeeds. But I think that's too much to ask. */ |
| 1678 | |||
| 1679 | /* MSVCRT's _access crashes with D_OK. */ | ||
| 1538 | if (tmp && sys_access (tmp, D_OK) == 0) | 1680 | if (tmp && sys_access (tmp, D_OK) == 0) |
| 1539 | { | 1681 | { |
| 1540 | char * var = alloca (strlen (tmp) + 8); | 1682 | char * var = alloca (strlen (tmp) + 8); |
| @@ -1774,6 +1916,8 @@ init_environment (char ** argv) | |||
| 1774 | } | 1916 | } |
| 1775 | 1917 | ||
| 1776 | /* Remember the initial working directory for getwd. */ | 1918 | /* Remember the initial working directory for getwd. */ |
| 1919 | /* FIXME: Do we need to resolve possible symlinks in startup_dir? | ||
| 1920 | Does it matter anywhere in Emacs? */ | ||
| 1777 | if (!GetCurrentDirectory (MAXPATHLEN, startup_dir)) | 1921 | if (!GetCurrentDirectory (MAXPATHLEN, startup_dir)) |
| 1778 | abort (); | 1922 | abort (); |
| 1779 | 1923 | ||
| @@ -1793,6 +1937,8 @@ init_environment (char ** argv) | |||
| 1793 | init_user_info (); | 1937 | init_user_info (); |
| 1794 | } | 1938 | } |
| 1795 | 1939 | ||
| 1940 | /* Called from expand-file-name when default-directory is not a string. */ | ||
| 1941 | |||
| 1796 | char * | 1942 | char * |
| 1797 | emacs_root_dir (void) | 1943 | emacs_root_dir (void) |
| 1798 | { | 1944 | { |
| @@ -2187,8 +2333,15 @@ GetCachedVolumeInformation (char * root_dir) | |||
| 2187 | return info; | 2333 | return info; |
| 2188 | } | 2334 | } |
| 2189 | 2335 | ||
| 2190 | /* Get information on the volume where name is held; set path pointer to | 2336 | /* Get information on the volume where NAME is held; set path pointer to |
| 2191 | start of pathname in name (past UNC header\volume header if present). */ | 2337 | start of pathname in NAME (past UNC header\volume header if present), |
| 2338 | if pPath is non-NULL. | ||
| 2339 | |||
| 2340 | Note: if NAME includes symlinks, the information is for the volume | ||
| 2341 | of the symlink, not of its target. That's because, even though | ||
| 2342 | GetVolumeInformation returns information about the symlink target | ||
| 2343 | of its argument, we only pass the root directory to | ||
| 2344 | GetVolumeInformation, not the full NAME. */ | ||
| 2192 | static int | 2345 | static int |
| 2193 | get_volume_info (const char * name, const char ** pPath) | 2346 | get_volume_info (const char * name, const char ** pPath) |
| 2194 | { | 2347 | { |
| @@ -2199,7 +2352,7 @@ get_volume_info (const char * name, const char ** pPath) | |||
| 2199 | if (name == NULL) | 2352 | if (name == NULL) |
| 2200 | return FALSE; | 2353 | return FALSE; |
| 2201 | 2354 | ||
| 2202 | /* find the root name of the volume if given */ | 2355 | /* Find the root name of the volume if given. */ |
| 2203 | if (isalpha (name[0]) && name[1] == ':') | 2356 | if (isalpha (name[0]) && name[1] == ':') |
| 2204 | { | 2357 | { |
| 2205 | rootname = temp; | 2358 | rootname = temp; |
| @@ -2239,7 +2392,8 @@ get_volume_info (const char * name, const char ** pPath) | |||
| 2239 | } | 2392 | } |
| 2240 | 2393 | ||
| 2241 | /* Determine if volume is FAT format (ie. only supports short 8.3 | 2394 | /* Determine if volume is FAT format (ie. only supports short 8.3 |
| 2242 | names); also set path pointer to start of pathname in name. */ | 2395 | names); also set path pointer to start of pathname in name, if |
| 2396 | pPath is non-NULL. */ | ||
| 2243 | static int | 2397 | static int |
| 2244 | is_fat_volume (const char * name, const char ** pPath) | 2398 | is_fat_volume (const char * name, const char ** pPath) |
| 2245 | { | 2399 | { |
| @@ -2248,7 +2402,8 @@ is_fat_volume (const char * name, const char ** pPath) | |||
| 2248 | return FALSE; | 2402 | return FALSE; |
| 2249 | } | 2403 | } |
| 2250 | 2404 | ||
| 2251 | /* Map filename to a valid 8.3 name if necessary. */ | 2405 | /* Map filename to a valid 8.3 name if necessary. |
| 2406 | The result is a pointer to a static buffer, so CAVEAT EMPTOR! */ | ||
| 2252 | const char * | 2407 | const char * |
| 2253 | map_w32_filename (const char * name, const char ** pPath) | 2408 | map_w32_filename (const char * name, const char ** pPath) |
| 2254 | { | 2409 | { |
| @@ -2257,6 +2412,7 @@ map_w32_filename (const char * name, const char ** pPath) | |||
| 2257 | char c; | 2412 | char c; |
| 2258 | char * path; | 2413 | char * path; |
| 2259 | const char * save_name = name; | 2414 | const char * save_name = name; |
| 2415 | int is_fat = 0; | ||
| 2260 | 2416 | ||
| 2261 | if (strlen (name) >= MAX_PATH) | 2417 | if (strlen (name) >= MAX_PATH) |
| 2262 | { | 2418 | { |
| @@ -2278,15 +2434,10 @@ map_w32_filename (const char * name, const char ** pPath) | |||
| 2278 | { | 2434 | { |
| 2279 | switch ( c ) | 2435 | switch ( c ) |
| 2280 | { | 2436 | { |
| 2437 | case ':': | ||
| 2281 | case '\\': | 2438 | case '\\': |
| 2282 | case '/': | 2439 | case '/': |
| 2283 | *str++ = '\\'; | 2440 | *str++ = (c == ':' ? ':' : '\\'); |
| 2284 | extn = 0; /* reset extension flags */ | ||
| 2285 | dots = 2; /* max 2 dots */ | ||
| 2286 | left = 8; /* max length 8 for main part */ | ||
| 2287 | break; | ||
| 2288 | case ':': | ||
| 2289 | *str++ = ':'; | ||
| 2290 | extn = 0; /* reset extension flags */ | 2441 | extn = 0; /* reset extension flags */ |
| 2291 | dots = 2; /* max 2 dots */ | 2442 | dots = 2; /* max 2 dots */ |
| 2292 | left = 8; /* max length 8 for main part */ | 2443 | left = 8; /* max length 8 for main part */ |
| @@ -2395,6 +2546,9 @@ opendir (char *filename) | |||
| 2395 | if (wnet_enum_handle != INVALID_HANDLE_VALUE) | 2546 | if (wnet_enum_handle != INVALID_HANDLE_VALUE) |
| 2396 | return NULL; | 2547 | return NULL; |
| 2397 | 2548 | ||
| 2549 | /* Note: We don't support traversal of UNC volumes via symlinks. | ||
| 2550 | Doing so would mean punishing 99.99% of use cases by resolving | ||
| 2551 | all the possible symlinks in FILENAME, recursively. */ | ||
| 2398 | if (is_unc_volume (filename)) | 2552 | if (is_unc_volume (filename)) |
| 2399 | { | 2553 | { |
| 2400 | wnet_enum_handle = open_unc_volume (filename); | 2554 | wnet_enum_handle = open_unc_volume (filename); |
| @@ -2411,6 +2565,9 @@ opendir (char *filename) | |||
| 2411 | 2565 | ||
| 2412 | strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN); | 2566 | strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN); |
| 2413 | dir_pathname[MAXPATHLEN] = '\0'; | 2567 | dir_pathname[MAXPATHLEN] = '\0'; |
| 2568 | /* Note: We don't support symlinks to file names on FAT volumes. | ||
| 2569 | Doing so would mean punishing 99.99% of use cases by resolving | ||
| 2570 | all the possible symlinks in FILENAME, recursively. */ | ||
| 2414 | dir_is_fat = is_fat_volume (filename, NULL); | 2571 | dir_is_fat = is_fat_volume (filename, NULL); |
| 2415 | 2572 | ||
| 2416 | return dirp; | 2573 | return dirp; |
| @@ -2457,6 +2614,9 @@ readdir (DIR *dirp) | |||
| 2457 | strcat (filename, "\\"); | 2614 | strcat (filename, "\\"); |
| 2458 | strcat (filename, "*"); | 2615 | strcat (filename, "*"); |
| 2459 | 2616 | ||
| 2617 | /* Note: No need to resolve symlinks in FILENAME, because | ||
| 2618 | FindFirst opens the directory that is the target of a | ||
| 2619 | symlink. */ | ||
| 2460 | dir_find_handle = FindFirstFile (filename, &dir_find_data); | 2620 | dir_find_handle = FindFirstFile (filename, &dir_find_data); |
| 2461 | 2621 | ||
| 2462 | if (dir_find_handle == INVALID_HANDLE_VALUE) | 2622 | if (dir_find_handle == INVALID_HANDLE_VALUE) |
| @@ -2650,21 +2810,34 @@ sys_access (const char * path, int mode) | |||
| 2650 | /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its | 2810 | /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its |
| 2651 | newer versions blow up when passed D_OK. */ | 2811 | newer versions blow up when passed D_OK. */ |
| 2652 | path = map_w32_filename (path, NULL); | 2812 | path = map_w32_filename (path, NULL); |
| 2653 | if (is_unc_volume (path)) | 2813 | /* If the last element of PATH is a symlink, we need to resolve it |
| 2654 | { | 2814 | to get the attributes of its target file. Note: any symlinks in |
| 2655 | attributes = unc_volume_file_attributes (path); | 2815 | PATH elements other than the last one are transparently resolved |
| 2656 | if (attributes == -1) { | 2816 | by GetFileAttributes below. */ |
| 2657 | errno = EACCES; | 2817 | if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0) |
| 2658 | return -1; | 2818 | path = chase_symlinks (path); |
| 2659 | } | 2819 | |
| 2660 | } | 2820 | if ((attributes = GetFileAttributes (path)) == -1) |
| 2661 | else if ((attributes = GetFileAttributes (path)) == -1) | ||
| 2662 | { | 2821 | { |
| 2663 | DWORD w32err = GetLastError (); | 2822 | DWORD w32err = GetLastError (); |
| 2664 | 2823 | ||
| 2665 | switch (w32err) | 2824 | switch (w32err) |
| 2666 | { | 2825 | { |
| 2826 | case ERROR_INVALID_NAME: | ||
| 2827 | case ERROR_BAD_PATHNAME: | ||
| 2828 | if (is_unc_volume (path)) | ||
| 2829 | { | ||
| 2830 | attributes = unc_volume_file_attributes (path); | ||
| 2831 | if (attributes == -1) | ||
| 2832 | { | ||
| 2833 | errno = EACCES; | ||
| 2834 | return -1; | ||
| 2835 | } | ||
| 2836 | break; | ||
| 2837 | } | ||
| 2838 | /* FALLTHROUGH */ | ||
| 2667 | case ERROR_FILE_NOT_FOUND: | 2839 | case ERROR_FILE_NOT_FOUND: |
| 2840 | case ERROR_BAD_NETPATH: | ||
| 2668 | errno = ENOENT; | 2841 | errno = ENOENT; |
| 2669 | break; | 2842 | break; |
| 2670 | default: | 2843 | default: |
| @@ -2700,7 +2873,8 @@ sys_chdir (const char * path) | |||
| 2700 | int | 2873 | int |
| 2701 | sys_chmod (const char * path, int mode) | 2874 | sys_chmod (const char * path, int mode) |
| 2702 | { | 2875 | { |
| 2703 | return _chmod (map_w32_filename (path, NULL), mode); | 2876 | path = chase_symlinks (map_w32_filename (path, NULL)); |
| 2877 | return _chmod (path, mode); | ||
| 2704 | } | 2878 | } |
| 2705 | 2879 | ||
| 2706 | int | 2880 | int |
| @@ -2980,6 +3154,7 @@ sys_rename (const char * oldname, const char * newname) | |||
| 2980 | 3154 | ||
| 2981 | if (result < 0) | 3155 | if (result < 0) |
| 2982 | { | 3156 | { |
| 3157 | DWORD w32err = GetLastError (); | ||
| 2983 | 3158 | ||
| 2984 | if (errno == EACCES | 3159 | if (errno == EACCES |
| 2985 | && newname_dev != oldname_dev) | 3160 | && newname_dev != oldname_dev) |
| @@ -2992,7 +3167,7 @@ sys_rename (const char * oldname, const char * newname) | |||
| 2992 | DWORD attributes; | 3167 | DWORD attributes; |
| 2993 | 3168 | ||
| 2994 | if ((attributes = GetFileAttributes (temp)) != -1 | 3169 | if ((attributes = GetFileAttributes (temp)) != -1 |
| 2995 | && attributes & FILE_ATTRIBUTE_DIRECTORY) | 3170 | && (attributes & FILE_ATTRIBUTE_DIRECTORY)) |
| 2996 | errno = EXDEV; | 3171 | errno = EXDEV; |
| 2997 | } | 3172 | } |
| 2998 | else if (errno == EEXIST) | 3173 | else if (errno == EEXIST) |
| @@ -3003,6 +3178,14 @@ sys_rename (const char * oldname, const char * newname) | |||
| 3003 | return result; | 3178 | return result; |
| 3004 | result = rename (temp, newname); | 3179 | result = rename (temp, newname); |
| 3005 | } | 3180 | } |
| 3181 | else if (w32err == ERROR_PRIVILEGE_NOT_HELD | ||
| 3182 | && is_symlink (temp)) | ||
| 3183 | { | ||
| 3184 | /* This is Windows prohibiting the user from creating a | ||
| 3185 | symlink in another place, since that requires | ||
| 3186 | privileges. */ | ||
| 3187 | errno = EPERM; | ||
| 3188 | } | ||
| 3006 | } | 3189 | } |
| 3007 | 3190 | ||
| 3008 | return result; | 3191 | return result; |
| @@ -3133,7 +3316,23 @@ generate_inode_val (const char * name) | |||
| 3133 | #endif | 3316 | #endif |
| 3134 | 3317 | ||
| 3135 | static PSECURITY_DESCRIPTOR | 3318 | static PSECURITY_DESCRIPTOR |
| 3136 | get_file_security_desc (const char *fname) | 3319 | get_file_security_desc_by_handle (HANDLE h) |
| 3320 | { | ||
| 3321 | PSECURITY_DESCRIPTOR psd = NULL; | ||
| 3322 | DWORD err; | ||
| 3323 | SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION | ||
| 3324 | | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ; | ||
| 3325 | |||
| 3326 | err = get_security_info (h, SE_FILE_OBJECT, si, | ||
| 3327 | NULL, NULL, NULL, NULL, &psd); | ||
| 3328 | if (err != ERROR_SUCCESS) | ||
| 3329 | return NULL; | ||
| 3330 | |||
| 3331 | return psd; | ||
| 3332 | } | ||
| 3333 | |||
| 3334 | static PSECURITY_DESCRIPTOR | ||
| 3335 | get_file_security_desc_by_name (const char *fname) | ||
| 3137 | { | 3336 | { |
| 3138 | PSECURITY_DESCRIPTOR psd = NULL; | 3337 | PSECURITY_DESCRIPTOR psd = NULL; |
| 3139 | DWORD sd_len, err; | 3338 | DWORD sd_len, err; |
| @@ -3349,18 +3548,24 @@ is_slow_fs (const char *name) | |||
| 3349 | 3548 | ||
| 3350 | /* MSVC stat function can't cope with UNC names and has other bugs, so | 3549 | /* MSVC stat function can't cope with UNC names and has other bugs, so |
| 3351 | replace it with our own. This also allows us to calculate consistent | 3550 | replace it with our own. This also allows us to calculate consistent |
| 3352 | inode values without hacks in the main Emacs code. */ | 3551 | inode values and owner/group without hacks in the main Emacs code. */ |
| 3353 | int | 3552 | |
| 3354 | stat (const char * path, struct stat * buf) | 3553 | static int |
| 3554 | stat_worker (const char * path, struct stat * buf, int follow_symlinks) | ||
| 3355 | { | 3555 | { |
| 3356 | char *name, *r; | 3556 | char *name, *save_name, *r; |
| 3357 | WIN32_FIND_DATA wfd; | 3557 | WIN32_FIND_DATA wfd; |
| 3358 | HANDLE fh; | 3558 | HANDLE fh; |
| 3359 | unsigned __int64 fake_inode; | 3559 | unsigned __int64 fake_inode = 0; |
| 3360 | int permission; | 3560 | int permission; |
| 3361 | int len; | 3561 | int len; |
| 3362 | int rootdir = FALSE; | 3562 | int rootdir = FALSE; |
| 3363 | PSECURITY_DESCRIPTOR psd = NULL; | 3563 | PSECURITY_DESCRIPTOR psd = NULL; |
| 3564 | int is_a_symlink = 0; | ||
| 3565 | DWORD file_flags = FILE_FLAG_BACKUP_SEMANTICS; | ||
| 3566 | DWORD access_rights = 0; | ||
| 3567 | DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1; | ||
| 3568 | FILETIME ctime, atime, wtime; | ||
| 3364 | 3569 | ||
| 3365 | if (path == NULL || buf == NULL) | 3570 | if (path == NULL || buf == NULL) |
| 3366 | { | 3571 | { |
| @@ -3368,7 +3573,7 @@ stat (const char * path, struct stat * buf) | |||
| 3368 | return -1; | 3573 | return -1; |
| 3369 | } | 3574 | } |
| 3370 | 3575 | ||
| 3371 | name = (char *) map_w32_filename (path, &path); | 3576 | save_name = name = (char *) map_w32_filename (path, &path); |
| 3372 | /* Must be valid filename, no wild cards or other invalid | 3577 | /* Must be valid filename, no wild cards or other invalid |
| 3373 | characters. We use _mbspbrk to support multibyte strings that | 3578 | characters. We use _mbspbrk to support multibyte strings that |
| 3374 | might look to strpbrk as if they included literal *, ?, and other | 3579 | might look to strpbrk as if they included literal *, ?, and other |
| @@ -3380,99 +3585,67 @@ stat (const char * path, struct stat * buf) | |||
| 3380 | return -1; | 3585 | return -1; |
| 3381 | } | 3586 | } |
| 3382 | 3587 | ||
| 3383 | /* If name is "c:/.." or "/.." then stat "c:/" or "/". */ | ||
| 3384 | r = IS_DEVICE_SEP (name[1]) ? &name[2] : name; | ||
| 3385 | if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0') | ||
| 3386 | { | ||
| 3387 | r[1] = r[2] = '\0'; | ||
| 3388 | } | ||
| 3389 | |||
| 3390 | /* Remove trailing directory separator, unless name is the root | 3588 | /* Remove trailing directory separator, unless name is the root |
| 3391 | directory of a drive or UNC volume in which case ensure there | 3589 | directory of a drive or UNC volume in which case ensure there |
| 3392 | is a trailing separator. */ | 3590 | is a trailing separator. */ |
| 3393 | len = strlen (name); | 3591 | len = strlen (name); |
| 3394 | rootdir = (path >= name + len - 1 | ||
| 3395 | && (IS_DIRECTORY_SEP (*path) || *path == 0)); | ||
| 3396 | name = strcpy (alloca (len + 2), name); | 3592 | name = strcpy (alloca (len + 2), name); |
| 3397 | 3593 | ||
| 3398 | if (is_unc_volume (name)) | 3594 | /* Avoid a somewhat costly call to is_symlink if the filesystem |
| 3399 | { | 3595 | doesn't support symlinks. */ |
| 3400 | DWORD attrs = unc_volume_file_attributes (name); | 3596 | if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0) |
| 3401 | 3597 | is_a_symlink = is_symlink (name); | |
| 3402 | if (attrs == -1) | 3598 | |
| 3403 | return -1; | 3599 | /* Plan A: Open the file and get all the necessary information via |
| 3404 | 3600 | the resulting handle. This solves several issues in one blow: | |
| 3405 | memset (&wfd, 0, sizeof (wfd)); | 3601 | |
| 3406 | wfd.dwFileAttributes = attrs; | 3602 | . retrieves attributes for the target of a symlink, if needed |
| 3407 | wfd.ftCreationTime = utc_base_ft; | 3603 | . gets attributes of root directories and symlinks pointing to |
| 3408 | wfd.ftLastAccessTime = utc_base_ft; | 3604 | root directories, thus avoiding the need for special-casing |
| 3409 | wfd.ftLastWriteTime = utc_base_ft; | 3605 | these and detecting them by examining the file-name format |
| 3410 | strcpy (wfd.cFileName, name); | 3606 | . retrieves more accurate attributes (e.g., non-zero size for |
| 3411 | } | 3607 | some directories, esp. directories that are junction points) |
| 3412 | else if (rootdir) | 3608 | . correctly resolves "c:/..", "/.." and similar file names |
| 3413 | { | 3609 | . avoids run-time penalties for 99% of use cases |
| 3414 | if (!IS_DIRECTORY_SEP (name[len-1])) | 3610 | |
| 3415 | strcat (name, "\\"); | 3611 | Plan A is always tried first, unless the user asked not to (but |
| 3416 | if (GetDriveType (name) < 2) | 3612 | if the file is a symlink and we need to follow links, we try Plan |
| 3417 | { | 3613 | A even if the user asked not to). |
| 3418 | errno = ENOENT; | 3614 | |
| 3419 | return -1; | 3615 | If Plan A fails, we go to Plan B (below), where various |
| 3420 | } | 3616 | potentially expensive techniques must be used to handle "special" |
| 3421 | memset (&wfd, 0, sizeof (wfd)); | 3617 | files such as UNC volumes etc. */ |
| 3422 | wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; | ||
| 3423 | wfd.ftCreationTime = utc_base_ft; | ||
| 3424 | wfd.ftLastAccessTime = utc_base_ft; | ||
| 3425 | wfd.ftLastWriteTime = utc_base_ft; | ||
| 3426 | strcpy (wfd.cFileName, name); | ||
| 3427 | } | ||
| 3428 | else | ||
| 3429 | { | ||
| 3430 | if (IS_DIRECTORY_SEP (name[len-1])) | ||
| 3431 | name[len - 1] = 0; | ||
| 3432 | |||
| 3433 | /* (This is hacky, but helps when doing file completions on | ||
| 3434 | network drives.) Optimize by using information available from | ||
| 3435 | active readdir if possible. */ | ||
| 3436 | len = strlen (dir_pathname); | ||
| 3437 | if (IS_DIRECTORY_SEP (dir_pathname[len-1])) | ||
| 3438 | len--; | ||
| 3439 | if (dir_find_handle != INVALID_HANDLE_VALUE | ||
| 3440 | && strnicmp (name, dir_pathname, len) == 0 | ||
| 3441 | && IS_DIRECTORY_SEP (name[len]) | ||
| 3442 | && xstrcasecmp (name + len + 1, dir_static.d_name) == 0) | ||
| 3443 | { | ||
| 3444 | /* This was the last entry returned by readdir. */ | ||
| 3445 | wfd = dir_find_data; | ||
| 3446 | } | ||
| 3447 | else | ||
| 3448 | { | ||
| 3449 | logon_network_drive (name); | ||
| 3450 | |||
| 3451 | fh = FindFirstFile (name, &wfd); | ||
| 3452 | if (fh == INVALID_HANDLE_VALUE) | ||
| 3453 | { | ||
| 3454 | errno = ENOENT; | ||
| 3455 | return -1; | ||
| 3456 | } | ||
| 3457 | FindClose (fh); | ||
| 3458 | } | ||
| 3459 | } | ||
| 3460 | |||
| 3461 | if (!(NILP (Vw32_get_true_file_attributes) | 3618 | if (!(NILP (Vw32_get_true_file_attributes) |
| 3462 | || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name))) | 3619 | || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name))) |
| 3463 | /* No access rights required to get info. */ | 3620 | /* Following symlinks requires getting the info by handle. */ |
| 3464 | && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, | 3621 | || (is_a_symlink && follow_symlinks)) |
| 3465 | FILE_FLAG_BACKUP_SEMANTICS, NULL)) | ||
| 3466 | != INVALID_HANDLE_VALUE) | ||
| 3467 | { | 3622 | { |
| 3623 | BY_HANDLE_FILE_INFORMATION info; | ||
| 3624 | |||
| 3625 | if (is_a_symlink && !follow_symlinks) | ||
| 3626 | file_flags |= FILE_FLAG_OPEN_REPARSE_POINT; | ||
| 3627 | /* READ_CONTROL access rights are required to get security info | ||
| 3628 | by handle. But if the OS doesn't support security in the | ||
| 3629 | first place, we don't need to try. */ | ||
| 3630 | if (is_windows_9x () != TRUE) | ||
| 3631 | access_rights |= READ_CONTROL; | ||
| 3632 | |||
| 3633 | fh = CreateFile (name, access_rights, 0, NULL, OPEN_EXISTING, | ||
| 3634 | file_flags, NULL); | ||
| 3635 | /* If CreateFile fails with READ_CONTROL, try again with zero as | ||
| 3636 | access rights. */ | ||
| 3637 | if (fh == INVALID_HANDLE_VALUE && access_rights) | ||
| 3638 | fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, | ||
| 3639 | file_flags, NULL); | ||
| 3640 | if (fh == INVALID_HANDLE_VALUE) | ||
| 3641 | goto no_true_file_attributes; | ||
| 3642 | |||
| 3468 | /* This is more accurate in terms of getting the correct number | 3643 | /* This is more accurate in terms of getting the correct number |
| 3469 | of links, but is quite slow (it is noticeable when Emacs is | 3644 | of links, but is quite slow (it is noticeable when Emacs is |
| 3470 | making a list of file name completions). */ | 3645 | making a list of file name completions). */ |
| 3471 | BY_HANDLE_FILE_INFORMATION info; | ||
| 3472 | |||
| 3473 | if (GetFileInformationByHandle (fh, &info)) | 3646 | if (GetFileInformationByHandle (fh, &info)) |
| 3474 | { | 3647 | { |
| 3475 | buf->st_nlink = info.nNumberOfLinks; | 3648 | nlinks = info.nNumberOfLinks; |
| 3476 | /* Might as well use file index to fake inode values, but this | 3649 | /* Might as well use file index to fake inode values, but this |
| 3477 | is not guaranteed to be unique unless we keep a handle open | 3650 | is not guaranteed to be unique unless we keep a handle open |
| 3478 | all the time (even then there are situations where it is | 3651 | all the time (even then there are situations where it is |
| @@ -3481,20 +3654,53 @@ stat (const char * path, struct stat * buf) | |||
| 3481 | fake_inode = info.nFileIndexHigh; | 3654 | fake_inode = info.nFileIndexHigh; |
| 3482 | fake_inode <<= 32; | 3655 | fake_inode <<= 32; |
| 3483 | fake_inode += info.nFileIndexLow; | 3656 | fake_inode += info.nFileIndexLow; |
| 3657 | serialnum = info.dwVolumeSerialNumber; | ||
| 3658 | fs_high = info.nFileSizeHigh; | ||
| 3659 | fs_low = info.nFileSizeLow; | ||
| 3660 | ctime = info.ftCreationTime; | ||
| 3661 | atime = info.ftLastAccessTime; | ||
| 3662 | wtime = info.ftLastWriteTime; | ||
| 3663 | fattrs = info.dwFileAttributes; | ||
| 3484 | } | 3664 | } |
| 3485 | else | 3665 | else |
| 3486 | { | 3666 | { |
| 3487 | buf->st_nlink = 1; | 3667 | /* We don't go to Plan B here, because it's not clear that |
| 3488 | fake_inode = 0; | 3668 | it's a good idea. The only known use case where |
| 3669 | CreateFile succeeds, but GetFileInformationByHandle fails | ||
| 3670 | (with ERROR_INVALID_FUNCTION) is for character devices | ||
| 3671 | such as NUL, PRN, etc. For these, switching to Plan B is | ||
| 3672 | a net loss, because we lose the character device | ||
| 3673 | attribute returned by GetFileType below (FindFirstFile | ||
| 3674 | doesn't set that bit in the attributes), and the other | ||
| 3675 | fields don't make sense for character devices anyway. | ||
| 3676 | Emacs doesn't really care for non-file entities in the | ||
| 3677 | context of l?stat, so neither do we. */ | ||
| 3678 | |||
| 3679 | /* w32err is assigned so one could put a breakpoint here and | ||
| 3680 | examine its value, when GetFileInformationByHandle | ||
| 3681 | fails. */ | ||
| 3682 | DWORD w32err = GetLastError (); | ||
| 3683 | |||
| 3684 | switch (w32err) | ||
| 3685 | { | ||
| 3686 | case ERROR_FILE_NOT_FOUND: /* can this ever happen? */ | ||
| 3687 | errno = ENOENT; | ||
| 3688 | return -1; | ||
| 3689 | } | ||
| 3489 | } | 3690 | } |
| 3490 | 3691 | ||
| 3491 | if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | 3692 | /* Test for a symlink before testing for a directory, since |
| 3492 | { | 3693 | symlinks to directories have the directory bit set, but we |
| 3493 | buf->st_mode = S_IFDIR; | 3694 | don't want them to appear as directories. */ |
| 3494 | } | 3695 | if (is_a_symlink && !follow_symlinks) |
| 3696 | buf->st_mode = S_IFLNK; | ||
| 3697 | else if (fattrs & FILE_ATTRIBUTE_DIRECTORY) | ||
| 3698 | buf->st_mode = S_IFDIR; | ||
| 3495 | else | 3699 | else |
| 3496 | { | 3700 | { |
| 3497 | switch (GetFileType (fh)) | 3701 | DWORD ftype = GetFileType (fh); |
| 3702 | |||
| 3703 | switch (ftype) | ||
| 3498 | { | 3704 | { |
| 3499 | case FILE_TYPE_DISK: | 3705 | case FILE_TYPE_DISK: |
| 3500 | buf->st_mode = S_IFREG; | 3706 | buf->st_mode = S_IFREG; |
| @@ -3508,21 +3714,143 @@ stat (const char * path, struct stat * buf) | |||
| 3508 | buf->st_mode = S_IFCHR; | 3714 | buf->st_mode = S_IFCHR; |
| 3509 | } | 3715 | } |
| 3510 | } | 3716 | } |
| 3717 | /* We produce the fallback owner and group data, based on the | ||
| 3718 | current user that runs Emacs, in the following cases: | ||
| 3719 | |||
| 3720 | . this is Windows 9X | ||
| 3721 | . getting security by handle failed, and we need to produce | ||
| 3722 | information for the target of a symlink (this is better | ||
| 3723 | than producing a potentially misleading info about the | ||
| 3724 | symlink itself) | ||
| 3725 | |||
| 3726 | If getting security by handle fails, and we don't need to | ||
| 3727 | resolve symlinks, we try getting security by name. */ | ||
| 3728 | if (is_windows_9x () != TRUE) | ||
| 3729 | psd = get_file_security_desc_by_handle (fh); | ||
| 3730 | if (psd) | ||
| 3731 | { | ||
| 3732 | get_file_owner_and_group (psd, name, buf); | ||
| 3733 | LocalFree (psd); | ||
| 3734 | } | ||
| 3735 | else if (is_windows_9x () == TRUE) | ||
| 3736 | get_file_owner_and_group (NULL, name, buf); | ||
| 3737 | else if (!(is_a_symlink && follow_symlinks)) | ||
| 3738 | { | ||
| 3739 | psd = get_file_security_desc_by_name (name); | ||
| 3740 | get_file_owner_and_group (psd, name, buf); | ||
| 3741 | xfree (psd); | ||
| 3742 | } | ||
| 3743 | else | ||
| 3744 | get_file_owner_and_group (NULL, name, buf); | ||
| 3511 | CloseHandle (fh); | 3745 | CloseHandle (fh); |
| 3512 | psd = get_file_security_desc (name); | ||
| 3513 | get_file_owner_and_group (psd, name, buf); | ||
| 3514 | } | 3746 | } |
| 3515 | else | 3747 | else |
| 3516 | { | 3748 | { |
| 3517 | /* Don't bother to make this information more accurate. */ | 3749 | no_true_file_attributes: |
| 3518 | buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? | 3750 | /* Plan B: Either getting a handle on the file failed, or the |
| 3519 | S_IFDIR : S_IFREG; | 3751 | caller explicitly asked us to not bother making this |
| 3520 | buf->st_nlink = 1; | 3752 | information more accurate. |
| 3521 | fake_inode = 0; | 3753 | |
| 3754 | Implementation note: In Plan B, we never bother to resolve | ||
| 3755 | symlinks, even if we got here because we tried Plan A and | ||
| 3756 | failed. That's because, even if the caller asked for extra | ||
| 3757 | precision by setting Vw32_get_true_file_attributes to t, | ||
| 3758 | resolving symlinks requires acquiring a file handle to the | ||
| 3759 | symlink, which we already know will fail. And if the user | ||
| 3760 | did not ask for extra precision, resolving symlinks will fly | ||
| 3761 | in the face of that request, since the user then wants the | ||
| 3762 | lightweight version of the code. */ | ||
| 3763 | rootdir = (path >= save_name + len - 1 | ||
| 3764 | && (IS_DIRECTORY_SEP (*path) || *path == 0)); | ||
| 3765 | |||
| 3766 | /* If name is "c:/.." or "/.." then stat "c:/" or "/". */ | ||
| 3767 | r = IS_DEVICE_SEP (name[1]) ? &name[2] : name; | ||
| 3768 | if (IS_DIRECTORY_SEP (r[0]) | ||
| 3769 | && r[1] == '.' && r[2] == '.' && r[3] == '\0') | ||
| 3770 | r[1] = r[2] = '\0'; | ||
| 3771 | |||
| 3772 | /* Note: If NAME is a symlink to the root of a UNC volume | ||
| 3773 | (i.e. "\\SERVER"), we will not detect that here, and we will | ||
| 3774 | return data about the symlink as result of FindFirst below. | ||
| 3775 | This is unfortunate, but that marginal use case does not | ||
| 3776 | justify a call to chase_symlinks which would impose a penalty | ||
| 3777 | on all the other use cases. (We get here for symlinks to | ||
| 3778 | roots of UNC volumes because CreateFile above fails for them, | ||
| 3779 | unlike with symlinks to root directories X:\ of drives.) */ | ||
| 3780 | if (is_unc_volume (name)) | ||
| 3781 | { | ||
| 3782 | fattrs = unc_volume_file_attributes (name); | ||
| 3783 | if (fattrs == -1) | ||
| 3784 | return -1; | ||
| 3785 | |||
| 3786 | ctime = atime = wtime = utc_base_ft; | ||
| 3787 | } | ||
| 3788 | else if (rootdir) | ||
| 3789 | { | ||
| 3790 | if (!IS_DIRECTORY_SEP (name[len-1])) | ||
| 3791 | strcat (name, "\\"); | ||
| 3792 | if (GetDriveType (name) < 2) | ||
| 3793 | { | ||
| 3794 | errno = ENOENT; | ||
| 3795 | return -1; | ||
| 3796 | } | ||
| 3797 | |||
| 3798 | fattrs = FILE_ATTRIBUTE_DIRECTORY; | ||
| 3799 | ctime = atime = wtime = utc_base_ft; | ||
| 3800 | } | ||
| 3801 | else | ||
| 3802 | { | ||
| 3803 | if (IS_DIRECTORY_SEP (name[len-1])) | ||
| 3804 | name[len - 1] = 0; | ||
| 3805 | |||
| 3806 | /* (This is hacky, but helps when doing file completions on | ||
| 3807 | network drives.) Optimize by using information available from | ||
| 3808 | active readdir if possible. */ | ||
| 3809 | len = strlen (dir_pathname); | ||
| 3810 | if (IS_DIRECTORY_SEP (dir_pathname[len-1])) | ||
| 3811 | len--; | ||
| 3812 | if (dir_find_handle != INVALID_HANDLE_VALUE | ||
| 3813 | && !(is_a_symlink && follow_symlinks) | ||
| 3814 | && strnicmp (save_name, dir_pathname, len) == 0 | ||
| 3815 | && IS_DIRECTORY_SEP (name[len]) | ||
| 3816 | && xstrcasecmp (name + len + 1, dir_static.d_name) == 0) | ||
| 3817 | { | ||
| 3818 | /* This was the last entry returned by readdir. */ | ||
| 3819 | wfd = dir_find_data; | ||
| 3820 | } | ||
| 3821 | else | ||
| 3822 | { | ||
| 3823 | logon_network_drive (name); | ||
| 3824 | |||
| 3825 | fh = FindFirstFile (name, &wfd); | ||
| 3826 | if (fh == INVALID_HANDLE_VALUE) | ||
| 3827 | { | ||
| 3828 | errno = ENOENT; | ||
| 3829 | return -1; | ||
| 3830 | } | ||
| 3831 | FindClose (fh); | ||
| 3832 | } | ||
| 3833 | /* Note: if NAME is a symlink, the information we get from | ||
| 3834 | FindFirstFile is for the symlink, not its target. */ | ||
| 3835 | fattrs = wfd.dwFileAttributes; | ||
| 3836 | ctime = wfd.ftCreationTime; | ||
| 3837 | atime = wfd.ftLastAccessTime; | ||
| 3838 | wtime = wfd.ftLastWriteTime; | ||
| 3839 | fs_high = wfd.nFileSizeHigh; | ||
| 3840 | fs_low = wfd.nFileSizeLow; | ||
| 3841 | fake_inode = 0; | ||
| 3842 | nlinks = 1; | ||
| 3843 | serialnum = volume_info.serialnum; | ||
| 3844 | } | ||
| 3845 | if (is_a_symlink && !follow_symlinks) | ||
| 3846 | buf->st_mode = S_IFLNK; | ||
| 3847 | else if (fattrs & FILE_ATTRIBUTE_DIRECTORY) | ||
| 3848 | buf->st_mode = S_IFDIR; | ||
| 3849 | else | ||
| 3850 | buf->st_mode = S_IFREG; | ||
| 3522 | 3851 | ||
| 3523 | get_file_owner_and_group (NULL, name, buf); | 3852 | get_file_owner_and_group (NULL, name, buf); |
| 3524 | } | 3853 | } |
| 3525 | xfree (psd); | ||
| 3526 | 3854 | ||
| 3527 | #if 0 | 3855 | #if 0 |
| 3528 | /* Not sure if there is any point in this. */ | 3856 | /* Not sure if there is any point in this. */ |
| @@ -3536,43 +3864,56 @@ stat (const char * path, struct stat * buf) | |||
| 3536 | } | 3864 | } |
| 3537 | #endif | 3865 | #endif |
| 3538 | 3866 | ||
| 3539 | /* MSVC defines _ino_t to be short; other libc's might not. */ | 3867 | buf->st_ino = fake_inode; |
| 3540 | if (sizeof (buf->st_ino) == 2) | ||
| 3541 | buf->st_ino = fake_inode ^ (fake_inode >> 16); | ||
| 3542 | else | ||
| 3543 | buf->st_ino = fake_inode; | ||
| 3544 | 3868 | ||
| 3545 | /* volume_info is set indirectly by map_w32_filename */ | 3869 | buf->st_dev = serialnum; |
| 3546 | buf->st_dev = volume_info.serialnum; | 3870 | buf->st_rdev = serialnum; |
| 3547 | buf->st_rdev = volume_info.serialnum; | ||
| 3548 | 3871 | ||
| 3549 | buf->st_size = wfd.nFileSizeHigh; | 3872 | buf->st_size = fs_high; |
| 3550 | buf->st_size <<= 32; | 3873 | buf->st_size <<= 32; |
| 3551 | buf->st_size += wfd.nFileSizeLow; | 3874 | buf->st_size += fs_low; |
| 3875 | buf->st_nlink = nlinks; | ||
| 3552 | 3876 | ||
| 3553 | /* Convert timestamps to Unix format. */ | 3877 | /* Convert timestamps to Unix format. */ |
| 3554 | buf->st_mtime = convert_time (wfd.ftLastWriteTime); | 3878 | buf->st_mtime = convert_time (wtime); |
| 3555 | buf->st_atime = convert_time (wfd.ftLastAccessTime); | 3879 | buf->st_atime = convert_time (atime); |
| 3556 | if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; | 3880 | if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; |
| 3557 | buf->st_ctime = convert_time (wfd.ftCreationTime); | 3881 | buf->st_ctime = convert_time (ctime); |
| 3558 | if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; | 3882 | if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; |
| 3559 | 3883 | ||
| 3560 | /* determine rwx permissions */ | 3884 | /* determine rwx permissions */ |
| 3561 | if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) | 3885 | if (is_a_symlink && !follow_symlinks) |
| 3562 | permission = S_IREAD; | 3886 | permission = S_IREAD | S_IWRITE | S_IEXEC; /* Posix expectations */ |
| 3563 | else | 3887 | else |
| 3564 | permission = S_IREAD | S_IWRITE; | 3888 | { |
| 3889 | if (fattrs & FILE_ATTRIBUTE_READONLY) | ||
| 3890 | permission = S_IREAD; | ||
| 3891 | else | ||
| 3892 | permission = S_IREAD | S_IWRITE; | ||
| 3565 | 3893 | ||
| 3566 | if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | 3894 | if (fattrs & FILE_ATTRIBUTE_DIRECTORY) |
| 3567 | permission |= S_IEXEC; | 3895 | permission |= S_IEXEC; |
| 3568 | else if (is_exec (name)) | 3896 | else if (is_exec (name)) |
| 3569 | permission |= S_IEXEC; | 3897 | permission |= S_IEXEC; |
| 3898 | } | ||
| 3570 | 3899 | ||
| 3571 | buf->st_mode |= permission | (permission >> 3) | (permission >> 6); | 3900 | buf->st_mode |= permission | (permission >> 3) | (permission >> 6); |
| 3572 | 3901 | ||
| 3573 | return 0; | 3902 | return 0; |
| 3574 | } | 3903 | } |
| 3575 | 3904 | ||
| 3905 | int | ||
| 3906 | stat (const char * path, struct stat * buf) | ||
| 3907 | { | ||
| 3908 | return stat_worker (path, buf, 1); | ||
| 3909 | } | ||
| 3910 | |||
| 3911 | int | ||
| 3912 | lstat (const char * path, struct stat * buf) | ||
| 3913 | { | ||
| 3914 | return stat_worker (path, buf, 0); | ||
| 3915 | } | ||
| 3916 | |||
| 3576 | /* Provide fstat and utime as well as stat for consistent handling of | 3917 | /* Provide fstat and utime as well as stat for consistent handling of |
| 3577 | file timestamps. */ | 3918 | file timestamps. */ |
| 3578 | int | 3919 | int |
| @@ -3713,31 +4054,460 @@ utime (const char *name, struct utimbuf *times) | |||
| 3713 | } | 4054 | } |
| 3714 | 4055 | ||
| 3715 | 4056 | ||
| 3716 | /* Symlink-related functions that always fail. Used in fileio.c and in | 4057 | /* Symlink-related functions. */ |
| 3717 | sysdep.c to avoid #ifdef's. */ | 4058 | #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY |
| 4059 | #define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 | ||
| 4060 | #endif | ||
| 4061 | |||
| 3718 | int | 4062 | int |
| 3719 | symlink (char const *dummy1, char const *dummy2) | 4063 | symlink (char const *filename, char const *linkname) |
| 3720 | { | 4064 | { |
| 3721 | errno = ENOSYS; | 4065 | char linkfn[MAX_PATH], *tgtfn; |
| 3722 | return -1; | 4066 | DWORD flags = 0; |
| 4067 | int dir_access, filename_ends_in_slash; | ||
| 4068 | |||
| 4069 | /* Diagnostics follows Posix as much as possible. */ | ||
| 4070 | if (filename == NULL || linkname == NULL) | ||
| 4071 | { | ||
| 4072 | errno = EFAULT; | ||
| 4073 | return -1; | ||
| 4074 | } | ||
| 4075 | if (!*filename) | ||
| 4076 | { | ||
| 4077 | errno = ENOENT; | ||
| 4078 | return -1; | ||
| 4079 | } | ||
| 4080 | if (strlen (filename) > MAX_PATH || strlen (linkname) > MAX_PATH) | ||
| 4081 | { | ||
| 4082 | errno = ENAMETOOLONG; | ||
| 4083 | return -1; | ||
| 4084 | } | ||
| 4085 | |||
| 4086 | strcpy (linkfn, map_w32_filename (linkname, NULL)); | ||
| 4087 | if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0) | ||
| 4088 | { | ||
| 4089 | errno = EPERM; | ||
| 4090 | return -1; | ||
| 4091 | } | ||
| 4092 | |||
| 4093 | /* Note: since empty FILENAME was already rejected, we can safely | ||
| 4094 | refer to FILENAME[1]. */ | ||
| 4095 | if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1]))) | ||
| 4096 | { | ||
| 4097 | /* Non-absolute FILENAME is understood as being relative to | ||
| 4098 | LINKNAME's directory. We need to prepend that directory to | ||
| 4099 | FILENAME to get correct results from sys_access below, since | ||
| 4100 | otherwise it will interpret FILENAME relative to the | ||
| 4101 | directory where the Emacs process runs. Note that | ||
| 4102 | make-symbolic-link always makes sure LINKNAME is a fully | ||
| 4103 | expanded file name. */ | ||
| 4104 | char tem[MAX_PATH]; | ||
| 4105 | char *p = linkfn + strlen (linkfn); | ||
| 4106 | |||
| 4107 | while (p > linkfn && !IS_ANY_SEP (p[-1])) | ||
| 4108 | p--; | ||
| 4109 | if (p > linkfn) | ||
| 4110 | strncpy (tem, linkfn, p - linkfn); | ||
| 4111 | tem[p - linkfn] = '\0'; | ||
| 4112 | strcat (tem, filename); | ||
| 4113 | dir_access = sys_access (tem, D_OK); | ||
| 4114 | } | ||
| 4115 | else | ||
| 4116 | dir_access = sys_access (filename, D_OK); | ||
| 4117 | |||
| 4118 | /* Since Windows distinguishes between symlinks to directories and | ||
| 4119 | to files, we provide a kludgey feature: if FILENAME doesn't | ||
| 4120 | exist, but ends in a slash, we create a symlink to directory. If | ||
| 4121 | FILENAME exists and is a directory, we always create a symlink to | ||
| 4122 | directory. */ | ||
| 4123 | filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]); | ||
| 4124 | if (dir_access == 0 || filename_ends_in_slash) | ||
| 4125 | flags = SYMBOLIC_LINK_FLAG_DIRECTORY; | ||
| 4126 | |||
| 4127 | tgtfn = (char *)map_w32_filename (filename, NULL); | ||
| 4128 | if (filename_ends_in_slash) | ||
| 4129 | tgtfn[strlen (tgtfn) - 1] = '\0'; | ||
| 4130 | |||
| 4131 | errno = 0; | ||
| 4132 | if (!create_symbolic_link (linkfn, tgtfn, flags)) | ||
| 4133 | { | ||
| 4134 | /* ENOSYS is set by create_symbolic_link, when it detects that | ||
| 4135 | the OS doesn't support the CreateSymbolicLink API. */ | ||
| 4136 | if (errno != ENOSYS) | ||
| 4137 | { | ||
| 4138 | DWORD w32err = GetLastError (); | ||
| 4139 | |||
| 4140 | switch (w32err) | ||
| 4141 | { | ||
| 4142 | /* ERROR_SUCCESS is sometimes returned when LINKFN and | ||
| 4143 | TGTFN point to the same file name, go figure. */ | ||
| 4144 | case ERROR_SUCCESS: | ||
| 4145 | case ERROR_FILE_EXISTS: | ||
| 4146 | errno = EEXIST; | ||
| 4147 | break; | ||
| 4148 | case ERROR_ACCESS_DENIED: | ||
| 4149 | errno = EACCES; | ||
| 4150 | break; | ||
| 4151 | case ERROR_FILE_NOT_FOUND: | ||
| 4152 | case ERROR_PATH_NOT_FOUND: | ||
| 4153 | case ERROR_BAD_NETPATH: | ||
| 4154 | case ERROR_INVALID_REPARSE_DATA: | ||
| 4155 | errno = ENOENT; | ||
| 4156 | break; | ||
| 4157 | case ERROR_DIRECTORY: | ||
| 4158 | errno = EISDIR; | ||
| 4159 | break; | ||
| 4160 | case ERROR_PRIVILEGE_NOT_HELD: | ||
| 4161 | case ERROR_NOT_ALL_ASSIGNED: | ||
| 4162 | errno = EPERM; | ||
| 4163 | break; | ||
| 4164 | case ERROR_DISK_FULL: | ||
| 4165 | errno = ENOSPC; | ||
| 4166 | break; | ||
| 4167 | default: | ||
| 4168 | errno = EINVAL; | ||
| 4169 | break; | ||
| 4170 | } | ||
| 4171 | } | ||
| 4172 | return -1; | ||
| 4173 | } | ||
| 4174 | return 0; | ||
| 3723 | } | 4175 | } |
| 3724 | 4176 | ||
| 4177 | /* A quick inexpensive test of whether FILENAME identifies a file that | ||
| 4178 | is a symlink. Returns non-zero if it is, zero otherwise. FILENAME | ||
| 4179 | must already be in the normalized form returned by | ||
| 4180 | map_w32_filename. | ||
| 4181 | |||
| 4182 | Note: for repeated operations on many files, it is best to test | ||
| 4183 | whether the underlying volume actually supports symlinks, by | ||
| 4184 | testing the FILE_SUPPORTS_REPARSE_POINTS bit in volume's flags, and | ||
| 4185 | avoid the call to this function if it doesn't. That's because the | ||
| 4186 | call to GetFileAttributes takes a non-negligible time, expecially | ||
| 4187 | on non-local or removable filesystems. See stat_worker for an | ||
| 4188 | example of how to do that. */ | ||
| 4189 | static int | ||
| 4190 | is_symlink (const char *filename) | ||
| 4191 | { | ||
| 4192 | DWORD attrs; | ||
| 4193 | WIN32_FIND_DATA wfd; | ||
| 4194 | HANDLE fh; | ||
| 4195 | |||
| 4196 | attrs = GetFileAttributes (filename); | ||
| 4197 | if (attrs == -1) | ||
| 4198 | { | ||
| 4199 | DWORD w32err = GetLastError (); | ||
| 4200 | |||
| 4201 | switch (w32err) | ||
| 4202 | { | ||
| 4203 | case ERROR_BAD_NETPATH: /* network share, can't be a symlink */ | ||
| 4204 | break; | ||
| 4205 | case ERROR_ACCESS_DENIED: | ||
| 4206 | errno = EACCES; | ||
| 4207 | break; | ||
| 4208 | case ERROR_FILE_NOT_FOUND: | ||
| 4209 | case ERROR_PATH_NOT_FOUND: | ||
| 4210 | default: | ||
| 4211 | errno = ENOENT; | ||
| 4212 | break; | ||
| 4213 | } | ||
| 4214 | return 0; | ||
| 4215 | } | ||
| 4216 | if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0) | ||
| 4217 | return 0; | ||
| 4218 | logon_network_drive (filename); | ||
| 4219 | fh = FindFirstFile (filename, &wfd); | ||
| 4220 | if (fh == INVALID_HANDLE_VALUE) | ||
| 4221 | return 0; | ||
| 4222 | FindClose (fh); | ||
| 4223 | return (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 | ||
| 4224 | && (wfd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK; | ||
| 4225 | } | ||
| 4226 | |||
| 4227 | /* If NAME identifies a symbolic link, copy into BUF the file name of | ||
| 4228 | the symlink's target. Copy at most BUF_SIZE bytes, and do NOT | ||
| 4229 | null-terminate the target name, even if it fits. Return the number | ||
| 4230 | of bytes copied, or -1 if NAME is not a symlink or any error was | ||
| 4231 | encountered while resolving it. The file name copied into BUF is | ||
| 4232 | encoded in the current ANSI codepage. */ | ||
| 3725 | ssize_t | 4233 | ssize_t |
| 3726 | readlink (const char *name, char *dummy1, size_t dummy2) | 4234 | readlink (const char *name, char *buf, size_t buf_size) |
| 3727 | { | 4235 | { |
| 3728 | /* `access' is much faster than `stat' on MS-Windows. */ | 4236 | const char *path; |
| 3729 | if (sys_access (name, 0) == 0) | 4237 | TOKEN_PRIVILEGES privs; |
| 3730 | errno = EINVAL; | 4238 | int restore_privs = 0; |
| 3731 | return -1; | 4239 | HANDLE sh; |
| 4240 | ssize_t retval; | ||
| 4241 | |||
| 4242 | if (name == NULL) | ||
| 4243 | { | ||
| 4244 | errno = EFAULT; | ||
| 4245 | return -1; | ||
| 4246 | } | ||
| 4247 | if (!*name) | ||
| 4248 | { | ||
| 4249 | errno = ENOENT; | ||
| 4250 | return -1; | ||
| 4251 | } | ||
| 4252 | |||
| 4253 | path = map_w32_filename (name, NULL); | ||
| 4254 | |||
| 4255 | if (strlen (path) > MAX_PATH) | ||
| 4256 | { | ||
| 4257 | errno = ENAMETOOLONG; | ||
| 4258 | return -1; | ||
| 4259 | } | ||
| 4260 | |||
| 4261 | errno = 0; | ||
| 4262 | if (is_windows_9x () == TRUE | ||
| 4263 | || (volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0 | ||
| 4264 | || !is_symlink (path)) | ||
| 4265 | { | ||
| 4266 | if (!errno) | ||
| 4267 | errno = EINVAL; /* not a symlink */ | ||
| 4268 | return -1; | ||
| 4269 | } | ||
| 4270 | |||
| 4271 | /* Done with simple tests, now we're in for some _real_ work. */ | ||
| 4272 | if (enable_privilege (SE_BACKUP_NAME, TRUE, &privs)) | ||
| 4273 | restore_privs = 1; | ||
| 4274 | /* Implementation note: From here and onward, don't return early, | ||
| 4275 | since that will fail to restore the original set of privileges of | ||
| 4276 | the calling thread. */ | ||
| 4277 | |||
| 4278 | retval = -1; /* not too optimistic, are we? */ | ||
| 4279 | |||
| 4280 | /* Note: In the next call to CreateFile, we use zero as the 2nd | ||
| 4281 | argument because, when the symlink is a hidden/system file, | ||
| 4282 | e.g. 'C:\Users\All Users', GENERIC_READ fails with | ||
| 4283 | ERROR_ACCESS_DENIED. Zero seems to work just fine, both for file | ||
| 4284 | and directory symlinks. */ | ||
| 4285 | sh = CreateFile (path, 0, 0, NULL, OPEN_EXISTING, | ||
| 4286 | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, | ||
| 4287 | NULL); | ||
| 4288 | if (sh != INVALID_HANDLE_VALUE) | ||
| 4289 | { | ||
| 4290 | BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | ||
| 4291 | REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)&reparse_buf[0]; | ||
| 4292 | DWORD retbytes; | ||
| 4293 | |||
| 4294 | if (!DeviceIoControl (sh, FSCTL_GET_REPARSE_POINT, NULL, 0, | ||
| 4295 | reparse_buf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, | ||
| 4296 | &retbytes, NULL)) | ||
| 4297 | errno = EIO; | ||
| 4298 | else if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK) | ||
| 4299 | errno = EINVAL; | ||
| 4300 | else | ||
| 4301 | { | ||
| 4302 | /* Copy the link target name, in wide characters, fro | ||
| 4303 | reparse_data, then convert it to multibyte encoding in | ||
| 4304 | the current locale's codepage. */ | ||
| 4305 | WCHAR *lwname; | ||
| 4306 | BYTE lname[MAX_PATH]; | ||
| 4307 | USHORT lname_len; | ||
| 4308 | USHORT lwname_len = | ||
| 4309 | reparse_data->SymbolicLinkReparseBuffer.PrintNameLength; | ||
| 4310 | WCHAR *lwname_src = | ||
| 4311 | reparse_data->SymbolicLinkReparseBuffer.PathBuffer | ||
| 4312 | + reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR); | ||
| 4313 | |||
| 4314 | /* According to MSDN, PrintNameLength does not include the | ||
| 4315 | terminating null character. */ | ||
| 4316 | lwname = alloca ((lwname_len + 1) * sizeof(WCHAR)); | ||
| 4317 | memcpy (lwname, lwname_src, lwname_len); | ||
| 4318 | lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */ | ||
| 4319 | |||
| 4320 | /* FIXME: Should we use the current file-name coding system | ||
| 4321 | instead of the fixed value of the ANSI codepage? */ | ||
| 4322 | lname_len = WideCharToMultiByte (w32_ansi_code_page, 0, lwname, -1, | ||
| 4323 | lname, MAX_PATH, NULL, NULL); | ||
| 4324 | if (!lname_len) | ||
| 4325 | { | ||
| 4326 | /* WideCharToMultiByte failed. */ | ||
| 4327 | DWORD w32err1 = GetLastError (); | ||
| 4328 | |||
| 4329 | switch (w32err1) | ||
| 4330 | { | ||
| 4331 | case ERROR_INSUFFICIENT_BUFFER: | ||
| 4332 | errno = ENAMETOOLONG; | ||
| 4333 | break; | ||
| 4334 | case ERROR_INVALID_PARAMETER: | ||
| 4335 | errno = EFAULT; | ||
| 4336 | break; | ||
| 4337 | case ERROR_NO_UNICODE_TRANSLATION: | ||
| 4338 | errno = ENOENT; | ||
| 4339 | break; | ||
| 4340 | default: | ||
| 4341 | errno = EINVAL; | ||
| 4342 | break; | ||
| 4343 | } | ||
| 4344 | } | ||
| 4345 | else | ||
| 4346 | { | ||
| 4347 | size_t size_to_copy = buf_size; | ||
| 4348 | BYTE *p = lname; | ||
| 4349 | BYTE *pend = p + lname_len; | ||
| 4350 | |||
| 4351 | /* Normalize like dostounix_filename does, but we don't | ||
| 4352 | want to assume that lname is null-terminated. */ | ||
| 4353 | if (*p && p[1] == ':' && *p >= 'A' && *p <= 'Z') | ||
| 4354 | *p += 'a' - 'A'; | ||
| 4355 | while (p <= pend) | ||
| 4356 | { | ||
| 4357 | if (*p == '\\') | ||
| 4358 | *p = '/'; | ||
| 4359 | ++p; | ||
| 4360 | } | ||
| 4361 | /* Testing for null-terminated LNAME is paranoia: | ||
| 4362 | WideCharToMultiByte should always return a | ||
| 4363 | null-terminated string when its 4th argument is -1 | ||
| 4364 | and its 3rd argument is null-terminated (which they | ||
| 4365 | are, see above). */ | ||
| 4366 | if (lname[lname_len - 1] == '\0') | ||
| 4367 | lname_len--; | ||
| 4368 | if (lname_len <= buf_size) | ||
| 4369 | size_to_copy = lname_len; | ||
| 4370 | strncpy (buf, lname, size_to_copy); | ||
| 4371 | /* Success! */ | ||
| 4372 | retval = size_to_copy; | ||
| 4373 | } | ||
| 4374 | } | ||
| 4375 | CloseHandle (sh); | ||
| 4376 | } | ||
| 4377 | else | ||
| 4378 | { | ||
| 4379 | /* CreateFile failed. */ | ||
| 4380 | DWORD w32err2 = GetLastError (); | ||
| 4381 | |||
| 4382 | switch (w32err2) | ||
| 4383 | { | ||
| 4384 | case ERROR_FILE_NOT_FOUND: | ||
| 4385 | case ERROR_PATH_NOT_FOUND: | ||
| 4386 | errno = ENOENT; | ||
| 4387 | break; | ||
| 4388 | case ERROR_ACCESS_DENIED: | ||
| 4389 | case ERROR_TOO_MANY_OPEN_FILES: | ||
| 4390 | errno = EACCES; | ||
| 4391 | break; | ||
| 4392 | default: | ||
| 4393 | errno = EPERM; | ||
| 4394 | break; | ||
| 4395 | } | ||
| 4396 | } | ||
| 4397 | if (restore_privs) | ||
| 4398 | { | ||
| 4399 | restore_privilege (&privs); | ||
| 4400 | revert_to_self (); | ||
| 4401 | } | ||
| 4402 | |||
| 4403 | return retval; | ||
| 3732 | } | 4404 | } |
| 3733 | 4405 | ||
| 4406 | /* If FILE is a symlink, return its target (stored in a static | ||
| 4407 | buffer); otherwise return FILE. | ||
| 4408 | |||
| 4409 | This function repeatedly resolves symlinks in the last component of | ||
| 4410 | a chain of symlink file names, as in foo -> bar -> baz -> ..., | ||
| 4411 | until it arrives at a file whose last component is not a symlink, | ||
| 4412 | or some error occurs. It returns the target of the last | ||
| 4413 | successfully resolved symlink in the chain. If it succeeds to | ||
| 4414 | resolve even a single symlink, the value returned is an absolute | ||
| 4415 | file name with backslashes (result of GetFullPathName). By | ||
| 4416 | contrast, if the original FILE is returned, it is unaltered. | ||
| 4417 | |||
| 4418 | Note: This function can set errno even if it succeeds. | ||
| 4419 | |||
| 4420 | Implementation note: we only resolve the last portion ("basename") | ||
| 4421 | of the argument FILE and of each following file in the chain, | ||
| 4422 | disregarding any possible symlinks in its leading directories. | ||
| 4423 | This is because Windows system calls and library functions | ||
| 4424 | transparently resolve symlinks in leading directories and return | ||
| 4425 | correct information, as long as the basename is not a symlink. */ | ||
| 4426 | static char * | ||
| 4427 | chase_symlinks (const char *file) | ||
| 4428 | { | ||
| 4429 | static char target[MAX_PATH]; | ||
| 4430 | char link[MAX_PATH]; | ||
| 4431 | ssize_t res, link_len; | ||
| 4432 | int loop_count = 0; | ||
| 4433 | |||
| 4434 | if (is_windows_9x () == TRUE || !is_symlink (file)) | ||
| 4435 | return (char *)file; | ||
| 4436 | |||
| 4437 | if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0) | ||
| 4438 | return (char *)file; | ||
| 4439 | |||
| 4440 | target[0] = '\0'; | ||
| 4441 | do { | ||
| 4442 | |||
| 4443 | /* Remove trailing slashes, as we want to resolve the last | ||
| 4444 | non-trivial part of the link name. */ | ||
| 4445 | while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1])) | ||
| 4446 | link[link_len--] = '\0'; | ||
| 4447 | |||
| 4448 | res = readlink (link, target, MAX_PATH); | ||
| 4449 | if (res > 0) | ||
| 4450 | { | ||
| 4451 | target[res] = '\0'; | ||
| 4452 | if (!(IS_DEVICE_SEP (target[1]) | ||
| 4453 | || IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1]))) | ||
| 4454 | { | ||
| 4455 | /* Target is relative. Append it to the directory part of | ||
| 4456 | the symlink, then copy the result back to target. */ | ||
| 4457 | char *p = link + link_len; | ||
| 4458 | |||
| 4459 | while (p > link && !IS_ANY_SEP (p[-1])) | ||
| 4460 | p--; | ||
| 4461 | strcpy (p, target); | ||
| 4462 | strcpy (target, link); | ||
| 4463 | } | ||
| 4464 | /* Resolve any "." and ".." to get a fully-qualified file name | ||
| 4465 | in link[] again. */ | ||
| 4466 | link_len = GetFullPathName (target, MAX_PATH, link, NULL); | ||
| 4467 | } | ||
| 4468 | } while (res > 0 && link_len > 0 && ++loop_count <= 100); | ||
| 4469 | |||
| 4470 | if (loop_count > 100) | ||
| 4471 | errno = ELOOP; | ||
| 4472 | |||
| 4473 | if (target[0] == '\0') /* not a single call to readlink succeeded */ | ||
| 4474 | return (char *)file; | ||
| 4475 | return target; | ||
| 4476 | } | ||
| 4477 | |||
| 4478 | /* MS-Windows version of careadlinkat (cf. ../lib/careadlinkat.c). We | ||
| 4479 | have a fixed max size for file names, so we don't need the kind of | ||
| 4480 | alloc/malloc/realloc dance the gnulib version does. We also don't | ||
| 4481 | support FD-relative symlinks. */ | ||
| 3734 | char * | 4482 | char * |
| 3735 | careadlinkat (int fd, char const *filename, | 4483 | careadlinkat (int fd, char const *filename, |
| 3736 | char *buffer, size_t buffer_size, | 4484 | char *buffer, size_t buffer_size, |
| 3737 | struct allocator const *alloc, | 4485 | struct allocator const *alloc, |
| 3738 | ssize_t (*preadlinkat) (int, char const *, char *, size_t)) | 4486 | ssize_t (*preadlinkat) (int, char const *, char *, size_t)) |
| 3739 | { | 4487 | { |
| 3740 | errno = ENOSYS; | 4488 | char linkname[MAX_PATH]; |
| 4489 | ssize_t link_size; | ||
| 4490 | |||
| 4491 | if (fd != AT_FDCWD) | ||
| 4492 | { | ||
| 4493 | errno = EINVAL; | ||
| 4494 | return NULL; | ||
| 4495 | } | ||
| 4496 | |||
| 4497 | link_size = preadlinkat (fd, filename, linkname, sizeof(linkname)); | ||
| 4498 | |||
| 4499 | if (link_size > 0) | ||
| 4500 | { | ||
| 4501 | char *retval = buffer; | ||
| 4502 | |||
| 4503 | linkname[link_size++] = '\0'; | ||
| 4504 | if (link_size > buffer_size) | ||
| 4505 | retval = (char *)(alloc ? alloc->allocate : xmalloc) (link_size); | ||
| 4506 | if (retval) | ||
| 4507 | memcpy (retval, linkname, link_size); | ||
| 4508 | |||
| 4509 | return retval; | ||
| 4510 | } | ||
| 3741 | return NULL; | 4511 | return NULL; |
| 3742 | } | 4512 | } |
| 3743 | 4513 | ||
| @@ -6060,6 +6830,7 @@ globals_of_w32 (void) | |||
| 6060 | g_b_init_lookup_account_sid = 0; | 6830 | g_b_init_lookup_account_sid = 0; |
| 6061 | g_b_init_get_sid_sub_authority = 0; | 6831 | g_b_init_get_sid_sub_authority = 0; |
| 6062 | g_b_init_get_sid_sub_authority_count = 0; | 6832 | g_b_init_get_sid_sub_authority_count = 0; |
| 6833 | g_b_init_get_security_info = 0; | ||
| 6063 | g_b_init_get_file_security = 0; | 6834 | g_b_init_get_file_security = 0; |
| 6064 | g_b_init_get_security_descriptor_owner = 0; | 6835 | g_b_init_get_security_descriptor_owner = 0; |
| 6065 | g_b_init_get_security_descriptor_group = 0; | 6836 | g_b_init_get_security_descriptor_group = 0; |
| @@ -6079,6 +6850,7 @@ globals_of_w32 (void) | |||
| 6079 | g_b_init_get_length_sid = 0; | 6850 | g_b_init_get_length_sid = 0; |
| 6080 | g_b_init_get_native_system_info = 0; | 6851 | g_b_init_get_native_system_info = 0; |
| 6081 | g_b_init_get_system_times = 0; | 6852 | g_b_init_get_system_times = 0; |
| 6853 | g_b_init_create_symbolic_link = 0; | ||
| 6082 | num_of_processors = 0; | 6854 | num_of_processors = 0; |
| 6083 | /* The following sets a handler for shutdown notifications for | 6855 | /* The following sets a handler for shutdown notifications for |
| 6084 | console apps. This actually applies to Emacs in both console and | 6856 | console apps. This actually applies to Emacs in both console and |