Compare commits

...

40 Commits

Author SHA1 Message Date
59e7a64f8e Only check user pages rather than all bytes in-between, for known-size pointers 2024-11-12 15:48:22 +00:00
cf4bf90cbb Implement user pointer checking for C strings 2024-11-12 15:34:45 +00:00
9a6abab95e Check access to user memory using page fault method (via get_user and put_user). 2024-11-12 15:00:16 +00:00
44f6a85163 Add get_user and put_user provided by spec. 2024-11-12 14:50:53 +00:00
83e044cf68 Let kernel handle its own page faults 2024-11-12 14:50:53 +00:00
Demetriades, Themis
7d9900c6d8 Merge branch 'exec-missing-validation' into 'master'
Add validation to check for missing files in exec() args

See merge request lab2425_autumn/pintos_22!36
2024-11-11 23:25:26 +00:00
EDiasAlberto
72afecfbda Add validation to check for missing files in exec() args 2024-11-11 23:10:02 +00:00
Demetriades, Themis
9e692ced9e Merge branch 'userprog-merge' into 'master'
Update variable references between conflicting merges to refer to the same...

See merge request lab2425_autumn/pintos_22!35
2024-11-11 22:59:45 +00:00
Demetriades, Themis
f194fa1fa8 Merge branch 'userprog-merge' into 'master'
Implement complete stack initialization, process_wait, and all system calls correctly except exec

See merge request lab2425_autumn/pintos_22!34
2024-11-11 22:56:28 +00:00
Themis Demetriades
e8713e26c6 Update variable references between conflicting merges to refer to the same data in stack initialization 2024-11-11 22:51:19 +00:00
Demetriades, Themis
47c3657b65 Merge branch 'process-wait' into 'userprog-merge'
Fix race-condition in process result (memory leak), fix infinite loop in donors_list

See merge request lab2425_autumn/pintos_22!33
2024-11-11 22:24:39 +00:00
Demetriades, Themis
4eafdf10ba Merge branch 'read-only-exec' into 'userprog-merge'
Combine syscall code with final stack initialization code

See merge request lab2425_autumn/pintos_22!32
2024-11-11 22:23:20 +00:00
EDiasAlberto
98a5818406 add file_deny_write and file_allow_write to process creation and exiting to make executable read-only 2024-11-11 17:55:24 +00:00
18c884234d Fix race-condition in process result (memory leak), fix infinite loop in donors_list 2024-11-11 17:35:49 +00:00
EDiasAlberto
5b1e4b561b merge process-wait 2024-11-11 15:50:21 +00:00
EDiasAlberto
1bfd73b202 Comment syscall functions and handlers 2024-11-10 01:32:58 +00:00
7778e05aa4 Fix deadlock by release of lock and semaphore in the wrong order 2024-11-09 11:06:36 +00:00
sBubshait
115c650c55 Fix Bug in thread initialisation: only init hash if USERPROG is defined 2024-11-08 19:10:42 +00:00
sBubshait
e40794e672 Fix Bug in fd_get_file: In case fd not found, then returns NULL, w/ E 2024-11-08 16:48:19 +00:00
sBubshait
8912ef4660 Implement writing to file system files in the write system call, w/ E 2024-11-08 16:26:47 +00:00
EDiasAlberto
18694d7b62 Implement file reading syscall and fix fd validation w/ S. 2024-11-08 16:25:49 +00:00
sBubshait
2a1cc3c361 Implement filesize and tell system calls, w/ E 2024-11-08 16:14:53 +00:00
EDiasAlberto
3cfbe198e0 Implement syscall for seek w/ S. 2024-11-08 16:10:46 +00:00
EDiasAlberto
75bd3fbde0 Implement syscall for close() and fix typing bug in fd_get_file w/ S. 2024-11-08 16:02:51 +00:00
sBubshait
a80084e907 Fix Bug in fd_get_file declaration use open_file instead of file, w/ E 2024-11-08 15:54:28 +00:00
sBubshait
5424276603 Add a helper function to get a file from its descriptor (FD), w/ E 2024-11-08 15:50:48 +00:00
EDiasAlberto
92e93b8060 Implement syscall for file opening and refactor open_files initialisation in thread.c w/ S. 2024-11-08 15:33:47 +00:00
sBubshait
5bd94894e0 Update thread structure to add a hash table of open files and initialise it, w/ E 2024-11-08 15:13:04 +00:00
sBubshait
5bbe7a03c0 Add in syscall hash helper functions for open_file struct: fd_hash and fd_less, w/ E 2024-11-08 15:12:08 +00:00
sBubshait
b112824a64 Implement the exec system call through process_execute, w/ E 2024-11-08 14:41:27 +00:00
EDiasAlberto
dca9d8f5a3 Implement syscall for file removal w/ S. 2024-11-08 14:35:23 +00:00
EDiasAlberto
a8676f2e09 Implement syscall for file creation, with relevant locks w/ S. 2024-11-08 14:34:08 +00:00
sBubshait
26e38be761 Update validate_user_pointer to check if the ptr is mapped to a physical memory address, w/ E 2024-11-08 14:21:16 +00:00
6ed1ccd21e Fix process_result locking by acquiring in process_wait as well to prevent freeing memory too early 2024-11-08 10:50:10 +00:00
84fe637c7e Remove process_result lock since it is an invalid solution
TODO : synchronise process_result in another way
2024-11-08 09:16:18 +00:00
d95894085b Implement syscall_exec via process_execute 2024-11-08 09:15:22 +00:00
fde70dcf59 Implement process_wait. 2024-11-08 03:56:15 +00:00
7349b4e66f Fix typo list_insert -> list_push_back for thread.child_results 2024-11-08 03:55:34 +00:00
ec8547aec9 Implement creation of process results 2024-11-08 03:31:48 +00:00
ddcd59fdf8 Add child and own process result information to struct thread 2024-11-08 03:30:58 +00:00
8 changed files with 447 additions and 49 deletions

