Files
s390-tools/dump2tar/src/tar.c
Jan Höppner d85cf20981 dump2tar: Change SET_STR_FIELD to copy strings correctly
Make sure strings are properly copied in the SET_STR_FIELD
macro by using util_strlcpy().

This gets rid of the following GCC8 compile warnings:

In function ‘init_header’,
    inlined from ‘emit_header’ at tar.c:189:2:
tar.c:81:2: warning: ‘strncpy’ specified bound 100 equals destination
size [-Wstringop-truncation]
  strncpy((obj)->name, (value), sizeof((obj)->name))
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tar.c:94:2: note: in expansion of macro ‘SET_STR_FIELD’
  SET_STR_FIELD(header, name, filename);
  ^~~~~~~~~~~~~
tar.c:81:2: warning: ‘strncpy’ specified bound 100 equals destination
size [-Wstringop-truncation]
  strncpy((obj)->name, (value), sizeof((obj)->name))
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tar.c:94:2: note: in expansion of macro ‘SET_STR_FIELD’
  SET_STR_FIELD(header, name, filename);
  ^~~~~~~~~~~~~
tar.c:81:2: warning: ‘strncpy’ specified bound 100 equals destination
size [-Wstringop-truncation]
  strncpy((obj)->name, (value), sizeof((obj)->name))
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tar.c:96:3: note: in expansion of macro ‘SET_STR_FIELD’
   SET_STR_FIELD(header, linkname, link);
   ^~~~~~~~~~~~~

Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2018-11-16 15:19:48 +01:00

273 lines
6.8 KiB
C

