Accondingly to the convention, the configuration file in the /config folder is named SHELL.
Prototype: int sys_waitpid(pid_t pid, userptr_t statusp, int options, pid_t *err);
sys_waitpid
has been implemented in kern/syscall/waitpid.c
, essentially following the same logic developed during OS161 lab 4. The basic structure is the following:
sys_waitpid
takes as parameters :
- the
pid
of the waited process - a user pointer to the status of the waited process (
statusp
) - an
options
flag, which is handled just for one value:if options == WNOHANG
and the waited process is not exited yet (!p->p_exited
), thensys_waitpid
returns 0. A flagp_exited
has been added tostruct proc
and it is initialized tofalse
insideproc_create
function, while it is set totrue
insidesys__exit
- a pointer to the error code
*err
. If something goes wrong then the error code is placed in*err
and the function returns-1
At the beginning some validity checks are performed, especially on statusp
, pid
and curproc
, i.e the one which is waiting:
statusp
must be properly aligned by 4 since it is auserptr_t
, hence it can not even point to a kernel memory region (statusp <= 0x80000000
). Finallystatusp
must be a valid pointer (statusp != 0x40000000
). Ifstatusp
isNULL
then it is not required to collect the exit status.pid
cannot be negative and it must belong to an existing processcurproc
checks, performed after functionproc_search_pid
is called, are the most interesting ones:- a process cannot wait for itself
- a process can perform waitpid just on its children, i.e waiting for itself or for a parent is not allowed
If all these checks are passed, then proc_wait
function is called. Inside the latter two wait options are possible:
- the parent process waits on child process semaphore (
P(proc->p_sem)
) - the parent process waits on a condition variable, hence
struct cv *p_cv
andstruct lock *p_cv_lock
fields have been added tostruct proc
. the condition iswhile (!proc->p_exited)
At the end the return status is copied onto user space through copyout
and the waited process' pid is returned.
Prototype: void sys__exit(int status);
sys__exit
has been implemented in kern/syscall/exit.c
This system call takes as parameter the exit status status
of the process which is exiting and doesn't return because the last action executed is calling thread_exit
, which should not return.
At the beginning all open files of curproc
are closed by calling cloas_all_files(struct proc *p)
, then status
is assigned to curproc->p_status
and the current thread is removed from the process through proc_remthread(struct thread *t)
. Of course waitpid
and exit
system calls are related, that is p->p_exited
flag is set to true and then either the semaphore or the condition variable is signaled.
A call tosys__exit
has been added inside kill_curthread
, so that if a user program tries to access to an illegal memory region it immediately exits with code
parameter passed to kill_curthread
.
Prototype: pid_t sys_getpid(void);
This system call has been implemented during lab 4 and it simply returns the pid of the current process.
Prototype: int sys_fork(struct trapframe *ctf, pid_t *retval);
sys_fork
has been implemented in kern/syscall/fork.c
, requiring the addition of two new fields in struct proc
:
-
struct proc *p_parent
, which is a pointer to the parent process. It is set tocurproc
(sincecurproc
is the parent) insidesys_fork
after the child process has been created. -
struct array *p_children
, which is the children array of the parent process.array
is a special data structure defined in OS161 which handles arrays of several types since it is structured like this:struct array {
void **v;
unsigned num, max;
};
it also has specific methods and functions, making easier children handling in
sys_fork
.
sys_fork
takes as parameter the parent's trapframe ctf
and the pointer *retval
. At the very beginning default validity checks are performed onto curproc
. Then the following workflow is done:
-
child process
child
is created throughproc_create_runprogram
:proc
data structure is allocated andcurproc->p_cwd
is copied inside child process. After child creation some validity checks are performed onchild->p_pid
-
child's trapframe
tf_child
is allocated andctf
is copied to it throughmemcpy(void *dest, const void *src, size_t len)
function, which copieslen
bytes fromsrc
todest
-
parent's address space is copied to child through
as_copy(struct addrspace *old, struct addrspace **ret)
-
child points to parent, i.e
p_parent
field ofchild
is set tocurproc
-
parent calls
thread_fork
withcall_enter_forked_process
as entrypoint. The latter is the caller function forenter_forked_process
. It's the child process which executes this function. This is what happens insideenter_forked_process
:- child's trapframe is duplicated so that it's on kernel stack and not on the heap.
- return values are set inside trapframe fields.
$a3
stores 0 , and$v0
stores return value (child process returns with zero) or$v0:$v1
if retval is 64-bit; on failure,$a3
stores 1, and$v0
stores error code. - child's address space is activated
mips_usermode
is called, and the newly created child process can start running as user process.
thread_fork
will set newly created child thread runnable and try to switch to it immediately. So it's highly possible that beforethread_fork
returns, the child thread is already running. This could be a problem if the child thread starts running before parent'sfileTable
is copied, because child thread would run without afileTable
. This problem has been handled by copying parent'sfileTable
to child process insidethread_fork
, in mutual exclusion.
- parent process returns with child's pid:
*retval = child->p_pid
. If something goes wrong then the error code is placed inside*retval
and the function returns-1
. Afterwards error checking is performed insidesyscall
function: if the return value is<0
, then the error code is placed inside theerr
variable.
Prototype: int sys_execv(char *program, char **args);
sys_execv
has been implemented in kern/syscall/execv.c
.
The overall flow of sys_execv
is:
- Copy arguments from user space into kernel buffer
- Open the executable, create a new address space and load the elf into it
- Copy the arguments from kernel buffer into user stack
- Return user mode using
enter_new_process
sys_execv
takes as parameters char *program
(the program name) and char **args
(array with arguments), both passed from user space. After some initial validity checks on arguments and curproc
, argc
(number of arguments) is computed and function copy_args_to_kbuf(char **args, int argc, int *buflen)
is called. The two global static arrays,char kargbuf[ARG_MAX]
and karg[ARG_MAX]
represent respectively the kernel buffer (kargbuf
is treated as a byte array) and the i-th argument that has to be copied onto kernel buffer (there's actually no need to store the whole argument array). These two variables, being global, are protected with the lockexec_lock
, created inside proc_bootstrap
in kern/proc/proc.c
and acquired in sys_execv
just before copying arguments. The main additional function implemented to make sys_execv
work is copy_args_to_kbuf(char **args, int argc, int *buflen)
.
The latter takes as parameters the user arguments, the number of arguments and a pointer to buflen
, which is how much does the user stack have to move to store the arguments from kernel buffer kbuf
. copy_args_to_kbuf
uses pointers arithmetic to build kargbuf
. This is the approach followed by the function:
-
at the beginning
kbuf
is initialized, that is:unsigned char *p_begin
points to the beginning of the arrayint last_offset = argc * sizeof(char *)
is the dimension of the portion ofkbuf
used to store the argument offsetint offset
deals with the "second" portion ofkbuf
, used to store the position of arguments in the stack. This will be used to copy arguments to user stack.unsigned char *p_end = p_begin + last_offset
is the memory location that stores the actual argumentkarg
int offset
stores the offset ofkarg
and it is placed inside memory region pointed byp_begin
-
a
for
loop contains all the copy process:- user arguments are copied inside
karg
throughcopyinstr
function offset
is computed moving fromlast_offset
bypadding
padding
is computed throughpadded_length
function, which aligns by 4 (as required by OS161 user stack) the passed string. It basically exploits modular arithmetic to return anint
value which is the required padding in order to obtain a multiple of 4 as length ofkarg
.karg
is copied inp_end
throughmemcpy
function.void *memcpy(void *dest, const void *src, size_t len)
copiesn
characters from memory areasrc
to memory areadest
offset
is stored in memory region pointed byp_begin
, so it is saved insidekbuf
.- final updates:
p_end
moves bypadding
to store the next argument in the next iteration.p_begin
moves by 4 bytes (i.esizeof(char *)
) in order to store the offset of the next argument in the next iteration.last_offset
is set tooffset
and*buflen
is incremented bypadding + sizeof(char *)
At the end of the
while
loop*buflen
is finally incremented by 4 bytes and the function returns zero. At this pointkbuf
contains both the arguments and their offset in kernel stack.Now
sys_execv
copiesprogram
(program name) in kernel memory throughcopyinstr
and performs the same actions performed byrunprogram
: open the executable related tokprogram
, create and activate the new address space, load the ELF executable and create a stack for the new address space.Finally the arguments have to be copied again to user space through
copyout
function, so thatenter_new_process
can be called and the user program is executed.However, before
copyout((void *)kbuf, (userptr_t)stackptr, buflen)
the kernel buffer has to be adjusted for user stack: at this point the functionchange_kbuf_for_userstack
is called. This function basically takes the old offset (theoffset
computed incopy_args_to_kbuf
), adds it tostackptr
and stores this new valuenew_offset
insidekbuf
at the same position as before. Of course before callingchange_kbuf_for_userstack
the stack pointer has to lower by*buflen
. - user arguments are copied inside
In order to test fork, waitpid, getpid
and exit
, testbin/forktest
has been chosen among testbin
programs. In this user program a process forks several times, generating processes "geometrically". Basically sys_fork
is called 4 times, generating a tree which is printed on stdout
through letters A, B, C, D.
After each fork
, sys_getpid
is called through check()
function: the latter calls getpid
in a for
loop to make sure each fork has its own address space.
In order to avoid out of memory errors in testbin/forktest
, the ramsize has been increased in root/sys161.conf
from 512K
to 4096K
. sys_waitpid
is also tested because once the parent process has forked several times, it waits for its children.
If sys_execv
is properly implemented, the OS161 shell should work. Namely, a user program should run (even in background) without crashing the kernel. When a user program (e.g testbin/palin
) is run inside OS161 shell, which performs the following:
- the shell, i.e the process
/bin/sh
, forks - The system search for the program until it finds it, calling
sys_execv
is called 3 times: once for/bin/palin
, once for/sbin/palin
and finally fortestbin/palin
and the user program is successfully executed.
runprogram
uses, let's say, the last part of execv, namely the copy of arguments from kernel stack onto user stack, in order to achieve arguments passing from main. This has been implemented and tested through testbin/argtest
, which prints on stdout
all the passed arguments. However this feature has been implemented differently from sys_execv
: instead of a static array of bytes, a dynamic array of strings argvptr
is used and the alignment check is done by an AND operation with 0x3
.
These different implementations are due to the fact that runprogram
has been improved while doing OS161 labs, while execv
has been written later, basically following the explanation in Pearls in Life blog http://jhshi.me/2012/03/11/os161-execv-system-call/index.html#.YcBaFbso-3c .
Prototype: int sys_chdir(userptr_t path, int *err);
sys_chdir
has been implemented in kern/syscall/curdir_syscalls.c
.
-
Parameters:
userptr_t path
: contains the destination path which we want to move at.int *err
: this integer pointer will collect the information about the eventual error.
-
Return value:
- 0, if the operation has been successfully completed;
- -1, if there's been an error (see err for the error code).
The path parameter is checked: since it contains an address, it must be
- not NULL
- not equal to 0x40000000
- lower than 0x80000000
Since the path has been read from the command line, the validity of this raw string is checked by the function
dir_parser
. After the allocation of the kpath, which will contain the userptr_t string into the kernel address space, we use copyinstr to indeed copy the content ofpath
into the kernel-levelkpath
.
The vfs_chdir
function (vfs.h) does the most of the work, getting the kpath
string. In case of error, this will return an error code, which is a positive number greater than zero that can be interpreted by the syscall.c mechanism to be verbose about the error occurred.
The sys_chdir
system call has been tested using the cd
command provided by OS161 in their menu. There've been notably created different folders in the /root folder in order to test this system call.
Prototype: int sys___getcwd(userptr_t buf, size_t size, int *err);
sys___getcwd
has been implemented in kern/syscall/curdir_syscalls.c
.
-
Parameters:
char *buf
: array of char in which it's going to be stored the current working directory path.size_t size
: size of thebuf
string.int *err
: this integer pointer will collect the information about the eventual error.
-
Return value:
- the size of the string read, if the operation has been successfully completed;
- -1, if there's been an error (see err for the error code).
The buf parameter is checked: since it contains an address, it must be
- not NULL
- not equal to 0x40000000
- lower than 0x80000000
First of all, we check for whether the current working directory is a NULL pointer, setting the error parameter to ENOTDIR (17, "Not a directory").
It's been introduced an ad-hoc function for setting the user space without involving the kernel level address space: after uio_kinit has been called, there are three major fields to be properly set: iovec->iov_ubase = buf;
uio->uio_segflg = UIO_USERSPACE;
uio->uio_space = proc_getas();
The vfs_getcwd
function (vfs.h) does the most of the work, getting as parameter the pointer to uio
(struct uio
) and returns a value which has to be checked to detect eventual errors.
The returned value is the difference between the size
of the buffer in which the string has been stored and the uio_resid
field, which stores the remaining amount of data to transfer: then, the returned value is the amount of data that has been read.
Testing the sys___getcwd
system call has been quite tricky. The only directory we've been able to retrieve has been the one in the root, which returns: emu0:
.
We have then implemented the sys_fstat
and the sys_mkdir
in order to mount another filesystem with the procedure described on the OS161 official website, that is:
OS/161 kernel [? for menu]: p /sbin/mksfs lhd1raw: myDisk
OS/161 kernel [? for menu]: mount sfs lhd1
OS/161 kernel [? for menu]: cd lhd1:
but even in this case, running the mkdir command has not been working as wished. Indeed, the sys___getcwd
has retrieved the lhd1:
string, acting the same way of when it's been called in the default filesystem.
int sys_dup2(int old_fd, int new_fd, int *err);
sys_dup2
has been implemented in kern/syscall/file_syscalls.c
.
-
Parameters:
int old_fd
: file descriptor related to the file to which the new_fd will be linked at the end of the callint new_fd
: file descriptor that will be associated to the file related to the old_fd at the end of the callint *err
: this integer pointer will collect the information about the eventual error.
-
Return value:
- 0, if the operation has been successfully completed;
- -1, if there's been an error (see err for the error code).
First of all, there's the acquisition of the process' spinlock in order to achieve exclusive access to the fileTable of the process. The fileTable is a pointer to an array of pointers of struct openfile whose size is OPEN_MAX (a constant defined in limits.h, representing the maximum number of files that a process can keep in its fileTable). The struct openfile is made up this way:
struct openfile
{
struct vnode *vn;
mode_t mode;
off_t offset;
int accmode;
struct lock *file_lock;
unsigned int ref_count;
};
After this operations, the two file descriptors passed as parameters are checked: they must be valid file descriptors (in the [0, OPEN_MAX]
set) and the old_fd
must be present inside the fileTable
.
If the two file descriptors are equals, the system call can terminate with no efforts, returning the new_fd
value.
Then, if the new_fd
is previously opened, the dup2 function will close it calling the sys_close
system call.
Now, curproc->fileTable[new_fd]
is NULL, so there's the allocation of a new struct openfile entry.
At this point, there's the increment of the reference counter ref_count
of the entry of the fileTable fileTable[old_fd]
, then it's time to copy all the field of the data structure from the old_fd
entry to the new_fd
one.
curproc->fileTable[new_fd]->vn = curproc->fileTable[old_fd]->vn;
curproc->fileTable[new_fd]->mode = curproc->fileTable[old_fd]->mode;
curproc->fileTable[new_fd]->offset = curproc->fileTable[old_fd]->offset;
curproc->fileTable[new_fd]->accmode = curproc->fileTable[old_fd]->accmode;
curproc->fileTable[new_fd]->file_lock = curproc->fileTable[old_fd]->file_lock;
curproc->fileTable[new_fd]->ref_count = curproc->fileTable[old_fd]->ref_count;
and, eventually, release the spinlock of the process.
Testing the dup2 involved the creation of an ad-hoc test called dup2test. This test is available at the following repository: https://github.com/lifeofvins/dup2test
If the constant in the define TEST_SECTION is set on 1:
a dup2 call (dup2(fd, STDOUT_FILENO)
) is performed such that it's possible to use printf/scanf to make r/w operations on a file (which filename is hardcoded for the sake of simplicity).
Check the file "dup-2-test.txt" in the root location to understand if the operation worked. The final content of the file (dup-2-test.txt) will be these lines of text:
"And what marks did you see by the wicket-gate?"
"None in particular."
"Good heaven! Did no one examine?"
"Yes, I examined, myself."
"And foud nothing?"
"It was all very confused. Sir Charles had evidently stood there for five or ten minutes."
"How do you know that?"
"Because the ash had twice dropped from his cigar."
In another test (not showed here), it's been tested also the usage of dup2(fd1, fd2)
in order to do r/w operations using the fd2 file descriptor on the file represented by the fd1 file descriptor.
If the constant in the define TEST_SECTION is set on 0:
two dup2 calls are performed:
dup2(fd2, STDIN_FILENO);
dup2(fd3, STDOUT_FILENO);
in order to read from the file descriptor STDOUT_FILENO and write on the STDIN_FILENO, which have been previously warped such that those operations will be done on other files (x.txt and aaa.txt).
Prototype: int sys_lseek(int fd, off_t offset, int whence, int *err);
sys_lseek
has been implemented in kern/syscall/file_syscalls.c
.
-
Parameters:
int fd
: file descriptor related to the file on which make the lseek operationoff_t offset
: this is an integer number representing the displacement from the position specified by the usage of the current position and thewhence
parameterint whence
: this value represents the point of the file from which it will be done the lseek operation. There are three different valid values:- SEEK_CUR: the position is the one at which the file is currently located
- SEEK_SET: the position is set to the diplacement passed as parameter in the
offset
parameter - SEEK_END: the position is set to the end of the file
int *err
: this integer pointer will collect the information about the eventual error.
-
Return value:
- 0, if the operation has been successfully completed;
- -1, if there's been an error (see err for the error code).
The first operations to be done are the checks over the parameters:
- The
fd
file descriptor shall be valid: this is checked with theis_valid_fd
function, which checksfd
is in the [0; OPEN_MAX] set and that it's actually present into thefileTable
of the process. - As said before, the
whence
parameter must be check in order to accept only one of the three valid values (SEEK_SET, SEEK_CUR and SEEK_END).
Now, based on the whence
parameter, it's time to compute the actual position for the file:
- If SEEK_SET, then the actual postion is basically the offset passed as parameter;
- If SEEK_CUR, then the actual position is the current position plus the offset passed as parameter;
- If SEEK_END, then VOP_STAT is called to know the size of the file, then the actual offset is computed summing up the size of the file obtained with this latter call and the offset passed as parameter.
During the entire call to sys_lseek
the access to the file is protected using the file_lock associated to the relative entry in the fileTable: curproc->fileTable[fd]->file_lock
.
The lseek
system call has been tested in two different ways.
The first way was using the program /testbin/tail
which is called as following:
p /testbin/tail <filename> <offset>
and it was tested with the x.txt file from the root folder.
Then, in order to test the usage of the whence
parameter in an exaustive way a test has been written that calls lseek three times as following:
...
lseek(fd1, 1, SEEK_SET);
read(fd1, buffer, 13);
buffer[14] = '\0';
printf("%s", buffer);
printf("\n---------------\n");
lseek(fd1, 1, SEEK_CUR);
read(fd1, buffer, 13);
buffer[14] = '\0';
printf("%s", buffer);
printf("\n---------------\n");
lseek(fd1, -13, SEEK_END);
read(fd1, buffer, 13);
buffer[14] = '\0';
printf("%s", buffer);
printf("\n---------------\n");
...
and the sys_lseek
function worked perfectly.
sys_read
has been implemented in kern/syscall/file_syscalls.c
and it is similar to the function implemented during lab 2. The basic structure is the following:
sys_read
takes as parameters :
fd
, the file descriptorbuf_ptr
is a user pointer to a buffer used to perform a reading operationsize
represents the size of the buffer- a pointer
*err
, in order to store the error type if an error occurred
At the beginning several checks are done to ensure the parameters correctness :
buf_ptr
needs to be a not-null pointer.buf_ptr
is asked to point to a segment that is within the user space.
Inside the function different operations are executed based on the value assumed by fd
:
-
if
fd == STDERR_FILENO || fd == STDOUT_FILENO
, to face the possibility of a previousdup2
call involvingSTDOUT_FILENO
orSTDERR_FILENO
, different scenarios must be taken into account:- If
curproc->fileTable[fd] == NULL
it means that does not exist an opened file withfd
as a file descriptor in the current process file table. An error type is stored in*err
and-1
is returned. - If
curproc->fileTable[fd]->vn == systemFileTable[fd].vn
we are in the case in which adup2
function has not been performed previously. It means that a reading operation is not allowed onSTDOUT_FILENO
orSTDERR_FILENO
, so the value of the file descriptor is not correct for our purposes. An error type is saved into*err
and-1
is returned. - If
curproc->fileTable[fd] != NULL
, one amongSTDOUT_FILENO
andSTDERR_FILENO
is a valid file descriptor of a opened file in the current process file table. In this case, a call tofile_read(fd, buf_ptr, size, err)
is returned. - If we are not in one of the previous situations it means that
fd
is a valid file descriptor for a reading operation on standard input. Consequently, a "classic" standard input operation is performed.
- If
-
if
fd == STDIN_FILENO
, two situations are possible:- the
if (curproc->fileTable[fd] != NULL)
clause extends the functionality of thedup2
to the case in whichSTDIN_FILENO
is the file descriptor of a file located in thefileTable
of the current process. The call tofile_read(fd, buf_ptr, size, err)
is returned. - in the other case a "classic" standard input operation is executed. In this case the
size
of the read bytes is returned.
- the
-
if we don't have none of the previous situations,
file_read(fd, buf_ptr, size, err)
is returned, and a file read operation is performed.
The function returns the number of bytes read.
sys_write
has been implemented in kern/syscall/file_syscalls.c
and it is similar to the function implemented during lab 2. The basic structure is the following:
sys_write
takes as parameters :
fd
, the file descriptorbuf_ptr
is a user pointer to a buffer used to perform a writing operationsize
represents the size of the buffer- a pointer
*err
, in order to store the error type if an error occurred
At the beginning several checks are done to ensure the parameters correctness :
buf_ptr
needs to be a not-null pointer.buf_ptr
is asked to point to a segment that is within the user space.
Inside the function different operations are executed based on the value assumed by fd
:
-
if
fd == STDIN_FILENO
, to consider the possibility of a previousdup2
call involvingSTDIN_FILENO
, different scenarios must be taken into account:- If
curproc->fileTable[fd] == NULL
it means that does not exist an opened file withfd
as a file descriptor in the current process file table. An error type is stored in*err
and-1
is returned. - If
curproc->fileTable[fd]->vn == systemFileTable[fd].vn
we are in the case in which adup2
function has not been performed previously. It means that a writing operation is not allowed onSTDIN_FILENO
, so the value of the file descriptor is not correct for our purposes. An error type is saved into*err
and-1
is returned. - If
curproc->fileTable[fd] != NULL
,STDIN_FILENO
is a valid file descriptor of a opened file in the current process file table. In this case, a call tofile_write(fd, buf_ptr, size, err)
is returned. - If we are not in one of the previous situations it means that
fd
is a valid file descriptor for a writing operation on standard output. Consequently, a "classic" standard output operation is performed.
- If
-
if
fd == STDOUT_FILENO || fd == STDERR_FILENO
, two situations are possible:- the
if (curproc->fileTable[fd] != NULL)
clause extends the functionality of thedup2
to the case in which one amongSTDOUT_FILENO
andSTDERR_FILENO
is the file descriptor of a file located in thefileTable
of the current process. The call tofile_write(fd, buf_ptr, size, err)
is returned. - in the other case a "classic" standard output operation is executed. In this case the
size
of the written bytes is returned.
- the
-
if we don't have none of the previous situations,
file_write(fd, buf_ptr, size, err)
is returned, and a file write operation is performed.
The function returns the number of bytes written.
sys_open
has been implemented in kern/syscall/file_syscalls.c
starting from the solution provided during the lab5.
sys_open
takes as parameters :
path
is a user pointer to the path of the fileopenflags
represents the access modemode
defines the permissions of the file- a pointer
*err
, in order to store the error type if an error occurred
The important data structure used in this function is struct openfile
, defined as follows:
struct openfile {
struct vnode *vn;
mode_t mode;
off_t offset;
int accmode;
struct lock *file_lock;
unsigned int ref_count;
}
struct vnode *vn;
is a pointer to a vnode.mode_t mode;
indicates the type of permissions allowed for the file to be opened.off_t offset;
represents the position inside the file where to start the read/write operation.int accmode;
tells us the access mode of the current file.struct lock *file_lock;
is used to enforce mutual exclusion between cuncurrent operations performed on the same file.unsigned int ref_count;
stores the number of processes "active" on this file.
At the beginning several validity checks are implemented:
- if
(path == NULL)
the error type is saved inside*errp
and-1
is returned. - if
accmode
is different from O_RDONLY, O_WRONLY or O_RDWR, there isn't a valid access mode for the file, therfore an error type is saved inside*errp
and-1
is returned.
If all the checks are completed successfully the core of the function starts.
First of all copyinstr
is used in order to copy PATH_MAX bytes from a user-space address path
to a kernel-space address fname
. After that, vfs_open
is called and a virtual node structure (struct vnode *v
) is obtained from it. If systemFileTable
(data structure used to track the opened files in the system) contains free slots where to place a openfile struct, then a pointer to an entry of systemFileTable
is assigned to of
, and its elements are initialized. Once checked the overall number of files opened in the system, we need to ensure that inside the fileTable
of the current process there is enough space, and if this is the case, we assign of
to an entry of fileTable
. Another situation that needs to be taken into account is the value of of->offset
. Indeed if the bitwise AND between openflags
and O_APPEND gives 1
as a result, it means that of->offset
should no longer be equal to zero but equal to the file size. So we need to check it and use So we need to check it and use VOP_STAT
to get file size if necessary.
If all went smoothly sys_open
returns the file descriptor, -1
otherwise.
sys_close
has been implemented in kern/syscall/file_syscalls.c
starting from the solution provided during the lab5.
sys_close
takes as parameters :
fd
, the file descriptor- a pointer
*err
, in order to store the error type if an error occurred
The aim of this function is to check possible errors occurred during the file close operation and to de-allocate objects related to the file management.
At the beginning, some validity checks are performed to make sure that:
fd
is a valid file descriptor.vn
andof
are not-null variables. If these conditions are not satisfied, an error type is saved inside*err
and-1
is returned.
Once passed the latters, the sys_close
controls if the process which is performing the close operation, is the last one working on the file (if (of->ref_count == 1)
). If so, the file is closed and the lock destroyed, of->ref_count
is decremented otherwise.
In any error occurs, sys_close
returns 0
.
The functions inside file_syscalls.c
have been tested with f_test.c
, located in userland/testbin/f_test
.
For our purposes f_test.c
was run respectively with 1
and 3
as arguments on the command line.
So, executing p f_test 1
checks different operations performed on a big file instead p f_test 3
makes sure that concurrent operations on the same file behave properly.
Several other tests that indirectly call system calls included in file_syscalls.c
have been performed:
userland/testbin/bigseek
, userland/testbin/badcall
and userland/testbin/tail
.