Although disks are easy to describe, supporting them is more tricky. As mentioned, a wide variety of disk devices could be attached to a system. Moreover, there are a lot of other things that behave like disks, but which aren't (disk images, RAM disks, network storage devices, etc.). This is the problem you're going to look at in more detail.
% mkdir yfs
% cp /yfs/assign1/Makefile yfs
% ls Makefile README %
% cvs import -m "YFS" yfs yfs start
unix % cd .. unix % rm -rf yfs
This will create a directory called 'yfs' and it will include the two files you created earlier. Do all of your subsequent work on these files and in this directory.unix % cvs checkout yfs
The first two functions are used to read and write blocks (as previously described). The GetBlockSize() and GetNumBlocks() functions merely query a disk to get its physical parameters. SyncDisk is a function that writes all data that hasn't been written to the disk yet (and is typically used with disk caching). ReleaseDisk() is a function that shuts down the disk and releases it. As you will notice, all of the functions take an argument of type YDISK *. This is a pointer to some kind of disk object---a data structure that contains information about the actual disk in question. We'll return to that shortly.int ReadBlock (YDISK *disk, int blocknum, void *buffer); int WriteBlock (YDISK *disk, int blocknum, void *buffer); int GetBlockSize (YDISK *disk); int GetNumBlocks (YDISK *disk); int SyncDisk (YDISK *disk); int ReleaseDisk (YDISK *disk);
Within the operating system, ALL functions that want to manipulate disks are going to use these 6 functions and only these 6 functions. In this sense, the functions are shielding the rest of the code from having to know the nasty details of how a disk actually works. Of course, your task in this assignment is to implement the 6 functions.
Once you've looked at the interface, you need to create two files. First, create a private header file called ydisk_impl.h. In this header file, you're going to put the definition of the YDISK structure.
Next, create a file ydisk.c. This file is going to contain the implementation of the 6 functions above. To start, just make 6 empty functions like this:/* ydisk_impl.h Definition of the YDISK structure */ #ifndef _YDISK_IMPL_H #define _YDISK_IMPL_H 1 #include "ydisk.h" struct YDISK { /* You'll fill this in as needed */ }; #endif
Note: The return value Y_ENOSUPP is an error code that means "operation not supported." This is fair enough--since you haven't actually made anything work yet./* ydisk.c Implementation of the disk interface */ #include "ydisk_impl.h" int ReadBlock (YDISK *disk, int blocknum, void *buffer) { return Y_ENOSUPP; } int WriteBlock (YDISK *disk, int blocknum, void *buffer) { return Y_ENOSUPP; } int GetBlockSize (YDISK *disk) { return Y_ENOSUPP; } int GetNumBlocks (YDISK *disk) { return Y_ENOSUPP; } int SyncDisk (YDISK *disk) { return Y_ENOSUPP; } int ReleaseDisk (YDISK *disk) { return Y_ENOSUPP; }
Once you've created these files, add them to your CVS project as follows:
% cvs add ydisk.c ydisk_impl.h
In this first part of the assignment, you're going to implement two different kinds of disk devices; a RAM disk and a disk image file.
In the file /yfs/include/yramdisk.h you'll find a function definition that creates a RAM disk.
It might be used as follows:/* Open up a RAM disk */ YDISK *CreateRamDisk(void *buffer, int blocksize, int nblocks, int flags);
You're going to need to implement RAM disks. Create a file ramdisk.c that looks like this:void *buffer; YDISK *d; buffer = (void *) malloc(512*1000); d = CreateRamDisk(buffer, 512, 1000, 0); ... /* Some disk operations */ ReadBlock(d, 13, ...); /* Read a block from the disk */ WriteBlock(d, 17, ...); /* Write block 17 to the disk */ ... etc ...
Add the file ramdisk.c to CVS.#include "ydisk_impl.h" YDISK *CreateRamDisk(void *buffer, int blocksize, int blocks, int flags) { /* You need to fill this in */ }
% cvs add ramdisk.c
The CreateDiskImage function is used to create a new disk image file. The OpenDiskImage function is used to open an existing disk image file and treat it as a disk. These functions work kind of like the RAM disk functions.int CreateDiskImage(const char *filename, int blocksize, int nblocks); YDISK *OpenDiskImage(const char *filename, int blocksize, int flags);
Create a file diskimage.c that looks like this:/* Create a disk image (only first time) */ CreateDiskImage("mydisk.img", 512, 1000); ... /* Use a disk image file */ YDISK *d; d = OpenDiskImage("mydisk.img",512, 0); ... /* Perform some disk operations */ ReadBlock(d, 13, ...); /* Read a block from the disk */ WriteBlock(d, 17, ...); /* Write block 17 to the disk */ ... etc ...
Add this file to CVS:#include "ydisk_impl.h" #include <unistd.h> #include <fcntl.h> int CreateDiskImage(const char *filename, int blocksize, int nblocks) { /* You fill this in */ } YDISK *OpenDiskImage(const char *filename, int blocksize, int flags) { /* You fill this in */ }
% cvs add diskimage.c
Type 'make' to see if they build correctly. If you get any errors, fix them. You are now ready to start the real project.% ls Makefile README diskimage.c ramdisk.c ydisk_impl.h ydisk.c
Finally, to make everything fit together, the file ydisk.c is merely going to dispatch to the correct function depending on what kind of disk it is. Of course, this is a little tricky since the ydisk.c file is not allowed to know anything about RAM disks, disk images, or any other kind of disk for that matter.
open() close() read() write() lseek()
Then, use this structure to coordinate the interaction between ydisk.c, ramdisk.c, diskimage.c. Hint: this would be part of the YDISK structure that is shared by all three files.struct DiskMethods { int (*read_block)(YDISK *, int, void *); int (*write_block)(YDISK *, int, void *); int (*blocksize)(YDISK *); int (*numblocks)(YDISK *); int (*sync)(YDISK *); int (*release)(YDISK *); };
The testing program will have a bunch of predefined operations to test various features of your disk devices. We will use this program to test your disk device.% /yfs/test/testdisk Welcome to Dave's evil ydisk tester. At the prompt below, you may execute any of the C functions in ydisk.h, yramdisk.h, and ydiskimage.h >>> m = malloc(512*1000) >>> d = CreateRamDisk(m,512,1000,0) >>> buffer = malloc(512) >>> ReadBlock(d,0,buffer) ...
In the early stages of your project, you may want to device other ways to test your code. Consider defining a file 'main.c' and writing a main() function that tests various features as you go.
Your final solution to the first assignment should be a directory of files that look like this:
To grade your solution, we will run various tests on your program using the /yfs/test/testdisk program.yfs/ Makefile README diskimage.c ramdisk.c ydisk_impl.h ydisk.c
If your solution fails to check out of CVS, does not build using make, or fails to run, you will receive no credit!unix % cvs checkout yfs unix % cd yfs unix % make unix % /yfs/test/testdisk
Make sure you test your solution by typing the above commands in some kind of junk directory---do not assume that your solution works until you have tested it yourself!
Your grade will consist of the following: