1717#include < sys/utime.h> // NOLINT
1818
1919#include " bin/builtin.h"
20+ #include " bin/crypto.h"
2021#include " bin/directory.h"
2122#include " bin/namespace.h"
2223#include " bin/utils.h"
@@ -503,6 +504,85 @@ bool File::RenameLink(Namespace* namespc,
503504 return (move_status != 0 );
504505}
505506
507+ static wchar_t * CopyToDartScopeString (wchar_t * string) {
508+ wchar_t * wide_path = reinterpret_cast <wchar_t *>(
509+ Dart_ScopeAllocate (MAX_PATH * sizeof (wchar_t ) + 1 ));
510+ wcscpy (wide_path, string);
511+ return wide_path;
512+ }
513+
514+ static wchar_t * CopyIntoTempFile (const char * src, const char * dest) {
515+ // This function will copy the file to a temp file in the destination
516+ // directory and return the path of temp file.
517+ // Creating temp file name has the same logic as Directory::CreateTemp(),
518+ // which tries with the rng and falls back to a uuid if it failed.
519+ const char * last_backslash = strrchr (dest, ' \\ ' );
520+ if (last_backslash == NULL ) {
521+ return NULL ;
522+ }
523+ int length_of_parent_dir = last_backslash - dest + 1 ;
524+ if (length_of_parent_dir + 8 > MAX_PATH) {
525+ return NULL ;
526+ }
527+ uint32_t suffix_bytes = 0 ;
528+ const int kSuffixSize = sizeof (suffix_bytes);
529+ if (Crypto::GetRandomBytes (kSuffixSize ,
530+ reinterpret_cast <uint8_t *>(&suffix_bytes))) {
531+ PathBuffer buffer;
532+ char * dir = reinterpret_cast <char *>(
533+ Dart_ScopeAllocate (1 + sizeof (char ) * length_of_parent_dir));
534+ memmove (dir, dest, length_of_parent_dir);
535+ dir[length_of_parent_dir] = ' \0 ' ;
536+ if (!buffer.Add (dir)) {
537+ return NULL ;
538+ }
539+
540+ char suffix[8 + 1 ];
541+ Utils::SNPrint (suffix, sizeof (suffix), " %x" , suffix_bytes);
542+ Utf8ToWideScope source_path (src);
543+ if (!buffer.Add (suffix)) {
544+ return NULL ;
545+ }
546+ if (CopyFileExW (source_path.wide (), buffer.AsStringW (), NULL , NULL , NULL ,
547+ 0 ) != 0 ) {
548+ return CopyToDartScopeString (buffer.AsStringW ());
549+ }
550+ }
551+ // UUID has a total of 36 characters in the form of
552+ // xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx.
553+ if (length_of_parent_dir + 36 > MAX_PATH) {
554+ return NULL ;
555+ }
556+ UUID uuid;
557+ RPC_STATUS status = UuidCreateSequential (&uuid);
558+ if ((status != RPC_S_OK) && (status != RPC_S_UUID_LOCAL_ONLY)) {
559+ return NULL ;
560+ }
561+ RPC_WSTR uuid_string;
562+ status = UuidToStringW (&uuid, &uuid_string);
563+ if (status != RPC_S_OK) {
564+ return NULL ;
565+ }
566+ PathBuffer buffer;
567+ char * dir = reinterpret_cast <char *>(
568+ Dart_ScopeAllocate (1 + sizeof (char ) * length_of_parent_dir));
569+ memmove (dir, dest, length_of_parent_dir);
570+ dir[length_of_parent_dir] = ' \0 ' ;
571+ Utf8ToWideScope dest_path (dir);
572+ if (!buffer.AddW (dest_path.wide ()) ||
573+ !buffer.AddW (reinterpret_cast <wchar_t *>(uuid_string))) {
574+ return NULL ;
575+ }
576+
577+ RpcStringFreeW (&uuid_string);
578+ Utf8ToWideScope source_path (src);
579+ if (CopyFileExW (source_path.wide (), buffer.AsStringW (), NULL , NULL , NULL ,
580+ 0 ) != 0 ) {
581+ return CopyToDartScopeString (buffer.AsStringW ());
582+ }
583+ return NULL ;
584+ }
585+
506586bool File::Copy (Namespace* namespc,
507587 const char * old_path,
508588 const char * new_path) {
@@ -511,11 +591,23 @@ bool File::Copy(Namespace* namespc,
511591 SetLastError (ERROR_FILE_NOT_FOUND);
512592 return false ;
513593 }
514- Utf8ToWideScope system_old_path (old_path);
515- Utf8ToWideScope system_new_path (new_path);
516- bool success = CopyFileExW (system_old_path.wide (), system_new_path.wide (),
517- NULL , NULL , NULL , 0 ) != 0 ;
518- return success;
594+
595+ wchar_t * temp_file = CopyIntoTempFile (old_path, new_path);
596+ if (temp_file == NULL ) {
597+ return false ;
598+ }
599+ Utf8ToWideScope system_new_dest (new_path);
600+
601+ // Remove the existing file. Otherwise, renaming will fail.
602+ if (Exists (namespc, new_path)) {
603+ DeleteFileW (system_new_dest.wide ());
604+ }
605+
606+ if (!MoveFileW (temp_file, system_new_dest.wide ())) {
607+ DeleteFileW (temp_file);
608+ return false ;
609+ }
610+ return true ;
519611}
520612
521613int64_t File::LengthFromPath (Namespace* namespc, const char * name) {
0 commit comments