dav/tar.c

Sun, 17 Dec 2023 14:25:34 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 17 Dec 2023 14:25:34 +0100
changeset 797
edbb20b1438d
parent 785
645f7e802873
permissions
-rw-r--r--

[Makefile] fix missing rules preventing dry-runs

We have to support dry-runs, because many IDEs are using
dry-runs to collect build information.

Some rules have dependencies that expect certain files or
directories to be just present. We added respective build
rules which invoke the test program. This way, the behavior
when running make normally is exactly the same, but dry-runs
are also not failing now.

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2018 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "tar.h"

#include <string.h>
#include <cx/string.h>
#include <libidav/utils.h>


const char* tar_error2str(TarError error) {
    switch(error) {
        default: break;
        case TAR_OK: return "ok";
        case TAR_PATH_TOO_LONG: return "path too long";
        case TAR_FILE_TOO_LARGE: return "file too large";
        case TAR_CONTENT_TOO_LARGE: return "tar content too large";
        case TAR_UNFINISHED_FILE: return "can't read a tar header at this position";
        case TAR_CONTENT_BROKEN: return "tar content broken";
    }
    return "error";
}

TarOutputStream* tar_open(FILE *f) {
    TarOutputStream *tar = malloc(sizeof(TarOutputStream));
    tar->file= f;
    tar->cur_filesize = 0;
    tar->cur_written = 0;
    tar->error = 0;
    return tar;
}


static int add_header(TarOutputStream *tar, char *path, uint32_t mode, uint64_t size, time_t mtime, int type) {
    // split path in prefix and name and check length
    char *p = util_parent_path(path);
    const char *n = util_resource_name(path);
    if(!p || !n) {
        return -1;
    }
    
    cxstring prefix = cx_str(p);
    cxstring name = cx_str(n);
    
    if(prefix.ptr[prefix.length-1] == '/') {
        prefix.length--;
    }
    
    if(prefix.length > 154) {
        tar->error = TAR_PATH_TOO_LONG;
        free(p);
        return -1;
    }
    if(name.length > 99) {
        tar->error = TAR_PATH_TOO_LONG;
        free(p);
        return -1;
    }
    
    // check file length
    if(size >= 077777777777 ) {
        tar->error = TAR_FILE_TOO_LARGE;
        free(p);
        return -1;
    }
    
    // set header fields
    TarHeader h;
    memset(&h, 0, sizeof(TarHeader));
    
    // name
    memcpy(h.name, name.ptr, name.length);
    // mode
    snprintf(h.mode, 8, "%07o", mode);
    h.mode[7] = ' ';
    // uid/gid
    memset(h.uid, '0', 8);
    h.uid[7] = ' ';
    memset(h.gid, '0', 8);
    h.gid[7] = ' ';
    // size
    snprintf(h.size, 12, "%011lo", (unsigned long)size);
    h.size[11] = ' ';
    // mtime
    uint64_t t = (uint64_t)mtime;
    snprintf(h.mtime, 12, "%011lo", mtime);
    h.mtime[11] = ' ';
    // chksum
    memset(h.chksum, ' ', 8);
    // typeflag
    h.typeflag = type;
    // linkname - zeros
    // magic
    snprintf(h.magic, 6, "ustar");
    // version
    h.version[0] = '0';
    h.version[1] = '0';
    // uname/gname - zero
    // devmajor/devminor
    char *devbuf = (char*)h.devmajor;
    snprintf(devbuf, 16, "%015o", 0);
    h.devmajor[7] = ' ';
    h.devminor[7] = ' ';
    // prefix
    memcpy(h.prefix, prefix.ptr, prefix.length);
    
    // compute checksum
    uint8_t *header = (uint8_t*)&h;
    uint32_t chksum = 0;
    for(int i=0;i<512;i++) {
        chksum += header[i];
    }
    snprintf(h.chksum, 8, "%07o", chksum);
    
    fwrite(&h, 1, 512, tar->file);
    
    free(p);
    
    return 0;
}


int tar_add_dir(TarOutputStream *tar, char *path, uint32_t mode, time_t mtime) {
    return add_header(tar, path, mode, 0, mtime, TAR_TYPE_DIRECTORY);
}

int tar_begin_file(
    TarOutputStream *tar,
        char *path,
        uint32_t mode,
        uint64_t size,
        time_t mtime)
{
    if(add_header(tar, path, mode, size, mtime, TAR_TYPE_FILE)) {
        return -1;
    }
    
    tar->cur_filesize = size;
    tar->cur_written = 0;
    
    return 0;
}

