segment_alloc may not allocate enough memory.
[mit-jos.git] / lib / file.c
blob21933be7cf8df03ab2dee1b4e8db056145a8a9fd
1 #include <inc/fs.h>
2 #include <inc/string.h>
3 #include <inc/lib.h>
5 #define debug 0
7 static int file_close(struct Fd *fd);
8 static ssize_t file_read(struct Fd *fd, void *buf, size_t n, off_t offset);
9 static ssize_t file_write(struct Fd *fd, const void *buf, size_t n, off_t offset);
10 static int file_stat(struct Fd *fd, struct Stat *stat);
11 static int file_trunc(struct Fd *fd, off_t newsize);
13 struct Dev devfile =
15 .dev_id = 'f',
16 .dev_name = "file",
17 .dev_read = file_read,
18 .dev_write = file_write,
19 .dev_close = file_close,
20 .dev_stat = file_stat,
21 .dev_trunc = file_trunc
24 // Helper functions for file access
25 static int fmap(struct Fd *fd, off_t oldsize, off_t newsize);
26 static int funmap(struct Fd *fd, off_t oldsize, off_t newsize, bool dirty);
28 // Open a file (or directory),
29 // returning the file descriptor index on success, < 0 on failure.
30 int
31 open(const char *path, int mode)
33 // Find an unused file descriptor page using fd_alloc.
34 // Then send a message to the file server to open a file
35 // using a function in fsipc.c.
36 // (fd_alloc does not allocate a page, it just returns an
37 // unused fd address. Do you need to allocate a page? Look
38 // at fsipc.c if you aren't sure.)
39 // Then map the file data (you may find fmap() helpful).
40 // Return the file descriptor index.
41 // If any step fails, use fd_close to free the file descriptor.
42 int r;
43 struct Fd *fd;
45 if ((r = fd_alloc(&fd)) < 0)
46 return r;
47 if ((r = fsipc_open(path, mode, fd)) < 0) {
48 fd_close(fd, 0);
49 return r;
51 if ((r = fmap(fd, 0, fd->fd_file.file.f_size)) < 0) {
52 fd_close(fd, 0);
53 return r;
55 return fd2num(fd);
58 // Clean up a file-server file descriptor.
59 // This function is called by fd_close.
60 static int
61 file_close(struct Fd *fd)
63 // Unmap any data mapped for the file,
64 // then tell the file server that we have closed the file
65 // (to free up its resources).
66 off_t size;
67 int id, r;
69 size = fd->fd_file.file.f_size;
70 id = fd->fd_file.id;
72 if ((r = funmap(fd, size, size, 1)) < 0)
73 return r;
74 fsipc_close(id);
76 return 0;
79 // Read 'n' bytes from 'fd' at the current seek position into 'buf'.
80 // Since files are memory-mapped, this amounts to a memmove()
81 // surrounded by a little red tape to handle the file size and seek pointer.
82 static ssize_t
83 file_read(struct Fd *fd, void *buf, size_t n, off_t offset)
85 size_t size;
87 // avoid reading past the end of file
88 size = fd->fd_file.file.f_size;
89 if (offset > size)
90 return 0;
91 if (offset + n > size)
92 n = size - offset;
94 // read the data by copying from the file mapping
95 memmove(buf, fd2data(fd) + offset, n);
96 return n;
99 // Find the page that maps the file block starting at 'offset',
100 // and store its address in '*blk'.
102 read_map(int fdnum, off_t offset, void **blk)
104 int r;
105 char *va;
106 struct Fd *fd;
108 if ((r = fd_lookup(fdnum, &fd)) < 0)
109 return r;
110 if (fd->fd_dev_id != devfile.dev_id)
111 return -E_INVAL;
112 va = fd2data(fd) + offset;
113 if (offset >= MAXFILESIZE)
114 return -E_NO_DISK;
115 if (!(vpd[PDX(va)] & PTE_P) || !(vpt[VPN(va)] & PTE_P))
116 return -E_NO_DISK;
117 *blk = (void*) va;
118 return 0;
121 // Write 'n' bytes from 'buf' to 'fd' at the current seek position.
122 static ssize_t
123 file_write(struct Fd *fd, const void *buf, size_t n, off_t offset)
125 int r;
126 size_t tot;
128 // don't write past the maximum file size
129 tot = offset + n;
130 if (tot > MAXFILESIZE)
131 return -E_NO_DISK;
133 // increase the file's size if necessary
134 if (tot > fd->fd_file.file.f_size) {
135 if ((r = file_trunc(fd, tot)) < 0)
136 return r;
139 // write the data
140 memmove(fd2data(fd) + offset, buf, n);
141 return n;
144 static int
145 file_stat(struct Fd *fd, struct Stat *st)
147 strcpy(st->st_name, fd->fd_file.file.f_name);
148 st->st_size = fd->fd_file.file.f_size;
149 st->st_isdir = (fd->fd_file.file.f_type == FTYPE_DIR);
150 return 0;
153 // Truncate or extend an open file to 'size' bytes
154 static int
155 file_trunc(struct Fd *fd, off_t newsize)
157 int r;
158 off_t oldsize;
159 uint32_t fileid;
161 if (newsize > MAXFILESIZE)
162 return -E_NO_DISK;
164 fileid = fd->fd_file.id;
165 oldsize = fd->fd_file.file.f_size;
166 if ((r = fsipc_set_size(fileid, newsize)) < 0)
167 return r;
168 assert(fd->fd_file.file.f_size == newsize);
170 if ((r = fmap(fd, oldsize, newsize)) < 0)
171 return r;
172 funmap(fd, oldsize, newsize, 0);
174 return 0;
177 // Call the file system server to obtain and map file pages
178 // when the size of the file as mapped in our memory increases.
179 // Harmlessly does nothing if oldsize >= newsize.
180 // Returns 0 on success, < 0 on error.
181 // If there is an error, unmaps any newly allocated pages.
182 static int
183 fmap(struct Fd* fd, off_t oldsize, off_t newsize)
185 size_t i;
186 char *va;
187 int r;
189 va = fd2data(fd);
190 for (i = ROUNDUP(oldsize, PGSIZE); i < newsize; i += PGSIZE) {
191 if ((r = fsipc_map(fd->fd_file.id, i, va + i)) < 0) {
192 // unmap anything we may have mapped so far
193 funmap(fd, i, oldsize, 0);
194 return r;
197 return 0;
200 // Unmap any file pages that no longer represent valid file pages
201 // when the size of the file as mapped in our address space decreases.
202 // Harmlessly does nothing if newsize >= oldsize.
203 static int
204 funmap(struct Fd* fd, off_t oldsize, off_t newsize, bool dirty)
206 size_t i;
207 char *va;
208 int r, ret;
210 va = fd2data(fd);
212 // Check vpd to see if anything is mapped.
213 if (!(vpd[VPD(va)] & PTE_P))
214 return 0;
216 ret = 0;
217 for (i = ROUNDUP(newsize, PGSIZE); i < oldsize; i += PGSIZE)
218 if (vpt[VPN(va + i)] & PTE_P) {
219 if (dirty
220 && (vpt[VPN(va + i)] & PTE_D)
221 && (r = fsipc_dirty(fd->fd_file.id, i)) < 0)
222 ret = r;
223 sys_page_unmap(0, va + i);
225 return ret;
228 // Delete a file
230 remove(const char *path)
232 return fsipc_remove(path);
235 // Synchronize disk with buffer cache
237 sync(void)
239 return fsipc_sync();