+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.
int conlength = 0;
#endif
#ifdef WINDOWSNT
- acl_t acl = NULL;
+ int result;
#endif
encoded_file = encoded_newname = Qnil;
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;
#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. */
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;
+}
+
\f
/* Support for browsing other processes and their attributes. See
process.c for the Lisp bindings. */
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);