aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/drivers/disk/sata.c44
-rw-r--r--kernel/drivers/memdevs.c60
-rw-r--r--kernel/drivers/tty/ldisc.c209
-rw-r--r--kernel/drivers/tty/tty.c52
-rw-r--r--kernel/fs/namev.c168
-rw-r--r--kernel/fs/open.c99
-rw-r--r--kernel/fs/s5fs/s5fs.c471
-rw-r--r--kernel/fs/vfs_syscall.c628
-rw-r--r--kernel/fs/vnode_specials.c32
-rw-r--r--kernel/main/kmain.c48
-rw-r--r--kernel/proc/kthread.c54
-rw-r--r--kernel/proc/proc.c225
-rw-r--r--kernel/proc/sched.c100
-rw-r--r--kernel/test/proctest.c271
14 files changed, 2350 insertions, 111 deletions
diff --git a/kernel/drivers/disk/sata.c b/kernel/drivers/disk/sata.c
index aa3ff41..d5ca117 100644
--- a/kernel/drivers/disk/sata.c
+++ b/kernel/drivers/disk/sata.c
@@ -488,8 +488,26 @@ void sata_init()
long sata_read_block(blockdev_t *bdev, char *buf, blocknum_t block,
size_t block_count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: sata_read_block");
- return -1;
+ // NOT_YET_IMPLEMENTED("DRIVERS: sata_read_block");
+
+ // convert block to sector
+ ssize_t lba = block * SATA_SECTORS_PER_BLOCK;
+ ssize_t count = block_count * SATA_SECTORS_PER_BLOCK;
+
+ // get the disk
+ ata_disk_t *disk = bdev_to_ata_disk(bdev);
+ if (disk == NULL)
+ {
+ dbg(DBG_DISK, "sata_read_block: disk is NULL\n");
+ return -1;
+ }
+
+ // call ahci_do_operation
+ long ret = ahci_do_operation(disk->port, lba, count, buf,
+ 0 // 0 for read
+ );
+
+ return ret;
}
/**
@@ -507,6 +525,24 @@ long sata_read_block(blockdev_t *bdev, char *buf, blocknum_t block,
long sata_write_block(blockdev_t *bdev, const char *buf, blocknum_t block,
size_t block_count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: sata_write_block");
- return -1;
+ // NOT_YET_IMPLEMENTED("DRIVERS: sata_write_block");
+
+ // convert block to sector
+ ssize_t lba = block * SATA_SECTORS_PER_BLOCK;
+ ssize_t count = block_count * SATA_SECTORS_PER_BLOCK;
+
+ // get the disk
+ ata_disk_t *disk = bdev_to_ata_disk(bdev);
+ if (disk == NULL)
+ {
+ dbg(DBG_DISK, "sata_write_block: disk is NULL\n");
+ return -1;
+ }
+
+ // call ahci_do_operation
+ long ret = ahci_do_operation(disk->port, lba, count, (void *)buf,
+ 1 // 1 for write
+ );
+
+ return ret;
}
diff --git a/kernel/drivers/memdevs.c b/kernel/drivers/memdevs.c
index 50815d3..815143a 100644
--- a/kernel/drivers/memdevs.c
+++ b/kernel/drivers/memdevs.c
@@ -43,7 +43,31 @@ chardev_ops_t zero_dev_ops = {.read = zero_read,
*/
void memdevs_init()
{
- NOT_YET_IMPLEMENTED("DRIVERS: memdevs_init");
+ // NOT_YET_IMPLEMENTED("DRIVERS: memdevs_init");
+
+ // create chardev_t's for null and zero
+ chardev_t *null_dev = kmalloc(sizeof(chardev_t));
+ if (null_dev == NULL)
+ {
+ dbg(DBG_DISK, "ERROR: kmalloc failed on null_dev in memdevs_init\n");
+ return;
+ }
+ chardev_t *zero_dev = kmalloc(sizeof(chardev_t));
+ if (zero_dev == NULL)
+ {
+ dbg(DBG_DISK, "ERROR: kmalloc failed on zero_dev in memdevs_init\n");
+ return;
+ }
+
+ // fill them in
+ null_dev->cd_id = MEM_NULL_DEVID;
+ null_dev->cd_ops = &null_dev_ops;
+ zero_dev->cd_id = MEM_ZERO_DEVID;
+ zero_dev->cd_ops = &zero_dev_ops;
+
+ // register them
+ chardev_register(null_dev);
+ chardev_register(zero_dev);
}
/**
@@ -58,8 +82,9 @@ void memdevs_init()
*/
static ssize_t null_read(chardev_t *dev, size_t pos, void *buf, size_t count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: null_read");
- return -ENOMEM;
+ // NOT_YET_IMPLEMENTED("DRIVERS: null_read");
+ // TODO: ask about buffer checking
+ return 0; // return reading no bytes
}
/**
@@ -76,8 +101,18 @@ static ssize_t null_read(chardev_t *dev, size_t pos, void *buf, size_t count)
static ssize_t null_write(chardev_t *dev, size_t pos, const void *buf,
size_t count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: null_write");
- return -ENOMEM;
+ // NOT_YET_IMPLEMENTED("DRIVERS: null_write");
+
+ // check if the buffer is NULL
+ if (buf == NULL)
+ {
+ return -EINVAL;
+ }
+
+ // there is no true writing, so just do nothing here
+
+ // return the number of bytes written
+ return count;
}
/**
@@ -93,8 +128,19 @@ static ssize_t null_write(chardev_t *dev, size_t pos, const void *buf,
*/
static ssize_t zero_read(chardev_t *dev, size_t pos, void *buf, size_t count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: zero_read");
- return 0;
+ // NOT_YET_IMPLEMENTED("DRIVERS: zero_read");
+
+ // check if the buffer is NULL
+ if (buf == NULL)
+ {
+ return -EINVAL;
+ }
+
+ // fill the buffer with zeros
+ memset(buf, 0, count);
+
+ // return the number of bytes read
+ return count;
}
/**
diff --git a/kernel/drivers/tty/ldisc.c b/kernel/drivers/tty/ldisc.c
index eaf0440..9b77433 100644
--- a/kernel/drivers/tty/ldisc.c
+++ b/kernel/drivers/tty/ldisc.c
@@ -16,7 +16,19 @@
*/
void ldisc_init(ldisc_t *ldisc)
{
- NOT_YET_IMPLEMENTED("DRIVERS: ldisc_init");
+ // NOT_YET_IMPLEMENTED("DRIVERS: ldisc_init");
+
+ // reset static buffer
+ memset(ldisc->ldisc_buffer, 0, LDISC_BUFFER_SIZE);
+
+ // init the queue
+ sched_queue_init(&ldisc->ldisc_read_queue);
+
+ // fill in other fields
+ ldisc->ldisc_head = 0;
+ ldisc->ldisc_tail = 0;
+ ldisc->ldisc_cooked = 0;
+ ldisc->ldisc_full = 0; // 0 not full
}
/**
@@ -33,8 +45,23 @@ void ldisc_init(ldisc_t *ldisc)
*/
long ldisc_wait_read(ldisc_t *ldisc)
{
- NOT_YET_IMPLEMENTED("DRIVERS: ldisc_wait_read");
- return -1;
+ // NOT_YET_IMPLEMENTED("DRIVERS: ldisc_wait_read");
+
+ // while there are no need chars to be read, sleep
+ // TODO: check if this is the right condition
+ while (
+ ldisc->ldisc_head == ldisc->ldisc_tail
+ )
+ {
+ long ret = sched_cancellable_sleep_on(&ldisc->ldisc_read_queue);
+ if (ret != 0)
+ {
+ return ret;
+ }
+ }
+
+ // if we are here, it means we have new chars to read
+ return 0;
}
/**
@@ -54,8 +81,57 @@ long ldisc_wait_read(ldisc_t *ldisc)
*/
size_t ldisc_read(ldisc_t *ldisc, char *buf, size_t count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: ldisc_read");
- return 0;
+ // NOT_YET_IMPLEMENTED("DRIVERS: ldisc_read");
+
+ dbg(DBG_DISK, "ldisc_read: count = %d\n", count);
+
+ // read from ldisc buffer to buf
+ size_t i = 0;
+ int break_loop = 0;
+ while (i < count && !break_loop)
+ {
+ // if we have no new chars to read, break loop
+ if (ldisc->ldisc_head == ldisc->ldisc_tail)
+ {
+ break_loop = 1;
+ continue;
+ }
+
+
+ char c = ldisc->ldisc_buffer[ldisc->ldisc_tail];
+ dbg(DBG_DISK, "ldisc_read: %c\n", c);
+ switch (c)
+ {
+
+ case EOT:
+ buf[i] = c;
+ ldisc->ldisc_tail = (ldisc->ldisc_tail + 1) % LDISC_BUFFER_SIZE;
+ break_loop = 1;
+ break;
+
+ // case ETX:
+ // ldisc->ldisc_tail = ldisc->ldisc_cooked;
+ // break;
+
+ case '\n':
+ buf[i] = c;
+ ldisc->ldisc_tail = (ldisc->ldisc_tail + 1) % LDISC_BUFFER_SIZE;
+ i++;
+ break_loop = 1;
+ break;
+
+ default:
+ buf[i] = c;
+ ldisc->ldisc_tail = (ldisc->ldisc_tail + 1) % LDISC_BUFFER_SIZE;
+ i++;
+ break;
+ }
+
+ dbg(DBG_DISK, "ldisc_read: i = %d\n", i);
+ }
+
+ // return the number of bytes read
+ return i;
}
/**
@@ -103,7 +179,112 @@ size_t ldisc_read(ldisc_t *ldisc, char *buf, size_t count)
*/
void ldisc_key_pressed(ldisc_t *ldisc, char c)
{
- NOT_YET_IMPLEMENTED("DRIVERS: ldisc_key_pressed");
+ // NOT_YET_IMPLEMENTED("DRIVERS: ldisc_key_pressed");
+
+ switch (c)
+ {
+ case '\b':
+ // if there is a character to remove
+ if (ldisc->ldisc_head != ldisc->ldisc_tail)
+ {
+ // remove the last char
+ ldisc->ldisc_head = (ldisc->ldisc_head - 1 + LDISC_BUFFER_SIZE) % LDISC_BUFFER_SIZE;
+
+ // emit a `\b` to the vterminal
+ vterminal_write(&ldisc_to_tty(ldisc)->tty_vterminal, "\b", 1);
+
+
+ if (ldisc->ldisc_full)
+ {
+ ldisc->ldisc_full = 0;
+ }
+ }
+ break;
+
+ case '\n':
+
+ // add the new char to the buffer
+ ldisc->ldisc_buffer[ldisc->ldisc_head] = c;
+
+ if (!ldisc->ldisc_full)
+ {
+ ldisc->ldisc_head = (ldisc->ldisc_head + 1) % LDISC_BUFFER_SIZE;
+ }
+
+ // cook the buffer
+ ldisc->ldisc_cooked = ldisc->ldisc_head;
+
+ sched_wakeup_on(&ldisc->ldisc_read_queue, 0);
+
+ // emit a `\n` to the vterminal
+ vterminal_write(&ldisc_to_tty(ldisc)->tty_vterminal, "\n", 1);
+
+ if (ldisc->ldisc_full)
+ {
+ ldisc->ldisc_full = 0;
+ }
+ break;
+
+ case EOT:
+ // add the new char to the buffer
+ dbg(DBG_DISK, "EOT\n");
+ ldisc->ldisc_buffer[ldisc->ldisc_head] = c;
+ if (!ldisc->ldisc_full)
+ {
+ ldisc->ldisc_head = (ldisc->ldisc_head + 1) % LDISC_BUFFER_SIZE;
+ }
+
+ // cook the buffer
+ ldisc->ldisc_cooked = ldisc->ldisc_head;
+
+ // wake up the thread that is sleeping on the wait queue of the line discipline
+ sched_wakeup_on(&ldisc->ldisc_read_queue, 0);
+
+ vterminal_write(&ldisc_to_tty(ldisc)->tty_vterminal, "\n", 1);
+
+ if (ldisc->ldisc_full)
+ {
+ ldisc->ldisc_full = 0;
+ }
+ break;
+
+ case ETX:
+ // clear uncooked portion of the line
+ dbg(DBG_DISK, "ETX\n");
+ ldisc->ldisc_head = (ldisc->ldisc_cooked + 1) % LDISC_BUFFER_SIZE;
+ ldisc->ldisc_cooked = ldisc->ldisc_head;
+
+ // wake up the thread that is sleeping on the wait queue of the line discipline
+ sched_wakeup_on(&ldisc->ldisc_read_queue, 0);
+
+ // emit a `\n` to the vterminal
+ vterminal_write(&ldisc_to_tty(ldisc)->tty_vterminal, "\n", 1);
+ break;
+
+ default:
+ // if the buffer is full, ignore the incoming char
+ // if (ldisc->ldisc_full)
+ // {
+ // return;
+ // }
+
+ // if none applies, fallback to vterminal_key_pressed
+ // vterminal_write(ldisc_to_tty(ldisc), &c, 1);
+
+ // update the buffer if it's not full
+ if (!ldisc->ldisc_full)
+ {
+ ldisc->ldisc_buffer[ldisc->ldisc_head] = c;
+ ldisc->ldisc_head = (ldisc->ldisc_head + 1) % LDISC_BUFFER_SIZE;
+ if (ldisc->ldisc_head + 1 == ldisc->ldisc_tail)
+ {
+ ldisc->ldisc_full = 1;
+ }
+ vterminal_key_pressed(&ldisc_to_tty(ldisc)->tty_vterminal);
+ }
+
+ break;
+ }
}
/**
@@ -115,6 +296,18 @@ void ldisc_key_pressed(ldisc_t *ldisc, char c)
*/
size_t ldisc_get_current_line_raw(ldisc_t *ldisc, char *s)
{
- NOT_YET_IMPLEMENTED("DRIVERS: ldisc_get_current_line_raw");
- return 0;
+ // NOT_YET_IMPLEMENTED("DRIVERS: ldisc_get_current_line_raw");
+
+ // copy the raw part of the line discipline buffer into the buffer provided
+ size_t i = 0;
+ size_t j = ldisc->ldisc_cooked;
+ while (j != ldisc->ldisc_head)
+ {
+ s[i] = ldisc->ldisc_buffer[j];
+ j = (j + 1) % LDISC_BUFFER_SIZE;
+ i++;
+ }
+
+ // return the number of bytes copied
+ return i;
}
diff --git a/kernel/drivers/tty/tty.c b/kernel/drivers/tty/tty.c
index 3694877..e3c77be 100644
--- a/kernel/drivers/tty/tty.c
+++ b/kernel/drivers/tty/tty.c
@@ -69,8 +69,33 @@ void tty_init()
*/
ssize_t tty_read(chardev_t *cdev, size_t pos, void *buf, size_t count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: tty_read");
- return -1;
+ // NOT_YET_IMPLEMENTED("DRIVERS: tty_read");
+
+ // get the mapped tty
+ tty_t *tty = cd_to_tty(cdev);
+
+ // set the IPL to INTR_KEYBOARD
+ uint8_t prev_ipl = intr_setipl(INTR_KEYBOARD);
+
+ // lock the read mutex of the tty
+ kmutex_lock(&tty->tty_read_mutex);
+
+ dbg(DBG_DISK, "tty_read: before wait_read\n");
+
+ // wait until there is something in the line discipline's buffer
+ ldisc_wait_read(&tty->tty_ldisc);
+
+ dbg(DBG_DISK, "tty_read: after wait_read\n");
+
+ // read from the ldisc's buffer if there are new characters
+ ssize_t bytes_read = ldisc_read(&tty->tty_ldisc, buf, count);
+ // unlock the read mutex of the tty
+ kmutex_unlock(&tty->tty_read_mutex);
+
+ // revert the IPL
+ intr_setipl(prev_ipl);
+
+ return bytes_read;
}
/**
@@ -88,8 +113,27 @@ ssize_t tty_read(chardev_t *cdev, size_t pos, void *buf, size_t count)
*/
ssize_t tty_write(chardev_t *cdev, size_t pos, const void *buf, size_t count)
{
- NOT_YET_IMPLEMENTED("DRIVERS: tty_write");
- return -1;
+ // NOT_YET_IMPLEMENTED("DRIVERS: tty_write");
+
+ // get the mapped tty
+ tty_t *tty = cd_to_tty(cdev);
+
+ // set the IPL to INTR_KEYBOARD
+ uint8_t prev_ipl = intr_setipl(INTR_KEYBOARD);
+
+ // lock the write mutex of the tty
+ kmutex_lock(&tty->tty_write_mutex);
+
+ // write to the terminal
+ size_t bytes_written = vterminal_write(&tty->tty_vterminal, buf, count);
+
+ // unlock the write mutex of the tty
+ kmutex_unlock(&tty->tty_write_mutex);
+ // revert the IPL
+ intr_setipl(prev_ipl);
+
+
+ return bytes_written;
}
static void tty_receive_char_multiplexer(uint8_t c)
diff --git a/kernel/fs/namev.c b/kernel/fs/namev.c
index e8b01e8..dd44450 100644
--- a/kernel/fs/namev.c
+++ b/kernel/fs/namev.c
@@ -76,8 +76,27 @@ long namev_is_descendant(vnode_t *a, vnode_t *b)
long namev_lookup(vnode_t *dir, const char *name, size_t namelen,
vnode_t **res_vnode)
{
- NOT_YET_IMPLEMENTED("VFS: namev_lookup");
- return 0;
+ // // NOT_YET_IMPLEMENTED("VFS: namev_lookup");
+
+ // KASSERT(NULL != dir);
+ // KASSERT(NULL != name);
+ // KASSERT(NULL != res_vnode);
+
+ // if (namelen == 0)
+ // {
+ // return -EINVAL;
+ // }
+
+ if (
+ dir->vn_ops == NULL
+ || dir->vn_ops->lookup == NULL
+ || !S_ISDIR(dir->vn_mode)
+ )
+ {
+ return -ENOTDIR;
+ }
+
+ return dir->vn_ops->lookup(dir, name, namelen, res_vnode);
}
/*
@@ -187,10 +206,89 @@ static const char *namev_tokenize(const char **search, size_t *len)
long namev_dir(vnode_t *base, const char *path, vnode_t **res_vnode,
const char **name, size_t *namelen)
{
- NOT_YET_IMPLEMENTED("VFS: namev_dir");
+ // NOT_YET_IMPLEMENTED("VFS: namev_dir");
+
+ // Check if the pathname is nullish
+ if (path == NULL || *path == '\0')
+ {
+ return -EINVAL;
+ }
+
+ // Get a vnode based on the pathname
+ vnode_t *dir = NULL;
+ if (*path == '/')
+ {
+ dir = vfs_root_fs.fs_root;
+ }
+ else if (base == NULL)
+ {
+ dir = curproc->p_cwd;
+ }
+ else
+ {
+ dir = base;
+ }
+ // Refcount it
+ vref(dir);
+
+ // Tokenize the path
+ const char *token = NULL;
+ size_t len = 0;
+
+ while ((token = namev_tokenize(&path, &len)) && len > 0)
+ {
+ if (path == NULL || *path == '\0')
+ {
+ break;
+ }
+
+ // Check if the whole path is a sequence of '/' then a null
+ int index = 0;
+ while (path[index] == '/')
+ {
+ index++;
+ }
+ if (path[index] == '\0')
+ {
+ break;
+ }
+
+ // Check if the name is too long
+ if (len > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ // Lookup the token
+ vnode_t *next = NULL;
+ vlock(dir);
+ long err = namev_lookup(dir, token, len, &next);
+ vunlock(dir);
+ if (err < 0)
+ {
+ vput(&dir);
+ return err;
+ }
+
+ // Reduce refcount and move to the next token
+ vput(&dir);
+ dir = next;
+ }
+
+ // Check if it's a directory
+ if (!S_ISDIR(dir->vn_mode))
+ {
+ vput(&dir);
+ return -ENOTDIR;
+ }
+
+ // Assign the ret vars
+ *res_vnode = dir;
+ *name = token;
+ *namelen = len;
return 0;
}
-
/*
* Open the file specified by `base` and `path`, or create it, if necessary.
* Return the file's vnode via `res_vnode`, which should be returned unlocked
@@ -217,7 +315,67 @@ long namev_dir(vnode_t *base, const char *path, vnode_t **res_vnode,
long namev_open(vnode_t *base, const char *path, int oflags, int mode,
devid_t devid, struct vnode **res_vnode)
{
- NOT_YET_IMPLEMENTED("VFS: namev_open");
+ // NOT_YET_IMPLEMENTED("VFS: namev_open");
+
+ // KASSERT(NULL != base);
+ KASSERT(NULL != path);
+ KASSERT(NULL != res_vnode);
+
+ const char *name = NULL;
+ vnode_t *dir = NULL;
+ size_t namelen = 0;
+
+ long err = namev_dir(base, path, &dir, &name, &namelen);
+ if (err < 0)
+ {
+ return err;
+ }
+ if (namelen > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ vnode_t *res = NULL;
+ vlock(dir);
+ err = namev_lookup(dir, name, namelen, &res);
+ vunlock(dir);
+
+ if (err < 0)
+ {
+ if (!(oflags & O_CREAT))
+ {
+ vput(&dir);
+ return err;
+ }
+ else
+ {
+ vlock(dir);
+ err = dir->vn_ops->mknod(dir, name, namelen, mode, devid, &res);
+ vunlock(dir);
+
+ vput(&dir);
+ *res_vnode = res;
+ return err;
+ }
+ }
+
+ if (path[strlen(path) - 1] == '/' && !S_ISDIR(res->vn_mode))
+ {
+ vput(&dir);
+ vput(&res);
+ return -ENOTDIR;
+ }
+
+ if (S_ISDIR(res->vn_mode) && (oflags & O_CREAT))
+ {
+ vput(&dir);
+ vput(&res);
+ return -EISDIR; // TODO: check if this error
+ }
+
+ vput(&dir);
+ *res_vnode = res;
return 0;
}
diff --git a/kernel/fs/open.c b/kernel/fs/open.c
index 8c9fee6..8f0d893 100644
--- a/kernel/fs/open.c
+++ b/kernel/fs/open.c
@@ -62,6 +62,101 @@ long get_empty_fd(int *fd)
*/
long do_open(const char *filename, int oflags)
{
- NOT_YET_IMPLEMENTED("VFS: do_open");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_open");
+
+ // Check if oflags is valid
+ if ((oflags & O_WRONLY) && (oflags & O_RDWR))
+ {
+ return -EINVAL;
+ }
+
+ // Get an empty file descriptor
+ int fd = -1;
+ long ret = get_empty_fd(&fd);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ // Check if the file is a directory
+ if (filename[strlen(filename) - 1] == '/' && (oflags & O_WRONLY))
+ {
+ return -EISDIR;
+ }
+
+ // Open the file
+ vnode_t *res_vnode = NULL;
+ vnode_t *base = curproc->p_cwd;
+ ret = namev_open(base, filename, oflags, S_IFREG, 0, &res_vnode);
+ if (ret < 0)
+ {
+ if (res_vnode != NULL)
+ {
+ vput(&res_vnode);
+ }
+ return ret;
+ }
+
+ // Check if the vnode is a directory
+ if (S_ISDIR(res_vnode->vn_mode) && ((oflags & O_WRONLY) || (oflags & O_RDWR)))
+ {
+ vput(&res_vnode);
+ return -EISDIR;
+ }
+
+ // Check if the vnode is a blockdev or chardev
+ if (S_ISCHR(res_vnode->vn_mode) && res_vnode->vn_dev.chardev == NULL)
+ {
+ vput(&res_vnode);
+ return -ENXIO;
+ }
+ if (S_ISBLK(res_vnode->vn_mode) && res_vnode->vn_dev.blockdev == NULL)
+ {
+ vput(&res_vnode);
+ return -ENXIO;
+ }
+
+ // Convert oflags to file access flags
+ int fmode = 0;
+ if (oflags & O_RDONLY)
+ {
+ fmode |= FMODE_READ;
+ }
+
+ if (oflags & O_WRONLY)
+ {
+ fmode |= FMODE_WRITE;
+ }
+ else
+ {
+ fmode |= FMODE_READ;
+ }
+
+ if (oflags & O_RDWR)
+ {
+ fmode |= FMODE_READ | FMODE_WRITE;
+ }
+
+ if (oflags & O_APPEND)
+ {
+ fmode |= FMODE_APPEND;
+ }
+ // Truncate the file if O_TRUNC is specified
+ if (oflags & O_TRUNC && S_ISREG(res_vnode->vn_mode))
+ {
+ vlock(res_vnode);
+ res_vnode->vn_ops->truncate_file(res_vnode);
+ vunlock(res_vnode);
+ }
+
+ // Create the file descriptor
+ file_t *file = fcreate(fd, res_vnode, fmode);
+ if (file == NULL)
+ {
+ vput(&res_vnode);
+ return -ENOMEM;
+ }
+
+ vput(&res_vnode);
+ return fd;
}
diff --git a/kernel/fs/s5fs/s5fs.c b/kernel/fs/s5fs/s5fs.c
index d1df3a5..2bcec7d 100644
--- a/kernel/fs/s5fs/s5fs.c
+++ b/kernel/fs/s5fs/s5fs.c
@@ -210,7 +210,56 @@ long s5fs_mount(fs_t *fs)
*/
static void s5fs_read_vnode(fs_t *fs, vnode_t *vn)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_read_vnode");
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_read_vnode");
+
+ // Get the s5_node from the vnode
+ s5_node_t *s5_node = VNODE_TO_S5NODE(vn);
+
+ // Get the inode
+ s5_inode_t *s5_inode = &s5_node->inode;
+ KASSERT(s5_inode);
+ // Update linkcount
+ s5_inode->s5_linkcount++;
+
+ // Get the inode from the disk
+ pframe_t *pf;
+ s5_get_file_disk_block(vn, S5_INODE_BLOCK(vn->vn_vno), S5_INODE_OFFSET(vn->vn_vno), 0, &pf);
+ memcpy(s5_inode, pf->pf_addr, sizeof(s5_inode_t));
+
+ // Release the disk block
+ s5_release_disk_block(&pf);
+
+ // Initialize the dirtied_inode field
+ s5_node->dirtied_inode = 0;
+
+ // Initialize the vnode fields
+ // lan and inode trivial
+ vn->vn_len = s5_inode->s5_un.s5_size;
+ vn->vn_i = s5_inode;
+ // Initialize the vn_ops and mode field based on the type
+ switch (s5_inode->s5_type)
+ {
+ case S5_TYPE_DIR: // directory
+ vn->vn_ops = &s5fs_dir_vops;
+ vn->vn_mode = S_IFDIR;
+ break;
+ case S5_TYPE_CHR: // character device
+ vn->vn_ops = NULL;
+ vn->vn_mode = S_IFCHR;
+ vn->vn_devid = s5_inode->s5_indirect_block;
+ break;
+ case S5_TYPE_BLK: // block device
+ vn->vn_ops = NULL;
+ vn->vn_mode = S_IFBLK;
+ vn->vn_devid = s5_inode->s5_indirect_block;
+ break;
+ case S5_TYPE_DATA: // regular file
+ vn->vn_ops = &s5fs_file_vops;
+ vn->vn_mode = S_IFREG;
+ break;
+ default: // unknown type
+ panic("Unknown inode type %d\n", s5_inode->s5_type);
+ }
}
/* Clean up the inode corresponding to the given vnode.
@@ -226,7 +275,38 @@ static void s5fs_read_vnode(fs_t *fs, vnode_t *vn)
*/
static void s5fs_delete_vnode(fs_t *fs, vnode_t *vn)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_delete_vnode");
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_delete_vnode");
+
+ // Get the s5_node from the vnode
+ s5_node_t *s5_node = VNODE_TO_S5NODE(vn);
+
+ // Get the inode
+ s5_inode_t *s5_inode = &s5_node->inode;
+ KASSERT(s5_inode);
+
+ // Update linkcount
+ s5_inode->s5_linkcount--;
+
+ // Check if the inode is no longer in use
+ if (s5_inode->s5_linkcount == 0)
+ {
+ // Free the inode and return
+ s5_free_inode(FS_TO_S5FS(fs), s5_inode->s5_number);
+ return;
+ }
+
+ // Check if the inode is dirty
+ if (s5_node->dirtied_inode)
+ {
+ // Write the inode back to disk and return
+ pframe_t *pf;
+ s5_get_file_disk_block(vn, S5_INODE_BLOCK(vn->vn_vno), S5_INODE_OFFSET(vn->vn_vno), 1, &pf);
+ memcpy(pf->pf_addr, s5_inode, sizeof(s5_inode_t));
+ s5_release_disk_block(&pf);
+ return;
+ }
+
+ // Do nothing if the inode is unchanged
}
/*
@@ -285,8 +365,8 @@ static void s5fs_sync(fs_t *fs)
static ssize_t s5fs_read(vnode_t *vnode, size_t pos, void *buf, size_t len)
{
KASSERT(!S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_read");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_read");
+ return s5_read_file(VNODE_TO_S5NODE(vnode), pos, buf, len);
}
/* Wrapper around s5_write_file. */
@@ -294,8 +374,8 @@ static ssize_t s5fs_write(vnode_t *vnode, size_t pos, const void *buf,
size_t len)
{
KASSERT(!S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_write");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_write");
+ return s5_write_file(VNODE_TO_S5NODE(vnode), pos, buf, len);
}
/*
@@ -334,8 +414,68 @@ static long s5fs_mknod(struct vnode *dir, const char *name, size_t namelen,
int mode, devid_t devid, struct vnode **out)
{
KASSERT(S_ISDIR(dir->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_mknod");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_mknod");
+
+ // Check if the mode is not supported
+ if (mode != S_IFCHR && mode != S_IFBLK && mode != S_IFREG)
+ {
+ return -ENOTSUP;
+ }
+
+ // Allocate a new inode, get its properties first
+ s5fs_t *s5fs = VNODE_TO_S5FS(dir);
+ uint16_t type;
+ if(S_ISCHR(mode))
+ {
+ type = S5_TYPE_CHR;
+ }
+ else if(S_ISBLK(mode))
+ {
+ type = S5_TYPE_BLK;
+ }
+ else if(S_ISREG(mode))
+ {
+ type = S5_TYPE_DATA;
+ }
+ else
+ {
+ panic("Invalid s5 mode!\n");
+ }
+
+ // Allocate the inode
+ s5_inode_t *s5_inode;
+ long inode_num = s5_alloc_inode(s5fs, type, &s5_inode);
+ // Check if the inode allocation failed
+ if (inode_num < 0)
+ {
+ return inode_num;
+ }
+
+ // Create the new vnode
+ vnode_t *new_vnode = vget(dir->vn_fs, inode_num);
+ // Check if the vnode creation failed
+ if (!new_vnode)
+ {
+ s5_free_inode(s5fs, inode_num);
+ return -ENOMEM;
+ }
+
+ // Set the devid for the new inode
+ s5_inode->s5_indirect_block = devid;
+
+ // Link the new inode/vnode to the parent directory
+ long link = s5_link(dir, name, namelen, new_vnode);
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link;
+ }
+
+ // Set the out pointer to the new vnode
+ *out = new_vnode;
+ return 0;
}
/* Search for a given entry within a directory.
@@ -356,8 +496,37 @@ static long s5fs_mknod(struct vnode *dir, const char *name, size_t namelen,
long s5fs_lookup(vnode_t *dir, const char *name, size_t namelen,
vnode_t **ret)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_lookup");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_lookup");
+
+ // Find the directory entry
+ size_t filepos;
+ long ino = s5_find_dirent(VNODE_TO_S5NODE(dir), name, namelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Check if the found vnode is the directory itself
+ if (ino == dir->vn_vno)
+ {
+ vref(dir);
+ *ret = dir;
+ return 0;
+ }
+
+
+ // If not, get the found vnode
+ vnode_t *found_vnode = vget(dir->vn_fs, ino);
+ // Check if the vnode creation failed
+ if (!found_vnode)
+ {
+ return -ENOMEM;
+ }
+ // Increment the reference count
+ // vref(found_vnode);
+ *ret = found_vnode;
+ return 0;
}
/* Wrapper around s5_link.
@@ -369,8 +538,15 @@ static long s5fs_link(vnode_t *dir, const char *name, size_t namelen,
vnode_t *child)
{
KASSERT(S_ISDIR(dir->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_link");
- return -1;
+ //NOT_YET_IMPLEMENTED("S5FS: s5fs_link");
+
+ // Check if the child is a directory
+ if (S_ISDIR(child->vn_mode))
+ {
+ return -EISDIR;
+ }
+
+ return s5_link(VNODE_TO_S5NODE(dir), name, namelen, VNODE_TO_S5NODE(child));
}
/* Remove the directory entry in dir corresponding to name and namelen.
@@ -389,8 +565,29 @@ static long s5fs_unlink(vnode_t *dir, const char *name, size_t namelen)
KASSERT(S_ISDIR(dir->vn_mode) && "should be handled at the VFS level");
KASSERT(!name_match(".", name, namelen));
KASSERT(!name_match("..", name, namelen));
- NOT_YET_IMPLEMENTED("S5FS: s5fs_unlink");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_unlink");
+
+ // Find the directory entry
+ s5_node_t *s5_node = VNODE_TO_S5NODE(dir);
+ size_t filepos;
+ long ino = s5_find_dirent(s5_node, name, namelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Get the found vnode
+ vnode_t *child = vget_locked(dir->vn_fs, ino);
+ KASSERT(!S_ISDIR(child->vn_mode) && "should be handled at the VFS level");
+
+ // Remove the directory entry
+ s5_remove_dirent(s5_node, name, namelen, filepos);
+ // Check to see if this failed (TODO: ask in hours)
+
+ // Release the vnode
+ vput_locked(&child);
+ return 0;
}
/* Change the name or location of a file.
@@ -441,8 +638,98 @@ static long s5fs_rename(vnode_t *olddir, const char *oldname, size_t oldnamelen,
vnode_t *newdir, const char *newname,
size_t newnamelen)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_rename");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_rename");
+
+ // Check if the new name is too long
+ if (newnamelen >= NAME_LEN)
+ {
+ return -ENAMETOOLONG;
+ }
+
+ // Find the old directory entry
+ s5_node_t *s5_node = VNODE_TO_S5NODE(olddir);
+ size_t filepos;
+ long ino = s5_find_dirent(s5_node, oldname, oldnamelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Get the found vnode
+ vnode_t *child = vget_locked(olddir->vn_fs, ino);
+ KASSERT(!S_ISDIR(child->vn_mode) && "should be handled at the VFS level");
+
+ // Check if the new directory is not a directory
+ if (!S_ISDIR(newdir->vn_mode))
+ {
+ vput_locked(&child);
+ return -ENOTDIR;
+ }
+
+ // Find the new directory entry
+ s5_node_t *new_s5_node = VNODE_TO_S5NODE(newdir);
+ size_t new_filepos;
+ long new_ino = s5_find_dirent(new_s5_node, newname, newnamelen, &new_filepos);
+ // Check if the directory entry is new
+ if (new_ino == -ENOENT)
+ {
+ // Link the new directory
+ long link = s5_link(new_s5_node, newname, newnamelen, VNODE_TO_S5NODE(child));
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput_locked(&child);
+ return link;
+ }
+
+ // Remove the old directory entry
+ s5_remove_dirent(s5_node, oldname, oldnamelen, filepos);
+ // Check if this failed (TODO: ask in hours)
+
+ return link;
+ }
+
+ // Else, the new directory entry was found and we need to replace it
+ // Get the new found vnode
+ vnode_t *new_child = vget_locked(newdir->vn_fs, new_ino);
+ KASSERT(!S_ISDIR(new_child->vn_mode) && "should be handled at the VFS level");
+
+ // Check if the old and new vnodes are the same
+ if (child->vn_vno == new_child->vn_vno)
+ {
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return 0;
+ }
+
+ // Check if the new vnode is a directory
+ if (S_ISDIR(new_child->vn_mode))
+ {
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return -EISDIR;
+ }
+
+ // Remove the new directory entry
+ s5_remove_dirent(new_s5_node, newname, newnamelen, new_filepos);
+ // Check if this failed (TODO: ask in hours)
+ // Link the new directory
+ long link = s5_link(new_s5_node, newname, newnamelen, VNODE_TO_S5NODE(child));
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return link;
+ }
+
+ // Remove the old directory entry
+ s5_remove_dirent(s5_node, oldname, oldnamelen, filepos);
+
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return link;
}
/* Create a directory.
@@ -473,8 +760,66 @@ static long s5fs_mkdir(vnode_t *dir, const char *name, size_t namelen,
struct vnode **out)
{
KASSERT(S_ISDIR((dir)->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_mkdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_mkdir");
+
+ // Allocate a new inode, get its properties first
+ s5fs_t *s5fs = VNODE_TO_S5FS(dir);
+ uint16_t type = S5_TYPE_DIR;
+
+ // Allocate the inode
+ s5_inode_t *s5_inode;
+ long inode_num = s5_alloc_inode(s5fs, type, &s5_inode);
+ // Check if the inode allocation failed
+ if (inode_num < 0)
+ {
+ return inode_num;
+ }
+
+ // Create the new vnode
+ vnode_t *new_vnode = vget(dir->vn_fs, inode_num);
+ // Check if the vnode creation failed
+ if (!new_vnode)
+ {
+ s5_free_inode(s5fs, inode_num);
+ return -ENOMEM;
+ }
+
+ // Create the "." entry
+ long link = s5_link(VNODE_TO_S5NODE(new_vnode), ".", 1, VNODE_TO_S5NODE(new_vnode));
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link;
+ }
+
+ // Create the ".." entry
+ long link2 = s5_link(VNODE_TO_S5NODE(new_vnode), "..", 2, VNODE_TO_S5NODE(dir));
+ // Check if the link operation failed
+ if (link2 < 0)
+ {
+ s5_remove_dirent(VNODE_TO_S5NODE(new_vnode), ".", 1, 0);
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link2;
+ }
+
+ // Link the new directory to the parent
+ long link3 = s5_link(VNODE_TO_S5NODE(dir), name, namelen, VNODE_TO_S5NODE(new_vnode));
+ // Check if the link operation failed
+ if (link3 < 0)
+ {
+ s5_remove_dirent(VNODE_TO_S5NODE(new_vnode), ".", 1, 0);
+ s5_remove_dirent(VNODE_TO_S5NODE(new_vnode), "..", 2, 0);
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link3;
+ }
+
+ // Set the out pointer to the new vnode
+ *out = new_vnode;
+ return 0;
}
/* Remove a directory.
@@ -495,8 +840,50 @@ static long s5fs_rmdir(vnode_t *parent, const char *name, size_t namelen)
KASSERT(!name_match(".", name, namelen));
KASSERT(!name_match("..", name, namelen));
KASSERT(S_ISDIR(parent->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_rmdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_rmdir");
+
+ // Find the directory entry
+ s5_node_t *s5_node = VNODE_TO_S5NODE(parent);
+ size_t filepos;
+ long ino = s5_find_dirent(s5_node, name, namelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Get the found vnode
+ vnode_t *child = vget_locked(parent->vn_fs, ino);
+ KASSERT(S_ISDIR(child->vn_mode) && "should be handled at the VFS level");
+
+ // Check if this is a directory
+ if (!S_ISDIR(child->vn_mode))
+ {
+ vput_locked(&child);
+ return -ENOTDIR;
+ }
+
+ // Check if the directory is not empty
+ if (child->vn_len > 2 * sizeof(s5_dirent_t))
+ {
+ vput_locked(&child);
+ return -ENOTEMPTY;
+ }
+
+ // Remove the "." entry
+ s5_remove_dirent(VNODE_TO_S5NODE(child), ".", 1, 0);
+ // Check if this failed (TODO: ask in hours)
+
+ // Remove the ".." entry
+ s5_remove_dirent(VNODE_TO_S5NODE(child), "..", 2, 0);
+
+ // Remove the directory entry
+ s5_remove_dirent(s5_node, name, namelen, filepos);
+ // Check if this failed (TODO: ask in hours)
+
+ // Release the vnode
+ vput_locked(&child);
+ return 0;
}
/* Read a directory entry.
@@ -518,8 +905,23 @@ static long s5fs_rmdir(vnode_t *parent, const char *name, size_t namelen)
static long s5fs_readdir(vnode_t *vnode, size_t pos, struct dirent *d)
{
KASSERT(S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_readdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_readdir");
+
+ // Read the directory entry
+ s5_dirent_t s5_dirent;
+ long bytes_read = s5_read_file(VNODE_TO_S5NODE(vnode), pos, &s5_dirent, sizeof(s5_dirent_t));
+ // Check if the read operation failed
+ if (bytes_read < 0)
+ {
+ return bytes_read;
+ }
+
+ // Initialize the dirent
+ d->d_ino = s5_dirent.s5d_inode;
+ d->d_off = pos + sizeof(s5_dirent_t);
+ memcpy(d->d_name, s5_dirent.s5d_name, 28);
+
+ return bytes_read;
}
/* Get file status.
@@ -542,8 +944,29 @@ static long s5fs_readdir(vnode_t *vnode, size_t pos, struct dirent *d)
*/
static long s5fs_stat(vnode_t *vnode, stat_t *ss)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_stat");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_stat");
+
+ // Get the inode
+ s5_inode_t *s5_inode = &VNODE_TO_S5NODE(vnode)->inode;
+
+ // Initialize the stat struct
+ ss->st_blocks = s5_inode_blocks(s5_inode);
+ ss->st_mode = vnode->vn_mode;
+ ss->st_rdev = vnode->vn_devid;
+ ss->st_ino = s5_inode->s5_number;
+ ss->st_nlink = s5_inode->s5_linkcount;
+ ss->st_blksize = S5_BLOCK_SIZE;
+ ss->st_size = s5_inode->s5_un.s5_size;
+ ss->st_dev = VNODE_TO_S5FS(vnode)->s5f_bdev->bd_id;
+
+ // Set all other fields to 0
+ ss->st_uid = 0;
+ ss->st_gid = 0;
+ ss->st_atime = 0;
+ ss->st_mtime = 0;
+ ss->st_ctime = 0;
+
+ return 0;
}
/**
diff --git a/kernel/fs/vfs_syscall.c b/kernel/fs/vfs_syscall.c
index e05fe5f..205020b 100644
--- a/kernel/fs/vfs_syscall.c
+++ b/kernel/fs/vfs_syscall.c
@@ -26,8 +26,58 @@
*/
ssize_t do_read(int fd, void *buf, size_t len)
{
- NOT_YET_IMPLEMENTED("VFS: do_read");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_read");
+
+ // Check if the file descriptor is valid
+ if (fd < 0 || fd >= NFILES)
+ {
+ return -EBADF;
+ }
+
+ // fget the file
+ file_t *file = fget(fd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+
+ // Check if the file is open for reading
+ if (!(file->f_mode & FMODE_READ))
+ {
+ fput(&file);
+ return -EBADF;
+ }
+
+ // Check if the file is a directory
+ if (S_ISDIR(file->f_vnode->vn_mode))
+ {
+ fput(&file);
+ return -EISDIR;
+ }
+
+ // Check is read is valid
+ if (file->f_vnode->vn_ops->read == NULL)
+ {
+ fput(&file);
+ return -EISDIR;
+ }
+
+ // Read the file
+ vlock(file->f_vnode);
+ ssize_t bytes_read = file->f_vnode->vn_ops->read(file->f_vnode, file->f_pos, buf, len);
+ vunlock(file->f_vnode);
+
+ // Check if the read was successful
+ if (bytes_read < 0)
+ {
+ fput(&file);
+ return bytes_read;
+ }
+
+ // Update the file position
+ file->f_pos += bytes_read;
+ fput(&file);
+ return bytes_read;
}
/*
@@ -46,8 +96,60 @@ ssize_t do_read(int fd, void *buf, size_t len)
*/
ssize_t do_write(int fd, const void *buf, size_t len)
{
- NOT_YET_IMPLEMENTED("VFS: do_write");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_write");
+
+ // Check if the file descriptor is valid
+ if (fd < 0 || fd >= NFILES)
+ {
+ return -EBADF;
+ }
+
+ // dbg(DBG_PRINT, "VFS: do_write: fd = %d, len = %ld\n", fd, len);
+
+ // fget the file
+ file_t *file = fget(fd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+
+ // Check if the file is open for writing
+ if (!(file->f_mode & FMODE_WRITE))
+ {
+ fput(&file);
+ return -EBADF;
+ }
+
+ // Check is write is valid
+ if (file->f_vnode->vn_ops->write == NULL)
+ {
+ fput(&file);
+ return -EISDIR;
+ }
+
+ // Move the file position to the end of the file if the file is open in append mode
+ if (file->f_mode & FMODE_APPEND)
+ {
+ vlock(file->f_vnode);
+ file->f_pos = file->f_vnode->vn_len;
+ vunlock(file->f_vnode);
+ }
+
+ // Write the file
+ vlock(file->f_vnode);
+ ssize_t bytes_written = file->f_vnode->vn_ops->write(file->f_vnode, file->f_pos, buf, len);
+ vunlock(file->f_vnode);
+
+ // Check if the write was successful
+ if (bytes_written < 0)
+ {
+ fput(&file);
+ return bytes_written;
+ }
+
+ file->f_pos += bytes_written;
+ fput(&file);
+ return bytes_written;
}
/*
@@ -63,8 +165,26 @@ ssize_t do_write(int fd, const void *buf, size_t len)
*/
long do_close(int fd)
{
- NOT_YET_IMPLEMENTED("VFS: do_close");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_close");
+
+ // Check if the file descriptor is valid
+ if (fd < 0 || fd >= NFILES)
+ {
+ return -EBADF;
+ }
+
+ // fget the file
+ file_t *file = fget(fd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+
+ // Close the file
+ fput(&file);
+ fput(&curproc->p_files[fd]);
+
+ return 0;
}
/*
@@ -78,8 +198,33 @@ long do_close(int fd)
*/
long do_dup(int fd)
{
- NOT_YET_IMPLEMENTED("VFS: do_dup");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_dup");
+
+ // Check if the file descriptor is valid
+ if (fd < 0 || fd >= NFILES)
+ {
+ return -EBADF;
+ }
+
+ // fget the file
+ file_t *file = fget(fd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+
+ // Get an empty file descriptor
+ int new_fd = -1;
+ long ret = get_empty_fd(&new_fd);
+ if (ret < 0)
+ {
+ fput(&file);
+ return ret;
+ }
+
+ // Copy the file
+ curproc->p_files[new_fd] = file;
+ return new_fd;
}
/*
@@ -94,8 +239,40 @@ long do_dup(int fd)
*/
long do_dup2(int ofd, int nfd)
{
- NOT_YET_IMPLEMENTED("VFS: do_dup2");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_dup2");
+
+ // Check if the file descriptors are valid
+ if (ofd < 0 || ofd >= NFILES || nfd < 0 || nfd >= NFILES)
+ {
+ return -EBADF;
+ }
+ // Check if the file descriptors are the same
+ if (!curproc->p_files[ofd])
+ {
+ return -EBADF;
+ }
+ if (ofd == nfd)
+ {
+ return nfd;
+ }
+
+ // fget the file
+ file_t *file = fget(ofd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+ // figet the new file
+ file_t *new_file = fget(nfd);
+ if (new_file != NULL)
+ {
+ fput(&new_file);
+ do_close(nfd);
+ }
+
+ // Copy the file
+ curproc->p_files[nfd] = file;
+ return nfd;
}
/*
@@ -117,8 +294,20 @@ long do_dup2(int ofd, int nfd)
*/
long do_mknod(const char *path, int mode, devid_t devid)
{
- NOT_YET_IMPLEMENTED("VFS: do_mknod");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_mknod");
+
+ // Check if the mode is valid
+ if (mode != S_IFCHR && mode != S_IFBLK && mode != S_IFREG)
+ {
+ return -EINVAL;
+ }
+
+ // get vnode
+ vnode_t *vnode = NULL;
+ vnode_t *base = curproc->p_cwd;
+ long ret = namev_open(base, path, O_CREAT, mode, devid, &vnode);
+ vput(&vnode);
+ return ret;
}
/*
@@ -143,8 +332,53 @@ long do_mknod(const char *path, int mode, devid_t devid)
*/
long do_mkdir(const char *path)
{
- NOT_YET_IMPLEMENTED("VFS: do_mkdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_mkdir");
+
+ // Call namev_dir() to find the parent of the directory to be created
+ size_t len = 0;
+ const char *name = NULL;
+ vnode_t *dir = NULL;
+ long ret = namev_dir(curproc->p_cwd, path, &dir, &name, &len);
+ dbg(DBG_TEST, "VFS: do_mkdir: namev_dir returned %ld\n", ret);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+ if (!S_ISDIR(dir->vn_mode))
+ {
+ vput(&dir);
+ return -ENOTDIR;
+ }
+ if (len > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ vnode_t *base = NULL;
+ vlock(dir);
+ ret = namev_lookup(dir, name, len, &base);
+ vunlock(dir);
+ // Check if the directory already exists
+ if (ret >= 0)
+ {
+ vput(&base);
+ vput(&dir);
+ return -EEXIST;
+ }
+
+ // Create the directory
+ long err = dir->vn_ops->mkdir(dir, name, len, &base);
+ if (err < 0)
+ {
+ vput(&dir);
+ return err;
+ }
+
+ vput(&base);
+ vput(&dir);
+ return 0;
}
/*
@@ -165,8 +399,75 @@ long do_mkdir(const char *path)
*/
long do_rmdir(const char *path)
{
- NOT_YET_IMPLEMENTED("VFS: do_rmdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_rmdir");
+
+ // Call namev_dir() to find the parent of the directory to be removed
+ size_t len = 0;
+ const char *name = NULL;
+ vnode_t *dir = NULL;
+ long ret = namev_dir(curproc->p_cwd, path, &dir, &name, &len);
+
+ // Check if the directory is valid
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ // Check if the directory is a directory
+ if (!S_ISDIR(dir->vn_mode))
+ {
+ vput(&dir);
+ return -ENOTDIR;
+ }
+
+ // Check if name is too long
+ if (len > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ // Check if the directory is empty
+ if (len == 1 && name[0] == '.')
+ {
+ vput(&dir);
+ return -EINVAL;
+ }
+ if (len == 2 && name[0] == '.' && name[1] == '.')
+ {
+ vput(&dir);
+ return -ENOTEMPTY;
+ }
+
+ // Remove the directory
+ vnode_t *base = NULL;
+ vlock(dir);
+ ret = namev_lookup(dir, name, len, &base);
+ vunlock(dir);
+
+ // Check if the directory exists
+ if (ret < 0)
+ {
+ vput(&dir);
+ return ret;
+ }
+
+ // Check if the directory is a directory
+ if (!S_ISDIR(base->vn_mode))
+ {
+ vput(&base);
+ vput(&dir);
+ return -ENOTDIR;
+ }
+
+ vput(&base);
+ // Remove the directory
+ vlock(dir);
+ ret = dir->vn_ops->rmdir(dir, name, len);
+ vunlock(dir);
+ vput(&dir);
+
+ return ret;
}
/*
@@ -183,8 +484,62 @@ long do_rmdir(const char *path)
*/
long do_unlink(const char *path)
{
- NOT_YET_IMPLEMENTED("VFS: do_unlink");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_unlink");
+
+ // Call namev_dir() to find the parent of the file to be unlinked
+ size_t len = 0;
+ const char *name = NULL;
+ vnode_t *dir = NULL;
+ long ret = namev_dir(curproc->p_cwd, path, &dir, &name, &len);
+
+ // Check if the directory is valid
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ // Check if the directory is a directory
+ if (!S_ISDIR(dir->vn_mode))
+ {
+ vput(&dir);
+ return -ENOTDIR;
+ }
+
+ // Check if name is too long
+ if (len > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ // Remove the link
+ vnode_t *base = NULL;
+ vlock(dir);
+ ret = namev_lookup(dir, name, len, &base);
+ vunlock(dir);
+
+ // Check if the file exists
+ if (ret < 0)
+ {
+ vput(&dir);
+ return ret;
+ }
+ // Check if the file is a directory
+ if (S_ISDIR(base->vn_mode))
+ {
+ vput(&base);
+ vput(&dir);
+ return -EPERM;
+ }
+
+ vput(&base);
+ // Call the unlink operation
+ vlock(dir);
+ ret = dir->vn_ops->unlink(dir, name, len);
+ vunlock(dir);
+ vput(&dir);
+
+ return ret;
}
/*
@@ -206,8 +561,56 @@ long do_unlink(const char *path)
*/
long do_link(const char *oldpath, const char *newpath)
{
- NOT_YET_IMPLEMENTED("VFS: do_link");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_link");
+
+ // Resolve the oldpath
+ vnode_t *old_vnode = NULL;
+
+ long ret = namev_resolve(NULL, oldpath, &old_vnode);
+ // Check if the oldpath is valid
+ if (ret < 0)
+ {
+ return ret;
+ }
+ // Check if the oldpath is a directory
+ if (S_ISDIR(old_vnode->vn_mode))
+ {
+ vput(&old_vnode);
+ return -EPERM;
+ }
+
+ // Get the directory of the newpath
+ size_t len = 0;
+ const char *name = NULL;
+ vnode_t *dir = NULL;
+ ret = namev_dir(curproc->p_cwd, newpath, &dir, &name, &len);
+ // Check if the directory is valid
+ if (ret < 0)
+ {
+ vput(&old_vnode);
+ return ret;
+ }
+ // Check if the directory is a directory
+ if (!S_ISDIR(dir->vn_mode))
+ {
+ vput(&old_vnode);
+ return -ENOTDIR;
+ }
+ // Check if name is too long
+ if (len > NAME_LEN)
+ {
+ vput(&old_vnode);
+ return -ENAMETOOLONG;
+ }
+
+ // Lock the vnodes and call link
+ vlock_in_order(old_vnode, dir);
+ ret = dir->vn_ops->link(old_vnode, dir, name, len);
+ vunlock_in_order(old_vnode, dir);
+
+ vput(&old_vnode);
+ vput(&dir);
+ return ret;
}
/* Rename a file or directory.
@@ -231,7 +634,7 @@ long do_link(const char *oldpath, const char *newpath)
* projects this is harder and you will get no extra credit (but you
* will get our admiration). Please make sure the normal version works first.
* Steps:
- * 1. namev_dir oldpath --> olddir vnode
+ * 1. oldpath --> olddir vnode
* 2. namev_dir newpath --> newdir vnode
* 3. Lock the global filesystem `vnode_rename_mutex`
* 4. Lock the olddir and newdir in ancestor-first order (see `vlock_in_order`)
@@ -244,8 +647,57 @@ long do_link(const char *oldpath, const char *newpath)
*/
long do_rename(const char *oldpath, const char *newpath)
{
- NOT_YET_IMPLEMENTED("VFS: do_rename");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_rename");
+
+ // Get the old directory
+ size_t old_len = 0;
+ const char *old_name = NULL;
+ vnode_t *old_dir = NULL;
+ long ret = namev_dir(curproc->p_cwd, oldpath, &old_dir, &old_name, &old_len);
+ // Check if the old directory is valid
+ if (ret < 0)
+ {
+ return ret;
+ }
+ // Check if the old directory is a directory
+ if (!S_ISDIR(old_dir->vn_mode))
+ {
+ return -ENOTDIR;
+ }
+
+ // Get the new directory
+ size_t new_len = 0;
+ const char *new_name = NULL;
+ vnode_t *new_dir = NULL;
+ ret = namev_dir(curproc->p_cwd, newpath, &new_dir, &new_name, &new_len);
+ // Check if the new directory is valid
+ if (ret < 0)
+ {
+ vput(&old_dir);
+ return ret;
+ }
+ // Check if the new directory is a directory
+ if (!S_ISDIR(new_dir->vn_mode))
+ {
+ vput(&old_dir);
+ return -ENOTDIR;
+ }
+
+ // Check if the names are too long
+ if (old_len > NAME_LEN || new_len > NAME_LEN)
+ {
+ vput(&old_dir);
+ return -ENAMETOOLONG;
+ }
+
+ // Lock the vnodes and call rename
+ vlock_in_order(old_dir, new_dir);
+ ret = old_dir->vn_ops->rename(old_dir, old_name, old_len, new_dir, new_name, new_len);
+ vunlock_in_order(old_dir, new_dir);
+
+ vput(&old_dir);
+ vput(&new_dir);
+ return ret;
}
/* Set the current working directory to the directory represented by path.
@@ -262,8 +714,27 @@ long do_rename(const char *oldpath, const char *newpath)
*/
long do_chdir(const char *path)
{
- NOT_YET_IMPLEMENTED("VFS: do_chdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_chdir");
+
+ // Resolve the path
+ vnode_t *vnode = NULL;
+ long ret = namev_resolve(NULL, path, &vnode);
+ // Check if the path is valid
+ if (ret < 0)
+ {
+ return ret;
+ }
+ // Check if the path is a directory
+ if (!S_ISDIR(vnode->vn_mode))
+ {
+ vput(&vnode);
+ return -ENOTDIR;
+ }
+
+ // Set the current working directory
+ vput(&curproc->p_cwd);
+ curproc->p_cwd = vnode;
+ return 0;
}
/*
@@ -282,8 +753,43 @@ long do_chdir(const char *path)
*/
ssize_t do_getdent(int fd, struct dirent *dirp)
{
- NOT_YET_IMPLEMENTED("VFS: do_getdent");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_getdent");
+
+ // Check if the file descriptor is valid
+ if (fd < 0 || fd >= NFILES)
+ {
+ return -EBADF;
+ }
+
+ // fget the file
+ file_t *file = fget(fd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+
+ // Check if the file is a directory
+ if (!S_ISDIR(file->f_vnode->vn_mode))
+ {
+ fput(&file);
+ return -ENOTDIR;
+ }
+
+ // Read the directory entry
+ vlock(file->f_vnode);
+ ssize_t bytes_read = file->f_vnode->vn_ops->readdir(file->f_vnode, file->f_pos, dirp);
+ vunlock(file->f_vnode);
+ // Check if the read was successful
+ if (bytes_read <= 0)
+ {
+ fput(&file);
+ return bytes_read;
+ }
+
+ // Update the file position
+ file->f_pos += bytes_read;
+ fput(&file);
+ return sizeof(dirent_t);
}
/*
@@ -301,8 +807,51 @@ ssize_t do_getdent(int fd, struct dirent *dirp)
*/
off_t do_lseek(int fd, off_t offset, int whence)
{
- NOT_YET_IMPLEMENTED("VFS: do_lseek");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_lseek");
+
+ // Check if the file descriptor is valid
+ if (fd < 0 || fd >= NFILES)
+ {
+ return -EBADF;
+ }
+
+ // fget the file
+ file_t *file = fget(fd);
+ if (file == NULL)
+ {
+ return -EBADF;
+ }
+
+ // Check if the whence is valid
+ off_t new_pos = 0;
+ switch (whence)
+ {
+ case SEEK_SET:
+ new_pos = offset;
+ break;
+ case SEEK_CUR:
+ new_pos = file->f_pos + offset;
+ break;
+ case SEEK_END:
+ new_pos = file->f_vnode->vn_len + offset;
+ break;
+ default:
+ fput(&file);
+ return -EINVAL;
+ }
+
+ // Check if the new position is negative
+ if (new_pos < 0)
+ {
+ fput(&file);
+ return -EINVAL;
+ }
+
+ // Update the file position
+ file->f_pos = new_pos;
+ off_t pos = file->f_pos;
+ fput(&file);
+ return pos;
}
/* Use buf to return the status of the file represented by path.
@@ -312,8 +861,25 @@ off_t do_lseek(int fd, off_t offset, int whence)
*/
long do_stat(const char *path, stat_t *buf)
{
- NOT_YET_IMPLEMENTED("VFS: do_stat");
- return -1;
+ // NOT_YET_IMPLEMENTED("VFS: do_stat");
+
+ // Resolve the path
+ vnode_t *vnode = NULL;
+ long ret = namev_resolve(NULL, path, &vnode);
+
+ // Check if the path is valid
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ // Get the status of the file
+ vlock(vnode);
+ ret = vnode->vn_ops->stat(vnode, buf);
+ vunlock(vnode);
+ vput(&vnode);
+
+ return ret;
}
#ifdef __MOUNTING__
diff --git a/kernel/fs/vnode_specials.c b/kernel/fs/vnode_specials.c
index cd21549..fb6df0b 100644
--- a/kernel/fs/vnode_specials.c
+++ b/kernel/fs/vnode_specials.c
@@ -110,8 +110,20 @@ static long special_file_stat(vnode_t *file, stat_t *ss)
static ssize_t chardev_file_read(vnode_t *file, size_t pos, void *buf,
size_t count)
{
- NOT_YET_IMPLEMENTED("VFS: chardev_file_read");
- return 0;
+ // NOT_YET_IMPLEMENTED("VFS: chardev_file_read");
+
+ // check if the vnode represetns a chardev
+ if (file->vn_dev.chardev == NULL)
+ {
+ return -ENXIO;
+ }
+
+ // Unlock the file and call read
+ vunlock(file);
+ ssize_t ret = file->vn_dev.chardev->cd_ops->read(file->vn_dev.chardev, pos, buf, count);
+ vlock(file);
+
+ return ret;
}
/*
@@ -125,8 +137,20 @@ static ssize_t chardev_file_read(vnode_t *file, size_t pos, void *buf,
static long chardev_file_write(vnode_t *file, size_t pos, const void *buf,
size_t count)
{
- NOT_YET_IMPLEMENTED("VFS: chardev_file_write");
- return 0;
+ // NOT_YET_IMPLEMENTED("VFS: chardev_file_write");
+
+ // check if the vnode represents a chardev
+ if (file->vn_dev.chardev == NULL)
+ {
+ return -ENXIO;
+ }
+
+ // Unlock the file and call write
+ vunlock(file);
+ long ret = file->vn_dev.chardev->cd_ops->write(file->vn_dev.chardev, pos, buf, count);
+ vlock(file);
+
+ return ret;
}
/*
diff --git a/kernel/main/kmain.c b/kernel/main/kmain.c
index 8dcb7e5..4c49b9a 100644
--- a/kernel/main/kmain.c
+++ b/kernel/main/kmain.c
@@ -86,6 +86,8 @@ void kmain()
for (size_t i = 0; i < sizeof(init_funcs) / sizeof(init_funcs[0]); i++)
init_funcs[i]();
+ dbg(DBG_PROC, "Starting init process\n");
+
initproc_start();
panic("\nReturned to kmain()\n");
}
@@ -156,7 +158,35 @@ static void *initproc_run(long arg1, void *arg2)
make_devices();
#endif
- NOT_YET_IMPLEMENTED("PROCS: initproc_run");
+ // NOT_YET_IMPLEMENTED("PROCS: initproc_run");
+
+ // print all the threads in the system
+ // list_iterate(&curproc->p_threads, thread, kthread_t, kt_plink)
+ // {
+ // dbg(DBG_THR, "Thread: %s\n", thread->kt_proc->p_name);
+ // }
+
+ // proctest_main(0, NULL);
+
+ // dbg(DBG_PROC, "%s", "In main thread!\n");
+
+#ifdef __DRIVERS__
+ // driverstest_main(0, NULL);
+ char name[32] = {0};
+ for (long i = 0; i < __NTERMS__; i++)
+ {
+ snprintf(name, sizeof(name), "kshell%ld", i);
+ proc_t *proc = proc_create("ksh");
+ kthread_t *thread = kthread_create(proc, kshell_proc_run, i, NULL);
+ sched_make_runnable(thread);
+ }
+#endif
+
+ // see if there are any children to wait for
+ while (do_waitpid(-1, 0, 0) != -ECHILD)
+ {
+ // do nothing
+ }
return NULL;
}
@@ -175,7 +205,21 @@ static void *initproc_run(long arg1, void *arg2)
*/
void initproc_start()
{
- NOT_YET_IMPLEMENTED("PROCS: initproc_start");
+ // NOT_YET_IMPLEMENTED("PROCS: initproc_start");
+
+ dbg(DBG_PROC, "Setting up the initial process and preparing it to run\n");
+
+ proc_t *init_proc = proc_create("init");
+ KASSERT(init_proc != NULL);
+
+ kthread_t *init_thread = kthread_create(init_proc, initproc_run, 0, NULL);
+ KASSERT(init_thread != NULL);
+
+ sched_make_runnable(init_thread);
+
+ context_make_active(&curcore.kc_ctx); // start the scheduler
+ // context_make_active(&init_thread->kt_ctx);
+ // TODO: ask about how the core is linked to scheduler
}
void initproc_finish()
diff --git a/kernel/proc/kthread.c b/kernel/proc/kthread.c
index 1104b31..d066dac 100644
--- a/kernel/proc/kthread.c
+++ b/kernel/proc/kthread.c
@@ -68,8 +68,43 @@ void kthread_init()
kthread_t *kthread_create(proc_t *proc, kthread_func_t func, long arg1,
void *arg2)
{
- NOT_YET_IMPLEMENTED("PROCS: kthread_create");
- return NULL;
+ // NOT_YET_IMPLEMENTED("PROCS: kthread_create");
+ dbg(DBG_THR, "ATTEMPT to create a new thread with proc name=%s, id=%d\n", proc->p_name, proc->p_pid);
+
+ kthread_t *new_thread = slab_obj_alloc(kthread_allocator);
+ if (new_thread == NULL)
+ {
+ return NULL;
+ }
+ new_thread->kt_state = KT_NO_STATE;
+
+ new_thread->kt_kstack = alloc_stack();
+ if (new_thread->kt_kstack == NULL)
+ {
+ slab_obj_free(kthread_allocator, new_thread);
+ return NULL;
+ }
+
+ new_thread->kt_proc = proc;
+ context_setup(&new_thread->kt_ctx, func, arg1, arg2, new_thread->kt_kstack,
+ DEFAULT_STACK_SIZE, new_thread->kt_proc->p_pml4);
+
+ // give default values to rest of struct
+ new_thread->kt_retval = NULL;
+ new_thread->kt_errno = 0;
+ new_thread->kt_cancelled = 0;
+ new_thread->kt_wchan = NULL;
+ list_link_init(&new_thread->kt_plink);
+ list_link_init(&new_thread->kt_qlink);
+ list_init(&new_thread->kt_mutexes);
+ new_thread->kt_recent_core = 0;
+
+ // put this thread on the process's thread list
+ list_insert_tail(&proc->p_threads, &new_thread->kt_plink);
+
+ dbg(DBG_THR, "SUCCESFULLY created a new THREAD with proc name=%s, id=%d\n", proc->p_name, proc->p_pid);
+
+ return new_thread;
}
/*
@@ -124,7 +159,15 @@ void kthread_destroy(kthread_t *thr)
*/
void kthread_cancel(kthread_t *thr, void *retval)
{
- NOT_YET_IMPLEMENTED("PROCS: kthread_cancel");
+ // NOT_YET_IMPLEMENTED("PROCS: kthread_cancel");
+ KASSERT(thr != curthr);
+
+ // FIXME: ask about the use of check_curthr_cancelled() in syscall_handler()
+ int status = (int) retval;
+ dbg(DBG_THR, "Cancelling thread with proc name=%s, id=%d, status=%d\n",
+ thr->kt_proc->p_name, thr->kt_proc->p_pid, status);
+ thr->kt_retval = retval;
+ sched_cancel(thr);
}
/*
@@ -132,5 +175,8 @@ void kthread_cancel(kthread_t *thr, void *retval)
*/
void kthread_exit(void *retval)
{
- NOT_YET_IMPLEMENTED("PROCS: kthread_exit");
+ // NOT_YET_IMPLEMENTED("PROCS: kthread_exit");
+ dbg(DBG_THR, "Exiting thread with proc name=%s, id=%d, status=%d\n",
+ curthr->kt_proc->p_name, curthr->kt_proc->p_pid, (int) retval);
+ proc_thread_exiting(retval);
}
diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c
index f8453bd..91be451 100644
--- a/kernel/proc/proc.c
+++ b/kernel/proc/proc.c
@@ -174,8 +174,68 @@ proc_t *proc_lookup(pid_t pid)
*/
proc_t *proc_create(const char *name)
{
- NOT_YET_IMPLEMENTED("PROCS: proc_create");
- return NULL;
+ // NOT_YET_IMPLEMENTED("PROCS: proc_create");
+ int proc_pid = _proc_getid();
+ if (proc_pid == -1)
+ {
+ return NULL;
+ }
+
+ proc_t *proc = (proc_t *)slab_obj_alloc(proc_allocator);
+ if (proc == NULL)
+ {
+ return NULL;
+ }
+
+ pml4_t *pml4 = pt_create();
+ if (pml4 == NULL)
+ {
+ slab_obj_free(proc_allocator, proc);
+ return NULL;
+ }
+
+ // if successful building memory, fill the struct
+ proc->p_pid = proc_pid;
+ strncpy(proc->p_name, name, PROC_NAME_LEN);
+ proc->p_pml4 = pml4;
+ proc->p_state = PROC_RUNNING; // TODO: check if this is correct
+ proc->p_status = 0;
+ list_init(&proc->p_threads);
+ list_init(&proc->p_children);
+ list_link_init(&proc->p_child_link);
+ list_link_init(&proc->p_list_link);
+ sched_queue_init(&proc->p_wait);
+
+
+ if (proc_pid == PID_INIT)
+ {
+ // if it's init proc, set to global variable
+ proc_initproc = proc;
+ }
+
+ proc->p_pproc = proc_initproc;
+ list_insert_tail(&curproc->p_children, &proc->p_child_link);
+ list_insert_tail(&proc_list, &proc->p_list_link);
+ dbg(DBG_PROC, "SUCESSFULLY created PROCESS name=%s & pid =%d\n", name, proc_pid);
+
+#ifdef __VFS__
+ // clone and ref the files from curproc
+ for (int fd = 0; fd < NFILES; fd++)
+ {
+ proc->p_files[fd] = NULL;
+ }
+ if (proc->p_pid != PID_INIT)
+ {
+ proc->p_cwd = proc->p_pproc->p_cwd;
+ vref(proc->p_cwd);
+ }
+ else
+ {
+ proc->p_cwd = NULL;
+ }
+#endif
+
+ return proc;
}
/*
@@ -197,7 +257,52 @@ proc_t *proc_create(const char *name)
*/
void proc_cleanup(long status)
{
- NOT_YET_IMPLEMENTED("PROCS: proc_cleanup");
+ // NOT_YET_IMPLEMENTED("PROCS: proc_cleanup");
+ dbg(DBG_PROC, "proc_cleanup called on proc with pid=%d with exit status=%d\n", curproc->p_pid, status);
+
+#ifdef __VFS__
+ for (int fd = 0; fd < NFILES; fd++)
+ {
+ if (curproc->p_files[fd])
+ {
+ fput(curproc->p_files + fd);
+ }
+ }
+ if (curproc->p_cwd)
+ {
+ vput(&curproc->p_cwd);
+ }
+#endif
+
+
+ if (curproc->p_pid == PID_INIT)
+ {
+ // if it's init proc, shutdown weenix
+ initproc_finish(status);
+ }
+
+ // if it's not init proc, reparent child processes to parent proc
+ if (curproc->p_pproc == NULL)
+ {
+ panic("parent process not found in proc_pleanup");
+ }
+
+ list_link_t *link;
+ list_iterate(&curproc->p_children, child, proc_t, p_child_link)
+ {
+ // remove & insert to init process
+ list_insert_tail(&curproc->p_pproc->p_children, &child->p_child_link);
+ list_remove(&child->p_child_link);
+ child->p_pproc = proc_initproc;
+ }
+
+ // update state and status
+ curproc->p_state = PROC_DEAD;
+ // if (curthr->kt_cancelled) {
+ // curproc->p_status = curthr->kt_retval;
+ // } else {
+ curproc->p_status = status;
+ // }
}
/*
@@ -216,7 +321,13 @@ void proc_cleanup(long status)
*/
void proc_thread_exiting(void *retval)
{
- NOT_YET_IMPLEMENTED("PROCS: proc_thread_exiting");
+ // NOT_YET_IMPLEMENTED("PROCS: proc_thread_exiting");
+
+ proc_cleanup((long)retval);
+ curthr->kt_state = KT_EXITED;
+
+ sched_broadcast_on(&curproc->p_pproc->p_wait);
+ sched_switch(0);
}
/*
@@ -227,7 +338,16 @@ void proc_thread_exiting(void *retval)
*/
void proc_kill(proc_t *proc, long status)
{
- NOT_YET_IMPLEMENTED("PROCS: proc_kill");
+ // NOT_YET_IMPLEMENTED("PROCS: proc_kill");
+ dbg(DBG_PROC, "proc_kill called on proc with pid=%d\n", proc->p_pid);
+
+ KASSERT(proc != curproc && "proc_kill called on curproc");
+
+ list_iterate(&proc->p_threads, thr, kthread_t, kt_plink)
+ {
+ dbg(DBG_PROC, "cancelling thread on proc with pid=%d with status=%ld\n", proc->p_pid, status);
+ kthread_cancel(thr, (void *)status);
+ }
}
/*
@@ -241,7 +361,18 @@ void proc_kill(proc_t *proc, long status)
*/
void proc_kill_all()
{
- NOT_YET_IMPLEMENTED("PROCS: proc_kill_all");
+ // NOT_YET_IMPLEMENTED("PROCS: proc_kill_all");
+
+ list_iterate(&proc_list, p, proc_t, p_list_link)
+ {
+ if (p->p_pid != curproc->p_pid && p->p_pid != PID_IDLE)
+ {
+ proc_kill(p, -1);
+ }
+ }
+
+ // kill this proc
+ do_exit(0);
}
/*
@@ -265,7 +396,9 @@ void proc_destroy(proc_t *proc)
for (int fd = 0; fd < NFILES; fd++)
{
if (proc->p_files[fd])
+ {
fput(proc->p_files + fd);
+ }
}
if (proc->p_cwd)
{
@@ -278,7 +411,7 @@ void proc_destroy(proc_t *proc)
vmmap_destroy(&proc->p_vmmap);
#endif
- dbg(DBG_THR, "destroying P%d\n", proc->p_pid);
+ dbg(DBG_PROC, "destroying P%d\n", proc->p_pid);
KASSERT(proc->p_pml4);
pt_destroy(proc->p_pml4);
@@ -316,7 +449,77 @@ void proc_destroy(proc_t *proc)
*/
pid_t do_waitpid(pid_t pid, int *status, int options)
{
- NOT_YET_IMPLEMENTED("PROCS: do_waitpid");
+ // NOT_YET_IMPLEMENTED("PROCS: do_waitpid");
+
+ if (pid == 0 || options != 0 || pid < -1)
+ {
+ return -ENOTSUP;
+ }
+
+ dbg(DBG_PROC, "waiting for pid=%d\n", pid);
+
+ if (pid > 0)
+ {
+ proc_t *child = proc_lookup(pid);
+ if (child == NULL || child->p_pproc != curproc)
+ {
+ return -ECHILD;
+ }
+
+ // sleep until this specific child process exits
+ while (child->p_state != PROC_DEAD)
+ {
+ dbg(DBG_PROC, "waiting for specific child, calling sched_sleep_on\n");
+ sched_sleep_on(&curproc->p_wait);
+ }
+
+ if (status != NULL)
+ {
+ dbg(DBG_PROC, "setting status to %d\n", child->p_status);
+ *status = child->p_status;
+ }
+ list_remove(&child->p_child_link);
+ proc_destroy(child);
+
+ dbg(DBG_PROC, "exited child pid=%d\n", child->p_pid);
+ return pid;
+ }
+ else if (pid == -1)
+ {
+ if (list_empty(&curproc->p_children))
+ {
+ return -ECHILD;
+ }
+
+
+ while(1) {
+ proc_t *child;
+ list_iterate(&curproc->p_children, child, proc_t, p_child_link)
+ {
+ if (child->p_state == PROC_DEAD)
+ {
+ dbg(DBG_PROC, "found a dead thread with pid=%d\n", child->p_pid);
+ if (status != NULL)
+ {
+ dbg(DBG_PROC, "setting status to %d\n", child->p_status);
+ *status = child->p_status;
+ }
+ int child_pid = child->p_pid;
+
+
+ list_remove(&child->p_child_link);
+ proc_destroy(child);
+
+ dbg(DBG_PROC, "exited child pid=%d\n", child->p_pid);
+ return child_pid;
+ }
+ }
+
+ dbg(DBG_PROC, "waiting for any child, calling sched_sleep_on\n");
+ sched_sleep_on(&curproc->p_wait);
+ }
+ }
+
return 0;
}
@@ -325,7 +528,11 @@ pid_t do_waitpid(pid_t pid, int *status, int options)
*/
void do_exit(long status)
{
- NOT_YET_IMPLEMENTED("PROCS: do_exit");
+ // NOT_YET_IMPLEMENTED("PROCS: do_exit");
+
+ dbg(DBG_PROC, "proc exiting with pid=%d and status=%ld\n", curproc->p_pid, status);
+
+ kthread_exit((void *)status);
}
/*==========
diff --git a/kernel/proc/sched.c b/kernel/proc/sched.c
index 3bc20c8..b422ef1 100644
--- a/kernel/proc/sched.c
+++ b/kernel/proc/sched.c
@@ -162,7 +162,23 @@ void sched_init(void)
*/
long sched_cancellable_sleep_on(ktqueue_t *queue)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_cancellable_sleep_on");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_cancellable_sleep_on");
+
+ KASSERT(queue != NULL);
+
+ if (curthr->kt_cancelled)
+ {
+ return -EINTR;
+ }
+
+ curthr->kt_state = KT_SLEEP_CANCELLABLE;
+ sched_switch(queue);
+
+ if (curthr->kt_cancelled)
+ {
+ return -EINTR;
+ }
+
return 0;
}
@@ -174,7 +190,15 @@ long sched_cancellable_sleep_on(ktqueue_t *queue)
*/
void sched_cancel(kthread_t *thr)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_cancel");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_cancel");
+
+ thr->kt_cancelled = 1;
+
+ if (thr->kt_state == KT_SLEEP_CANCELLABLE)
+ {
+ ktqueue_remove(thr->kt_wchan, thr);
+ sched_make_runnable(thr);
+ }
}
/*
@@ -210,7 +234,21 @@ void sched_cancel(kthread_t *thr)
*/
void sched_switch(ktqueue_t *queue)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_switch");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_switch");
+
+ // KASSERT(intr_getipl == IPL_HIGH);
+ intr_disable();
+ int oldIPL = intr_setipl(IPL_LOW); // allow interrupts to wake up the idling core
+
+ KASSERT(curthr->kt_state != KT_ON_CPU);
+
+ curcore.kc_queue = queue;
+ last_thread_context = &curthr->kt_ctx;
+
+ context_switch(&curthr->kt_ctx, &curcore.kc_ctx);
+
+ intr_enable();
+ intr_setipl(oldIPL);
}
/*
@@ -236,7 +274,23 @@ void sched_yield()
*/
void sched_make_runnable(kthread_t *thr)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_make_runnable");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_make_runnable");
+
+ dbg(DBG_SCHED, "Making thread with proc pid %d runnable\n in thread\n", thr->kt_proc->p_pid);
+ if (curthr)
+ {
+ dbg(DBG_SCHED, "I did this ^^ with thread %d\n", curthr->kt_proc->p_pid);
+ } else {
+ dbg(DBG_SCHED, "I did this ^^ with a null thread!\n");
+ }
+
+ KASSERT(thr != curthr);
+ KASSERT(thr->kt_state != KT_RUNNABLE);
+
+ int oldIPL = intr_setipl(IPL_HIGH);
+ thr->kt_state = KT_RUNNABLE;
+ ktqueue_enqueue(&kt_runq, thr);
+ intr_setipl(oldIPL);
}
/*
@@ -255,7 +309,12 @@ void sched_make_runnable(kthread_t *thr)
*/
void sched_sleep_on(ktqueue_t *q)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_sleep_on");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_sleep_on");
+
+ int oldIPL = intr_setipl(IPL_HIGH);
+ curthr->kt_state = KT_SLEEP;
+ sched_switch(q);
+ intr_setipl(oldIPL);
}
/*
@@ -271,7 +330,26 @@ void sched_sleep_on(ktqueue_t *q)
*/
void sched_wakeup_on(ktqueue_t *q, kthread_t **ktp)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_wakeup_on");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_wakeup_on");
+
+ if (sched_queue_empty(q) || q == NULL)
+ {
+ if (ktp)
+ {
+ *ktp = NULL;
+ }
+ return;
+ }
+
+ int oldIPL = intr_setipl(IPL_HIGH); // don't allow interrupts while modifying the queue
+ kthread_t *thr = ktqueue_dequeue(q);
+ if (ktp)
+ {
+ *ktp = thr;
+ }
+
+ sched_make_runnable(thr);
+ intr_setipl(oldIPL);
}
/*
@@ -279,7 +357,12 @@ void sched_wakeup_on(ktqueue_t *q, kthread_t **ktp)
*/
void sched_broadcast_on(ktqueue_t *q)
{
- NOT_YET_IMPLEMENTED("PROCS: sched_broadcast_on");
+ // NOT_YET_IMPLEMENTED("PROCS: sched_broadcast_on");
+
+ while (!sched_queue_empty(q))
+ {
+ sched_make_runnable(ktqueue_dequeue(q));
+ }
}
/*===============
@@ -360,6 +443,9 @@ void core_switch()
KASSERT(mapped_paddr == expected_paddr);
curthr = next_thread;
+
+ dbg(DBG_THR, "Switching to curthr thread %d\n", curthr->kt_proc->p_pid);
+
curthr->kt_state = KT_ON_CPU;
curproc = curthr->kt_proc;
context_switch(&curcore.kc_ctx, &curthr->kt_ctx);
diff --git a/kernel/test/proctest.c b/kernel/test/proctest.c
index 31067cd..e8b8ba8 100644
--- a/kernel/test/proctest.c
+++ b/kernel/test/proctest.c
@@ -21,6 +21,7 @@ void *test_func(long arg1, void *arg2)
test_assert(arg1 == proc_as_arg->p_pid, "Arguments are not set up correctly");
test_assert(proc_as_arg->p_state == PROC_RUNNING, "Process state is not running");
test_assert(list_empty(&proc_as_arg->p_children), "There should be no child processes");
+ dbg(DBG_TEST, "Process %s is running\n", proc_as_arg->p_name);
return NULL;
}
@@ -36,6 +37,7 @@ void test_termination()
int status;
while (do_waitpid(-1, &status, 0) != -ECHILD)
{
+ dbg(DBG_TEST, "Waiting for child process to terminate\n");
test_assert(status == 0, "Returned status not set correctly");
count++;
}
@@ -43,6 +45,265 @@ void test_termination()
"Expected: %d, Actual: %d number of processes have been cleaned up\n", num_procs_created, count);
}
+/*
+ Tests the edge cases of do_waitpid.
+ 1. No child processes to wait for
+ 2. Waiting on a specific process id
+ 3. Waiting any child process (-1)
+*/
+void test_do_waitpid()
+{
+ int status;
+ int ret = do_waitpid(-1, &status, 0);
+ test_assert(ret == -ECHILD, "No child processes to wait for");
+
+ proc_t *new_proc1 = proc_create("proc test 1");
+ kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1);
+ sched_make_runnable(new_kthread1);
+
+ proc_t *new_proc2 = proc_create("proc test 2");
+ kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2);
+ sched_make_runnable(new_kthread2);
+
+ proc_t *new_proc3 = proc_create("proc test 3");
+ kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3);
+ sched_make_runnable(new_kthread3);
+
+ // wait for a specific process id
+ ret = do_waitpid(new_proc2->p_pid, &status, 0);
+ test_assert(ret == new_proc2->p_pid, "Waiting for a specific process id");
+
+ dbg(DBG_TEST, "Successfully removed proc 2 with pid: %d\n", new_proc2->p_pid);
+
+ // wait for any child process
+ ret = do_waitpid(-1, &status, 0);
+ test_assert(ret == new_proc1->p_pid, "Waiting for any child process");
+ dbg(DBG_TEST, "Successfully removed proc 1 with pid: %d\n", new_proc1->p_pid);
+ ret = do_waitpid(-1, &status, 0);
+ test_assert(ret == new_proc3->p_pid, "Waiting for any child process");
+ dbg(DBG_TEST, "Successfully removed proc 3 with pid: %d\n", new_proc3->p_pid);
+
+ ret = do_waitpid(-1, &status, 0);
+ test_assert(ret == -ECHILD, "No child processes to wait for");
+}
+
+/*
+ Tests proc_kill_all on a single process.
+*/
+void *test_func_kill(long arg1, void *arg2)
+{
+ // function to be called by the process
+ proc_t *proc_as_arg = (proc_t *)arg2;
+ test_assert(arg1 == proc_as_arg->p_pid, "Arguments are not set up correctly");
+ test_assert(proc_as_arg->p_state == PROC_RUNNING, "Process state is not running");
+ test_assert(list_empty(&proc_as_arg->p_children), "There should be no child processes");
+ dbg(DBG_TEST, "Process with pid=%d is running and calling proc_kill_all\n", proc_as_arg->p_pid);
+ proc_kill_all();
+ return NULL;
+}
+void test_proc_kill_all()
+{
+ proc_t *new_proc_kill = proc_create("proc test kill");
+ kthread_t *new_kthread_kill = kthread_create(new_proc_kill, test_func_kill, new_proc_kill->p_pid, new_proc_kill);
+ sched_make_runnable(new_kthread_kill);
+
+ proc_t *new_proc1 = proc_create("proc test 1");
+ kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1);
+ sched_make_runnable(new_kthread1);
+
+ proc_t *new_proc2 = proc_create("proc test 2");
+ kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2);
+ sched_make_runnable(new_kthread2);
+
+ // wait on the process that calls proc_kill_all
+ int status;
+ int ret = do_waitpid(new_proc_kill->p_pid, &status, 0);
+ test_assert(ret == new_proc_kill->p_pid, "Waiting for a specific process id");
+ dbg(DBG_TEST, "Successfully removed proc_kill with pid: %d\n", new_proc_kill->p_pid);
+
+ // wait for all the threads to finish
+ int count = 0;
+ while (do_waitpid(-1, &status, 0) != -ECHILD)
+ {
+ dbg(DBG_TEST, "Waiting for child process to terminate\n");
+ test_assert(status == 0, "Returned status not set correctly");
+ count++;
+ }
+ test_assert(count == 2,
+ "Expected: %d, Actual: %d number of processes have been cleaned up\n", 2, count);
+
+}
+
+/*
+ Test to run several threads and processes concurrently.
+*/
+void test_multiple()
+{
+ int num_procs_created = 0;
+ proc_t *new_proc1 = proc_create("proc test 1");
+ kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1);
+ num_procs_created++;
+ sched_make_runnable(new_kthread1);
+
+ proc_t *new_proc2 = proc_create("proc test 2");
+ kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2);
+ num_procs_created++;
+ sched_make_runnable(new_kthread2);
+
+ proc_t *new_proc3 = proc_create("proc test 3");
+ kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3);
+ num_procs_created++;
+ sched_make_runnable(new_kthread3);
+
+ // print all of the threads in the system
+ list_iterate(&curproc->p_threads, thread, kthread_t, kt_plink)
+ {
+ dbg(DBG_THR, "Thread: %s\n", thread->kt_proc->p_name);
+ }
+
+ // wait for all the threads to finish
+ int count = 0;
+ int status;
+ while (do_waitpid(-1, &status, 0) != -ECHILD)
+ {
+ dbg(DBG_TEST, "Waiting for child process to terminate\n");
+ test_assert(status == 0, "Returned status not set correctly");
+ count++;
+ }
+ test_assert(count == num_procs_created,
+ "Expected: %d, Actual: %d number of processes have been cleaned up\n", num_procs_created, count);
+}
+
+/*
+ Test that creates several child processes and forces them to terminate out of order.
+ Then it checks to see if the processes are cleaned up correctly.
+*/
+void *func_forever_yielding(long arg1, void *arg2)
+{
+ // function that always just yields
+ // goal is that it is cleaned up by a different process
+ while (1)
+ {
+ // have a way to stop the thread
+ int *to_stop = (int *)arg2;
+ if (*to_stop)
+ {
+ dbg(DBG_TEST, "Thread with pid=%d is stopping yield loop\n", curproc->p_pid);
+ do_exit(1); // return different status code
+ break;
+ }
+ // wait a bit
+ for (int i = 0; i < 1000000; i++)
+ {
+ ;
+ }
+ dbg(DBG_TEST, "Thread with pid=%d is yielding on it's parent\n", curproc->p_pid);
+ sched_yield();
+ }
+ return NULL;
+}
+void test_out_of_order_termination()
+{
+ // create a yielding proc that will last for a long time
+ proc_t *new_proc1 = proc_create("yieling proc 1");
+ int stop_func_1 = 0;
+ kthread_t *new_kthread1 = kthread_create(new_proc1, func_forever_yielding, new_proc1->p_pid, &stop_func_1);
+ sched_make_runnable(new_kthread1);
+
+ proc_t *new_proc2 = proc_create("yielding proc 2");
+ int stop_func_2 = 0;
+ kthread_t *new_kthread2 = kthread_create(new_proc2, func_forever_yielding, new_proc2->p_pid, &stop_func_2);
+ sched_make_runnable(new_kthread2);
+
+ proc_t *new_proc3 = proc_create("proc test 3");
+ kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3);
+ sched_make_runnable(new_kthread3);
+
+ proc_t *new_proc4 = proc_create("proc test 4");
+ kthread_t *new_kthread4 = kthread_create(new_proc4, test_func, new_proc4->p_pid, new_proc4);
+ sched_make_runnable(new_kthread4);
+
+ // let to first and second procs go and yield
+ sched_yield();
+
+ // the first and second proc should yield to the third proc and fourth proc
+ // which will return here to the init proc
+ test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc");
+ test_assert(new_proc3->p_state == PROC_DEAD, "Third proc should be dead");
+ test_assert(new_proc4->p_state == PROC_DEAD, "Fourth proc should be dead");
+
+ // destroy procs 3 and 4
+
+ // terminate the second proc
+ stop_func_2 = 1;
+ sched_yield();
+
+ // we should return back to the init proc
+ test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc");
+ test_assert(new_proc2->p_state == PROC_DEAD, "Second proc should be dead");
+
+ // terminate the first proc
+ stop_func_1 = 1;
+ sched_yield();
+
+ // we should return back to the init proc
+ test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc");
+ test_assert(new_proc1->p_state == PROC_DEAD, "First proc should be dead");
+
+ // clean up proc 2 using do_waitpid, with specific status 1
+ int status;
+ int ret = do_waitpid(new_proc2->p_pid, &status, 0);
+ test_assert(ret == new_proc2->p_pid, "wrong specific process id");
+ test_assert(status == 1, "Returned status not set correctly");
+ test_assert(new_proc2->p_status == 1, "Status not set correctly");
+
+ // clean up the rest of the dead procs using do_waitpid
+ int count = 0;
+ while ((ret = do_waitpid(-1, &status, 0)) != -ECHILD)
+ {
+ dbg(DBG_TEST, "found child with pid=%d that needs to be cleaned up\n", ret);
+ if (ret == new_proc1->p_pid) {
+ test_assert(status == 1, "Returned status not set correctly for proc 1 with pid=%d", new_proc1->p_pid);
+ } else {
+ test_assert(status == 0, "Returned status not set correctly");
+ }
+ count++;
+ }
+ test_assert(count == 3,
+ "Expected: %d, Actual: %d number of processes have been cleaned up\n", 3, count);
+}
+
+/*
+ Test threads' cancellation fields.
+*/
+void *did_run_func(long arg1, void *arg2)
+{
+ // arg2 is a boolean flag that indicated this ran
+ *(int *)arg2 = 1;
+ return NULL;
+}
+void test_cancellation()
+{
+ proc_t *new_proc1 = proc_create("proc test 1");
+ int did_run = 0;
+ kthread_t *new_kthread1 = kthread_create(new_proc1, did_run_func, new_proc1->p_pid, (void *)&did_run);
+ test_assert(new_kthread1->kt_cancelled == 0, "Thread should not be cancelled");
+ sched_make_runnable(new_kthread1);
+
+ proc_kill(new_proc1, 1);
+
+ test_assert(new_kthread1->kt_cancelled == 1, "Thread should be cancelled");
+
+ // wait for the thread to finish
+ int status;
+ int ret = do_waitpid(new_proc1->p_pid, &status, 0);
+ test_assert(ret != -ECHILD, "Should have found the process");
+ test_assert(ret == new_proc1->p_pid, "Should have found the correct process");
+ test_assert(status == 1, "Returned status not set correctly");
+ test_assert(did_run == 0, "Thread should not have run if it was cancelled");
+}
+
+
long proctest_main(long arg1, void *arg2)
{
dbg(DBG_TEST, "\nStarting Procs tests\n");
@@ -51,6 +312,16 @@ long proctest_main(long arg1, void *arg2)
// Add more tests here!
// We highly recommend looking at section 3.8 on the handout for help!
+ dbg(DBG_TEST, "\nStarting test_multiple\n");
+ test_multiple();
+ dbg(DBG_TEST, "\nStarting test_do_waitpid\n");
+ test_do_waitpid();
+ dbg(DBG_TEST, "\nStarting test_proc_kill_all\n");
+ test_proc_kill_all();
+ dbg(DBG_TEST, "\nStarting test_out_of_order_termination\n");
+ test_out_of_order_termination();
+ dbg(DBG_TEST, "\nStarting test_cancellation\n");
+ test_cancellation();
test_fini();
return 0;