kernel debug: priv can be NULL early on
[minix.git] / lib / libvtreefs / read.c
blobd5357622ece7bba99d915abe1bf1c28355946813
1 /* VTreeFS - read.c - by Alen Stojanov and David van Moolenbroek */
3 #include "inc.h"
4 #include <dirent.h>
6 #define GETDENTS_BUFSIZ 4096
7 #define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
9 /*===========================================================================*
10 * fs_read *
11 *===========================================================================*/
12 int fs_read(void)
14 /* Read from a file.
16 cp_grant_id_t gid;
17 struct inode *node;
18 off_t pos;
19 size_t len;
20 char *ptr;
21 int r;
23 if (fs_m_in.REQ_SEEK_POS_HI != 0)
24 return EIO;
26 /* Try to get inode by to its inode number. */
27 if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
28 return EINVAL;
30 /* Check whether the node is a regular file. */
31 if (!S_ISREG(node->i_stat.mode))
32 return EINVAL;
34 /* Get the values from the request message. */
35 gid = fs_m_in.REQ_GRANT;
36 pos = fs_m_in.REQ_SEEK_POS_LO;
38 /* Call the read hook, if any. */
39 if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) {
40 len = fs_m_in.REQ_NBYTES;
42 /* On success, the read hook provides us with a pointer to the
43 * resulting data. This avoids copying overhead.
45 r = vtreefs_hooks->read_hook(node, pos, &ptr, &len,
46 get_inode_cbdata(node));
48 assert(len <= fs_m_in.REQ_NBYTES);
50 /* Copy the resulting data to user space. */
51 if (r == OK && len > 0) {
52 r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
53 0, (vir_bytes) ptr, len);
55 } else {
56 /* Feign an empty file. */
57 r = OK;
58 len = 0;
61 if (r == OK) {
62 fs_m_out.RES_SEEK_POS_HI = 0;
63 fs_m_out.RES_SEEK_POS_LO = pos + len;
64 fs_m_out.RES_NBYTES = len;
67 return r;
70 /*===========================================================================*
71 * fs_getdents *
72 *===========================================================================*/
73 int fs_getdents(void)
75 /* Retrieve directory entries.
77 struct inode *node, *child = NULL;
78 struct dirent *dent;
79 char *name;
80 size_t len, off, user_off, user_left;
81 off_t pos;
82 int r, skip, get_next, indexed;
83 static char buf[GETDENTS_BUFSIZ];
85 if (fs_m_in.REQ_SEEK_POS_HI != 0)
86 return EIO;
88 if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
89 return EINVAL;
91 off = 0;
92 user_off = 0;
93 user_left = fs_m_in.REQ_MEM_SIZE;
94 indexed = node->i_indexed;
95 get_next = FALSE;
96 child = NULL;
98 /* Call the getdents hook, if any, to "refresh" the directory. */
99 if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) {
100 r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node));
101 if (r != OK) return r;
104 for (pos = fs_m_in.REQ_SEEK_POS_LO; ; pos++) {
105 /* Determine which inode and name to use for this entry. */
106 if (pos == 0) {
107 /* The "." entry. */
108 child = node;
109 name = ".";
111 else if (pos == 1) {
112 /* The ".." entry. */
113 child = get_parent_inode(node);
114 if (child == NULL)
115 child = node;
116 name = "..";
118 else if (pos - 2 < indexed) {
119 /* All indexed entries. */
120 child = get_inode_by_index(node, pos - 2);
122 /* If there is no inode with this particular index,
123 * continue with the next index number.
125 if (child == NULL) continue;
127 name = child->i_name;
129 else {
130 /* All non-indexed entries. */
132 /* If this is the first loop iteration, first get to
133 * the non-indexed child identified by the current
134 * position.
136 if (get_next == FALSE) {
137 skip = pos - indexed - 2;
138 child = get_first_inode(node);
140 /* Skip indexed children. */
141 while (child != NULL &&
142 child->i_index != NO_INDEX)
143 child = get_next_inode(child);
145 /* Skip to the right position. */
146 while (child != NULL && skip-- > 0)
147 child = get_next_inode(child);
149 get_next = TRUE;
151 else {
152 child = get_next_inode(child);
155 /* No more children? Then stop. */
156 if (child == NULL)
157 break;
159 assert(!is_inode_deleted(child));
161 name = child->i_name;
164 len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name));
166 /* Is the user buffer too small to store another record? */
167 if (user_off + off + len > user_left) {
168 /* Is the user buffer too small for even a single
169 * record?
171 if (user_off == 0 && off == 0)
172 return EINVAL;
174 break;
177 /* If our own buffer cannot contain the new record, copy out
178 * first.
180 if (off + len > sizeof(buf)) {
181 r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
182 user_off, (vir_bytes) buf, off);
183 if (r != OK) return r;
185 user_off += off;
186 user_left -= off;
187 off = 0;
190 /* Fill in the actual directory entry. */
191 dent = (struct dirent *) &buf[off];
192 dent->d_ino = get_inode_number(child);
193 dent->d_off = pos;
194 dent->d_reclen = len;
195 strcpy(dent->d_name, name);
197 off += len;
200 /* If there is anything left in our own buffer, copy that out now. */
201 if (off > 0) {
202 r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
203 user_off, (vir_bytes) buf, off);
204 if (r != OK)
205 return r;
207 user_off += off;
210 fs_m_out.RES_SEEK_POS_HI = 0;
211 fs_m_out.RES_SEEK_POS_LO = pos;
212 fs_m_out.RES_NBYTES = user_off;
214 return OK;