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)
This commit is contained in:
Eli Zaretskii 2013-12-14 10:29:42 +02:00
parent 276bc3337b
commit ec4440cf5e
4 changed files with 137 additions and 46 deletions

View file

@ -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.

View file

@ -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))
result = w32_copy_file (SSDATA (encoded_file), SSDATA (encoded_newname),
!NILP (keep_time), !NILP (preserve_uid_gid),
!NILP (preserve_extended_attributes));
switch (result)
{
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)
{
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;

111
src/w32.c
View file

@ -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. */

View file

@ -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);