* same with xv6
[mascara-docs.git] / i386 / ucla / src / lab5 / lib / fd.c
blobac1e8874a08d4656f07c98b60801d29b2a69b645
1 #include <inc/lib.h>
3 // Maximum number of file descriptors a program may hold open concurrently
4 #define NFD 32
5 // Bottom of file descriptor area
6 #define FDTABLE 0xD0000000
7 // Bottom of file data area. We reserve one data page for each FD,
8 // which devices can use if they choose.
9 #define FDDATA (FDTABLE + NFD*PGSIZE)
11 static bool fd_isopen(const struct Fd *fd);
14 // --------------------------------------------------------------
15 // Low-level file descriptor manipulators
16 // --------------------------------------------------------------
18 // Return the 'struct Fd' for a file descriptor number.
19 // Returns NULL on failure, a pointer on success.
20 // If 'must_exist' is true, then the file descriptor must currently be open
21 // or NULL is returned.
22 // Returns 0 on success, < 0 error code on failure.
24 int
25 fd_lookup(int fdnum, struct Fd **fd_store, bool must_exist)
27 struct Fd *fd = (struct Fd *) (FDTABLE + fdnum * PGSIZE);
29 if (fdnum < 0 || fdnum >= NFD || (must_exist && !fd_isopen(fd))) {
30 *fd_store = 0;
31 return -E_INVAL;
32 } else {
33 *fd_store = fd;
34 return 0;
38 // Return true iff 'fd' is a valid file descriptor pointer.
40 static bool
41 fd_valid(const struct Fd *fd)
43 return PGOFF(fd) == 0 && fd >= (const struct Fd *) FDTABLE
44 && fd < (const struct Fd *) (FDTABLE + NFD * PGSIZE);
47 // Return true iff 'fd' is currently open.
49 static bool
50 fd_isopen(const struct Fd *fd)
52 assert(fd_valid(fd));
53 return (vpd[PDX(fd)] & PTE_P) && (vpt[PGNUM(fd)] & PTE_P);
56 // Return the file descriptor number for a 'struct Fd'.
58 int
59 fd2num(struct Fd *fd)
61 assert(fd_valid(fd));
62 return ((uintptr_t) fd - FDTABLE) / PGSIZE;
65 // Return the file descriptor data pointer for a 'struct Fd'.
67 char *
68 fd2data(struct Fd *fd)
70 int num = fd2num(fd);
71 return (char *) (FDDATA + num * PGSIZE);
75 // Finds the smallest index from 0 to NFD-1 that doesn't have
76 // its fd page mapped.
77 // Sets *fd_store to the corresponding fd page virtual address.
79 // Does NOT actually allocate an fd page.
80 // It is up to the caller to allocate the page somehow.
81 // This means that if someone calls fd_find_unused twice in a row
82 // without allocating the first page we return, we'll return the same
83 // page the second time.
85 // Returns 0 on success, < 0 on error. Errors are:
86 // -E_MAX_FD: no more file descriptors
87 // On error, *fd_store is set to 0.
88 int
89 fd_find_unused(struct Fd **fd_store)
91 int i;
92 struct Fd *fd;
94 for (i = 0; i < NFD; i++) {
95 (void) fd_lookup(i, &fd, false);
96 if (!fd_isopen(fd)) {
97 *fd_store = fd;
98 return 0;
101 *fd_store = 0;
102 return -E_MAX_OPEN;
105 // Frees file descriptor 'fd' by closing the corresponding file
106 // and unmapping the file descriptor page.
107 // If 'must_exist' is 0, then fd can be a closed or nonexistent file
108 // descriptor; the function will return 0 and have no other effect.
109 // If 'must_exist' is 1, then fd_close returns -E_INVAL when passed a
110 // closed or nonexistent file descriptor.
111 // Returns 0 on success, < 0 on error.
113 fd_close(struct Fd *fd, bool must_exist)
115 struct Fd *fd2;
116 struct Dev *dev;
117 int r;
118 if ((r = fd_lookup(fd2num(fd), &fd2, must_exist)) < 0
119 || fd != fd2)
120 return (must_exist ? -E_INVAL : 0);
121 if ((r = dev_lookup(fd->fd_dev_id, &dev)) >= 0) {
122 if (dev->dev_close)
123 r = (*dev->dev_close)(fd);
124 else
125 r = 0;
127 // Make sure fd is unmapped. Might be a no-op if
128 // (*dev->dev_close)(fd) already unmapped it.
129 (void) sys_page_unmap(0, fd);
130 return r;
133 static struct Dev *devtab[] =
135 &devfile,
136 &devpipe,
137 &devcons,
142 dev_lookup(int dev_id, struct Dev **dev)
144 int i;
145 for (i = 0; devtab[i]; i++)
146 if (devtab[i]->dev_id == dev_id) {
147 *dev = devtab[i];
148 return 0;
150 cprintf("[%08x] unknown device type %d\n", thisenv->env_id, dev_id);
151 *dev = 0;
152 return -E_INVAL;
156 // --------------------------------------------------------------
157 // File descriptor interface functions
158 // --------------------------------------------------------------
161 close(int fdnum)
163 struct Fd *fd;
164 int r;
165 if ((r = fd_lookup(fdnum, &fd, true)) < 0)
166 return r;
167 else
168 return fd_close(fd, true);
171 void
172 close_all(void)
174 int i;
175 for (i = 0; i < NFD; i++)
176 close(i);
179 // Make file descriptor 'newfdnum' a duplicate of file descriptor 'oldfdnum'.
180 // For instance, writing onto either file descriptor will affect the
181 // file and the file offset of the other.
182 // Closes any previously open file descriptor at 'newfdnum'.
183 // This is implemented using virtual memory tricks (of course!).
185 dup(int oldfdnum, int newfdnum)
187 int r;
188 char *ova, *nva;
189 pte_t pte;
190 struct Fd *oldfd, *newfd;
192 if ((r = fd_lookup(oldfdnum, &oldfd, true)) < 0
193 || (r = fd_lookup(newfdnum, &newfd, false)) < 0)
194 return r;
195 close(newfdnum);
197 ova = fd2data(oldfd);
198 nva = fd2data(newfd);
200 if ((vpd[PDX(ova)] & PTE_P) && (vpt[PGNUM(ova)] & PTE_P))
201 if ((r = sys_page_map(0, ova, 0, nva, vpt[PGNUM(ova)] & PTE_USER)) < 0)
202 goto err;
203 if ((r = sys_page_map(0, oldfd, 0, newfd, vpt[PGNUM(oldfd)] & PTE_USER)) < 0)
204 goto err;
206 return newfdnum;
208 err:
209 sys_page_unmap(0, newfd);
210 sys_page_unmap(0, nva);
211 return r;
214 ssize_t
215 read(int fdnum, void *buf, size_t n)
217 int r;
218 struct Dev *dev;
219 struct Fd *fd;
221 if ((r = fd_lookup(fdnum, &fd, true)) < 0
222 || (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
223 return r;
224 if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
225 cprintf("[%08x] read %d -- bad mode\n", thisenv->env_id, fdnum);
226 return -E_INVAL;
228 if (!dev->dev_read)
229 return -E_NOT_SUPP;
230 return (*dev->dev_read)(fd, buf, n);
233 ssize_t
234 readn(int fdnum, void *buf, size_t n)
236 int m;
237 size_t tot;
239 for (tot = 0; tot < n; tot += m) {
240 m = read(fdnum, (char*)buf + tot, n - tot);
241 if (m < 0)
242 return m;
243 if (m == 0)
244 break;
246 return tot;
249 ssize_t
250 write(int fdnum, const void *buf, size_t n)
252 int r;
253 struct Dev *dev;
254 struct Fd *fd;
256 if ((r = fd_lookup(fdnum, &fd, true)) < 0
257 || (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
258 return r;
259 if ((fd->fd_omode & O_ACCMODE) == O_RDONLY)
260 return -E_INVAL;
261 if (!dev->dev_write)
262 return -E_NOT_SUPP;
263 return (*dev->dev_write)(fd, buf, n);
267 seek(int fdnum, off_t offset)
269 int r;
270 struct Fd *fd;
272 if ((r = fd_lookup(fdnum, &fd, true)) < 0)
273 return r;
274 fd->fd_offset = offset;
275 return 0;
279 ftruncate(int fdnum, off_t newsize)
281 int r;
282 struct Dev *dev;
283 struct Fd *fd;
285 if ((r = fd_lookup(fdnum, &fd, true)) < 0
286 || (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
287 return r;
288 if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
289 cprintf("[%08x] ftruncate %d -- bad mode\n", thisenv->env_id, fdnum);
290 return -E_INVAL;
292 if (!dev->dev_trunc)
293 return -E_NOT_SUPP;
294 return (*dev->dev_trunc)(fd, newsize);
298 fstat(int fdnum, struct Stat *stat)
300 int r;
301 struct Dev *dev;
302 struct Fd *fd;
304 if ((r = fd_lookup(fdnum, &fd, true)) < 0
305 || (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
306 return r;
307 if (!dev->dev_stat)
308 return -E_NOT_SUPP;
309 stat->st_name[0] = 0;
310 stat->st_size = 0;
311 stat->st_ftype = 0;
312 stat->st_dev = dev;
313 return (*dev->dev_stat)(fd, stat);
317 stat(const char *path, struct Stat *stat)
319 int fd, r;
321 if ((fd = open(path, O_RDONLY)) < 0)
322 return fd;
323 r = fstat(fd, stat);
324 close(fd);
325 return r;