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