View File

@@ -170,6 +170,9 @@ list_insert (struct list_elem *before, struct list_elem *elem)
{ {
ASSERT (is_interior (before) || is_tail (before)); ASSERT (is_interior (before) || is_tail (before));
ASSERT (elem != NULL); ASSERT (elem != NULL);
// Sanity checks to prevent (some) loop lists
ASSERT (before != elem);
ASSERT (before->prev != elem);
elem->prev = before->prev; elem->prev = before->prev;
elem->next = before; elem->next = before;

View File

@@ -341,6 +341,7 @@ lock_release (struct lock *lock)
released, transfer the remaining orphaned donors to its donor list. */ released, transfer the remaining orphaned donors to its donor list. */
if (max_donor != NULL) if (max_donor != NULL)
{ {
list_remove (&max_donor->donor_elem);
while (!list_empty (&orphan_list)) while (!list_empty (&orphan_list))
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list)); list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
} }

View File

@@ -1,5 +1,6 @@
#include "threads/thread.h" #include "threads/thread.h"
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <stddef.h> #include <stddef.h>
#include <random.h> #include <random.h>
#include <stdio.h> #include <stdio.h>
@@ -9,12 +10,14 @@
#include "threads/flags.h" #include "threads/flags.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/intr-stubs.h" #include "threads/intr-stubs.h"
#include "threads/malloc.h"
#include "threads/palloc.h" #include "threads/palloc.h"
#include "threads/switch.h" #include "threads/switch.h"
#include "threads/synch.h" #include "threads/synch.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#ifdef USERPROG #ifdef USERPROG
#include "userprog/process.h" #include "userprog/process.h"
#include "userprog/syscall.h"
#endif #endif
/* Random value for struct thread's `magic' member. /* Random value for struct thread's `magic' member.
@@ -68,6 +71,7 @@ static void kernel_thread (thread_func *, void *aux);
static void idle (void *aux UNUSED); static void idle (void *aux UNUSED);
static struct thread *running_thread (void); static struct thread *running_thread (void);
static struct thread *next_thread_to_run (void); static struct thread *next_thread_to_run (void);
static void init_process_result (struct thread *t);
static void init_thread (struct thread *, const char *name, int nice, static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu); int priority, fp32_t recent_cpu);
static bool is_thread (struct thread *) UNUSED; static bool is_thread (struct thread *) UNUSED;
@@ -110,6 +114,7 @@ thread_init (void)
initial_thread_recent_cpu); initial_thread_recent_cpu);
initial_thread->status = THREAD_RUNNING; initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid (); initial_thread->tid = allocate_tid ();
initial_thread->result = NULL; /* Main thread cannot be waited for. */
} }
/* Starts preemptive thread scheduling by enabling interrupts. /* Starts preemptive thread scheduling by enabling interrupts.
@@ -236,6 +241,11 @@ thread_create (const char *name, int priority,
struct thread *parent_thread = thread_current (); struct thread *parent_thread = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu); init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
tid = t->tid = allocate_tid (); tid = t->tid = allocate_tid ();
init_process_result (t);
#ifdef USERPROG
hash_init (&t->open_files, fd_hash, fd_less, NULL);
#endif
/* Prepare thread for first run by initializing its stack. /* Prepare thread for first run by initializing its stack.
Do this atomically so intermediate values for the 'stack' Do this atomically so intermediate values for the 'stack'
@@ -259,6 +269,10 @@ thread_create (const char *name, int priority,
intr_set_level (old_level); intr_set_level (old_level);
/* No need to synchronise child_results since it is only ever accessed by one
thread. By the nature of increasing TIDs, this list is ordered. */
list_push_back (&parent_thread->child_results, &t->result->elem);
/* Add to run queue. */ /* Add to run queue. */
thread_unblock (t); thread_unblock (t);
@@ -632,6 +646,18 @@ is_thread (struct thread *t)
return t != NULL && t->magic == THREAD_MAGIC; return t != NULL && t->magic == THREAD_MAGIC;
} }
/* Allocate and initialise a process result for given thread. */
static void
init_process_result (struct thread *t)
{
struct process_result *result = malloc (sizeof (struct process_result));
result->tid = t->tid;
result->exit_status = t->exit_status;
lock_init (&result->lock);
sema_init (&result->sema, 0);
t->result = result;
}
/* Does basic initialization of T as a blocked thread named /* Does basic initialization of T as a blocked thread named
NAME. */ NAME. */
static void static void
@@ -660,6 +686,7 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
t->priority = t->base_priority; t->priority = t->base_priority;
t->exit_status = -1; t->exit_status = -1;
list_init (&t->child_results);
old_level = intr_disable (); old_level = intr_disable ();
list_push_back (&all_list, &t->allelem); list_push_back (&all_list, &t->allelem);