size_t tar_fwrite(const void *ptr, size_t s, size_t n, TarOutputStream *stream) {
    size_t w = fwrite(ptr, s, n, stream->file);
    stream->cur_written += w;
    if(stream->cur_written > stream->cur_filesize) {
        stream->error = TAR_CONTENT_TOO_LARGE;
        return 0;
    }
    return w;
}

int tar_end_file(TarOutputStream *tar) {
    size_t pad = 512 - tar->cur_written % 512;
    char buf[512];
    memset(buf, 0, 512);
    fwrite(buf, 1, pad, tar->file);
    
    tar->cur_filesize = 0;
    tar->cur_written = 0;
    return 0;
}

int tar_close(TarOutputStream *tar) {
    char buf[512];
    memset(buf, 0, 512);
    fwrite(buf, 1, 512, tar->file);
    return 0;
}

TarInputStream* tar_inputstream_open(FILE *f) {
    TarInputStream *tar = malloc(sizeof(TarInputStream));
    memset(tar, 0, sizeof(TarInputStream));
    tar->file = f;
    return tar;
}

TarEntry* tar_read_entry(TarInputStream *tar) {
    if(tar->cur_read < tar->cur_entry.size) {
        tar->error = TAR_UNFINISHED_FILE;
        return NULL;
    }
    
    TarHeader h;
    memset(&h, 0, sizeof(TarHeader));
    
    if(fread(&h, 1, 512, tar->file) != 512) {
        tar->error = TAR_ERROR;
        return NULL;
    }
    
    // some checks
    tar->error = TAR_CONTENT_BROKEN; // set error for all following returns
    
    char *buf = (char*)&h;
    uint64_t chksum = 0;
    int chksumfield = 8 * 32;
    for(int i=0;i<148;i++) {
        chksum += buf[i];
    }
    chksum += chksumfield;
    for(int i=156;i<512;i++) {
        chksum += buf[i];
    }
    
    if(chksum != chksumfield) {
        long long int cks = strtoll(h.chksum, NULL, 8);
        if(cks != chksum) {
            return NULL;
        }
    }
    
    if(memcmp(h.magic, "ustar\0", 6)) {
        if(!memcmp(h.magic, "\0\0\0\0", 4)) {
            tar->error = TAR_OK;
        }
        return NULL;
    }
    if(memcmp(h.version, "00", 2)) {
        return NULL;
    }
    
    // check if name and prefix are null terminated
    int nameterm = 0;
    int prefixterm = 0;
    for(int i=0;i<100;i++) {
        if(h.name[i] == 0) {
            nameterm = 1;
            break;
        }
    }
    for(int i=0;i<155;i++) {
        if(h.prefix[i] == 0) {
            prefixterm = 1;
            break;
        }
    }
    if(!nameterm || !prefixterm) {
        return NULL;
    }
    
    // get size
    if(!(h.size[11] == ' ' || h.size[11] == 0)) {
        return NULL;
    }
    long long int size = strtoll(h.size, NULL, 8);
    if(size < 0) {
        return NULL;
    }
    
    if(h.name[0] == 0) {
        return NULL;
    }
    
    // get path
    char *path = h.prefix[0] != 0 ? util_concat_path(h.prefix, h.name) : strdup(h.name);
    
    if(tar->cur_entry.path) {
        free(tar->cur_entry.path);
    }
    
    tar->cur_entry.path = path;
    tar->cur_entry.size = (uint64_t)size;
    tar->cur_entry.type = h.typeflag;
    tar->cur_read = 0;
    
    tar->error = TAR_OK;
    return &tar->cur_entry;
}

size_t tar_fread(void *ptr, size_t s, size_t n, TarInputStream *stream) {
    size_t bufsize = s*n;
    size_t available = stream->cur_entry.size - stream->cur_read;
    
    size_t nb = available > bufsize ? bufsize : available;
    if(nb == 0) {
        return 0;
    }
    size_t r = fread(ptr, 1, nb, stream->file);
    if(r != nb) {
        stream->error = TAR_ERROR;
        return 0;
    }
    
    stream->cur_read += r;
    if(stream->cur_read >= stream->cur_entry.size) {
        // read padding
        size_t pad = 512 - stream->cur_read % 512;
        char buf[512];
        if(fread(buf, 1, pad, stream->file) != pad) {
            stream->error = TAR_ERROR;
            return 0;
        }
    }
    
    return r;
}

int tar_seek(TarInputStream *stream, long offset, int whence) {
    return fseek(stream->file, offset, whence);
}

int tar_inputstream_close(TarInputStream *tar) {
    if(tar->cur_entry.path) {
        free(tar->cur_entry.path);
    }
    free(tar);
    return 0;
}

mercurial