1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
#include "errno.h"
#include "fs/fcntl.h"
#include "fs/file.h"
#include "fs/vfs.h"
#include "fs/vfs_syscall.h"
#include "fs/vnode.h"
#include "globals.h"
#include "util/debug.h"
#include <fs/vnode.h>
// NOTE: IF DOING MULTI-THREADED PROCS, NEED TO SYNCHRONIZE ACCESS TO FILE
// DESCRIPTORS, AND, MORE GENERALLY SPEAKING, p_files, IN PARTICULAR IN THIS
// FUNCTION AND ITS CALLERS.
/*
* Go through curproc->p_files and find the first null entry.
* If one exists, set fd to that index and return 0.
*
* Error cases get_empty_fd is responsible for generating:
* - EMFILE: no empty file descriptor
*/
long get_empty_fd(int *fd)
{
for (*fd = 0; *fd < NFILES; (*fd)++)
{
if (!curproc->p_files[*fd])
{
return 0;
}
}
*fd = -1;
return -EMFILE;
}
/*
* Open the file at the provided path with the specified flags.
*
* Returns the file descriptor on success, or error cases:
* - EINVAL: Invalid oflags
* - EISDIR: Trying to open a directory with write access
* - ENXIO: Blockdev or chardev vnode does not have an actual underlying device
* - ENOMEM: Not enough kernel memory (if fcreate() fails)
*
* Hints:
* 1) Use get_empty_fd() to get an available fd.
* 2) Use namev_open() with oflags, mode S_IFREG, and devid 0.
* 3) Check for EISDIR and ENXIO errors.
* 4) Convert oflags (O_RDONLY, O_WRONLY, O_RDWR, O_APPEND) into corresponding
* file access flags (FMODE_READ, FMODE_WRITE, FMODE_APPEND).
* 5) Use fcreate() to create and initialize the corresponding file descriptor
* with the vnode from 2) and the mode from 4).
*
* When checking oflags, you only need to check that the read and write
* permissions are consistent. However, because O_RDONLY is 0 and O_RDWR is 2,
* there's no way to tell if both were specified. So, you really only need
* to check if O_WRONLY and O_RDWR were specified.
*
* If O_TRUNC specified and the vnode represents a regular file, make sure to call the
* the vnode's truncate routine (to reduce the size of the file to 0).
*
* If a vnode represents a chardev or blockdev, then the appropriate field of
* the vnode->vn_dev union will point to the device. Otherwise, the union will be NULL.
*/
long do_open(const char *filename, int oflags)
{
// 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))
{
res_vnode->vn_ops->truncate_file(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;
}
|