Skip to content

Instantly share code, notes, and snippets.

@supechicken
Last active August 19, 2022 13:47
Show Gist options
  • Save supechicken/030a621da06e97272befdc7090bf68f5 to your computer and use it in GitHub Desktop.
Save supechicken/030a621da06e97272befdc7090bf68f5 to your computer and use it in GitHub Desktop.
A simple C program that adds remove-on-copy support to "tar c | tar x" command
/*
remove_on_copy.c: Remove each file listed in tar archive once they sent to stdout
Usage:
stdin: Tar archive (only ustar format is supported) created by tar -c
stdout: Tar extract command (tar -x)
Option:
-v Enable verbose output
Example usage:
tar -c . | ./remove_on_copy | tar -xp -C /
# equivalent to:
# rsync --remove-source-files . /
This file can be built with:
gcc remove_on_copy.c -lm -o remove_on_copy
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
bool verbose = false;
int main (int argc, char * argv[]) {
int exitstatus = 0;
if ( argc > 1 && strcmp(argv[1], "-v") == 0 ) verbose = true;
// process each file in archive
while (true) {
char name[100], octal_size[12], type[1];
// filename (first 100 byte)
{
read(STDIN_FILENO, name, 100);
if (name[0] == '\0') {
// exit if filename is empty (extra empty blocks created by tar)
break;
} else {
// write it back to stdout
write(STDOUT_FILENO, name, 100);
}
}
// metadata between filename and file size (24 bytes)
{
// read it from stdin and pass it to stdout
char buf[24];
read(STDIN_FILENO, buf, 24);
write(STDOUT_FILENO, buf, 24);
}
// file size in octal (12 bytes)
{
read(STDIN_FILENO, octal_size, 12);
write(STDOUT_FILENO, octal_size, 12);
}
// metadata between file size and type flag (20 bytes)
{
char buf[20];
read(STDIN_FILENO, buf, 20);
write(STDOUT_FILENO, buf, 20);
}
// type flag (1 byte)
{
read(STDIN_FILENO, type, 1);
write(STDOUT_FILENO, type, 1);
}
// remaining metadata (355 bytes)
{
char buf[355];
read(STDIN_FILENO, buf, 355);
write(STDOUT_FILENO, buf, 355);
}
// 100 + 24 + 12 + 20 + 1 + 355 = 512, end of file meta
// calculate number of blocks taken by the file, write all blocks
// to stdout (contain contents of the file)
int size = strtol(octal_size, NULL, 8),
block = ceil(size / 512.0);
//if (verbose == true) fprintf(stderr, "[tar_remove_on_copy]: \e[0;33mname: %s, type: %c, size: %dB\e[0m\n", name, type[0], size);
for (int i=0; i < block; i++) {
// each block takes 512 bytes
char buf[512];
read(STDIN_FILENO, buf, 512);
write(STDOUT_FILENO, buf, 512);
}
// ignore directory, remove file only
if (type[0] != '5') {
// remove the file
if ( remove(name) == 0 ) {
if (verbose == true) fprintf(stderr, "[tar_remove_on_copy]: \e[1;32mFile removed: %s\e[0m\n", name);
} else {
fprintf(stderr, "[tar_remove_on_copy]: \e[1;31m%s%s\e[0m\n", "Failed to remove file: ", name);
exitstatus = 1;
}
}
fflush(stderr);
}
return exitstatus;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment