aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii2013-12-14 10:29:42 +0200
committerEli Zaretskii2013-12-14 10:29:42 +0200
commitec4440cf5ee9b885957a774354894b62713258c5 (patch)
treeab8a997a398cc37c4f0d3270679c7d6a1e0a8d61 /src
parent276bc3337b27bcd76aa2735ed96c160c6a1b573a (diff)
downloademacs-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/ChangeLog11
-rw-r--r--src/fileio.c60
-rw-r--r--src/w32.c111
-rw-r--r--src/w32.h1
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 @@
12013-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
12013-12-13 Paul Eggert <eggert@cs.ucla.edu> 122013-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;
diff --git a/src/w32.c b/src/w32.c
index e5488642118..e4678637cbb 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -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
6005int
6006w32_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. */
diff --git a/src/w32.h b/src/w32.h
index cca95855a78..74460a50440 100644
--- a/src/w32.h
+++ b/src/w32.h
@@ -185,6 +185,7 @@ extern int filename_to_ansi (const char *, char *);
185extern int filename_from_utf16 (const wchar_t *, char *); 185extern int filename_from_utf16 (const wchar_t *, char *);
186extern int filename_to_utf16 (const char *, wchar_t *); 186extern int filename_to_utf16 (const char *, wchar_t *);
187extern Lisp_Object ansi_encode_filename (Lisp_Object); 187extern Lisp_Object ansi_encode_filename (Lisp_Object);
188extern int w32_copy_file (const char *, const char *, int, int, int);
188 189
189extern BOOL init_winsock (int load_now); 190extern BOOL init_winsock (int load_now);
190extern void srandom (int); 191extern void srandom (int);