diff options
| author | Eli Zaretskii | 2013-12-14 10:29:42 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2013-12-14 10:29:42 +0200 |
| commit | ec4440cf5ee9b885957a774354894b62713258c5 (patch) | |
| tree | ab8a997a398cc37c4f0d3270679c7d6a1e0a8d61 /src | |
| parent | 276bc3337b27bcd76aa2735ed96c160c6a1b573a (diff) | |
| download | emacs-ec4440cf5ee9b885957a774354894b62713258c5.tar.gz emacs-ec4440cf5ee9b885957a774354894b62713258c5.zip | |
Fix copy-file on MS-Windows with file names outside of current locale.
src/fileio.c (Fcopy_file) [WINDOWSNT]: Move most of the
Windows-specific code to w32.c. Change error message text to
match that of Posix platforms.
src/w32.c (w32_copy_file): New function, most of the code copied and
reworked from Fcopy_file. Improve error handling. Plug memory
leak when errors are thrown. Support file names outside of the
current codepage. (Bug#7100)
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 11 | ||||
| -rw-r--r-- | src/fileio.c | 60 | ||||
| -rw-r--r-- | src/w32.c | 111 | ||||
| -rw-r--r-- | src/w32.h | 1 |
4 files changed, 137 insertions, 46 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 4bd5191d5c6..df145600556 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,14 @@ | |||
| 1 | 2013-12-14 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * fileio.c (Fcopy_file) [WINDOWSNT]: Move most of the | ||
| 4 | Windows-specific code to w32.c. Change error message text to | ||
| 5 | match that of Posix platforms. | ||
| 6 | |||
| 7 | * w32.c (w32_copy_file): New function, most of the code copied and | ||
| 8 | reworked from Fcopy_file. Improve error handling. Plug memory | ||
| 9 | leak when errors are thrown. Support file names outside of the | ||
| 10 | current codepage. (Bug#7100) | ||
| 11 | |||
| 1 | 2013-12-13 Paul Eggert <eggert@cs.ucla.edu> | 12 | 2013-12-13 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 13 | ||
| 3 | * lread.c (load_path_default): Prototype. | 14 | * lread.c (load_path_default): Prototype. |
diff --git a/src/fileio.c b/src/fileio.c index f6c31ebf1b9..deb913cbdac 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -1959,7 +1959,7 @@ entries (depending on how Emacs was built). */) | |||
| 1959 | int conlength = 0; | 1959 | int conlength = 0; |
| 1960 | #endif | 1960 | #endif |
| 1961 | #ifdef WINDOWSNT | 1961 | #ifdef WINDOWSNT |
| 1962 | acl_t acl = NULL; | 1962 | int result; |
| 1963 | #endif | 1963 | #endif |
| 1964 | 1964 | ||
| 1965 | encoded_file = encoded_newname = Qnil; | 1965 | encoded_file = encoded_newname = Qnil; |
| @@ -1996,52 +1996,20 @@ entries (depending on how Emacs was built). */) | |||
| 1996 | out_st.st_mode = 0; | 1996 | out_st.st_mode = 0; |
| 1997 | 1997 | ||
| 1998 | #ifdef WINDOWSNT | 1998 | #ifdef WINDOWSNT |
| 1999 | if (!NILP (preserve_extended_attributes)) | 1999 | result = w32_copy_file (SSDATA (encoded_file), SSDATA (encoded_newname), |
| 2000 | { | 2000 | !NILP (keep_time), !NILP (preserve_uid_gid), |
| 2001 | acl = acl_get_file (SDATA (encoded_file), ACL_TYPE_ACCESS); | 2001 | !NILP (preserve_extended_attributes)); |
| 2002 | if (acl == NULL && acl_errno_valid (errno)) | 2002 | switch (result) |
| 2003 | report_file_error ("Getting ACL", file); | ||
| 2004 | } | ||
| 2005 | if (!CopyFile (SDATA (encoded_file), | ||
| 2006 | SDATA (encoded_newname), | ||
| 2007 | FALSE)) | ||
| 2008 | { | ||
| 2009 | /* CopyFile doesn't set errno when it fails. By far the most | ||
| 2010 | "popular" reason is that the target is read-only. */ | ||
| 2011 | report_file_errno ("Copying file", list2 (file, newname), | ||
| 2012 | GetLastError () == 5 ? EACCES : EPERM); | ||
| 2013 | } | ||
| 2014 | /* CopyFile retains the timestamp by default. */ | ||
| 2015 | else if (NILP (keep_time)) | ||
| 2016 | { | ||
| 2017 | struct timespec now; | ||
| 2018 | DWORD attributes; | ||
| 2019 | char * filename; | ||
| 2020 | |||
| 2021 | filename = SDATA (encoded_newname); | ||
| 2022 | |||
| 2023 | /* Ensure file is writable while its modified time is set. */ | ||
| 2024 | attributes = GetFileAttributes (filename); | ||
| 2025 | SetFileAttributes (filename, attributes & ~FILE_ATTRIBUTE_READONLY); | ||
| 2026 | now = current_timespec (); | ||
| 2027 | if (set_file_times (-1, filename, now, now)) | ||
| 2028 | { | ||
| 2029 | /* Restore original attributes. */ | ||
| 2030 | SetFileAttributes (filename, attributes); | ||
| 2031 | xsignal2 (Qfile_date_error, | ||
| 2032 | build_string ("Cannot set file date"), newname); | ||
| 2033 | } | ||
| 2034 | /* Restore original attributes. */ | ||
| 2035 | SetFileAttributes (filename, attributes); | ||
| 2036 | } | ||
| 2037 | if (acl != NULL) | ||
| 2038 | { | 2003 | { |
| 2039 | bool fail = | 2004 | case -1: |
| 2040 | acl_set_file (SDATA (encoded_newname), ACL_TYPE_ACCESS, acl) != 0; | 2005 | report_file_error ("Copying file", list2 (file, newname)); |
| 2041 | if (fail && acl_errno_valid (errno)) | 2006 | case -2: |
| 2042 | report_file_error ("Setting ACL", newname); | 2007 | report_file_error ("Copying permissions from", file); |
| 2043 | 2008 | case -3: | |
| 2044 | acl_free (acl); | 2009 | xsignal2 (Qfile_date_error, |
| 2010 | build_string ("Resetting file times"), newname); | ||
| 2011 | case -4: | ||
| 2012 | report_file_error ("Copying permissions to", newname); | ||
| 2045 | } | 2013 | } |
| 2046 | #else /* not WINDOWSNT */ | 2014 | #else /* not WINDOWSNT */ |
| 2047 | immediate_quit = 1; | 2015 | immediate_quit = 1; |
| @@ -140,6 +140,7 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX { | |||
| 140 | #include <sddl.h> | 140 | #include <sddl.h> |
| 141 | 141 | ||
| 142 | #include <sys/acl.h> | 142 | #include <sys/acl.h> |
| 143 | #include <acl.h> | ||
| 143 | 144 | ||
| 144 | /* This is not in MinGW's sddl.h (but they are in MSVC headers), so we | 145 | /* This is not in MinGW's sddl.h (but they are in MSVC headers), so we |
| 145 | define them by hand if not already defined. */ | 146 | define them by hand if not already defined. */ |
| @@ -6001,6 +6002,116 @@ careadlinkat (int fd, char const *filename, | |||
| 6001 | return NULL; | 6002 | return NULL; |
| 6002 | } | 6003 | } |
| 6003 | 6004 | ||
| 6005 | int | ||
| 6006 | w32_copy_file (const char *from, const char *to, | ||
| 6007 | int keep_time, int preserve_ownership, int copy_acls) | ||
| 6008 | { | ||
| 6009 | acl_t acl = NULL; | ||
| 6010 | BOOL copy_result; | ||
| 6011 | wchar_t from_w[MAX_PATH], to_w[MAX_PATH]; | ||
| 6012 | char from_a[MAX_PATH], to_a[MAX_PATH]; | ||
| 6013 | |||
| 6014 | /* We ignore preserve_ownership for now. */ | ||
| 6015 | preserve_ownership = preserve_ownership; | ||
| 6016 | |||
| 6017 | if (copy_acls) | ||
| 6018 | { | ||
| 6019 | acl = acl_get_file (from, ACL_TYPE_ACCESS); | ||
| 6020 | if (acl == NULL && acl_errno_valid (errno)) | ||
| 6021 | return -2; | ||
| 6022 | } | ||
| 6023 | if (w32_unicode_filenames) | ||
| 6024 | { | ||
| 6025 | filename_to_utf16 (from, from_w); | ||
| 6026 | filename_to_utf16 (to, to_w); | ||
| 6027 | copy_result = CopyFileW (from_w, to_w, FALSE); | ||
| 6028 | } | ||
| 6029 | else | ||
| 6030 | { | ||
| 6031 | filename_to_ansi (from, from_a); | ||
| 6032 | filename_to_ansi (to, to_a); | ||
| 6033 | copy_result = CopyFileA (from_a, to_a, FALSE); | ||
| 6034 | } | ||
| 6035 | if (!copy_result) | ||
| 6036 | { | ||
| 6037 | /* CopyFile doesn't set errno when it fails. By far the most | ||
| 6038 | "popular" reason is that the target is read-only. */ | ||
| 6039 | DWORD err = GetLastError (); | ||
| 6040 | |||
| 6041 | switch (err) | ||
| 6042 | { | ||
| 6043 | case ERROR_FILE_NOT_FOUND: | ||
| 6044 | errno = ENOENT; | ||
| 6045 | break; | ||
| 6046 | case ERROR_ACCESS_DENIED: | ||
| 6047 | errno = EACCES; | ||
| 6048 | break; | ||
| 6049 | case ERROR_ENCRYPTION_FAILED: | ||
| 6050 | errno = EIO; | ||
| 6051 | break; | ||
| 6052 | default: | ||
| 6053 | errno = EPERM; | ||
| 6054 | break; | ||
| 6055 | } | ||
| 6056 | |||
| 6057 | if (acl) | ||
| 6058 | acl_free (acl); | ||
| 6059 | return -1; | ||
| 6060 | } | ||
| 6061 | /* CopyFile retains the timestamp by default. However, see | ||
| 6062 | "Community Additions" for CopyFile: it sounds like that is not | ||
| 6063 | entirely true. Testing on Windows XP confirms that modified time | ||
| 6064 | is copied, but creation and last-access times are not. | ||
| 6065 | FIXME? */ | ||
| 6066 | else if (!keep_time) | ||
| 6067 | { | ||
| 6068 | struct timespec now; | ||
| 6069 | DWORD attributes; | ||
| 6070 | |||
| 6071 | if (w32_unicode_filenames) | ||
| 6072 | { | ||
| 6073 | /* Ensure file is writable while its times are set. */ | ||
| 6074 | attributes = GetFileAttributesW (to_w); | ||
| 6075 | SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY); | ||
| 6076 | now = current_timespec (); | ||
| 6077 | if (set_file_times (-1, to, now, now)) | ||
| 6078 | { | ||
| 6079 | /* Restore original attributes. */ | ||
| 6080 | SetFileAttributesW (to_w, attributes); | ||
| 6081 | if (acl) | ||
| 6082 | acl_free (acl); | ||
| 6083 | return -3; | ||
| 6084 | } | ||
| 6085 | /* Restore original attributes. */ | ||
| 6086 | SetFileAttributesW (to_w, attributes); | ||
| 6087 | } | ||
| 6088 | else | ||
| 6089 | { | ||
| 6090 | attributes = GetFileAttributesA (to_a); | ||
| 6091 | SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY); | ||
| 6092 | now = current_timespec (); | ||
| 6093 | if (set_file_times (-1, to, now, now)) | ||
| 6094 | { | ||
| 6095 | SetFileAttributesA (to_a, attributes); | ||
| 6096 | if (acl) | ||
| 6097 | acl_free (acl); | ||
| 6098 | return -3; | ||
| 6099 | } | ||
| 6100 | SetFileAttributesA (to_a, attributes); | ||
| 6101 | } | ||
| 6102 | } | ||
| 6103 | if (acl != NULL) | ||
| 6104 | { | ||
| 6105 | bool fail = | ||
| 6106 | acl_set_file (to, ACL_TYPE_ACCESS, acl) != 0; | ||
| 6107 | acl_free (acl); | ||
| 6108 | if (fail && acl_errno_valid (errno)) | ||
| 6109 | return -4; | ||
| 6110 | } | ||
| 6111 | |||
| 6112 | return 0; | ||
| 6113 | } | ||
| 6114 | |||
| 6004 | 6115 | ||
| 6005 | /* Support for browsing other processes and their attributes. See | 6116 | /* Support for browsing other processes and their attributes. See |
| 6006 | process.c for the Lisp bindings. */ | 6117 | process.c for the Lisp bindings. */ |
| @@ -185,6 +185,7 @@ extern int filename_to_ansi (const char *, char *); | |||
| 185 | extern int filename_from_utf16 (const wchar_t *, char *); | 185 | extern int filename_from_utf16 (const wchar_t *, char *); |
| 186 | extern int filename_to_utf16 (const char *, wchar_t *); | 186 | extern int filename_to_utf16 (const char *, wchar_t *); |
| 187 | extern Lisp_Object ansi_encode_filename (Lisp_Object); | 187 | extern Lisp_Object ansi_encode_filename (Lisp_Object); |
| 188 | extern int w32_copy_file (const char *, const char *, int, int, int); | ||
| 188 | 189 | ||
| 189 | extern BOOL init_winsock (int load_now); | 190 | extern BOOL init_winsock (int load_now); |
| 190 | extern void srandom (int); | 191 | extern void srandom (int); |