Answered step by step
Verified Expert Solution
Question
1 Approved Answer
0107451 Laboratory 5: The FAT-12 File System Department of Communications and Computer Engineering Tafila Technical University 1 Submission Turn in your well-formatted and commented source
0107451 Laboratory 5: The FAT-12 File System Department of Communications and Computer Engineering Tafila Technical University 1 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. 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 Ox00 Ox03 OxOB OxOD Ox Ox10 Ox11 Ox13 Ox15 Ox16 Ox18 Ox1A Ox10 Length Contents 3 Binary offset of boot loader 8 Volume Label (ASCII, null padded) 2 Bytes sector 1 Sectors cluster 2 Reserved sectors 1 Number of FATs (generally 2) 2 Number of Root Directory entries 2 Number of logical sectors 1 Medium descriptor 2 Sectors per FAT 2 Sectors per track 2 Number of heads 2 Number of hidden sectors Display Format hex ASCII decimal decimal decimal decimal decimal decimal hex decimal decimal decimal decimal Table 1: The Layout of FAT-12 Boot Sector 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. 1 off_t lseek(int fd, off_t offset, int whence) Sets the offset of the file descriptor fd to offset, depending on whence. If whence is SEEK_SET, then the offset is figured from the start of the file. If whence is SEEK_CUR, then the offset is relative to the current offset. 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 at a device file such as /dev/fdo. We can use a file image as a substitute for an actual floppy disk. Your floppy 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 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 Ox0600 Ox31 1 Ox0601 Ox36 6 Ox0602 Ox53 S Ox0603 Ox45 E Ox0604 Ox43 C 2/4 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 0x20 is stored at Ox0078, and 0x50 is stored at 0x0079. If we interpret this as a short value (2 bytes) starting at address 0x0078, then the value is stored as 0x2050, but it represents 0x5020. 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 example, 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 -C image | less to more easily read it) 00000000 eb 3c 90 60 6b 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 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 cluster 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 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: - 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 uint24_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. You may choose to either use a packed structure or to read the data byte by byte; you do not need to do both. 4 4/4 0107451 Laboratory 5: The FAT-12 File System Department of Communications and Computer Engineering Tafila Technical University 1 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. 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 Ox00 Ox03 OxOB OxOD Ox Ox10 Ox11 Ox13 Ox15 Ox16 Ox18 Ox1A Ox10 Length Contents 3 Binary offset of boot loader 8 Volume Label (ASCII, null padded) 2 Bytes sector 1 Sectors cluster 2 Reserved sectors 1 Number of FATs (generally 2) 2 Number of Root Directory entries 2 Number of logical sectors 1 Medium descriptor 2 Sectors per FAT 2 Sectors per track 2 Number of heads 2 Number of hidden sectors Display Format hex ASCII decimal decimal decimal decimal decimal decimal hex decimal decimal decimal decimal Table 1: The Layout of FAT-12 Boot Sector 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. 1 off_t lseek(int fd, off_t offset, int whence) Sets the offset of the file descriptor fd to offset, depending on whence. If whence is SEEK_SET, then the offset is figured from the start of the file. If whence is SEEK_CUR, then the offset is relative to the current offset. 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 at a device file such as /dev/fdo. We can use a file image as a substitute for an actual floppy disk. Your floppy 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 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 Ox0600 Ox31 1 Ox0601 Ox36 6 Ox0602 Ox53 S Ox0603 Ox45 E Ox0604 Ox43 C 2/4 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 0x20 is stored at Ox0078, and 0x50 is stored at 0x0079. If we interpret this as a short value (2 bytes) starting at address 0x0078, then the value is stored as 0x2050, but it represents 0x5020. 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 example, 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 -C image | less to more easily read it) 00000000 eb 3c 90 60 6b 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 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 cluster 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 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: - 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 uint24_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. You may choose to either use a packed structure or to read the data byte by byte; you do not need to do both. 4 4/4
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started