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 /*===========================================================================*
16 * do_read *
17 *===========================================================================*/
do_read(ino_t ino_nr,struct fsdriver_data * data,size_t count,off_t pos,int call)18 ssize_t do_read(ino_t ino_nr, struct fsdriver_data *data, size_t count,
19 off_t pos, int call)
20 {
21 /* Read data from a file.
22 */
23 struct inode *ino;
24 size_t size, off;
25 char *ptr;
26 int r, chunk;
27
28 if ((ino = find_inode(ino_nr)) == NULL)
29 return EINVAL;
30
31 if (IS_DIR(ino)) return EISDIR;
32
33 if ((r = get_handle(ino)) != OK)
34 return r;
35
36 assert(count > 0);
37
38 /* Use the buffer from below to eliminate extra copying. */
39 size = sffs_table->t_readbuf(&ptr);
40 off = 0;
41
42 while (count > 0) {
43 chunk = MIN(count, size);
44
45 if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
46 break;
47
48 chunk = r;
49
50 if ((r = fsdriver_copyout(data, off, ptr, chunk)) != OK)
51 break;
52
53 count -= chunk;
54 off += chunk;
55 pos += chunk;
56 }
57
58 if (r < 0)
59 return r;
60
61 return off;
62 }
63
64 /*===========================================================================*
65 * do_getdents *
66 *===========================================================================*/
do_getdents(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t * posp)67 ssize_t do_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
68 off_t *posp)
69 {
70 /* Retrieve directory entries.
71 */
72 struct fsdriver_dentry fsdentry;
73 char name[NAME_MAX+1];
74 struct inode *ino, *child;
75 struct sffs_attr attr;
76 off_t pos;
77 int r;
78 /* must be at least sizeof(struct dirent) + NAME_MAX */
79 static char buf[BLOCK_SIZE];
80
81 if ((ino = find_inode(ino_nr)) == NULL)
82 return EINVAL;
83
84 if (!IS_DIR(ino)) return ENOTDIR;
85
86 if (*posp < 0 || *posp >= ULONG_MAX) return EINVAL;
87
88 /* We are going to need at least one free inode to store children in. */
89 if (!have_free_inode()) return ENFILE;
90
91 /* If we don't have a directory handle yet, get one now. */
92 if ((r = get_handle(ino)) != OK)
93 return r;
94
95 fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf));
96
97 /* We use the seek position as file index number. The first position is for
98 * the "." entry, the second position is for the ".." entry, and the next
99 * position numbers each represent a file in the directory.
100 */
101 for (;;) {
102 /* Determine which inode and name to use for this entry.
103 * We have no idea whether the host will give us "." and/or "..",
104 * so generate our own and skip those from the host.
105 */
106 pos = (*posp)++;
107
108 if (pos == 0) {
109 /* Entry for ".". */
110 child = ino;
111
112 strcpy(name, ".");
113
114 get_inode(child);
115 }
116 else if (pos == 1) {
117 /* Entry for "..", but only when there is a parent. */
118 if (ino->i_parent == NULL)
119 continue;
120
121 child = ino->i_parent;
122
123 strcpy(name, "..");
124
125 get_inode(child);
126 }
127 else {
128 /* Any other entry, not being "." or "..". */
129 attr.a_mask = SFFS_ATTR_MODE;
130
131 r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
132 sizeof(name), &attr);
133
134 if (r != OK) {
135 /* No more entries? Then close the handle and stop. */
136 if (r == ENOENT) {
137 put_handle(ino);
138
139 break;
140 }
141
142 /* FIXME: what if the error is ENAMETOOLONG? */
143 return r;
144 }
145
146 if (!strcmp(name, ".") || !strcmp(name, ".."))
147 continue;
148
149 if ((child = lookup_dentry(ino, name)) == NULL) {
150 child = get_free_inode();
151
152 /* We were promised a free inode! */
153 assert(child != NULL);
154
155 child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
156
157 add_dentry(ino, name, child);
158 }
159 }
160
161 r = fsdriver_dentry_add(&fsdentry, INODE_NR(child), name, strlen(name),
162 IS_DIR(child) ? DT_DIR : DT_REG);
163
164 put_inode(child);
165
166 if (r < 0)
167 return r;
168 if (r == 0)
169 break;
170 }
171
172 return fsdriver_dentry_finish(&fsdentry);
173 }
174