View File

@@ -2,8 +2,11 @@
#define THREADS_THREAD_H #define THREADS_THREAD_H
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <list.h> #include <list.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "threads/synch.h"
#include "threads/fixed-point.h" #include "threads/fixed-point.h"
/* States in a thread's life cycle. */ /* States in a thread's life cycle. */
@@ -29,6 +32,18 @@ typedef int tid_t;
#define NICE_DEFAULT 0 /* Default niceness. */ #define NICE_DEFAULT 0 /* Default niceness. */
#define NICE_MAX 20 /* Highest niceness. */ #define NICE_MAX 20 /* Highest niceness. */
/* A process result, synchronised between parent and child. */
struct process_result
{
tid_t tid; /* The tid of the child process. */
int exit_status; /* The exit status of the child process. Initially set
to -1, then to exit_status when child dies. */
struct lock lock; /* Lock the exit_status and sema. */
struct semaphore sema; /* Semaphore to signal the parent that the exit_status
has been set. */
struct list_elem elem; /* List element for the parent's children list. */
};
/* A kernel thread or user process. /* A kernel thread or user process.
Each thread structure is stored in its own 4 kB page. The Each thread structure is stored in its own 4 kB page. The
@@ -108,6 +123,12 @@ struct thread
int nice; /* Nice value for this thread */ int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */ fp32_t recent_cpu; /* Amount of time this process received */
/* Process wait properties. */
struct process_result *result; /* Result of the process. */
struct list child_results; /* List of children's of this thread
process results. */
struct file *exec_file; /* Thread's currently running file */
/* Shared between thread.c and synch.c. */ /* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */ struct list_elem elem; /* List element. */
@@ -116,6 +137,7 @@ struct thread
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */
struct hash open_files; /* Hash Table of FD -> Struct File */
#endif #endif
/* Owned by thread.c. */ /* Owned by thread.c. */

