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 |