summaryrefslogblamecommitdiff
path: root/image/backends/tar.cc
blob: cab25ede6c53ca92b080aa68e6cb87a305e714ce (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                           

                          
                      




                     
                   
                             


















                                        



                                                              




















                                                   
                                                                     












                                                                       




                                                          
                                                      
 



                                                  

                                                                              












                                                                                      

                                                                                  
             
                                                                                  





























                                                                          













                                      


                                                                  




                                                             


                                                                            




                                                                 


                                                                             




                                                             


                                                                          





         
/*
 * tar.cc - Implementation of the tarball Horizon Image Creation backend
 * image, the image processing utilities for
 * Project Horizon
 *
 * Copyright (c) 2020 Adélie Linux and contributors.  All rights reserved.
 * This code is licensed under the AGPL 3.0 license, as noted in the
 * LICENSE-code file in the root directory of this repository.
 *
 * SPDX-License-Identifier: AGPL-3.0-only
 */

#include <archive.h>
#include <archive_entry.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include "basic.hh"
#include "util/filesystem.hh"
#include "util/output.hh"

namespace Horizon {
namespace Image {

class TarBackend : public BasicBackend {
public:
    enum CompressionType {
        None,
        GZip,
        BZip2,
        XZ
    };

private:
    CompressionType comp;
    struct archive *a;

public:
    TarBackend(const std::string &ir, const std::string &out,
               const std::map<std::string, std::string> &opts,
               CompressionType _c = None)
        : BasicBackend{ir, out, opts}, comp{_c} {};

    int prepare() override {
        int res;

        a = archive_write_new();
        archive_write_set_format_pax_restricted(a);

        switch(comp) {
        case None:
            break;
        case GZip:
            archive_write_add_filter_gzip(a);
            break;
        case BZip2:
            archive_write_add_filter_bzip2(a);
            break;
        case XZ:
            archive_write_add_filter_xz(a);
            break;
        }

        res = archive_write_open_filename(a, this->out_path.c_str());
        if(res < ARCHIVE_OK) {
            if(res < ARCHIVE_WARN) {
                output_error("tar backend", archive_error_string(a));
                return res;
            } else {
                output_warning("tar backend", archive_error_string(a));
            }
        }

        return 0;
    }

    int create() override {
        struct archive_entry *entry = archive_entry_new();
        error_code ec;
        int fd, r, code = 0;
        struct stat s;
        void *buff;
        std::string target = this->ir_dir + "/target";

        umount((ir_dir + "/target/sys").c_str());
        umount((ir_dir + "/target/proc").c_str());
        umount((ir_dir + "/target/dev").c_str());

        for(const auto& dent : fs::recursive_directory_iterator(target, ec)) {
            fs::path relpath = dent.path().lexically_relative(target);
#define OUTPUT_FAILURE(x) \
    output_error("tar backend", "failed to " x " '" + std::string(dent.path()) + "'",\
                 strerror(errno));
            r = lstat(dent.path().c_str(), &s);
            if(r == -1) {
                OUTPUT_FAILURE("stat")
                code = -1;
                goto ret;
            }
            archive_entry_copy_stat(entry, &s);
            if(dent.is_symlink()) {
                archive_entry_set_filetype(entry, AE_IFLNK);
                fs::path resolved = fs::read_symlink(dent.path(), ec);
                const fs::path::value_type *c_rpath = resolved.u8string().c_str();
                archive_entry_update_symlink_utf8(entry, c_rpath);
            }
            archive_entry_update_pathname_utf8(entry, relpath.u8string().c_str());
            if(archive_write_header(this->a, entry) != ARCHIVE_OK) {
                output_error("tar backend", archive_error_string(a));
                code = -1;
                goto ret;
            }
            if(dent.is_regular_file() && dent.file_size() > 0) {
                fd = open(dent.path().c_str(), O_RDONLY);
                if(fd == -1) {
                    OUTPUT_FAILURE("open")
                    code = -1;
                    goto ret;
                }
                buff = mmap(NULL, dent.file_size(), PROT_READ, MAP_SHARED,
                            fd, 0);
                if(buff == MAP_FAILED) {
                    OUTPUT_FAILURE("map")
                    close(fd);
                    code = -1;
                    goto ret;
                }
                archive_write_data(this->a, buff, dent.file_size());
                close(fd);
            }
            archive_write_finish_entry(this->a);
            archive_entry_clear(entry);
        }

ret:
        archive_entry_free(entry);
        return code;
    }

    int finalise() override {
        archive_write_close(a);
        archive_write_free(a);

        return 0;
    }
};

__attribute__((constructor(400)))
void register_tar_backend() {
    BackendManager::register_backend(
    {"tar", "Create a tarball (.tar)",
        [](const std::string &ir_dir, const std::string &out_path,
           const std::map<std::string, std::string> &opts) {
            return new TarBackend(ir_dir, out_path, opts);
        }
    });

    BackendManager::register_backend(
    {"tgz", "Create a tarball with GZ compression (.tar.gz)",
        [](const std::string &ir_dir, const std::string &out_path,
           const std::map<std::string, std::string> &opts) {
            return new TarBackend(ir_dir, out_path, opts, TarBackend::GZip);
        }
    });

    BackendManager::register_backend(
    {"tbz", "Create a tarball with BZip2 compression (.tar.bz2)",
        [](const std::string &ir_dir, const std::string &out_path,
           const std::map<std::string, std::string> &opts) {
            return new TarBackend(ir_dir, out_path, opts, TarBackend::BZip2);
        }
    });

    BackendManager::register_backend(
    {"txz", "Create a tarball with XZ compression (.tar.xz)",
        [](const std::string &ir_dir, const std::string &out_path,
           const std::map<std::string, std::string> &opts) {
            return new TarBackend(ir_dir, out_path, opts, TarBackend::XZ);
        }
    });
}

}
}