View File

@@ -145,6 +145,14 @@ page_fault (struct intr_frame *f)
write = (f->error_code & PF_W) != 0; write = (f->error_code & PF_W) != 0;
user = (f->error_code & PF_U) != 0; user = (f->error_code & PF_U) != 0;
/* Kernel page fault is further handled by the kernel itself. */
if (!user)
{
f->eip = (void *)f->eax;
f->eax = 0xffffffff;
return;
}
/* To implement virtual memory, delete the rest of the function /* To implement virtual memory, delete the rest of the function
body, and replace it with code that brings in the page to body, and replace it with code that brings in the page to
which fault_addr refers. */ which fault_addr refers. */

View File

@@ -1,6 +1,7 @@
#include "userprog/process.h" #include "userprog/process.h"
#include <debug.h> #include <debug.h>
#include <inttypes.h> #include <inttypes.h>
#include <list.h>
#include <round.h> #include <round.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -80,6 +81,10 @@ process_execute (const char *cmd)
of the process. */ of the process. */
char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr); char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr);
/* Validates that the current file to be executed is a valid file */
if (filesys_open (file_name) == NULL)
return TID_ERROR;
/* Create a new thread to execute the command, by initializing /* Create a new thread to execute the command, by initializing
it running the function 'start_process' with the appropriate it running the function 'start_process' with the appropriate
arguments. For details of arguments, see 'start_process'. */ arguments. For details of arguments, see 'start_process'. */
@@ -136,6 +141,13 @@ start_process (void *proc_start_data)
goto fail; goto fail;
} }
/* NOTE: Currently, the file being executed is closed in load () and then
reopened here. Because load is an exported public function, this
might be necessary. */
struct file *exec_file = filesys_open (data->file_name);
thread_current ()->exec_file = exec_file;
file_deny_write (exec_file);
/* Start the user process by simulating a return from an /* Start the user process by simulating a return from an
interrupt, implemented by intr_exit (in interrupt, implemented by intr_exit (in
threads/intr-stubs.S). Because intr_exit takes all of its threads/intr-stubs.S). Because intr_exit takes all of its
@@ -261,12 +273,39 @@ push_to_stack (void **esp, void *data, size_t data_size)
int int
process_wait (tid_t child_tid UNUSED) process_wait (tid_t child_tid UNUSED)
{ {
/* As a temporary wait, waiting will just put the thread to sleep for one struct process_result *child_result = NULL;
second (TIMER_FREQ = 100 ticks ~ 1 second). */ struct list_elem *e;
/* TODO: Implement process_wait () correctly. Remove the next line. */ struct thread *cur = thread_current ();
timer_sleep (TIMER_FREQ); for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results); e = list_next (e))
return 0; /* TODO: Change this too */ {
struct process_result *result
= list_entry (e, struct process_result, elem);
if (result->tid == child_tid)
{
child_result = result;
break;
}
/* List is ordered, allowing us to break early. */
else if (result->tid > child_tid)
break;
}
if (child_result == NULL)
return -1;
/* Wait for child to die. */
sema_down (&child_result->sema);
/* We need lock release in process_exit, so we need to acquire (and possibly
wait) for it here to ensure we don't free the lock memory before it is
released in process_exit. */
lock_acquire (&child_result->lock);
/* To prevent waiting for child twice, remove it from the list.
No need to use lock since this is the only thread with access to
the struct process_result now. */
list_remove (&child_result->elem);
int exit_status = child_result->exit_status;
lock_release (&child_result->lock);
free (child_result);
return exit_status;
} }
/* Free the current process's resources. */ /* Free the current process's resources. */
@@ -277,6 +316,49 @@ process_exit (void)
uint32_t *pd; uint32_t *pd;
printf ("%s: exit(%d)\n", cur->name, cur->exit_status); printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
file_close (cur->exec_file);
/* Update process result. */
if (cur->result != NULL)
{
lock_acquire (&cur->result->lock);
cur->result->exit_status = cur->exit_status;
/* Parent has died, child has to free the struct process_result * */
if (sema_try_down (&cur->result->sema))
{
lock_release (&cur->result->lock);
free (cur->result);
}
/* Parent is still alive and will be the one to free the
struct process_result *, and may be waiting so call sema_up */
else
{
sema_up (&cur->result->sema);
lock_release (&cur->result->lock);
}
}
/* Free child process results or signal parent's death. */
struct list_elem *e;
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results); e = list_next (e))
{
struct process_result *result
= list_entry (e, struct process_result, elem);
lock_acquire (&result->lock);
/* Child has died (and was not waited for). Free the result. */
if (sema_try_down (&result->sema))
{
lock_release (&result->lock);
free (result);
}
/* Child is still alive, signal via sema that parent has died. */
else
{
sema_up (&result->sema);
lock_release (&result->lock);
}
}
/* Destroy the current process's page directory and switch back /* Destroy the current process's page directory and switch back
to the kernel-only page directory. */ to the kernel-only page directory. */

