Скопируйте файл разумным, безопасным и эффективным способом

Я ищу хороший способ скопировать файл (двоичный или текстовый). Я написал несколько образцов, каждый работает. Но я хочу услышать мнение опытных программистов.

Я упустил хорошие примеры и ищу способ, который работает с 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;
}
Другие вопросы по тегам