CMSC 412 Programming Assignment #5

Due May 15, 2002 (5:00 PM)

Introduction

The purpose of this project is to add interposes communication via message buffers to the Geek OS kernel.  In addition, you will add the notion of user's and Access Control Lists.

Message Passing

Message passing allows processes to communicate by sending messages.   In this project, those messages will be sent via message queues.  You can think of the message queues as mailboxes.   A “send” to a mailbox will add a message to the queue of messages corresponding to that mailbox. A “receive” takes one of the message and returns it to the receiver.  Because the message queues are queues, a send should attach a message to the end of a queue of messages.  A receive should dequeue the message from the front of a queue. The message queue is a queue of arrays of bytes.   The kernel must allocate space to hold each message and copy it from the process sending the message.   On a receive, the kernel will dequeue the first message in the queue, copy the message into the buffer passed by the receiver, and then free up the memory used by the message.   If the message is longer that the buffer size of the receiver, part given to the receiver, while the rest will form a new message to be placed at the head of the message queue.   The key new system call to allow message passing is:

int MessageQueueCreate(char *name)

This will allow you to “create” a message queue from which to send messages. If there is already a mailbox with this given name, then find an unused file descriptor from the process’s file descriptor table, and assign the mailbox number to this file descriptor (element). Return the file descriptor as the result of this system call.  If  this is the first call to MessageQueueCreate with the given name, it creates a new entry in the MQ array. Return -1 if all mailboxes are currently in use (i.e., all mailboxes have at least one user), or all file descriptors are currently in use.

You will use the read, write, and close system calls from project #4 to implement sending data to and from message queues, and closing a message queue. To implement message passing, you will need to modify the vfs.c to create a new "filesystem" (FS_TYPE_IPC) that is really just a message-passing interface.  All message queues will have a fixed size of 4KB.  An attempt to write to a full message queue should block the process, and an attempt to read from an empty message queue will block the process.  The operations of message queues are defined for the case of one process reading from a message queue and a second process writing to it.  You do not need to handle the case of more than one process reading from (or writing to) the same message queue.

You will also need to modify the Spawn system call to take two additional parameters (stdin and stdout).  These extra parameters should be the file descriptors that an application process will read from or write to. These file descriptors must be valid open file descriptors in the parent process that is spawning the child process. Every process will have two pre-defined file descriptors (0 and 1) that will refer to the standard input and standard output of that process (as passed by the spawn system call).

Finally, you should modify the Print_String system call to be simply a call the write system call to the stdout (1) file descriptor.  This will allow the output of one process to be either redirected to a file (if the open file descriptor passed to the spawn system call is a file) or piped into another process (if the open file descriptor passed into the process is a message queue). You may want to define Debug_Print_String to be the old Print_String system calls to allow debugging output while you debug this call.

There is also another file system called FS_TYPE_CONSOLE. This is the screen and keyboard.  The initial process (started in main.c) should have its file descriptors 0 and 1 set to read and write (respectively) from this special file system type.  You should modify the vfs.c code to handle read and write to the console device (basically clone the code from Print_String and GetKey).

Users and I/O Protection

This project will also introduce the idea of user's and I/O protection to the OS.  Adding the system calls SetAcl, SetSetUid, and SetEffectiveUID will do this.  The key idea to adding users is simply to have a field in the process data structure that identifies the current user that is running this process. The user is simply a 32-bit integer called the uid.  There is a special user (uid 0) that is the super user. The super user (and only the super user) may change it's the user id of a running process via the SetEffectiveUID(int uid) system call.  If the uid of a process is 1 when this system call is made, the uid is changed to the passed uid. The int GetUid()call returns the current UID of the process. When a process is spawned, it normally will inherit the uid of the parent process.  However, if the setUid bit is enabled for the executable, the process should inherit the uid of executable file rather than the parent process.

 

The uid is used by the file system to check if a particular user is able to perform a particular operation on a given file.  A process running with the uid of the superuser can open any file regardless of the ACLs. If the uid is any other value, the I/O operation will only succeed if the uid has appropriate privilege based on the ACL of the file (or directory).  For the Open system call, if the file exists the current uid must have the requested access level (i.e. Read privilege if the file is being opened with read access).  If the file does not exist, then the user must have write access to the parent directory. The read, write, seek, stat, and close system calls do not require checking the ACL since the check is made on the Open call. The createDirectory call requires write access to the parent directory. The delete and setAcl calls require write access to the named file. You do not need to implement negative rights as part of the ACLs (i.e., all users except uid x have read access to a file).

 

The int SetAcl(char *file, int uid, int permissions) system call sets the file permissions for the passed uid on the named file.  If the permissions are 0, this should delete any access that uid had to the passed file.  If the request is for a new uid to have some privilege for a file , and the ACL table for that file is full, you should return -1 as an error code.  This code should also be used for invalid permissions or for non-existent files.

New System Calls

The following system calls will need to be added to your operating system.

 

Call

User Function

Call Number

SYS_SETACL

SetAcl

22

SYS_SETSETUID

SetSetUid

23

SYS_SET_EFFECTIVE_UID

SetEffectiveUID

24

SYS_MQ_CREATE

MessageQueueCreate

26

SYS_GET_UID

GetUid

27

 

Recall that system call #25 was used in project #4.

What to turn in

You should submit a compressed tar file for the project5 directory. 

Files

shell.c

A command line shell with I/O redirection (to be posted later).

elf.c

Modified to use the vfs to read files from pfat or GeekOS filesystems

wc.c

A simple version of the word count command.  It counts the number of characters, words, and lines in a file.

cat.c

A program similar to the UNIX command cat.  It reads input from the named file and writes it to stdout.

ls.c

An updated version of ls.c that also prints the ACL information.

setacl.c

A command line tool to set ACL info for files.