summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2013-12-14 10:29:42 +0200
committerEli Zaretskii <eliz@gnu.org>2013-12-14 10:29:42 +0200
commitec4440cf5ee9b885957a774354894b62713258c5 (patch)
treeab8a997a398cc37c4f0d3270679c7d6a1e0a8d61 /src
parent276bc3337b27bcd76aa2735ed96c160c6a1b573a (diff)
downloademacs-ec4440cf5ee9b885957a774354894b62713258c5.tar.gz
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 @@
+2013-12-14 Eli Zaretskii <eliz@gnu.org>
+
+ * 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.
+
+ * 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)
+
2013-12-13 Paul Eggert <eggert@cs.ucla.edu>
* 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). */)
int conlength = 0;
#endif
#ifdef WINDOWSNT
- acl_t acl = NULL;
+ int result;
#endif
encoded_file = encoded_newname = Qnil;
@@ -1996,52 +1996,20 @@ entries (depending on how Emacs was built). */)
out_st.st_mode = 0;
#ifdef WINDOWSNT
- if (!NILP (preserve_extended_attributes))
- {
- acl = acl_get_file (SDATA (encoded_file), ACL_TYPE_ACCESS);
- if (acl == NULL && acl_errno_valid (errno))
- report_file_error ("Getting ACL", file);
- }
- if (!CopyFile (SDATA (encoded_file),
- SDATA (encoded_newname),
- FALSE))
- {
- /* CopyFile doesn't set errno when it fails. By far the most
- "popular" reason is that the target is read-only. */
- report_file_errno ("Copying file", list2 (file, newname),
- GetLastError () == 5 ? EACCES : EPERM);
- }
- /* CopyFile retains the timestamp by default. */
- else if (NILP (keep_time))
- {
- struct timespec now;
- DWORD attributes;
- char * filename;
-
- filename = SDATA (encoded_newname);
-
- /* Ensure file is writable while its modified time is set. */
- attributes = GetFileAttributes (filename);
- SetFileAttributes (filename, attributes & ~FILE_ATTRIBUTE_READONLY);
- now = current_timespec ();
- if (set_file_times (-1, filename, now, now))
- {
- /* Restore original attributes. */
- SetFileAttributes (filename, attributes);
- xsignal2 (Qfile_date_error,
- build_string ("Cannot set file date"), newname);
- }
- /* Restore original attributes. */
- SetFileAttributes (filename, attributes);
- }
- if (acl != NULL)
+ result = w32_copy_file (SSDATA (encoded_file), SSDATA (encoded_newname),
+ !NILP (keep_time), !NILP (preserve_uid_gid),
+ !NILP (preserve_extended_attributes));
+ switch (result)
{
- bool fail =
- acl_set_file (SDATA (encoded_newname), ACL_TYPE_ACCESS, acl) != 0;
- if (fail && acl_errno_valid (errno))
- report_file_error ("Setting ACL", newname);
-
- acl_free (acl);
+ case -1:
+ report_file_error ("Copying file", list2 (file, newname));
+ case -2:
+ report_file_error ("Copying permissions from", file);
+ case -3:
+ xsignal2 (Qfile_date_error,
+ build_string ("Resetting file times"), newname);
+ case -4:
+ report_file_error ("Copying permissions to", newname);
}
#else /* not WINDOWSNT */
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 {
#include <sddl.h>
#include <sys/acl.h>
+#include <acl.h>
/* This is not in MinGW's sddl.h (but they are in MSVC headers), so we
define them by hand if not already defined. */
@@ -6001,6 +6002,116 @@ careadlinkat (int fd, char const *filename,
return NULL;
}
+int
+w32_copy_file (const char *from, const char *to,
+ int keep_time, int preserve_ownership, int copy_acls)
+{
+ acl_t acl = NULL;
+ BOOL copy_result;
+ wchar_t from_w[MAX_PATH], to_w[MAX_PATH];
+ char from_a[MAX_PATH], to_a[MAX_PATH];
+
+ /* We ignore preserve_ownership for now. */
+ preserve_ownership = preserve_ownership;
+
+ if (copy_acls)
+ {
+ acl = acl_get_file (from, ACL_TYPE_ACCESS);
+ if (acl == NULL && acl_errno_valid (errno))
+ return -2;
+ }
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (from, from_w);
+ filename_to_utf16 (to, to_w);
+ copy_result = CopyFileW (from_w, to_w, FALSE);
+ }
+ else
+ {
+ filename_to_ansi (from, from_a);
+ filename_to_ansi (to, to_a);
+ copy_result = CopyFileA (from_a, to_a, FALSE);
+ }
+ if (!copy_result)
+ {
+ /* CopyFile doesn't set errno when it fails. By far the most
+ "popular" reason is that the target is read-only. */
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case ERROR_ACCESS_DENIED:
+ errno = EACCES;
+ break;
+ case ERROR_ENCRYPTION_FAILED:
+ errno = EIO;
+ break;
+ default:
+ errno = EPERM;
+ break;
+ }
+
+ if (acl)
+ acl_free (acl);
+ return -1;
+ }
+ /* CopyFile retains the timestamp by default. However, see
+ "Community Additions" for CopyFile: it sounds like that is not
+ entirely true. Testing on Windows XP confirms that modified time
+ is copied, but creation and last-access times are not.
+ FIXME? */
+ else if (!keep_time)
+ {
+ struct timespec now;
+ DWORD attributes;
+
+ if (w32_unicode_filenames)
+ {
+ /* Ensure file is writable while its times are set. */
+ attributes = GetFileAttributesW (to_w);
+ SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY);
+ now = current_timespec ();
+ if (set_file_times (-1, to, now, now))
+ {
+ /* Restore original attributes. */
+ SetFileAttributesW (to_w, attributes);
+ if (acl)
+ acl_free (acl);
+ return -3;
+ }
+ /* Restore original attributes. */
+ SetFileAttributesW (to_w, attributes);
+ }
+ else
+ {
+ attributes = GetFileAttributesA (to_a);
+ SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY);
+ now = current_timespec ();
+ if (set_file_times (-1, to, now, now))
+ {
+ SetFileAttributesA (to_a, attributes);
+ if (acl)
+ acl_free (acl);
+ return -3;
+ }
+ SetFileAttributesA (to_a, attributes);
+ }
+ }
+ if (acl != NULL)
+ {
+ bool fail =
+ acl_set_file (to, ACL_TYPE_ACCESS, acl) != 0;
+ acl_free (acl);
+ if (fail && acl_errno_valid (errno))
+ return -4;
+ }
+
+ return 0;
+}
+
/* Support for browsing other processes and their attributes. See
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 *);
extern int filename_from_utf16 (const wchar_t *, char *);
extern int filename_to_utf16 (const char *, wchar_t *);
extern Lisp_Object ansi_encode_filename (Lisp_Object);
+extern int w32_copy_file (const char *, const char *, int, int, int);
extern BOOL init_winsock (int load_now);
extern void srandom (int);