Скопируйте файл разумным, безопасным и эффективным способом
Я ищу хороший способ скопировать файл (двоичный или текстовый). Я написал несколько образцов, каждый работает. Но я хочу услышать мнение опытных программистов.
Я упустил хорошие примеры и ищу способ, который работает с C++.
ANSI-C ПУТЬ
#include <iostream>
#include <cstdio> // fopen, fclose, fread, fwrite, BUFSIZ
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
// BUFSIZE default is 8192 bytes
// BUFSIZE of 1 means one chareter at time
// good values should fit to blocksize, like 1024 or 4096
// higher values reduce number of system calls
// size_t BUFFER_SIZE = 4096;
char buf[BUFSIZ];
size_t size;
FILE* source = fopen("from.ogv", "rb");
FILE* dest = fopen("to.ogv", "wb");
// clean and more secure
// feof(FILE* stream) returns non-zero if the end of file indicator for stream is set
while (size = fread(buf, 1, BUFSIZ, source)) {
fwrite(buf, 1, size, dest);
}
fclose(source);
fclose(dest);
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
POSIX-WAY (K & R использует это в "Языке программирования C", более низкого уровня)
#include <iostream>
#include <fcntl.h> // open
#include <unistd.h> // read, write, close
#include <cstdio> // BUFSIZ
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
// BUFSIZE defaults to 8192
// BUFSIZE of 1 means one chareter at time
// good values should fit to blocksize, like 1024 or 4096
// higher values reduce number of system calls
// size_t BUFFER_SIZE = 4096;
char buf[BUFSIZ];
size_t size;
int source = open("from.ogv", O_RDONLY, 0);
int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);
while ((size = read(source, buf, BUFSIZ)) > 0) {
write(dest, buf, size);
}
close(source);
close(dest);
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
KISS-C++- Streambuffer-WAY
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
ifstream source("from.ogv", ios::binary);
ofstream dest("to.ogv", ios::binary);
dest << source.rdbuf();
source.close();
dest.close();
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
COPY-АЛГОРИТМ-C++- ПУТЬ
#include <iostream>
#include <fstream>
#include <ctime>
#include <algorithm>
#include <iterator>
using namespace std;
int main() {
clock_t start, end;
start = clock();
ifstream source("from.ogv", ios::binary);
ofstream dest("to.ogv", ios::binary);
istreambuf_iterator<char> begin_source(source);
istreambuf_iterator<char> end_source;
ostreambuf_iterator<char> begin_dest(dest);
copy(begin_source, end_source, begin_dest);
source.close();
dest.close();
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
СОБСТВЕННЫЙ-BUFFER-C++- ПУТЬ
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
ifstream source("from.ogv", ios::binary);
ofstream dest("to.ogv", ios::binary);
// file size
source.seekg(0, ios::end);
ifstream::pos_type size = source.tellg();
source.seekg(0);
// allocate memory for buffer
char* buffer = new char[size];
// copy file
source.read(buffer, size);
dest.write(buffer, size);
// clean up
delete[] buffer;
source.close();
dest.close();
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
LINUX-WAY // требуется ядро>= 2.6.33
#include <iostream>
#include <sys/sendfile.h> // sendfile
#include <fcntl.h> // open
#include <unistd.h> // close
#include <sys/stat.h> // fstat
#include <sys/types.h> // fstat
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
int source = open("from.ogv", O_RDONLY, 0);
int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);
// struct required, rationale: function stat() exists also
struct stat stat_source;
fstat(source, &stat_source);
sendfile(dest, source, 0, stat_source.st_size);
close(source);
close(dest);
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
Среда
- GNU/LINUX (Archlinux)
- Ядро 3.3
- GLIBC-2.15, LIBSTDC++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
- Использование RUNLEVEL 3 (многопользовательский, сеть, терминал, без графического интерфейса)
- INTEL SSD-Postville 80 ГБ, заполненный до 50%
- Скопируйте OGG-видеофайл на 270 МБ
Действия по воспроизведению
1. $ rm from.ogg
2. $ reboot # kernel and filesystem buffers are in regular
3. $ (time ./program) &>> report.txt # executes program, redirects output of program and append to file
4. $ sha256sum *.ogv # checksum
5. $ rm to.ogg # remove copy, but no sync, kernel and fileystem buffers are used
6. $ (time ./program) &>> report.txt # executes program, redirects output of program and append to file
Результаты (время процессора используется)
Program Description UNBUFFERED|BUFFERED
ANSI C (fread/frwite) 490,000|260,000
POSIX (K&R, read/write) 450,000|230,000
FSTREAM (KISS, Streambuffer) 500,000|270,000
FSTREAM (Algorithm, copy) 500,000|270,000
FSTREAM (OWN-BUFFER) 500,000|340,000
SENDFILE (native LINUX, sendfile) 410,000|200,000
Размер файла не меняется.
sha256sum выводит те же результаты.
Видео файл по-прежнему воспроизводится.
Вопросы
- Какой метод вы бы предпочли?
- Знаете ли вы лучшие решения?
- Видите ли вы какие-либо ошибки в моем коде?
Вы знаете причину, чтобы избежать решения?
FSTREAM (KISS, Streambuffer)
Мне очень нравится этот, потому что он очень короткий и простой. Насколько я знаю, оператор << перегружен для rdbuf() и ничего не конвертирует. Правильный?
Спасибо
Обновление 1
Я изменил источник во всех примерах таким образом, чтобы дескрипторы файлов были открыты и закрыты при измерении часов (). Других существенных изменений в исходном коде нет. Результаты не изменились! Я также использовал время, чтобы перепроверить мои результаты.
Обновление 2
Пример ANSI C изменен: условие цикла while больше не вызывает feof(), вместо этого я переместил fread() в условие. Похоже, код теперь работает на 10000 часов быстрее.
Измерение изменилось: прежние результаты всегда буферизировались, потому что я повторял старую командную строку rm to.ogv && sync && time ./program для каждой программы несколько раз. Теперь я перезагружаю систему для каждой программы. Небуферизованные результаты являются новыми и не вызывают удивления. Небуферизованные результаты не изменились.
Если я не удаляю старую копию, программы реагируют по-разному. Перезапись существующего буферизованного файла происходит быстрее с POSIX и SENDFILE, все другие программы работают медленнее. Возможно, параметры усечения или создания влияют на это поведение. Но перезапись существующих файлов одной и той же копией не является реальным вариантом использования.
Выполнение копирования с помощью cp занимает 0,44 секунды без буферизации и 0,30 секунды с буферизацией. Таким образом, cp немного медленнее, чем образец POSIX. Выглядит хорошо для меня.
Может быть, я добавлю также образцы и результаты mmap() и copy_file()
от boost::filesystem.
Обновление 3
Я также разместил это на странице блога и немного расширил. Включая splice (), которая является низкоуровневой функцией ядра Linux. Возможно, последуют еще примеры с Java. http://www.ttyhoney.com/blog/?page_id=69
9 ответов
Скопируйте файл разумным способом:
#include <fstream>
int main()
{
std::ifstream src("from.ogv", std::ios::binary);
std::ofstream dst("to.ogv", std::ios::binary);
dst << src.rdbuf();
}
Это настолько просто и интуитивно понятно, что стоит дополнительных затрат. Если бы мы делали это много, лучше прибегнуть к обращению ОС к файловой системе. я уверен boost
имеет метод копирования файла в своем классе файловой системы.
Существует метод C для взаимодействия с файловой системой:
#include <copyfile.h>
int
copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
В C++17 стандартный способ копирования файла будет включать <filesystem>
заголовок и использование:
bool copy_file( const std::filesystem::path& from,
const std::filesystem::path& to);
bool copy_file( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options);
Первая форма эквивалентна второй с copy_options::none
используется как опция (см. также copy_file
).
filesystem
библиотека была изначально разработана как boost.filesystem
и, наконец, объединены с ISO C++ с C++17.
Очень много!
Буфер пути "ANSI C" является избыточным, так как FILE
уже буферизован. (Размер этого внутреннего буфера BUFSIZ
на самом деле определяет.)
"OWN-BUFFER-C++-WAY" будет медленным по мере прохождения fstream
, который выполняет много виртуальной диспетчеризации и снова поддерживает внутренние буферы или каждый объект потока. ("COPY-ALGORITHM-C++-WAY" не страдает этим, так как streambuf_iterator
класс обходит слой потока.)
Я предпочитаю "COPY-ALGORITHM-C++-WAY", но без создания fstream
Просто создай голое std::filebuf
случаи, когда фактическое форматирование не требуется.
Для повышения производительности вы не можете превзойти файловые дескрипторы POSIX. Это уродливо, но портативно и быстро на любой платформе.
Путь Linux кажется невероятно быстрым - возможно, ОС позволила функции вернуться до завершения ввода-вывода? В любом случае, этого недостаточно для многих приложений.
РЕДАКТИРОВАТЬ: Ах, "родной Linux" может улучшить производительность, чередуя чтения и записи с асинхронным вводом-выводом. Скопление команд может помочь драйверу диска решить, когда лучше искать. Вы можете попробовать Boost Asio или pthreads для сравнения. Что касается "не может разбить дескрипторы файлов POSIX"… ну, это правда, если вы что-то делаете с данными, а не просто слепо копируете.
Я хочу сделать очень важное замечание, что метод LINUX, использующий sendfile(), имеет большую проблему в том, что он не может копировать файлы размером более 2 ГБ! Я реализовал его, следуя этому вопросу, и столкнулся с проблемами, потому что использовал его для копирования файлов HDF5 размером много ГБ.
http://man7.org/linux/man-pages/man2/sendfile.2.html
sendfile() будет передавать не более 0x7ffff000 (2,147,479,552) байтов, возвращая количество фактически переданных байтов. (Это верно как для 32-битных, так и для 64-битных систем.)
Qt имеет метод для копирования файлов:
#include <QFile>
QFile::copy("originalFile.example","copiedFile.example");
Обратите внимание, что для использования этого вам необходимо установить Qt (инструкции здесь) и включить его в свой проект (если вы используете Windows и не являетесь администратором, вы можете скачать Qt здесь). Также смотрите этот ответ.
Для тех, кто любит повышение:
boost::filesystem::path mySourcePath("foo.bar");
boost::filesystem::path myTargetPath("bar.foo");
// Variant 1: Overwrite existing
boost::filsystem::copy_file(mySourcePath, myTargetPath, boost::filesystem::copy_option::overwrite_if_exists);
// Variant 2: Fail if exists
boost::filsystem::copy_file(mySourcePath, myTargetPath, boost::filesystem::copy_option::fail_if_exists);
Обратите внимание, что boost::filesystem::path также доступен как wpath для Unicode. И что вы могли бы также использовать
using namespace boost::filesystem
если вам не нравятся эти длинные имена
Я не совсем уверен, что такое "хороший способ" копирования файла, но, предполагая, что "хороший" означает "быстрый", я мог бы немного расширить тему.
Современные операционные системы уже давно оптимизированы для работы с обычным копированием файлов. Никакой хитрый код не сможет победить это. Возможно, что какой-то вариант ваших методов копирования окажется быстрее в одном тестовом сценарии, но, скорее всего, в других случаях они будут работать хуже.
Обычно sendfile
функция, вероятно, возвращается до того, как запись была зафиксирована, таким образом создается впечатление, что она работает быстрее остальных. Я не читал код, но это наверняка потому, что он выделяет свой собственный выделенный буфер, торгуя памятью на время. И причина, по которой он не работает для файлов размером более 2 ГБ.
Пока вы имеете дело с небольшим количеством файлов, все происходит внутри различных буферов (сначала среда выполнения C++, если вы используете iostream
, внутренние, по-видимому, дополнительный буфер размером с файл в случае sendfile
). Доступ к фактическим носителям данных возможен только после того, как было перемещено достаточно данных, чтобы оправдать затраты на вращение жесткого диска.
Полагаю, вы могли бы немного улучшить характеристики в определенных случаях. С верхней части моей головы:
- Если вы копируете огромный файл на тот же диск, использование буфера большего размера, чем у ОС, может немного улучшить ситуацию (но мы, вероятно, говорим здесь о гигабайтах).
- Если вы хотите скопировать один и тот же файл в два разных физических места назначения, вы, вероятно, быстрее откроете три файла одновременно, чем вызовите два
copy_file
последовательно (хотя вы вряд ли заметите разницу, пока файл помещается в кеш ОС) - Если вы имеете дело с большим количеством крошечных файлов на жестком диске, вы можете захотеть читать их партиями, чтобы минимизировать время поиска (хотя ОС уже кэширует записи каталога, чтобы избежать сумасшедшего поиска, а крошечные файлы, скорее всего, в любом случае значительно уменьшат пропускную способность диска).
Но все это выходит за рамки функции копирования файлов общего назначения.
Так что, по моему мнению, возможно, опытного программиста, копия файла C++ должна просто использовать C++17 file_copy
специальная функция, если не известно больше о контексте, в котором происходит копирование файла, и не могут быть изобретены некоторые хитрые стратегии, чтобы перехитрить ОС.
Самый простой способ в С++17 и более поздних версиях:
Использовать
#include <filesystem>
и метод. Есть 4 перегрузки для метода копирования. Вы можете проверить это по этой
void copy( const std::filesystem::path& from,
const std::filesystem::path& to );
void copy( const std::filesystem::path& from,
const std::filesystem::path& to,
std::error_code& ec );
void copy( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options );
void copy( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options,
std::error_code& ec );
С
copy()
метод может копировать файлы и каталоги с некоторыми параметрами, такими как рекурсивный, нерекурсивный, копировать только каталоги или перезаписывать или пропускать существующие файлы и т. д. вы можете прочитать больше о параметрах копирования по этой ссылке
Это пример кода ссылкеотсюда с некоторыми изменениями:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
// create directories. create all directories if not exist.
fs::create_directories("sandbox/dir/subdir");
// create file with content 'a'
std::ofstream("sandbox/file1.txt").put('a');
// copy file
fs::copy("sandbox/file1.txt", "sandbox/file2.txt");
// copy directory (non-recursive)
fs::copy("sandbox/dir", "sandbox/dir2");
// copy directory (recursive)
const auto copyOptions = fs::copy_options::update_existing
| fs::copy_options::recursive
;
fs::copy("sandbox", "sandbox_copy", copyOptions);
// remove sanbox directory and all sub directories and sub files.
fs::remove_all("sandbox");
}
Теоретически наиболее эффективным способом копирования файлов является использование карты памяти, поэтому процесс копирования может выполняться полностью в режиме ядра.
Если файл меньше 2 ГБ, вы можете использовать следующий код на платформах Unix:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "usage: %s <source> <target>\n", argv[0]);
return EXIT_FAILURE;
}
int source_fd = open(argv[1], O_RDONLY, 0);
if (source_fd < 0) {
perror("open source");
return EXIT_FAILURE;
}
int target_fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666);
if (target_fd < 0) {
perror("open target");
return EXIT_FAILURE;
}
struct stat stat;
int r = fstat(source_fd, &stat);
if (r < 0) {
perror("fstat");
return EXIT_FAILURE;
}
char *buf = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, source_fd, 0);
if (buf == MAP_FAILED) {
perror("mmap");
return EXIT_FAILURE;
}
r = write(target_fd, buf, stat.st_size);
if (r < 0) {
perror("write");
return EXIT_FAILURE;
} else if (r != stat.st_size) {
fprintf(stderr, "write: copied file truncated to %d bytes\n", r);
return EXIT_FAILURE;
} else {
printf("write: %d bytes copied\n", r);
}
munmap(buf, stat.st_size);
close(source_fd);
close(target_fd);
return EXIT_SUCCESS;
}
При копировании файла размером 2 ГБ использование времени составляет:
real 0m1.842s
user 0m0.000s
sys 0m1.505s
Но если размер файла превышает 2 ГБ,write()
урежет файл до 2 ГБ, поэтому его нельзя будет использовать. Мы должны сопоставить файл назначения и использовать для копирования файла. Сmemcpy
используется, мы можем видеть время, проведенное в пользовательском режиме.
Вот универсальный вариант:
import sys
import mmap
if len(sys.argv) != 3:
print(f'Usage: {sys.argv[0]} <source> <destination>')
sys.exit(1)
with open(sys.argv[1], 'rb') as src, open(sys.argv[2], 'wb') as dst:
mmapped_src = mmap.mmap(src.fileno(), 0, access=mmap.ACCESS_READ)
print(f"{dst.write(mmapped_src)} bytes written")
mmapped_src.close()
При копировании файла размером 3,2 ГБ использование времени составляет:
real 0m4.426s
user 0m0.030s
sys 0m2.793s
Вот Unix-версия:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
int src_fd, dst_fd;
void *src_map, *dst_map;
struct stat src_stat;
if (argc != 3) {
printf("Usage: %s <source> <destination>\n", argv[0]);
return 1;
}
src_fd = open(argv[1], O_RDONLY);
if (src_fd == -1) {
perror("open source");
return 1;
}
if (fstat(src_fd, &src_stat) == -1) {
perror("fstat");
return 1;
}
src_map = mmap(NULL, src_stat.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0);
if (src_map == MAP_FAILED) {
perror("mmap source");
return 1;
}
dst_fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, src_stat.st_mode);
if (dst_fd == -1) {
perror("open destination");
return 1;
}
if (ftruncate(dst_fd, src_stat.st_size) == -1) {
perror("ftruncate");
return 1;
}
dst_map = mmap(NULL, src_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, dst_fd, 0);
if (dst_map == MAP_FAILED) {
perror("mmap destination");
return 1;
}
memcpy(dst_map, src_map, src_stat.st_size);
printf("Copied %ld bytes from %s to %s\n", src_stat.st_size, argv[1], argv[2]);
munmap(src_map, src_stat.st_size);
munmap(dst_map, src_stat.st_size);
close(src_fd);
close(dst_fd);
return 0;
}
При копировании файла размером 3,2 ГБ использование времени составляет:
real 0m3.365s
user 0m0.788s
sys 0m2.471s
Вот версия для Windows:
#include <stdio.h>
#include <windows.h>
void PrintLastError(const char *name) {
char *msg;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &msg, 0, NULL);
fprintf(stderr, "%s: %s", name, msg);
LocalFree(msg);
exit(1);
}
int main(int argc, char* argv[]) {
HANDLE hSrc, hDst;
HANDLE hSrcMap, hDstMap;
LPVOID lpSrcMap, lpDstMap;
DWORD dwSrcSize, dwDstSize;
if (argc != 3) {
printf("Usage: %s <source> <destination>\n", argv[0]);
return 1;
}
hSrc = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hSrc == INVALID_HANDLE_VALUE) {
PrintLastError("CreateFile");
return 1;
}
dwSrcSize = GetFileSize(hSrc, NULL);
if (dwSrcSize == INVALID_FILE_SIZE) {
PrintLastError("GetFileSize");
goto SRC_MAP_FAIL;
}
hSrcMap = CreateFileMapping(hSrc, NULL, PAGE_READONLY, 0, 0, NULL);
if (hSrcMap == NULL) {
PrintLastError("CreateFileMapping");
goto SRC_MAP_FAIL;
}
lpSrcMap = MapViewOfFile(hSrcMap, FILE_MAP_READ, 0, 0, 0);
if (lpSrcMap == NULL) {
PrintLastError("MapViewOfFile");
goto SRC_VIEW_FAIL;
}
hDst = CreateFile(argv[2], GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDst == INVALID_HANDLE_VALUE) {
PrintLastError("CreateFile");
goto DEST_OPEN_FAIL;
}
dwDstSize = dwSrcSize;
hDstMap = CreateFileMapping(hDst, NULL, PAGE_READWRITE, 0, dwDstSize, NULL);
if (hDstMap == NULL) {
PrintLastError("CreateFileMapping");
goto DEST_MAP_FAIL;
}
lpDstMap = MapViewOfFile(hDstMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpDstMap == NULL) {
PrintLastError("MapViewOfFile");
goto DEST_VIEW_FAIL;
}
memcpy(lpDstMap, lpSrcMap, dwSrcSize);
printf("Copied %lu bytes from %s to %s", dwSrcSize, argv[1], argv[2]);
UnmapViewOfFile(lpDstMap);
DEST_VIEW_FAIL:
CloseHandle(hDstMap);
DEST_MAP_FAIL:
CloseHandle(hDst);
DEST_OPEN_FAIL:
UnmapViewOfFile(lpSrcMap);
SRC_VIEW_FAIL:
CloseHandle(hSrcMap);
SRC_MAP_FAIL:
CloseHandle(hSrc);
return 0;
}