March 8, 2008 Project #0 ========== This project is due Saturday March 29th at 11:59pm. You should send me email at "kensmith@dcsl.buffalo.edu" saying you are done. This project description is very complicated. You will need to read it once, then wait a while to let things "settle in", then read it again, and repeat that cycle a couple of times. Even then you will probably have questions. Feel free to ask. General Overview ---------------- You are to implement an in-kernel "mailbox facility". Processes will be allowed to write a message to a mailbox, and then read the message from the mailbox. It will be possible for different processes to do the writing/reading, and even different processes run by different users as described below. There are to be two general types of mailboxes, "Global" and "User". For both types you will need to keep track of who owns the mailbox, what group(s) that user had been in when they created the mailbox, and permissions similar to the UNIX file permissions that determine what operations if any other users are allowed to do on the mailbox. Once created the mailbox should accept only one message at a time - the message will need to be read (and thus removed from the mailbox) before another message can be written to it, or the mailbox will need to be cleared without reading the message (a separate system call will be allowed for that). Specifics --------- All of the new system calls you write for the mailboxes will have a "mailbox type" argument that says whether it is a User Mailbox or a Global Mailbox. In the descriptions given below I will give the new system call names and exactly what the arguments to each system call should be. You must use these descriptions. That way my test programs can be used to test your kernel in case I don't particularly like what you come up with for test programs. :-) Before mailboxes can be created a 'mailbox group' needs to be created. Individual non-root users may only create their own "User Mailbox Group" which you can keep track of using their user-id. Once they have created their "User Mailbox Group" they may then create named mailboxes inside of it. For root-owned processes they are allowed to create "User Mailbox Groups" that are owned by other user-ids. And only root-owned processes may create "Global Mailbox Groups". Global Mailbox Groups are to be kept track of by their name. Names can be up to 8 characters. The system calls you should add (in this order, with the system call numbers starting off with 484) are: create_mboxgr(int type, char *name, uid_t uid, int perms) The type passed in should be 0 to indicate this is a "User Mailbox Group", a value of 1 should indicate a "Global Mailbox Group". For "User Mailbox Groups" the name argument is meaningless and should be a NULL pointer, anything else is an error. If the uid specified is zero that means "Create a mailbox group for the user who called the system call". So if user 'kensmith' runs a program that calls create_mboxgr(2) with the uid argument zero that means create the mailbox group for user kensmith. Normal users are not allowed to create mailbox groups for other users, but root-owned processes are allowed to do that. In the case of a root-owned process calling create_mboxgr(2) it may provide a non-zero value for uid and the resulting mailbox group should be owned by that uid. The "perms" argument should be a bitmask exactly like the file permission - bits 2, 1, and 0 should be "read", "write", and "admin" permissions for "other". Bits 5, 4, and 3 should be "read", "write", and "admin" permissions for processes that are in one of the same groups as the process that created the mailbox group (you should record all group(s) that process had been in as part of your mailbox group data structure). And bits 8, 7, and 6 should be "read", "write", and "admin" for the owner of the mailbox group. There should be a limit of 50 Global Mailbox Groups. destroy_mboxgr(int type, char *name, uid_t uid) Type, name, and uid are all as described above for create_mboxgr(2). So, for example, the name should be NULL if type is 0. If the process that calls destroy_mboxgr(2) has "admin" permissions on the mailbox group it should be allowed to destroy the mailbox group. Any mailboxes that exist for that group should also be destroyed as part of this operation - it is NOT an error to call this with mailboxes still in existence. join_mboxgr(char *name, pid_t pid) This is a request for a process to join a Global Mailbox Group. If the pid argument is 0 then the process that called join_mboxgr(2) should join that Global Mailbox Group, but if pid is non-zero the process with that process-id should be made part of the Global Mailbox Group instead. The Global Mailbox Group must already have been created, and whether the identified process should be allowed into the Global Mailbox Group should be determined by whether it has either "read" or "write" permissions (or both) based on who owns the Mailbox Group, what groups it's in, etc. A root-owned process that calls join_mboxgr(2) may put any process into any (existing) Global Mailbox Group. Once a process is placed into a Global Mailbox Group any child processes it creates from that point on should inherit the Global Mailbox Group from the parent process. Note this dictates to some degree where you will need to record this information. A NULL pointer for the name argument indicates that the calling processes wants to not be in any Global Mailbox Group. create_mbox(int type, char *name, uid_t uid, int perms) All arguments are as described above for the other system calls. Whether this operation should be allowed depends on whether the calling process has "admin" privileges on the "Mailbox Group" the mailbox is being created in. If type is 1 that indicates this is a Global Mailbox, and the name of the Global Mailbox Group should have been set by a call to join_mboxgr(2) beforehand (or it could have been inherited from a parent process - either way the Global Mailbox Group for the process must be set). The name argument will be the mailbox name. If uid is 0 then the mailbox should be created in the User Mailbox Group for the user the calling process is owned by. Note that non-root processes may provide a non-zero value here. If that other user had created a User Mailbox Group with permissions such that other user(s) are granted "admin" permissions then those other users are allowed to create mailboxes in that Mailbox Group. You should record the ownership and group information based on the calling process, even if the resulting mailbox is going into a different user's User Mailbox Group. The name of the mailbox may be up to 255 characters. A maximum of eight mailboxes should be allowed for any Mailbox Group (either User or Global). destroy_mbox(int type, char *name, uid_t uid) The arguments are as described above. This should be allowed if the calling process has "admin" permissions on the mailbox. write_mbox(int type, char *name, uid_t uid, char *message, int flag) This writes to the identified mailbox, based on type, mailbox name, and if type had been 1 the Global Mailbox Group of the process that called write_mbox(2). Use the same semantics as above for the uid argument. This should succeed if the calling process had "write" permissions to the identifed Mailbox. This should return an appropriate error code for situations like permission being denied, etc. Note that it's possible for things like the User Mailbox Group or Global Mailbox Group to have been destroyed by some other process while this process was running so be careful with error checking here. The message can be up to 255 characters. If the mailbox being written to already had a message in it that message should not be overwritten. For this case if the 'flag' argument is 0 the calling process should be put to sleep until writing to the mailbox becomes possible (it is either read or cleared). If the 'flag' argument is anything other than 0 the error code EWOULDBLOCK should be returned instead. read_mbox(int type, char *name, uid_t uid, char *message, int len, int flag) This reads from the identified mailbox, using the same semantics as write_mbox(2). The calling process must have "read" permissions on the identified mailbox. "message" is where the message should be copied to. "len" is how many bytes the calling process made the buffer and only that many bytes should be copied out if the message in the mailbox is larger. The "flag" argument gives the same information as it did for write_mbox(2), so if it is 0 and there is no message in the mailbox the calling process should be put to sleep. Once a message is read from a mailbox it should be considered "gone", another message would need to be written to the mailbox before another read from it should be allowed, even if the calling process only got part of the message because "len" wasn't big enough. This is the only system call you are to add that should return anything other than an error code. This one should return the number of bytes worth of message that got copied out to the user (the smaller of len or how big the message itself had been). Look at how write(2) handles returning how many bytes get transferred and how it handles returning error codes since you may still need to return "permission denied" error codes. And again it's possible other processes destroyed Mailbox Groups or mailboxes themselves so be careful with error checking. clear_mbox(int type, char *name, uid_t uid) Remove a message from the identified mailbox without actually reading it. This requires "read" or "admin" permissions for the calling process on the identified mailbox. Notes ----- Note that in some cases I mentioned things that root-owned processes are allowed to do versus non-root-owned processes. I did not spell out everything I expect however. You should now have a feel for what things root is allowed to do, what non-root is allowed to do, etc. Part of your grade will be based on you doing the proper checks. You will be adding data structures to the kernel to help you keep track of the mailbox groups and mailboxes themselves. Many of them will need to be dynamically allocated using the kernel's malloc(9) function. Keep in mind FreeBSD is SMP - another part of your grade will be based on how well you protect your new data structures from being accessed by multiple threads at the same time. You will also need to have written some test programs to help you test your new system calls. Note that I tend to prefer small test programs that take command line arguments for "interesting" things (e.g. uid, name, etc) and do small tasks instead of one big test program that does a pre-defined (and not easily changable) sequence of events. What to Hand In --------------- As mentioned all I expect is email saying "I'm done". However in addition to the kernel code itself you will need to provide, in ~/OSI/Project_0 the following: 1) Manual pages for each of the new system calls. See the Lab Manual for hints on how to make these. 2) Your test programs, both source code and executables. 3) CHANGES - a file that lists what file(s) you needed to change as part of doing this project and a brief description of what you changed. 4) TESTING - a file that contains a description of how you tested your work. Describe briefly what each of the test programs do, and then describe how you used them to test various aspects of the new system calls. If during testing you set your kernel up to print out debugging information make sure you turn that off before handing in your work. Extra stuff written during grading will cost a few points. -- Ken Smith - From there to here, from here to | kensmith@cse.buffalo.edu there, funny things are everywhere. | - Theodore Geisel |