View File

@@ -1,13 +1,31 @@
#include "userprog/syscall.h" #include "userprog/syscall.h"
#include "devices/shutdown.h" #include "devices/shutdown.h"
#include "devices/input.h" #include "devices/input.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/malloc.h"
#include "threads/thread.h" #include "threads/thread.h"
#include "threads/synch.h"
#include "userprog/process.h" #include "userprog/process.h"
#include "userprog/pagedir.h"
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
#include <syscall-nr.h> #include <syscall-nr.h>
#define MAX_SYSCALL_ARGS 3
static struct lock filesys_lock;
static unsigned fd_counter = MIN_USER_FD;
struct open_file
{
int fd; /* File Descriptor / Identifier */
struct file *file; /* Pointer to the associated file */
struct hash_elem elem; /* elem for a hash table */
};
static void syscall_handler (struct intr_frame *); static void syscall_handler (struct intr_frame *);
/* A syscall_function is a function that receives up to 3 arguments, the /* A syscall_function is a function that receives up to 3 arguments, the
@@ -30,7 +48,12 @@ static void syscall_seek (int fd, unsigned position);
static unsigned syscall_tell (int fd); static unsigned syscall_tell (int fd);
static void syscall_close (int fd); static void syscall_close (int fd);
static void *validate_user_pointer (const void *ptr, size_t size); static struct open_file *fd_get_file (int fd);
static void validate_user_pointer (const void *ptr, size_t size,
bool check_write);
static void validate_user_string (const char *str, bool check_write);
static int get_user (const uint8_t *);
static bool put_user (uint8_t *, uint8_t);
/* A struct defining a syscall_function pointer along with its arity. */ /* A struct defining a syscall_function pointer along with its arity. */
typedef struct typedef struct
@@ -63,18 +86,25 @@ static const syscall_arguments syscall_lookup[] =
static const int LOOKUP_SIZE static const int LOOKUP_SIZE
= sizeof (syscall_lookup) / sizeof (syscall_arguments); = sizeof (syscall_lookup) / sizeof (syscall_arguments);
/* Initialises the syscall handling system, as well as a global lock to
synchronise all file access between processes. */
void void
syscall_init (void) syscall_init (void)
{ {
intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
lock_init (&filesys_lock);
} }
/* Function that takes a interrupt frame containing a syscall and its args.
Validates the arguments and pointers before calling the relevant
high-level system call function, storing its output (if any) in f->eax */
static void static void
syscall_handler (struct intr_frame *f) syscall_handler (struct intr_frame *f)
{ {
/* First, read the system call number from the stack. */ /* First, read the system call number from the stack. */
validate_user_pointer (f->esp, 1); validate_user_pointer (f->esp, sizeof (uintptr_t), false);
unsigned syscall_number = *(int *) f->esp; uintptr_t syscall_number = *(int *)f->esp;
/* Ensures the number corresponds to a system call that can be handled. */ /* Ensures the number corresponds to a system call that can be handled. */
if (syscall_number >= LOOKUP_SIZE) if (syscall_number >= LOOKUP_SIZE)
@@ -84,16 +114,18 @@ syscall_handler (struct intr_frame *f)
/* Next, read and copy the arguments from the stack pointer. */ /* Next, read and copy the arguments from the stack pointer. */
validate_user_pointer (f->esp + sizeof (uintptr_t), validate_user_pointer (f->esp + sizeof (uintptr_t),
syscall.arity * sizeof (uintptr_t)); syscall.arity * sizeof (uintptr_t), false);
uintptr_t args[3] = {0}; uintptr_t args[MAX_SYSCALL_ARGS] = { 0 };
for (int i=0; i < syscall.arity; i++) for (int i = 0; i < syscall.arity && i < MAX_SYSCALL_ARGS; i++)
args[i] = *(uintptr_t *) (f->esp + sizeof (uintptr_t) * (i + 1)); args[i] = *(uintptr_t *)(f->esp + sizeof (uintptr_t) * (i + 1));
/* Call the function that handles this system call with the arguments. When /* Call the function that handles this system call with the arguments. When
there is a return value it is stored in f->eax. */ there is a return value it is stored in f->eax. */
f->eax = syscall.function (args[0], args[1], args[2]); f->eax = syscall.function (args[0], args[1], args[2]);
} }
/* Called upon a "halt" syscall, resulting in a complete shutdown of the
process, via shutdown_power_off (); */
static void static void
syscall_halt (void) syscall_halt (void)
{ {
@@ -109,56 +141,123 @@ syscall_exit (int status)
thread_exit (); thread_exit ();
} }
/* Executes a given command with the relevant args, by calling process_execute.
Acquires the filesystem lock as process_execute accesses the file system.
Returns PID for the process that is running the CMD_LINE
*/
static pid_t static pid_t
syscall_exec (const char *cmd_line UNUSED) syscall_exec (const char *cmd_line)
{ {
//TODO validate_user_string (cmd_line, false);
return 0;
lock_acquire (&filesys_lock);
pid_t pid = process_execute(cmd_line);
lock_release (&filesys_lock);
return pid;
} }
/* Handles the syscall of wait. Effectively a wrapper for process_wait as the
necessary validation and such all happens in process_wait anyway. */
static int static int
syscall_wait (pid_t pid) syscall_wait (pid_t pid)
{ {
return process_wait (pid); return process_wait (pid);
} }
/* Handles the syscall for file creation. First validates the user file
pointer. Acquires the file system lock to prevent synchronisation issues,
and then uses FILESYS_CREATE to create the file, returning the same status */
static bool static bool
syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) syscall_create (const char *file UNUSED, unsigned initial_size UNUSED)
{ {
//TODO validate_user_string (file, false);
return 0;
lock_acquire (&filesys_lock);
bool status = filesys_create (file, initial_size);
lock_release (&filesys_lock);
return status;
} }
/* Handles the syscall for file removal. First validates the user file pointer.
Acquires the file system lock to prevent synchronisation issues, and then
uses FILESYS_REMOVE to remove the file, returning the same success status */
static bool static bool
syscall_remove (const char *file UNUSED) syscall_remove (const char *file)
{ {
//TODO validate_user_string (file, false);
return 0;
lock_acquire (&filesys_lock);
bool status = filesys_remove (file);
lock_release (&filesys_lock);
return status;
} }
/* Handles the syscall for opening a file connection. First, validates the file
pointer. Then it acquires a lock for the file system, in order to open the
connection without synchronisation issues. It then maps a new fd to this file
in the hash table before returning the fd. */
static int static int
syscall_open (const char *file UNUSED) syscall_open (const char *file)
{ {
//TODO validate_user_string (file, false);
return 0;
lock_acquire (&filesys_lock);
struct file *ptr = filesys_open (file);
lock_release (&filesys_lock);
if (ptr == NULL)
return -1;
/* Allocate space for a struct representing a mapping from an FD to a struct
file. */
struct open_file *file_info
= (struct open_file*) malloc (sizeof (struct open_file));
if (file_info == NULL)
return -1;
/* Populate the above struct, with a unique FD and the current open file */
file_info->fd = fd_counter++;
file_info->file = ptr;
/* Add the new FD->file mapping to the hashtable for the current thread */
hash_insert (&thread_current ()->open_files, &file_info->elem);
/* Return the new FD */
return file_info->fd;
} }
/* Handles the syscall for getting a file's size. Converts a provided FD into
the asssociated file struct. Acquire the lock for the filesystem and use
FILE_LENGTH to calculate the length for return. */
static int static int
syscall_filesize (int fd UNUSED) syscall_filesize (int fd)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
return 0; if (file_info == NULL)
return -1;
lock_acquire (&filesys_lock);
int bytes = file_length (file_info->file);
lock_release (&filesys_lock);
return bytes;
} }
/* Handles the syscall for reading SIZE bytes from a file referenced by FD.
If the FD references the console, use input_getc (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, read up to
SIZE bytes and then return the number of bytes read.*/
static int static int
syscall_read (int fd, void *buffer, unsigned size) syscall_read (int fd, void *buffer, unsigned size)
{ {
/* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are /* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are
allowed. */ allowed. */
if (fd < 0 && fd != STDOUT_FILENO) if (fd < 0 || fd == STDOUT_FILENO)
return -1; return -1;
validate_user_pointer (buffer, size); validate_user_pointer (buffer, size, true);
if (fd == STDIN_FILENO) if (fd == STDIN_FILENO)
{ {
@@ -172,10 +271,21 @@ syscall_read (int fd, void *buffer, unsigned size)
else else
{ {
/* Reading from a file. */ /* Reading from a file. */
return 0; // TODO: Implement Write to Files struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return -1;
lock_acquire (&filesys_lock);
int bytes_written = file_read (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes_written;
} }
} }
/* Handles the syscall for writing SIZE bytes to a file referenced by FD.
If the FD references the console, use put_buf (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, write up to
SIZE bytes and then return the number of bytes written.*/
static int static int
syscall_write (int fd, const void *buffer, unsigned size) syscall_write (int fd, const void *buffer, unsigned size)
{ {
@@ -184,7 +294,7 @@ syscall_write (int fd, const void *buffer, unsigned size)
if (fd <= 0) if (fd <= 0)
return 0; return 0;
validate_user_pointer (buffer, size); validate_user_pointer (buffer, size, false);
if (fd == STDOUT_FILENO) if (fd == STDOUT_FILENO)
{ {
@@ -195,40 +305,178 @@ syscall_write (int fd, const void *buffer, unsigned size)
else else
{ {
/* Writing to a file. */ /* Writing to a file. */
return 0; // TODO: Implement Write to Files struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
int bytes = file_write (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes;
} }
} }
/* Handles the syscall for seeking to POSITION bytes in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_seek to adjust the cursor to a specific position in
the file.*/
static void static void
syscall_seek (int fd UNUSED, unsigned position UNUSED) syscall_seek (int fd, unsigned position)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
lock_acquire (&filesys_lock);
file_seek (file_info->file, position);
lock_release (&filesys_lock);
}
} }
/* Handles the syscall for returning the next byte in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_tell to read the next byte.*/
static unsigned static unsigned
syscall_tell (int fd UNUSED) syscall_tell (int fd)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
return 0; if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
unsigned pos = file_tell (file_info->file);
lock_release (&filesys_lock);
return pos;
} }
/* Handles the syscall for closing a connection to a file. Converts the FD to
its associated file struct. If it exists, it removes it from the hash table,
acquires the filesystem lock, and uses file_close to close the connection.*/
static void static void
syscall_close (int fd UNUSED) syscall_close (int fd)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
hash_delete (&thread_current ()->open_files, &file_info->elem);
lock_acquire (&filesys_lock);
file_close (file_info->file);
lock_release (&filesys_lock);
free (file_info);
}
}
/* Hashing function needed for the open_file table. Returns a hash for an entry,
based on its FD. */
unsigned
fd_hash (const struct hash_elem *element, void *aux UNUSED)
{
return hash_int (hash_entry (element, struct open_file, elem)->fd);
}
/* Comparator function for the open_file table. Compares two entries based on
the FDs. */
bool
fd_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
struct open_file *a = hash_entry (a_, struct open_file, elem);
struct open_file *b = hash_entry (b_, struct open_file, elem);
return a->fd < b->fd;
}
/* Gets a file from its descriptor (FD number). If there is no file with the fd
FD it returns NULL. */
static struct open_file *
fd_get_file (int fd)
{
/* We have to set up a fake open_file in order to be able to search the hash
table. See hash.h. */
struct open_file fake_file_info;
fake_file_info.fd = fd;
struct hash_elem *e
= hash_find (&thread_current ()->open_files, &fake_file_info.elem);
if (e == NULL)
return NULL;
return hash_entry (e, struct open_file, elem);
} }
/* Validates if a block of memory starting at PTR and of size SIZE bytes is /* Validates if a block of memory starting at PTR and of size SIZE bytes is
fully contained within user virtual memory. Kills the thread (by calling fully contained within valid user virtual memory. thread_exit () if the
thread_exit) if the memory is invalid. Otherwise, returns the PTR given. memory is invalid.
If the size is 0, the function does no checks and returns PTR.*/ If the size is 0, the function does no checks and returns PTR. */
static void * static void
validate_user_pointer (const void *ptr, size_t size) validate_user_pointer (const void *ptr, size_t size, bool check_write)
{ {
if (size > 0 && (ptr == NULL || if (size == 0)
!is_user_vaddr (ptr) || return;
!is_user_vaddr (ptr + size - 1))) /* ptr < ptr + size - 1, so sufficient to check that (ptr + size -1) is a
valid user virtual memory address. */
void *last = ptr + size - 1;
if (!is_user_vaddr (last))
thread_exit (); thread_exit ();
ptr = pg_round_down (ptr);
return ptr; while (ptr <= last)
{
int result;
/* Check read access to pointer. */
if ((result = get_user (ptr)) == -1)
thread_exit ();
/* Check write access to pointer (if required). */
if (check_write && !put_user (ptr, result))
thread_exit ();
ptr += PGSIZE;
}
}
/* Validates of a C-string starting at ptr is fully contained within valid
user virtual memory. thread_exit () if the memory is invalid. */
static void
validate_user_string (const char *ptr, bool check_write)
{
while (true)
{
if (!is_user_vaddr (ptr))
thread_exit ();
int result;
if ((result = get_user ((const uint8_t *)ptr)) == -1)
thread_exit ();
if (check_write && !put_user ((uint8_t *)ptr, result))
thread_exit ();
if (*ptr == '\0')
return;
ptr++;
}
}
/* PROVIDED BY SPEC.
Reads a byte at user virtual address UADDR.
UADDR must be below PHYS_BASE.
Returns the byte value if successful, -1 if a segfault occurred. */
static int
get_user (const uint8_t *uaddr)
{
int result;
asm ("movl $1f, %0; movzbl %1, %0; 1:" : "=&a"(result) : "m"(*uaddr));
return result;
}
/* PROVIDED BY SPEC.
Writes BYTE to user address UDST.
UDST must be below PHYS_BASE.
Returns true if successful, false if a segfault occurred. */
static bool
put_user (uint8_t *udst, uint8_t byte)
{
int error_code;
asm ("movl $1f, %0; movb %b2, %1; 1:"
: "=&a"(error_code), "=m"(*udst)
: "q"(byte));
return error_code != -1;
} }

View File

@@ -1,8 +1,15 @@
#ifndef USERPROG_SYSCALL_H #ifndef USERPROG_SYSCALL_H
#define USERPROG_SYSCALL_H #define USERPROG_SYSCALL_H
#include <hash.h>
#define MIN_USER_FD 2
typedef int pid_t; typedef int pid_t;
void syscall_init (void); void syscall_init (void);
unsigned fd_hash (const struct hash_elem *element, void *aux);
bool fd_less (const struct hash_elem *a, const struct hash_elem *b, void *aux);
#endif /* userprog/syscall.h */ #endif /* userprog/syscall.h */