Link Search Menu Expand Document

Due by 11:59 PM on Sunday, Oct 18

Project 2: System Description

You will add a few new system calls for managing a new data abstraction managed by the kernel called “mailboxes”. The mailboxes and their contents will exist only in the Kernel address space. You will develop the system calls specified below in order to access the boxes and their contents by user processes.

Each mailbox should be identified by a string, ie a const char* which is passed in at creation time. Each mailbox can store an “unlimited” amount of data. However, that is limited by the amount available on the mailbox itself. Should the application require, it can use a different system call to resize the mailbox.

The data stored is binary data and should NOT be treated as strings – this means that the data may have zero bytes (i.e NUL terminators) embedded within them. To this end, you should NOT be using any string related functions on them (i.e. strlen() or strcpy()). For instance the following are both valid “messages” to put in the mailbox:

unsigned char data1[] = { 'a', 'b', 'c' };
unsigned char data2[] =  { 0x00, 255, 3, 0x00 };
This is very, very, very important. If you use string functions on the data you will get the wrong result and you will lose major points. DO NOT DO IT.

Data can be written and read from a mailbox with “random access”. This means that the calling program will identify where in the mailbox (i.e., the offset) to begin the read/write operation and provide how many bytes to read/write (i.e., the length). Once data is written to a mailbox it will stay there until it is explicitly overwritten by a process that has access to the mailbox (i.e., there is no destructive read and there is no notion of a “message state” like some IPC mechanisms that you may have seen in class)

You do not need to provide locking/synchronization which would allow for concurrent access. In this project you can pretend that locking does not exist.

As this code will be part of the kernel itself, correctness and efficiency should be of primary concern to you in the implementation. Particularly inefficient (memory-wise or algorithmic solutions to the problem at hand may be penalized in grading. In regard to correctness, you will probably find that the majority of your code for this assignment will be spent in ensuring that arguments and other such information passed in from user-space is valid. If in doubt, assume that the data passed in is invalid. Users tend to do a lot of really stupid things, after all. Crashing the kernel because a NULL or otherwise invalid pointer is passed in will result in a significant deduction of points.

Any pointers that cannot be read or written for the amount that is required shall be reported back to the user with a bad pointer error (that is to say -EFAULT) and the call must not affect the state of the mailbox system in any way. For instance, if the user sends data and says that it is 20000 bytes long, but you can only read 20 bytes of it successfully, you must report an error. Data must never be put into the system unless you can read the whole sequence of data that is passed in.

Finally, you are to implement this system on your own. The IPC systems within the kernel already will not be helpful to you in implementing this assignment.

New system calls

The new system calls that you have to implement in order to provide this functionality are the following:

long create_mbx_421(const char __user *name, unsigned long size);
long resize_mbx_421(const char __user *name, unsigned long size);
long delete_mbx_421(const char __user *name);
long write_mbx_421(const char __user *name, const unsigned char __user *data, unsigned long offset, unsigned long n);
long read_mbx_421(const char __user *name, unsigned char __user *data, unsigned long offset, unsigned long n);
long size_mbx_421(const char __user *name);
long list_mbx_421(char __user  **list, unsigned long max_entries);
long count_mbx_421(void);
It is very important that you add the system calls in this order exactly to the system calls table.

Remember from Project 0 that system calls are defined in the kernel by way of using a SYSCALL_DEFINE macro. So, for instance, the write_mbx_421 syscall from the list above would defined as follows:

SYSCALL_DEFINE4(write_mbx_421, const char __user*, name, unsigned char __user *, data,
                unsigned long, offset, unsigned long, n) {
    /* Code goes here */
}

See the <linux/syscalls.h> file for more information about these macros.

Each system call returns an appropriate non-negative integer on success, and a negative integer on failure which indicates the error that occurred. See the <errno.h> header file for a list of error codes (this file is also located in /usr/include/errno.h). Suggested error codes for several error conditions are listed below (this does not necessarily cover all error cases you might encounter):

  • -EPERM: Permission denied
  • -ENOMEM: Out of memory during an allocation
  • -EFAULT: Supplied an invalid pointer or one that cannot be read/written for the entire requested length
  • -ENOENT: Invalid mailbox name specified
  • -EEXIST: Mailbox already exists on creation
  • -EINVAL: Value passed is invalid
  • -ENOSPC: Attempting to write beyond mailbox length
Please note the - in front of the error code. You always need to return a negative number in case of an error.

The kernel must be very careful with memory access. Remember that users often don’t properly error check their code very well and that malicious users do also exist. You should be very careful in your code to ensure that any pointers that come in from user-space (all marked with __user above are checked sufficiently. Also, you should ensure to not leak private information outside the kernel, such as your internal data structure.


Table of contents