xref: /minix/minix/lib/libsffs/read.c (revision 7f5f010b)
1 /* This file contains file and directory reading file system call handlers.
2  *
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
6  *
7  * Created:
8  *   April 2009 (D.C. van Moolenbroek)
9  */
10 
11 #include "inc.h"
12 
13 #include <dirent.h>
14 
15 #define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
16 
17 /*===========================================================================*
18  *				do_read					     *
19  *===========================================================================*/
20 int do_read(void)
21 {
22 /* Read data from a file.
23  */
24   struct inode *ino;
25   off_t pos;
26   size_t count, size;
27   vir_bytes off;
28   char *ptr;
29   int r, chunk;
30 
31   if ((ino = find_inode(m_in.m_vfs_fs_readwrite.inode)) == NULL)
32 	return EINVAL;
33 
34   if (IS_DIR(ino)) return EISDIR;
35 
36   if ((r = get_handle(ino)) != OK)
37 	return r;
38 
39   pos = m_in.m_vfs_fs_readwrite.seek_pos;
40   count = m_in.m_vfs_fs_readwrite.nbytes;
41 
42   assert(count > 0);
43 
44   /* Use the buffer from below to eliminate extra copying. */
45   size = sffs_table->t_readbuf(&ptr);
46   off = 0;
47 
48   while (count > 0) {
49 	chunk = MIN(count, size);
50 
51 	if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
52 		break;
53 
54 	chunk = r;
55 
56 	r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_readwrite.grant, off,
57 		(vir_bytes) ptr, chunk);
58 
59 	if (r != OK)
60 		break;
61 
62 	count -= chunk;
63 	off += chunk;
64 	pos += chunk;
65   }
66 
67   if (r < 0)
68 	return r;
69 
70   m_out.m_fs_vfs_readwrite.seek_pos = pos;
71   m_out.m_fs_vfs_readwrite.nbytes = off;
72 
73   return OK;
74 }
75 
76 /*===========================================================================*
77  *				do_getdents				     *
78  *===========================================================================*/
79 int do_getdents(void)
80 {
81 /* Retrieve directory entries.
82  */
83   char name[NAME_MAX+1];
84   struct inode *ino, *child;
85   struct dirent *dent;
86   struct sffs_attr attr;
87   size_t len, off, user_off, user_left;
88   off_t pos;
89   int r, namelen;
90   /* must be at least sizeof(struct dirent) + NAME_MAX */
91   static char buf[BLOCK_SIZE];
92 
93   attr.a_mask = SFFS_ATTR_MODE;
94 
95   if ((ino = find_inode(m_in.m_vfs_fs_getdents.inode)) == NULL)
96 	return EINVAL;
97 
98   if(m_in.m_vfs_fs_getdents.seek_pos >= ULONG_MAX) return EINVAL;
99 
100   if (!IS_DIR(ino)) return ENOTDIR;
101 
102   /* We are going to need at least one free inode to store children in. */
103   if (!have_free_inode()) return ENFILE;
104 
105   /* If we don't have a directory handle yet, get one now. */
106   if ((r = get_handle(ino)) != OK)
107 	return r;
108 
109   off = 0;
110   user_off = 0;
111   user_left = m_in.m_vfs_fs_getdents.mem_size;
112 
113   /* We use the seek position as file index number. The first position is for
114    * the "." entry, the second position is for the ".." entry, and the next
115    * position numbers each represent a file in the directory.
116    */
117   for (pos = m_in.m_vfs_fs_getdents.seek_pos; ; pos++) {
118 	/* Determine which inode and name to use for this entry.
119 	 * We have no idea whether the host will give us "." and/or "..",
120 	 * so generate our own and skip those from the host.
121 	 */
122 	if (pos == 0) {
123 		/* Entry for ".". */
124 		child = ino;
125 
126 		strcpy(name, ".");
127 
128 		get_inode(child);
129 	}
130 	else if (pos == 1) {
131 		/* Entry for "..", but only when there is a parent. */
132 		if (ino->i_parent == NULL)
133 			continue;
134 
135 		child = ino->i_parent;
136 
137 		strcpy(name, "..");
138 
139 		get_inode(child);
140 	}
141 	else {
142 		/* Any other entry, not being "." or "..". */
143 		r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
144 			sizeof(name), &attr);
145 
146 		if (r != OK) {
147 			/* No more entries? Then close the handle and stop. */
148 			if (r == ENOENT) {
149 				put_handle(ino);
150 
151 				break;
152 			}
153 
154 			/* FIXME: what if the error is ENAMETOOLONG? */
155 			return r;
156 		}
157 
158 		if (!strcmp(name, ".") || !strcmp(name, ".."))
159 			continue;
160 
161 		if ((child = lookup_dentry(ino, name)) == NULL) {
162 			child = get_free_inode();
163 
164 			/* We were promised a free inode! */
165 			assert(child != NULL);
166 
167 			child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
168 
169 			add_dentry(ino, name, child);
170 		}
171 	}
172 
173 	/* record length incl. alignment. */
174 	namelen = strlen(name);
175 	len = _DIRENT_RECLEN(dent, namelen);
176 
177 	/* Is the user buffer too small to store another record?
178 	 * Note that we will be rerequesting the same dentry upon a subsequent
179 	 * getdents call this way, but we really need the name length for this.
180 	 */
181 	if (user_off + off + len > user_left) {
182 		put_inode(child);
183 
184 		/* Is the user buffer too small for even a single record? */
185 		if (user_off == 0 && off == 0)
186 			return EINVAL;
187 
188 		break;
189 	}
190 
191 	/* If our own buffer cannot contain the new record, copy out first. */
192 	if (off + len > sizeof(buf)) {
193 		r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_getdents.grant,
194 			user_off, (vir_bytes) buf, off);
195 
196 		if (r != OK) {
197 			put_inode(child);
198 
199 			return r;
200 		}
201 
202 		user_off += off;
203 		user_left -= off;
204 		off = 0;
205 	}
206 
207 	/* Fill in the actual directory entry. */
208 	dent = (struct dirent *) &buf[off];
209 	dent->d_ino = INODE_NR(child);
210 	dent->d_reclen = len;
211 	dent->d_namlen = namelen;
212 	dent->d_type = IS_DIR(child) ? DT_DIR : DT_REG;
213 	strcpy(dent->d_name, name);
214 
215 	off += len;
216 
217 	put_inode(child);
218   }
219 
220   /* If there is anything left in our own buffer, copy that out now. */
221   if (off > 0) {
222 	r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_getdents.grant, user_off,
223 		(vir_bytes) buf, off);
224 
225 	if (r != OK)
226 		return r;
227 
228 	user_off += off;
229   }
230 
231   m_out.m_fs_vfs_getdents.seek_pos = pos;
232   m_out.m_fs_vfs_getdents.nbytes = user_off;
233 
234   return OK;
235 }
236