#include "recordio.h" unsigned char readbyte(int fp); char pbuf[512]; // print buffer int eof = 1; // flag for end of file //----------------------------------------------------------- // FUNCTION rio_open: // If create is requested, open both the data file and the index file with O CREAT. // If O RDONLY or O RDWR is requested, make sure that the index file is present and return an error otherwise. // PARAMETER USAGE : // const char* pathname : the path of the file to open // int flags : the type of open // mode_t mode : file permissions // FUNCTION CALLED : // int16_t encodefd(int d1, int d2) // printb(unsigned n) : for debugging //----------------------------------------------------------- int rio_open(const char *pathname, int flags, mode_t mode) { ret:; int d, d1o, d2o; // output file descriptor char *name_extention = ".rinx."; // all index files must start with this char *index_filename = malloc(sizeof(char) * (strlen(name_extention) + strlen(pathname))); // allocate space for the index filename unsigned char byte; // storage as to read in byte by byte int record_position = 0; int n, buff; // counters record_descriptor rd; // record descriptor buff = 0; // set the initial buffer size int d1, d2; strcat(index_filename, name_extention); // construct the index filename strcat(index_filename, pathname); int index_tester = open(index_filename, O_RDONLY); if (index_tester > 0) { close(index_tester); } int datafile_tester = open(pathname, O_RDONLY); if (datafile_tester > 0) { close(datafile_tester); } else { // the file doesnt exist close(datafile_tester); d1 = open(pathname, flags, mode); // open the data file if (d1 < 0) { // error check fprintf(stderr, "cannot open %s because of %s\n", pathname, strerror(5)); return -1; } d2 = open(index_filename, O_RDWR | O_CREAT | O_EXCL, 0777); if (d2 < 0) { // error check fprintf(stderr, "cannot open %s because of %s\n", pathname, strerror(5)); return -1; } d = encodefd(d1, d2); // encrypt file descriptor return d; } d1 = open(pathname, flags, mode); // open the data file if (d1 < 0) { // error check fprintf(stderr, "cannot open %s because of %s\n", pathname, strerror(5)); return -1; } if (index_tester < 0) { // check if index file doesnt exist d2 = open(index_filename, O_RDWR | O_CREAT | O_EXCL, 0777); if (d2 < 0) { // error check fprintf(stderr, "cannot open %s because of %s\n", pathname, strerror(5)); return -1; } while (eof) { // read until end of file byte = readbyte(d1); // read in 1 byte if (eof == 0) { // check to see if you are at the end of file if (buff > 0) { // if the buffer is nonzero then we have a file which doesnt end in an endl rd.position = record_position - buff; // record position rd.length = buff; // record length n = write(d2, &rd, sizeof(record_descriptor)); // write the record descriptor if (n < 0) { fprintf(stderr, "create index eof: %s\n", strerror(EIO)); return -1; }; // error checking } eof = 1; break; // we are at the end of file } record_position += sizeof(byte); // increase bytecount buff += sizeof(byte); // increase bytecount if (byte == '\n') { // check if the byte is an endline rd.position = record_position - buff; // record position rd.length = buff; // record length n = write(d2, &rd, sizeof(record_descriptor)); // write the record descriptor if (n < 0) { fprintf(stderr, "create index norm: %s\n", strerror(EIO)); return -1; }; // error checking buff = 0; // reset the buffer } } close(d1); close(d2); goto ret; // This goes back to the top of the function if the file doesn't exist so that the file is created and opened correctly } else { // file exists d2 = open(index_filename, O_RDWR, 0777); if (d2 < 0) { // error check fprintf(stderr, "cannot open %s because of %s\n", pathname, strerror(5)); return -1; } } d = encodefd(d1, d2); // encrypt file descriptor /* ENCODEING/DECODEING CORRECTNESS TEST */ #ifdef DEBUG printf("d1_in = "); printb(d1); printf("d2_in = "); printb(d2); printf("d = "); printb(d); printf("d1_out = "); d1o = decodedfd(d); printb(d1o); printf("d2_out = "); d2o = decodeifd(d); printb(d2o); #endif return d; } //----------------------------------------------------------- // FUNCTION rio_read: // Allocate a buffer large enough to hold the requested buffer, // read the next record and return the pointer to the allocated area. // The I/O result should be returned through the return value argument. // PARAMETER USAGE : // int fd : the file descriptor // int * return_value : the record in the index file to read // FUNCTION CALLED : // int8_t decodeifd(int16_t d) // int8_t decodedfd(int16_t d) //----------------------------------------------------------- void *rio_read(int fd, int *return_value) { void *buffer; // the buffer to be returned int index_fd, data_fd; // file descriptors int r; // read error checker record_descriptor rd; // record descriptor index_fd = decodeifd(fd); // decrypt index file descriptor data_fd = decodedfd(fd); // decrypt data file descriptor r = read(index_fd, &rd, sizeof(rd)); // read in the record if (r < 0) { fprintf(stderr, "rio_read (index): %s\n", strerror(EIO)); return NULL; }; // error check int position = rd.position; // the position of the data in the datafile int length = rd.length; // the size of the data block buffer = (void *)malloc(length); // allocate buffer memory int r1 = read(data_fd, buffer, length); // read in the data to the buffer if (r1 < 0) { fprintf(stderr, "rio_read (data)%s\n", strerror(EIO)); return NULL; }; // error check *return_value = r1; // The I/O result should be returned through the return value argument. return buffer; // return the pointer to the allocated area. } //----------------------------------------------------------- // FUNCTION rio_write: // If appending to the file, create a record descriptor and fill-in the values. // Write the descriptor to the index file and the supplied data to the data file for the requested length. // If updating a record, read the record descriptor, check to see if the new record fits in the allocated area and rewrite. // Return an error otherwise. // PARAMETER USAGE : // int fd : the file descriptor // const void *buf : the buffer to be written // int count : requested length // FUNCTION CALLED : // int8_t decodeifd(int16_t d) // int8_t decodedfd(int16_t d) //----------------------------------------------------------- int rio_write(int fd, const void *buf, int count) { int index_fd, data_fd; // file descriptors int r, w, data_w; // read error checker record_descriptor rd; // record descriptor index_fd = decodeifd(fd); // decrypt index file descriptor data_fd = decodedfd(fd); // decrypt data file descriptor r = read(index_fd, &rd, sizeof(rd)); // read in the record if (r < 0) { fprintf(stderr, "rio_write (read index [to check if record_descriptor exists]): %s\n", strerror(EIO)); return -1; }; // error check if (r > 0) { int length = rd.length; // the size of the data block if (length < count) { fprintf(stderr, "rio_write: %s\n", strerror(ENOMEM)); return -1; } } else { // we are at the end of the index file || file is empty int check = lseek(index_fd, 0, SEEK_END); // check to see if the file is empty (newly created) if (check > 0) { // normal case lseek(index_fd, -sizeof(record_descriptor), SEEK_END); r = read(index_fd, &rd, sizeof(rd)); // read in the record if (r < 0) { fprintf(stderr, "rio_write (read index to make new record_descriptor): %s\n", strerror(EIO)); return -1; }; // error check int position = rd.position; // the position of the data in the datafile int length = rd.length; // the size of the data block rd.position = position + length; rd.length = count; } else { // special case for empty file rd.position = 0; rd.length = count; } w = write(index_fd, &rd, sizeof(record_descriptor)); if (w < 0) { fprintf(stderr, "rio_write (write index): %s\n", strerror(EIO)); return -1; }; // error check lseek(data_fd, rd.position, SEEK_SET); // seek to position in data file data_w = write(data_fd, buf, count); if (data_w < 0) { fprintf(stderr, "rio_write (write data): %s\n", strerror(EIO)); return -1; }; // error check return data_w; } rd.length = count; lseek(index_fd, -sizeof(record_descriptor), SEEK_CUR); w = write(index_fd, &rd, sizeof(record_descriptor)); if (w < 0) { fprintf(stderr, "rio_write (write index): %s\n", strerror(EIO)); return -1; }; // error check data_w = write(data_fd, buf, count); if (data_w < 0) { fprintf(stderr, "rio_write (write data): %s\n", strerror(EIO)); return -1; }; // error check return w; } //----------------------------------------------------------- // FUNCTION rio_lseek: // Seek both files to the beginning of the requested record so that the next I/O is performed at the requested position. // whence assumes the same values as lseek whence argument. // PARAMETER USAGE : // int fd : the file descriptor // int offset : the offset of the index file // int whence : the "mode" of the seek // FUNCTION CALLED : // int8_t decodeifd(int16_t d) // int8_t decodedfd(int16_t d) //----------------------------------------------------------- int rio_lseek(int fd, int offset, int whence) { int index_fd, data_fd; // file descriptors int s1, s2, r; // error values record_descriptor rd; index_fd = decodeifd(fd); // decrypt index file descriptor data_fd = decodedfd(fd); // decrypt data file descriptor s1 = lseek(index_fd, (offset) * sizeof(record_descriptor), whence); // seek the index file to offset r = read(index_fd, &rd, sizeof(rd)); // read in the record if (r < 0) { fprintf(stderr, "rio_lseek (SEEK_SET): %s\n", strerror(EIO)); return -1; }; // error check s1 = lseek(index_fd, (offset) * sizeof(record_descriptor), whence); // seek back to the index file because you read it in the previous step int position = rd.position; // the position of the data in the datafile s2 = lseek(data_fd, position, SEEK_SET); // seek to the position in the datafile @position return s1 / sizeof(record_descriptor); } //----------------------------------------------------------- // FUNCTION rio_close: // Close both files. // PARAMETER USAGE : // int fd : the file descriptor // FUNCTION CALLED : // int8_t decodeifd(int16_t d) // int8_t decodedfd(int16_t d) //----------------------------------------------------------- int rio_close(int fd) { int index_fd, data_fd; // file descriptors index_fd = decodeifd(fd); // decrypt index file descriptor data_fd = decodedfd(fd); // decrypt data file descriptor int r1 = close(data_fd); if (r1 < 0) { fprintf(stderr, "rio_close (datafile): %s\n", strerror(EIO)); return -1; }; int r2 = close(index_fd); if (r2 < 0) { fprintf(stderr, "rio_close (indexfile): %s\n", strerror(EIO)); return -1; }; return r1; } //----------------------------------------------------------- // FUNCTION encodefd: // takes the file descriptor of both inputs and turns it into a single file descriptor // PARAMETER USAGE : // int d1 : the data file descriptor // int d2 : the index file descriptor // FUNCTION CALLED : // none //----------------------------------------------------------- int16_t encodefd(int d1, int d2) { int16_t d = (((d1 & 0xff) << 8) | (d2 & 0xff)); // 16 bit handle return d; } //----------------------------------------------------------- // FUNCTION decodedfd: // returns the data file descriptor // PARAMETER USAGE : // int16_t d : the file descriptor // FUNCTION CALLED : // none //----------------------------------------------------------- int8_t decodedfd(int16_t d) { int8_t d1 = (d >> 8); // take the top 8 bits return d1; } //----------------------------------------------------------- // FUNCTION decodeifd: // returns the index file descriptor // PARAMETER USAGE : // int16_t d : the file descriptor // FUNCTION CALLED : // none //----------------------------------------------------------- int8_t decodeifd(int16_t d) { int8_t d2 = (d & 0xff); // take the bottom 8 bits return d2; } //----------------------------------------------------------- // FUNCTION printb: // prints a 32 bit binary number // PARAMETER USAGE : // unsigned : the number // FUNCTION CALLED : // none //----------------------------------------------------------- void printb(unsigned n) { unsigned i; for (i = 1 << 31; i > 0; i = i / 2) { (n & i) ? printf("1") : printf("0"); } printf("\n"); } //----------------------------------------------------------- // FUNCTION readbyte: // reads 1 byte from stdin and does error checking // PARAMETER USAGE : // none // FUNCTION CALLED : // ssize_t read(int fildes, void *buf, size_t nbyte) // int fprintf(FILE * restrict stream, const char * restrict format, ...); // char * strerror(int errnum); //----------------------------------------------------------- unsigned char readbyte(int fp) { int r; unsigned char byte; r = read(fp, &byte, sizeof(byte)); if (r < 0) { fprintf(stderr, "readbyte: %s\n", strerror(EIO)); exit(-1); }; if (r == 0) { eof = 0; }; return byte; }