/* +-----------------------------------------------+ | John Breaux - jab0910 - JohnBreaux@my.unt.edu | | bscopy.c - CSCE4600 Homework 1 Question 4 | +-----------------------------------------------+ */ #include // mkdir() #include // opendir() #include // atoi() #include // fixed-width integer #include // strerror(), strrchr() #include // printf(), fopen(), fclose() #include // errno #include // 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; }