VM: restore >4k secondary cache functionality
[minix.git] / lib / libsffs / read.c
blobe34bd28d2ee0261cfc903d1fea7e8f4d85cd2505
1 /* This file contains file and directory reading file system call handlers.
3 * The entry points into this file are:
4 * do_read perform the READ file system call
5 * do_getdents perform the GETDENTS file system call
7 * Created:
8 * April 2009 (D.C. van Moolenbroek)
9 */
11 #include "inc.h"
13 #include <dirent.h>
15 #define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
17 /*===========================================================================*
18 * do_read *
19 *===========================================================================*/
20 int do_read()
22 /* Read data from a file.
24 struct inode *ino;
25 u64_t pos;
26 size_t count, size;
27 vir_bytes off;
28 char *ptr;
29 int r, chunk;
31 if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
32 return EINVAL;
34 if (IS_DIR(ino)) return EISDIR;
36 if ((r = get_handle(ino)) != OK)
37 return r;
39 pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
40 count = m_in.REQ_NBYTES;
42 assert(count > 0);
44 /* Use the buffer from below to eliminate extra copying. */
45 size = sffs_table->t_readbuf(&ptr);
46 off = 0;
48 while (count > 0) {
49 chunk = MIN(count, size);
51 if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
52 break;
54 chunk = r;
56 r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, off,
57 (vir_bytes) ptr, chunk);
59 if (r != OK)
60 break;
62 count -= chunk;
63 off += chunk;
64 pos = add64u(pos, chunk);
67 if (r < 0)
68 return r;
70 m_out.RES_SEEK_POS_HI = ex64hi(pos);
71 m_out.RES_SEEK_POS_LO = ex64lo(pos);
72 m_out.RES_NBYTES = off;
74 return OK;
77 /*===========================================================================*
78 * do_getdents *
79 *===========================================================================*/
80 int do_getdents()
82 /* Retrieve directory entries.
84 char name[NAME_MAX+1];
85 struct inode *ino, *child;
86 struct dirent *dent;
87 struct sffs_attr attr;
88 size_t len, off, user_off, user_left;
89 off_t pos;
90 int r;
91 /* must be at least sizeof(struct dirent) + NAME_MAX */
92 static char buf[BLOCK_SIZE];
94 attr.a_mask = SFFS_ATTR_MODE;
96 if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL)
97 return EINVAL;
99 if (m_in.REQ_SEEK_POS_HI != 0) return EINVAL;
101 if (!IS_DIR(ino)) return ENOTDIR;
103 /* We are going to need at least one free inode to store children in. */
104 if (!have_free_inode()) return ENFILE;
106 /* If we don't have a directory handle yet, get one now. */
107 if ((r = get_handle(ino)) != OK)
108 return r;
110 off = 0;
111 user_off = 0;
112 user_left = m_in.REQ_MEM_SIZE;
114 /* We use the seek position as file index number. The first position is for
115 * the "." entry, the second position is for the ".." entry, and the next
116 * position numbers each represent a file in the directory.
118 for (pos = m_in.REQ_SEEK_POS_LO; ; pos++) {
119 /* Determine which inode and name to use for this entry.
120 * We have no idea whether the host will give us "." and/or "..",
121 * so generate our own and skip those from the host.
123 if (pos == 0) {
124 /* Entry for ".". */
125 child = ino;
127 strcpy(name, ".");
129 get_inode(child);
131 else if (pos == 1) {
132 /* Entry for "..", but only when there is a parent. */
133 if (ino->i_parent == NULL)
134 continue;
136 child = ino->i_parent;
138 strcpy(name, "..");
140 get_inode(child);
142 else {
143 /* Any other entry, not being "." or "..". */
144 r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
145 sizeof(name), &attr);
147 if (r != OK) {
148 /* No more entries? Then close the handle and stop. */
149 if (r == ENOENT) {
150 put_handle(ino);
152 break;
155 /* FIXME: what if the error is ENAMETOOLONG? */
156 return r;
159 if (!strcmp(name, ".") || !strcmp(name, ".."))
160 continue;
162 if ((child = lookup_dentry(ino, name)) == NULL) {
163 child = get_free_inode();
165 /* We were promised a free inode! */
166 assert(child != NULL);
168 child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
170 add_dentry(ino, name, child);
174 len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name));
176 /* Is the user buffer too small to store another record?
177 * Note that we will be rerequesting the same dentry upon a subsequent
178 * getdents call this way, but we really need the name length for this.
180 if (user_off + off + len > user_left) {
181 put_inode(child);
183 /* Is the user buffer too small for even a single record? */
184 if (user_off == 0 && off == 0)
185 return EINVAL;
187 break;
190 /* If our own buffer cannot contain the new record, copy out first. */
191 if (off + len > sizeof(buf)) {
192 r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT,
193 user_off, (vir_bytes) buf, off);
195 if (r != OK) {
196 put_inode(child);
198 return r;
201 user_off += off;
202 user_left -= off;
203 off = 0;
206 /* Fill in the actual directory entry. */
207 dent = (struct dirent *) &buf[off];
208 dent->d_ino = INODE_NR(child);
209 dent->d_off = pos;
210 dent->d_reclen = len;
211 strcpy(dent->d_name, name);
213 off += len;
215 put_inode(child);
218 /* If there is anything left in our own buffer, copy that out now. */
219 if (off > 0) {
220 r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, user_off,
221 (vir_bytes) buf, off);
223 if (r != OK)
224 return r;
226 user_off += off;
229 m_out.RES_SEEK_POS_HI = 0;
230 m_out.RES_SEEK_POS_LO = pos;
231 m_out.RES_NBYTES = user_off;
233 return OK;