236 lines
7.4 KiB
C
236 lines
7.4 KiB
C
/* +-----------------------------------------------+
|
|
| John Breaux - jab0910 - JohnBreaux@my.unt.edu |
|
|
| bscopy.c - CSCE4600 Homework 1 Question 4 |
|
|
+-----------------------------------------------+ */
|
|
|
|
#include <sys/stat.h> // mkdir()
|
|
#include <dirent.h> // opendir()
|
|
#include <stdlib.h> // atoi()
|
|
#include <stdint.h> // fixed-width integer
|
|
#include <string.h> // strerror(), strrchr()
|
|
#include <stdio.h> // printf(), fopen(), fclose()
|
|
#include <errno.h> // errno
|
|
#include <time.h> // clock()
|
|
|
|
// Bytes, Kilobytes, and Megabytes
|
|
#define B *1
|
|
#define KB *1024 B
|
|
#define MB *1024 KB
|
|
|
|
#define MAX_BLOCK_SIZE 2 KB
|
|
#define MAX_PATH_SIZE 4 KB
|
|
// F_ERROR: check file stream for errors; if error, print
|
|
#define F_ERROR(file_obj, ret) if (!file_obj) { printf ("%s: %s\n", #file_obj, strerror(ferror(file_obj)) ); return ret; }
|
|
// PATHCAT: concatenate paths
|
|
#define PATHCAT(dest, src) strncat(dest, src, MAX_PATH_SIZE - strlen(dest))
|
|
|
|
/* fcopy: copy in_filename to out_filename
|
|
args:
|
|
block: block size in bytes
|
|
in_filename: filename of the input file
|
|
out_filename: filename of the output file
|
|
returns:
|
|
size_t: number of bytes copied
|
|
*/
|
|
size_t fcopy (size_t block, char* in_filename, char* out_filename);
|
|
/* fcreate: create a new file at filename, if it doesn't exist.
|
|
If file is attempted to be created in a nonexistent directory,
|
|
recursively create directories until the error is resolved.
|
|
args:
|
|
size: size of file in bytes
|
|
filename: path to file
|
|
returns:
|
|
size_t: difference between number of bytes requested and actually created (should be zero)
|
|
*/
|
|
int fcreate(size_t size, char* filename);
|
|
/* dircreate: recursive mkdir (aka mkdir -p)
|
|
args:
|
|
path: path to directory
|
|
returns:
|
|
depth: number of directories created
|
|
*/
|
|
int dircreate(char* path);
|
|
|
|
// Tests in the first batch
|
|
#define FILESIZES_LEN 8
|
|
#define BLOCKSIZES_LEN 4
|
|
// {block size, file size}
|
|
const int tests_filesize[FILESIZES_LEN + 1] =
|
|
{1 MB, 2 MB, 4 MB, 8 MB, 16 MB, 32 MB, 64 MB, 128 MB, 0};
|
|
const int tests_blocksize[BLOCKSIZES_LEN + 1] =
|
|
{1 B, 4 B, 64 B, 1024 B, 0};
|
|
|
|
/* -----------------------------------------------------------*/
|
|
|
|
int test(double ret[BLOCKSIZES_LEN][FILESIZES_LEN], char* dir, const int bstests[BLOCKSIZES_LEN], const int fstests[FILESIZES_LEN]) {
|
|
// Create the directory dir
|
|
dircreate(dir);
|
|
char in[MAX_PATH_SIZE] = {0}, out[MAX_PATH_SIZE] = {0};
|
|
// Construct the input and output filenames
|
|
PATHCAT(in, dir);
|
|
PATHCAT(in, "/A");
|
|
PATHCAT(out, dir);
|
|
PATHCAT(out, "/B");
|
|
int fsize = -1, fs = 0;
|
|
while (fsize = fstests[fs]) {
|
|
// create the file (expensive, we only want to do this once per size)
|
|
fcreate(fsize, in);
|
|
// prepare to test with all 4 block sizes
|
|
int bsize = -1, bs = 0;
|
|
while (bsize = bstests[bs]) {
|
|
// start the clock
|
|
clock_t time = clock();
|
|
// perform the copy
|
|
int size = fcopy(bsize, in, out);
|
|
// stop the clock
|
|
time = clock() - time;
|
|
ret[bs][fs] = (double)time / CLOCKS_PER_SEC;
|
|
bs++;
|
|
}
|
|
fs++;
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* main */
|
|
|
|
int main (int argc, char** argv) {
|
|
int block_size;
|
|
char dir[MAX_PATH_SIZE] = {0};
|
|
// get argument "block size", "in", "out" from args
|
|
switch (argc) {
|
|
case 2:
|
|
PATHCAT(dir, argv[1]);
|
|
break;
|
|
case 1:
|
|
PATHCAT(dir, "/tmp/jab0910");
|
|
break;
|
|
default:
|
|
printf ("Usage: %s [directory]\n", argv[0]);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
// TODO: test
|
|
double results[BLOCKSIZES_LEN][FILESIZES_LEN] = {0};
|
|
test((double(*)[8])results, dir, tests_blocksize, tests_filesize);
|
|
/*
|
|
Size, 1, 4, 64, 1024
|
|
*/
|
|
printf("Size (B),\tblock = %d,\tblock = %d,\tblock = %d,\tblock = %d\n", tests_blocksize[0], tests_blocksize[1], tests_blocksize[2], tests_blocksize[3]);
|
|
for (int fs = 0; fs < FILESIZES_LEN; fs++)
|
|
{
|
|
printf("%9d", tests_filesize[fs]);
|
|
for (int bs = 0; bs < BLOCKSIZES_LEN; bs++) {
|
|
printf(",\t%2.6f", results[bs][fs]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
size_t fcopy (size_t block, char *in_filename, char *out_filename) {
|
|
FILE *in = fopen (in_filename, "r"); // Open in for reading
|
|
F_ERROR (in, 0);
|
|
FILE *out = fopen (out_filename, "w"); // Open out for writing
|
|
F_ERROR (out, 0);
|
|
// construct a buffer
|
|
uint8_t buffer[MAX_BLOCK_SIZE];
|
|
size_t bytes_copied = 0;
|
|
// until end of file,
|
|
while (!feof(in)) {
|
|
// Read data from in
|
|
size_t bytes_read = fread(&buffer, block, 1, in) * block;
|
|
// Write data to out
|
|
size_t bytes_written = fwrite(&buffer, block, bytes_read/block, out) * block;
|
|
// Make sure wrote same number of bytes as read
|
|
if (bytes_read == bytes_written) {
|
|
bytes_copied += bytes_read;
|
|
} else {
|
|
// Something's gone wrong
|
|
bytes_copied = -bytes_copied;
|
|
break;
|
|
}
|
|
}
|
|
// Close the files
|
|
fclose(in);
|
|
fclose(out);
|
|
// Return bytes copied
|
|
return bytes_copied;
|
|
}
|
|
|
|
// min
|
|
int min(int a, int b) {
|
|
return (a < b)? a: b;
|
|
}
|
|
// max of two pointers
|
|
void* pmax(void* a, void* b) {
|
|
return (a > b)? a: b;
|
|
}
|
|
|
|
// Recursive mkdir
|
|
int dircreate_recurse(char* path, int depth) {
|
|
if (depth == 2) {
|
|
printf("If you see this message, you've asked to create an awful lot of directories.\n");
|
|
}
|
|
// Try to create the directory
|
|
while (mkdir(path, 0700)) {
|
|
// handle errors
|
|
switch (errno){
|
|
//* path exists. Success!
|
|
case EEXIST:
|
|
return depth;
|
|
|
|
//? if at first you don't succeed, try and try again
|
|
case ENOENT: { // scope
|
|
char subpath[MAX_PATH_SIZE] = {0};
|
|
size_t l = 0;
|
|
// get the length of the substring
|
|
l = (char *)pmax(strrchr(path, '/'), path) - path;
|
|
// construct the substring
|
|
strncpy(subpath, path, min(l, MAX_PATH_SIZE));
|
|
depth = dircreate_recurse(subpath, ++depth); // recurse
|
|
// If an error occured, propagate it backwards immediately
|
|
if (depth < 0)
|
|
return depth;
|
|
break;
|
|
}
|
|
|
|
//! If at first you don't succeed, you fail
|
|
default:
|
|
printf("Could not create directory %s: Error %d (%s)\n", path, errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
return depth;
|
|
}
|
|
// wrap dircreate_recurse
|
|
int dircreate(char* path) {
|
|
return dircreate_recurse(path, 0);
|
|
}
|
|
|
|
int fcreate(size_t size, char* filename) {
|
|
//* First, check if the directory exists {
|
|
char* last_slash = strrchr(filename,'/');
|
|
// find the length of the substring
|
|
int l = (char *)pmax(strrchr(filename, '/'), filename) - filename;
|
|
char path[MAX_PATH_SIZE] = {0};
|
|
// construct the substring
|
|
strncpy(path, filename, min(l, MAX_PATH_SIZE));
|
|
dircreate(path); // recursively create new directories until satisfied
|
|
//* }
|
|
|
|
// then, try to create a file in that directory
|
|
uint8_t buffer[1 KB] = {0}; // Assemble a buffer
|
|
FILE *file = fopen(filename, "w"); // Open for writing
|
|
F_ERROR(file, -1); // abort if error
|
|
// Copy 1 KB chunks up until the desired filesize < 1024 bytes away
|
|
while (size > 1 KB) {
|
|
size -= 1 KB * fwrite(buffer, 1 KB, 1, file);
|
|
}
|
|
// Copy the rest of the file
|
|
if (size > 0) {
|
|
size -= fwrite(buffer, size, 1, file);
|
|
}
|
|
fclose(file);
|
|
// Return the number of unwritten bytes, if any
|
|
return size;
|
|
}
|