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:
parent
276bc3337b
commit
ec4440cf5e
4 changed files with 137 additions and 46 deletions
|
@ -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.
|
||||
|
|
60
src/fileio.c
60
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))
|
||||
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
111
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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue