Codes you need
/* * bytedump.c * * This program reads a set number of bytes from a file and prints * out the hex/ASCII values one byte at a time. * * Compile: gcc bytedump.c -o bytedump */
#include #include #include #include #include #include
#define SIZE 32 /* size of the read buffer */
/* helper functions for printing out the buffer */ char printable(char c); void bytedump(char *buf, int offset);
int main(int argc, char *argv[]) {
int floppy; /* file descriptor */ int offset; /* offset into the file */
unsigned char buf[SIZE]; /* read buffer */
/* check args */ if (argc
offset = atoi(argv[2]);
/* open the file and read from the requested offset */ floppy = open(argv[1], O_RDONLY); lseek(floppy, offset, SEEK_SET); read(floppy, buf, SIZE);
/* print out the buffer */ bytedump(buf, offset); printf(" "); return 0;
}
/* prints out the buffer one byte at a time */ void bytedump(unsigned char *buf, int offset) { int i; printf("addr value ascii "); for(i=0; i }
/* returns the character if it's printable, otherwise a space */ char printable(char c) { if (isgraph(c)) return c; else return ' '; }
/////////////////////////////////////////
/* bsdump.c * * Reads the boot sector of an MSDOS floppy disk */ #include #include #include #include
#define SIZE 32 /* size of the read buffer */ //define PRINT_HEX // un-comment this to print the values in hex for debugging
struct BootSector { unsigned char sName[9]; // The name of the volume unsigned short iBytesSector; // Bytes per Sector unsigned char iSectorsCluster; // Sectors per Cluster unsigned short iReservedSectors; // Reserved sectors unsigned char iNumberFATs; // Number of FATs unsigned short iRootEntries; // Number of Root Directory entries unsigned short iLogicalSectors; // Number of logical sectors unsigned char xMediumDescriptor; // Medium descriptor unsigned short iSectorsFAT; // Sectors per FAT unsigned short iSectorsTrack; // Sectors per Track unsigned short iHeads; // Number of heads unsigned short iHiddenSectors; // Number of hidden sectors };
unsigned short endianSwap(unsigned char one, unsigned char two); // Pre: Two initialized characters // Post: The characters are swapped (two, one) and returned as a short
void decodeBootSector(struct BootSector * pBootS, unsigned char buffer[]); // Pre: An initialized BootSector struct and a pointer to an array // of characters read from a BootSector // Post: The BootSector struct is filled out from the buffer data
void printBootSector(struct BootSector * pBootS); // Pre: A filled BootSector struct // Post: The information about the boot sector prints to the console
// entry point: int main(int argc, char * argv[]) { int pBootSector = 0; unsigned char buffer[SIZE]; struct BootSector sector; // Check for argument if (argc
// Converts two characters to an unsigned short with two, one unsigned short endianSwap(unsigned char one, unsigned char two) { // This is stub code! return 0x0000; }
// Fills out the BootSector Struct from the buffer void decodeBootSector(struct BootSector * pBootS, unsigned char buffer[]) { int i = 3; // Skip the first 3 bytes // Pull the name and put it in the struct (remember to null-terminate) // Read bytes/sector and convert to big endian // Read sectors/cluster, Reserved sectors and Number of Fats // Read root entries, logicical sectors and medium descriptor // Read and covert sectors/fat, sectors/track, and number of heads // Read hidden sectors return; }
// Displays the BootSector information to the console void printBootSector(struct BootSector * pBootS) { #ifndef PRINT_HEX printf(" Name: %s ", pBootS->sName); printf(" Bytes/Sector: %i ", pBootS->iBytesSector); printf(" Sectors/Cluster: %i ", pBootS->iSectorsCluster); printf(" Reserved Sectors: %i ", pBootS->iReservedSectors); printf(" Number of FATs: %i ", pBootS->iNumberFATs); printf(" Root Directory entries: %i ", pBootS->iRootEntries); printf(" Logical sectors: %i ", pBootS->iLogicalSectors); printf(" Medium descriptor: 0x%04x ", pBootS->xMediumDescriptor); printf(" Sectors/FAT: %i ", pBootS->iSectorsFAT); printf(" Sectors/Track: %i ", pBootS->iSectorsTrack); printf(" Number of heads: %i ", pBootS->iHeads); printf("Number of Hidden Sectors: %i ", pBootS->iHiddenSectors); #else printf(" Name: %s ", pBootS->sName); printf(" Bytes/Sector: 0x%04x ", pBootS->iBytesSector); printf(" Sectors/Cluster: 0x%02x ", pBootS->iSectorsCluster); printf(" Reserved Sectors: 0x%04x ", pBootS->iReservedSectors); printf(" Number of FATs: 0x%02x ", pBootS->iNumberFATs); printf(" Root Directory entries: 0x%04x ", pBootS->iRootEntries); printf(" Logical sectors: 0x%04x ", pBootS->iLogicalSectors); printf(" Medium descriptor: 0x%02x ", pBootS->xMediumDescriptor); printf(" Sectors/FAT: 0x%04x ", pBootS->iSectorsFAT); printf(" Sectors/Track: 0x%04x ", pBootS->iSectorsTrack); printf(" Number of heads: 0x%04x ", pBootS->iHeads); printf("Number of Hidden Sectors: 0x%04x ", pBootS->iHiddenSectors); #endif return; }
Submission Turn in your well-formatted and commented source code and a copy of the output as two separate files on the Thuraya LMS. Be ready to discuss your work with me. 2 Introduction In this lab, you will gain hands-on experience reading a FAT-12 file system. Using information we provide, you will decode the boot sector by hand. You will then write a program to do this automatically. 2.1 Terms Sector the smallest unit of transfer; It's also called a block. There are 512 bytes / sector on a floppy disk. 2 Boot sector stores vital information about the file system. The boot sector is laid out in the following way, starting at the beginning of the disk (logical sector 0, byte 0): All values are stored as unsigned little-endian numbers unless otherwise specified. Offset Length Contents Display Format 0x00 3 Binary offset of boot loader hex Ox03 8 Volume Label (ASCII, null padded) ASCII OxOB 2 Bytes / sector decimal OxOD 1 Sectors / cluster decimal OxOE Reserved sectors decimal 0x10 1 Number of FATS (generally 2) decimal Ox11 2 Number of Root Directory entries decimal 0x13 Number of logical sectors decimal 0x15 1 Medium descriptor hex 0x16 2 Sectors per FAT decimal 0x18 2 Sectors per track decimal 0x1A 2 Number of heads decimal 0x1C Number of hidden sectors decimal Table 1: The Layout of FAT-12 Boot Sector 2 2 2.2 Useful system calls int open(const char* path, int flags) Opens a file at path and returns a file descriptor. For example, open("/tmp/myfile", O_RDONLY) opens an existing file called myfile in the tmp directory in read-only mode. It returns a file descriptor that can be used like a handle to the open file. ssize_t read(int fd, void *buf, size_t count) Reads count bytes from the file descriptor fd into the memory location starting at buf. It starts reading from the current offset into the file. The offset is set to zero if the file has just been opened. Ac Go 2.3 Inspecting binary files You should have a floppy disk image (found in compressed file) and a program for dumping out the boot sector of the disk. In Unix, devices are treated as files. For example, the floppy drive is normally found itt a device file such as /dev/fdo. We can use a file image as a substitute for an actual floppy disk. Your Hoppy image contains a real FAT-12 file system; if you know how the bits are laid out, then you can decode the structures that describe the file system and work with the files. To help get you started, we're going to decode a few fields by hand. A program to read from the floppy disk is in the compressed file. Given a filename and an offset (in dec- imal notation), it will print out 32 bytes of hex and ASCII values. For example, ./bytedump image 1536 will produce the following output with this particular floppy image: Note that you have to give the offset to bytedump in the decimal format, not in Hex, $./bytedump image 1536 addr value ascii 0X0600 Ox31 1 Ox0601 Ox36 6 Ox0602 Ox53 S OX0603 Ox45 E Ox0604 Ox43 C Ox061e Ox00 Ox061f Ox00 The first column is the address (in hexadecimal), and the second is the data at that address (also in hexadecimal). The third column is the ASCII value of the data, but only if it was fit for printing. We can see that Ox31 is the ASCII character '1', and 0x00 is not printable Note that data is stored in the little endian format. This means that the most significant byte comes last. For example, if we had the output $ ./bytedump image 120 addr value ascii Ox0078 Ox20 Ox0079 Ox50 P We would know that Ox20 is stored at 0x0078, and Ox50 is stored at Ox0079. If we interpret this as a short value (2 bytes) starting at address Ox0078, then the value is stored as Ox2050, but it represents Ox5020. Similarly, if we were storing an int(4 bytes), the bytes would be stored in reverse order, from the least significant byte to the most significant byte. When reading data from the floppy disk into memory, we should be careful to understand the little endian format. For examplo, in reading a short value into memory, we should read two bytes from the file, and then reverse their order, before storing them into memory as a short value. A character is only a single byte, and hence there is no need for reversing the byte order if we read a single character from the file Alternately, there is a utility in Unix called hexdump that will show the binary contents of a file. This is useful to inspect a file containing binary data. If you use hexdump-C on the supplied image, the output will look something like this (use hexdump - image | less to more easily read it) 00000000 ab 3c 90 60 65 64 6f 73 66 73 00 00 02 10 01 00 1.<.mkdosfs...... e0 ob fo d6 c9 the first column is hexadecimal offset into file. since each line contains bytes this will always end in a middle columns are of third ascii representation or if byte not printable character. for example second having has value ox3c corresponding to character while so rendered as dot. exercises decoding boot sector by hand using supplied program and offsets tables above find decode values following fields that it starts at hex decimal sectors root directory entries create read then print information from sector. starting size field can be found table all course stored binary on disk. when you use format specified printf out correctly after converting any little endian data. call your new bsdump template bsdump-template.c should take name file may skip jump instruction some tips help maximize grade write separate functions perform tasks: function shorts big endian. storing struct. printing bitwise operators: left shift>> right shift & and or A G Sectors/FAT 3.2 Decoding the boot sector Create a program to read and then print information from the boot sector. The starting offset and size of each field can be found in Table 1. All of the information is (of course) stored as binary on disk. When you print the values, use the format specified in Table 1. Use printf to print out values correctly after converting any little endian data. Call your new program bsdump (see the program template bsdump-template.c). Your program should take the name of the file to read, and then decode and print out all of the values in the boot sector (you may skip the jump instruction). 4 Some tips to help maximize your grade Write separate functions to perform the following tasks: - A function for converting shorts from little to big endian. - A function for decoding the boot sector and storing the values in a struct. - A function for printing the boot sector from the struct. Use Bitwise operators: left shift >> right shift & and | or . Your conversion function should take advantage of bit shifting to reverse the bytes. One sugges tion is to take two chars and return a short. Restrict your solution to the bitwise operators (no multiplication or addition). Do not write a conversion function for binary to hex or hex to decimal. Let the format string in printf handle the dirty work. Format strings are described in man 3 printf". Do not attempt to generalize the decoding process simply write a function to decode a FAT12 boot sector one field after another. You shouldn't use any loops (except possibly for printing the name). Printing tips: 3 When you print out the values, make the values line up in a column (it's OK to hardcode spaces). For example, the following would be acceptable: Sectors/cluster 37 # of FATS 2 Media descriptor OxA8 Use "Ox to denote a hexadecimal value. You can pad variables with zeros see bytedump. - Be careful when printing the name it can be 8 characters without a binary zero at the end, so you have to ensure that you never print more than 8. - Your variables will probably have to be declared "unsigned" to print correctly. A comment every 3-4 lines on average should be fine for algorithms. For declarations, please put a comment by each that explains what the variable or structure is used for. You might look at bytedump.c for an example of the style we are looking for. Give your variables meaningful names. Indexes are usually i, j, and k, but other variables should have more descriptive names. Avoid using global variables unless you really need them. Global variables can be modified anywhere in a program, making code difficult to read and to debug. When printing out your code, ensure that the lines do not wrap around. Format your source so it fits within the limits of the printer. Not doing so makes it hard to read which defeats the purpose of indenting. 4.1 Packed structures The simplest way to read in the data from disk is to interpret it one byte at a time, and store the data in a structure. However, since structures in C are simply a definition of some binary data in memory, it is possible to define a structure that describes the FAT12 header. The file stdint.h defines a number of useful types such as uint16_t, which is an unsigned integer. Integers are always stored in memory in the byte order of the system, so you do not need to convert between little and big endian if you use this. Because several of the fields in the FAT12 header are not aligned to an even or odd byte bound- ary, the definition of a C structure may include padding to allow faster memory access. Since this is undesirable, you will need to add __attribute_-((_-packed__)) after the structure definition. See http://sig9.com/articles/gcc-packed-structures or a C manual for more information. There is no int24_t type; the simplest way to represent a 3-byte field is by using an array of 3 uint8_t values. Alternatively, you can use a bitfield of length 24 - for an introduction to the bitfield feature of structures, see http://publications.gbdirect.co.uk/c_book/chapter6/bitfields.html. Bitfields are rarely used in data structures as the slow access times due to compiler-generated bitmasking will outweigh the memory savings. may choose to either use a packed structure or to read the data byte by byte; you do not need to do both. . GO You Submission Turn in your well-formatted and commented source code and a copy of the output as two separate files on the Thuraya LMS. Be ready to discuss your work with me. 2 Introduction In this lab, you will gain hands-on experience reading a FAT-12 file system. Using information we provide, you will decode the boot sector by hand. You will then write a program to do this automatically. 2.1 Terms Sector the smallest unit of transfer; It's also called a block. There are 512 bytes / sector on a floppy disk. 2 Boot sector stores vital information about the file system. The boot sector is laid out in the following way, starting at the beginning of the disk (logical sector 0, byte 0): All values are stored as unsigned little-endian numbers unless otherwise specified. Offset Length Contents Display Format 0x00 3 Binary offset of boot loader hex Ox03 8 Volume Label (ASCII, null padded) ASCII OxOB 2 Bytes / sector decimal OxOD 1 Sectors / cluster decimal OxOE Reserved sectors decimal 0x10 1 Number of FATS (generally 2) decimal Ox11 2 Number of Root Directory entries decimal 0x13 Number of logical sectors decimal 0x15 1 Medium descriptor hex 0x16 2 Sectors per FAT decimal 0x18 2 Sectors per track decimal 0x1A 2 Number of heads decimal 0x1C Number of hidden sectors decimal Table 1: The Layout of FAT-12 Boot Sector 2 2 2.2 Useful system calls int open(const char* path, int flags) Opens a file at path and returns a file descriptor. For example, open("/tmp/myfile", O_RDONLY) opens an existing file called myfile in the tmp directory in read-only mode. It returns a file descriptor that can be used like a handle to the open file. ssize_t read(int fd, void *buf, size_t count) Reads count bytes from the file descriptor fd into the memory location starting at buf. It starts reading from the current offset into the file. The offset is set to zero if the file has just been opened. Ac Go 2.3 Inspecting binary files You should have a floppy disk image (found in compressed file) and a program for dumping out the boot sector of the disk. In Unix, devices are treated as files. For example, the floppy drive is normally found itt a device file such as /dev/fdo. We can use a file image as a substitute for an actual floppy disk. Your Hoppy image contains a real FAT-12 file system; if you know how the bits are laid out, then you can decode the structures that describe the file system and work with the files. To help get you started, we're going to decode a few fields by hand. A program to read from the floppy disk is in the compressed file. Given a filename and an offset (in dec- imal notation), it will print out 32 bytes of hex and ASCII values. For example, ./bytedump image 1536 will produce the following output with this particular floppy image: Note that you have to give the offset to bytedump in the decimal format, not in Hex, $./bytedump image 1536 addr value ascii 0X0600 Ox31 1 Ox0601 Ox36 6 Ox0602 Ox53 S OX0603 Ox45 E Ox0604 Ox43 C Ox061e Ox00 Ox061f Ox00 The first column is the address (in hexadecimal), and the second is the data at that address (also in hexadecimal). The third column is the ASCII value of the data, but only if it was fit for printing. We can see that Ox31 is the ASCII character '1', and 0x00 is not printable Note that data is stored in the little endian format. This means that the most significant byte comes last. For example, if we had the output $ ./bytedump image 120 addr value ascii Ox0078 Ox20 Ox0079 Ox50 P We would know that Ox20 is stored at 0x0078, and Ox50 is stored at Ox0079. If we interpret this as a short value (2 bytes) starting at address Ox0078, then the value is stored as Ox2050, but it represents Ox5020. Similarly, if we were storing an int(4 bytes), the bytes would be stored in reverse order, from the least significant byte to the most significant byte. When reading data from the floppy disk into memory, we should be careful to understand the little endian format. For examplo, in reading a short value into memory, we should read two bytes from the file, and then reverse their order, before storing them into memory as a short value. A character is only a single byte, and hence there is no need for reversing the byte order if we read a single character from the file Alternately, there is a utility in Unix called hexdump that will show the binary contents of a file. This is useful to inspect a file containing binary data. If you use hexdump-C on the supplied image, the output will look something like this (use hexdump - image | less to more easily read it) 00000000 ab 3c 90 60 65 64 6f 73 66 73 00 00 02 10 01 00 1.<.mkdosfs...... e0 ob fo d6 c9 the first column is hexadecimal offset into file. since each line contains bytes this will always end in a middle columns are of third ascii representation or if byte not printable character. for example second having has value ox3c corresponding to character while so rendered as dot. exercises decoding boot sector by hand using supplied program and offsets tables above find decode values following fields that it starts at hex decimal sectors root directory entries create read then print information from sector. starting size field can be found table all course stored binary on disk. when you use format specified printf out correctly after converting any little endian data. call your new bsdump template bsdump-template.c should take name file may skip jump instruction some tips help maximize grade write separate functions perform tasks: function shorts big endian. storing struct. printing bitwise operators: left shift>> right shift & and or A G Sectors/FAT 3.2 Decoding the boot sector Create a program to read and then print information from the boot sector. The starting offset and size of each field can be found in Table 1. All of the information is (of course) stored as binary on disk. When you print the values, use the format specified in Table 1. Use printf to print out values correctly after converting any little endian data. Call your new program bsdump (see the program template bsdump-template.c). Your program should take the name of the file to read, and then decode and print out all of the values in the boot sector (you may skip the jump instruction). 4 Some tips to help maximize your grade Write separate functions to perform the following tasks: - A function for converting shorts from little to big endian. - A function for decoding the boot sector and storing the values in a struct. - A function for printing the boot sector from the struct. Use Bitwise operators: left shift >> right shift & and | or . Your conversion function should take advantage of bit shifting to reverse the bytes. One sugges tion is to take two chars and return a short. Restrict your solution to the bitwise operators (no multiplication or addition). Do not write a conversion function for binary to hex or hex to decimal. Let the format string in printf handle the dirty work. Format strings are described in man 3 printf". Do not attempt to generalize the decoding process simply write a function to decode a FAT12 boot sector one field after another. You shouldn't use any loops (except possibly for printing the name). Printing tips: 3 When you print out the values, make the values line up in a column (it's OK to hardcode spaces). For example, the following would be acceptable: Sectors/cluster 37 # of FATS 2 Media descriptor OxA8 Use "Ox to denote a hexadecimal value. You can pad variables with zeros see bytedump. - Be careful when printing the name it can be 8 characters without a binary zero at the end, so you have to ensure that you never print more than 8. - Your variables will probably have to be declared "unsigned" to print correctly. A comment every 3-4 lines on average should be fine for algorithms. For declarations, please put a comment by each that explains what the variable or structure is used for. You might look at bytedump.c for an example of the style we are looking for. Give your variables meaningful names. Indexes are usually i, j, and k, but other variables should have more descriptive names. Avoid using global variables unless you really need them. Global variables can be modified anywhere in a program, making code difficult to read and to debug. When printing out your code, ensure that the lines do not wrap around. Format your source so it fits within the limits of the printer. Not doing so makes it hard to read which defeats the purpose of indenting. 4.1 Packed structures The simplest way to read in the data from disk is to interpret it one byte at a time, and store the data in a structure. However, since structures in C are simply a definition of some binary data in memory, it is possible to define a structure that describes the FAT12 header. The file stdint.h defines a number of useful types such as uint16_t, which is an unsigned integer. Integers are always stored in memory in the byte order of the system, so you do not need to convert between little and big endian if you use this. Because several of the fields in the FAT12 header are not aligned to an even or odd byte bound- ary, the definition of a C structure may include padding to allow faster memory access. Since this is undesirable, you will need to add __attribute_-((_-packed__)) after the structure definition. See http://sig9.com/articles/gcc-packed-structures or a C manual for more information. There is no int24_t type; the simplest way to represent a 3-byte field is by using an array of 3 uint8_t values. Alternatively, you can use a bitfield of length 24 - for an introduction to the bitfield feature of structures, see http://publications.gbdirect.co.uk/c_book/chapter6/bitfields.html. Bitfields are rarely used in data structures as the slow access times due to compiler-generated bitmasking will outweigh the memory savings. may choose to either use a packed structure or to read the data byte by byte; you do not need to do both. . GO You