/*
* dump2tar - tool to dump files and command output into a tar archive
*
* TAR file generation
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <string.h>
#include "buffer.h"
#include "idcache.h"
#include "misc.h"
#include "tar.h"
#define LONGLINK "././@LongLink"
#define TYPE_LONGLINK 'K'
#define TYPE_LONGNAME 'L'
#define BLOCKSIZE 512
/* Basic TAR header */
struct tar_header {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
};
/* Store the octal value of @value to at most @len bytes at @dest */
static void set_octal(char *dest, size_t len, unsigned long value)
{
int i;
dest[len - 1] = 0;
for (i = len - 2; i >= 0; i--) {
dest[i] = '0' + (value & 7);
value >>= 3;
}
}
/* Store time @value to at most @len bytes at @dest */
static void set_time(char *dest, size_t len, time_t value)
{
time_t max = (1ULL << (3 * (len - 1))) - 1;
if (value >= 0 && value <= max) {
set_octal(dest, len, value);
return;
}
for (; len > 0; len--) {
dest[len - 1] = value & 0xff;
value >>= 8;
}
dest[0] |= 0x80;
}
#define SET_FIELD(obj, name, value) \
set_octal((obj)->name, sizeof((obj)->name), (unsigned long) (value))
#define SET_TIME_FIELD(obj, name, value) \
set_time((obj)->name, sizeof((obj)->name), (time_t) (value))
#define SET_STR_FIELD(obj, name, value) \
util_strlcpy((obj)->name, (value), sizeof((obj)->name))
/* Initialize the tar file @header with the provided data */
static void init_header(struct tar_header *header, const char *filename,
const char *link, size_t len, struct stat *stat,
char type)
{
unsigned int i, checksum;
unsigned char *c;
memset(header, 0, sizeof(*header));
/* Fill in header fields */
SET_STR_FIELD(header, name, filename);
if (link)
SET_STR_FIELD(header, linkname, link);
SET_FIELD(header, size, len);
if (stat) {
SET_FIELD(header, mode, stat->st_mode & 07777);
SET_FIELD(header, uid, stat->st_uid);
SET_FIELD(header, gid, stat->st_gid);
SET_TIME_FIELD(header, mtime, stat->st_mtime);
uid_to_name(stat->st_uid, header->uname, sizeof(header->uname));
gid_to_name(stat->st_gid, header->gname, sizeof(header->gname));
} else {
SET_FIELD(header, mode, 0644);
SET_FIELD(header, uid, 0);
SET_FIELD(header, gid, 0);
SET_TIME_FIELD(header, mtime, 0);
uid_to_name(0, header->uname, sizeof(header->uname));
gid_to_name(0, header->gname, sizeof(header->gname));
}
header->typeflag = type;
memcpy(header->magic, "ustar ", sizeof(header->magic));
memcpy(header->version, " ", sizeof(header->version));
/* Calculate checksum */
memset(header->chksum, ' ', sizeof(header->chksum));
checksum = 0;
c = (unsigned char *) header;
for (i = 0; i < sizeof(*header); i++)
checksum += c[i];
snprintf(header->chksum, 7, "%06o", checksum);
}
/* Emit zero bytes via @emit_cb to pad @len to a multiple of BLOCKSIZE */
static int emit_padding(emit_cb_t emit_cb, void *data, size_t len)
{
size_t pad = BLOCKSIZE - len % BLOCKSIZE;
char zeroes[BLOCKSIZE];
if (len % BLOCKSIZE > 0) {
memset(zeroes, 0, BLOCKSIZE);
return emit_cb(data, zeroes, pad);
}
return 0;
}
/* Emit @len bytes at @addr via @emit_cb and pad data to BLOCKSIZE with zero
* bytes */
static int emit_data(emit_cb_t emit_cb, void *data, void *addr, size_t len)
{
int rc;
if (len == 0)
return 0;
rc = emit_cb(data, addr, len);
if (rc)
return rc;
return emit_padding(emit_cb, data, len);
}
/* Emit a tar header via @emit_cb */
static int emit_header(emit_cb_t emit_cb, void *data, char *filename,
char *link, size_t len, struct stat *stat, char type)
{
struct tar_header header;
size_t namelen = strlen(filename);
size_t linklen;
int rc;
/* /proc can contain unreadable links which causes tar to complain
* during extract - use a dummy value to handle this more gracefully */
if (link && !*link)
link = " ";
linklen = link ? strlen(link) : 0;
if (linklen > sizeof(header.linkname)) {
rc = emit_header(emit_cb, data, LONGLINK, NULL, linklen + 1,
NULL, TYPE_LONGLINK);
if (rc)
return rc;
rc = emit_data(emit_cb, data, link, linklen + 1);
if (rc)
return rc;
}
if (namelen > sizeof(header.name)) {
rc = emit_header(emit_cb, data, LONGLINK, NULL, namelen + 1,
NULL, TYPE_LONGNAME);
if (rc)
return rc;
rc = emit_data(emit_cb, data, filename, namelen + 1);
if (rc)
return rc;
}
init_header(&header, filename, link, len, stat, type);
return emit_data(emit_cb, data, &header, sizeof(header));
}
struct emit_content_cb_data {
emit_cb_t emit_cb;
void *data;
size_t len;
int rc;
};
/* Callback for emitting a single chunk of data of a buffer */
static int emit_content_cb(void *data, void *addr, size_t len)
{
struct emit_content_cb_data *cb_data = data;
if (len > cb_data->len)
len = cb_data->len;
cb_data->len -= len;
cb_data->rc = cb_data->emit_cb(cb_data->data, addr, len);
if (cb_data->rc || cb_data->len == 0)
return 1;
return 0;
}
/* Emit at most @len bytes of contents of @buffer via @emit_cb and pad output
* to BLOCKSIZE with zero bytes */
static int emit_content(emit_cb_t emit_cb, void *data, struct buffer *buffer,
size_t len)
{
struct emit_content_cb_data cb_data;
cb_data.emit_cb = emit_cb;
cb_data.data = data;
cb_data.len = len;
cb_data.rc = 0;
buffer_iterate(buffer, emit_content_cb, &cb_data);
if (cb_data.rc)
return cb_data.rc;
return emit_padding(emit_cb, data, buffer->total);
}
/* Convert file meta data and content specified as @content into a
* stream of bytes that is reported via the @emit_cb callback. @data is
* passed through to the callback for arbitrary use. */
int tar_emit_file_from_buffer(char *filename, char *link, size_t len,
struct stat *stat, char type,
struct buffer *content, emit_cb_t emit_cb,
void *data)
{
int rc;
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
if (rc)
return rc;
if (content)
rc = emit_content(emit_cb, data, content, len);
return rc;
}
/* Convert file meta data and content specified as @addr and @len into a
* stream of bytes that is reported via the @emit_cb callback. @data is
* passed through to the callback for arbitrary use. */
int tar_emit_file_from_data(char *filename, char *link, size_t len,
struct stat *stat, char type, void *addr,
emit_cb_t emit_cb, void *data)
{
int rc;
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
if (rc)
return rc;
if (addr)
rc = emit_data(emit_cb, data, addr, len);
return rc;
}