-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfd.c
More file actions
318 lines (279 loc) · 6.79 KB
/
fd.c
File metadata and controls
318 lines (279 loc) · 6.79 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#include <inc/lib.h>
#define debug 0
// Maximum number of file descriptors a program may hold open concurrently
#define MAXFD 32
// Bottom of file descriptor area
#define FDTABLE 0xD0000000
// Bottom of file data area. We reserve one data page for each FD,
// which devices can use if they choose.
#define FILEDATA (FDTABLE + MAXFD*PGSIZE)
// Return the 'struct Fd*' for file descriptor index i
#define INDEX2FD(i) ((struct Fd*) (FDTABLE + (i)*PGSIZE))
// Return the file data page for file descriptor index i
#define INDEX2DATA(i) ((char*) (FILEDATA + (i)*PGSIZE))
// --------------------------------------------------------------
// File descriptor manipulators
// --------------------------------------------------------------
int
fd2num(struct Fd *fd)
{
return ((uintptr_t) fd - FDTABLE) / PGSIZE;
}
char*
fd2data(struct Fd *fd)
{
return INDEX2DATA(fd2num(fd));
}
// Finds the smallest i from 0 to MAXFD-1 that doesn't have
// its fd page mapped.
// Sets *fd_store to the corresponding fd page virtual address.
//
// fd_alloc does NOT actually allocate an fd page.
// It is up to the caller to allocate the page somehow.
// This means that if someone calls fd_alloc twice in a row
// without allocating the first page we return, we'll return the same
// page the second time.
//
// Hint: Use INDEX2FD.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_MAX_FD: no more file descriptors
// On error, *fd_store is set to 0.
int
fd_alloc(struct Fd **fd_store)
{
int i;
struct Fd *fd;
for (i = 0; i < MAXFD; i++) {
fd = INDEX2FD(i);
if ((vpd[PDX(fd)] & PTE_P) == 0 || (vpt[VPN(fd)] & PTE_P) == 0) {
*fd_store = fd;
return 0;
}
}
*fd_store = 0;
return -E_MAX_OPEN;
}
// Check that fdnum is in range and mapped.
// If it is, set *fd_store to the fd page virtual address.
//
// Returns 0 on success (the page is in range and mapped), < 0 on error.
// Errors are:
// -E_INVAL: fdnum was either not in range or not mapped.
int
fd_lookup(int fdnum, struct Fd **fd_store)
{
struct Fd *fd;
if (fdnum < 0 || fdnum >= MAXFD) {
if (debug)
cprintf("[%08x] bad fd %d\n", env->env_id, fd);
return -E_INVAL;
}
fd = INDEX2FD(fdnum);
if (!(vpd[PDX(fd)] & PTE_P) || !(vpt[VPN(fd)] & PTE_P)) {
if (debug)
cprintf("[%08x] closed fd %d\n", env->env_id, fd);
return -E_INVAL;
}
*fd_store = fd;
return 0;
}
// Frees file descriptor 'fd' by closing the corresponding file
// and unmapping the file descriptor page.
// If 'must_exist' is 0, then fd can be a closed or nonexistent file
// descriptor; the function will return 0 and have no other effect.
// If 'must_exist' is 1, then fd_close returns -E_INVAL when passed a
// closed or nonexistent file descriptor.
// Returns 0 on success, < 0 on error.
int
fd_close(struct Fd *fd, bool must_exist)
{
struct Fd *fd2;
struct Dev *dev;
int r;
if ((r = fd_lookup(fd2num(fd), &fd2)) < 0
|| fd != fd2)
return (must_exist ? r : 0);
if ((r = dev_lookup(fd->fd_dev_id, &dev)) >= 0) {
if (dev->dev_close)
r = (*dev->dev_close)(fd);
else
r = 0;
}
// Make sure fd is unmapped. Might be a no-op if
// (*dev->dev_close)(fd) already unmapped it.
(void) sys_page_unmap(0, fd);
return r;
}
// --------------------------------------------------------------
// File functions
// --------------------------------------------------------------
static struct Dev *devtab[] =
{
&devfile,
0
};
int
dev_lookup(int dev_id, struct Dev **dev)
{
int i;
for (i = 0; devtab[i]; i++)
if (devtab[i]->dev_id == dev_id) {
*dev = devtab[i];
return 0;
}
cprintf("[%08x] unknown device type %d\n", env->env_id, dev_id);
*dev = 0;
return -E_INVAL;
}
int
close(int fdnum)
{
struct Fd *fd;
int r;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
else
return fd_close(fd, 1);
}
void
close_all(void)
{
int i;
for (i = 0; i < MAXFD; i++)
close(i);
}
// Make file descriptor 'newfdnum' a duplicate of file descriptor 'oldfdnum'.
// For instance, writing onto either file descriptor will affect the
// file and the file offset of the other.
// Closes any previously open file descriptor at 'newfdnum'.
// This is implemented using virtual memory tricks (of course!).
int
dup(int oldfdnum, int newfdnum)
{
int r;
char *ova, *nva;
pte_t pte;
struct Fd *oldfd, *newfd;
if ((r = fd_lookup(oldfdnum, &oldfd)) < 0)
return r;
close(newfdnum);
newfd = INDEX2FD(newfdnum);
ova = fd2data(oldfd);
nva = fd2data(newfd);
if ((vpd[PDX(ova)] & PTE_P) && (vpt[VPN(ova)] & PTE_P))
if ((r = sys_page_map(0, ova, 0, nva, vpt[VPN(ova)] & PTE_USER)) < 0)
goto err;
if ((r = sys_page_map(0, oldfd, 0, newfd, vpt[VPN(oldfd)] & PTE_USER)) < 0)
goto err;
return newfdnum;
err:
sys_page_unmap(0, newfd);
sys_page_unmap(0, nva);
return r;
}
ssize_t
read(int fdnum, void *buf, size_t n)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
cprintf("[%08x] read %d -- bad mode\n", env->env_id, fdnum);
return -E_INVAL;
}
if (!dev->dev_read)
return -E_NOT_SUPP;
return (*dev->dev_read)(fd, buf, n);
}
ssize_t
readn(int fdnum, void *buf, size_t n)
{
int m, tot;
for (tot = 0; tot < n; tot += m) {
m = read(fdnum, (char*)buf + tot, n - tot);
if (m < 0)
return m;
if (m == 0)
break;
}
return tot;
}
ssize_t
write(int fdnum, const void *buf, size_t n)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
cprintf("[%08x] write %d -- bad mode\n", env->env_id, fdnum);
return -E_INVAL;
}
if (debug)
cprintf("write %d %p %d via dev %s\n",
fdnum, buf, n, dev->dev_name);
if (!dev->dev_write)
return -E_NOT_SUPP;
return (*dev->dev_write)(fd, buf, n);
}
int
seek(int fdnum, off_t offset)
{
int r;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
fd->fd_offset = offset;
return 0;
}
int
ftruncate(int fdnum, off_t newsize)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
cprintf("[%08x] ftruncate %d -- bad mode\n",
env->env_id, fdnum);
return -E_INVAL;
}
if (!dev->dev_trunc)
return -E_NOT_SUPP;
return (*dev->dev_trunc)(fd, newsize);
}
int
fstat(int fdnum, struct Stat *stat)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if (!dev->dev_stat)
return -E_NOT_SUPP;
stat->st_name[0] = 0;
stat->st_size = 0;
stat->st_isdir = 0;
stat->st_dev = dev;
return (*dev->dev_stat)(fd, stat);
}
int
stat(const char *path, struct Stat *stat)
{
int fd, r;
if ((fd = open(path, O_RDONLY)) < 0)
return fd;
r = fstat(fd, stat);
close(fd);
return r;
}