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
236
237
238
239
240
241
242
243
244
245
|
/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
#include "inc.h"
/*
* Directories will generally be accessed sequentially, but there is no
* guarantee that this is actually the case. In particular, multiple user
* processes may iterate over the same directory concurrently, and since
* process information is lost in the VFS/FS protocol, the result is a
* nonsequential pattern on the same single directory handle. Therefore, we
* must support random access. Since the VirtualBox shared folders interface
* does not allow for random access (the resume point cannot be used for this),
* we choose to read in the contents of the directory upon open, and cache it
* until the directory is closed again. The risk is that the cached contents
* will go stale.
*
* The directory will always be closed once one reader finishes going through
* the entire directory, so the problem is rather limited anyway. Ideally, the
* directory contents would be refreshed upon any invalidating local action as
* well as after a certain time span (since the file system can be changed from
* the host as well). This is currently not implemented, because the odds of
* things going wrong are pretty small, and the effects are not devastating.
*
* The calling functions may also request the same directory entry twice in a
* row, because the entry does not fit in the user buffer the first time. The
* routines here optimize for repeat-sequential access, while supporting fully
* random access as well.
*/
#define VBOXFS_DIRDATA_SIZE 8192 /* data allocation granularity */
typedef struct vboxfs_dirblock_s {
STAILQ_ENTRY(vboxfs_dirblock_s) next;
unsigned int count;
char data[VBOXFS_DIRDATA_SIZE];
} vboxfs_dirblock_t;
typedef struct {
STAILQ_HEAD(blocks, vboxfs_dirblock_s) blocks;
unsigned int index;
vboxfs_dirblock_t *block;
unsigned int bindex;
unsigned int bpos;
} vboxfs_dirdata_t;
/*
* Free the memory allocated for the given directory contents storage.
*/
static void
free_dir(vboxfs_dirdata_t *dirdata)
{
vboxfs_dirblock_t *block;
while (!STAILQ_EMPTY(&dirdata->blocks)) {
block = STAILQ_FIRST(&dirdata->blocks);
STAILQ_REMOVE_HEAD(&dirdata->blocks, next);
free(block);
}
free(dirdata);
}
/*
* Read all the contents of the given directory, allocating memory as needed to
* store the data.
*/
static int
read_dir(vboxfs_handle_t handle, sffs_dir_t *dirp)
{
vboxfs_dirdata_t *dirdata;
vboxfs_dirblock_t *block;
vbox_param_t param[8];
unsigned int count;
int r;
dirdata = (vboxfs_dirdata_t *) malloc(sizeof(vboxfs_dirdata_t));
if (dirdata == NULL)
return ENOMEM;
memset(dirdata, 0, sizeof(*dirdata));
STAILQ_INIT(&dirdata->blocks);
r = OK;
do {
block =
(vboxfs_dirblock_t *) malloc(sizeof(vboxfs_dirblock_t));
if (block == NULL) {
r = ENOMEM;
break;
}
vbox_set_u32(¶m[0], vboxfs_root);
vbox_set_u64(¶m[1], handle);
vbox_set_u32(¶m[2], 0); /* flags */
vbox_set_u32(¶m[3], sizeof(block->data));
vbox_set_ptr(¶m[4], NULL, 0, VBOX_DIR_OUT);
vbox_set_ptr(¶m[5], block->data, sizeof(block->data),
VBOX_DIR_IN);
vbox_set_u32(¶m[6], 0); /* resume point */
vbox_set_u32(¶m[7], 0); /* number of files */
/* If the call fails, stop. */
if ((r = vbox_call(vboxfs_conn, VBOXFS_CALL_LIST, param, 8,
NULL)) != OK) {
free(block);
break;
}
/* If the number of returned files is zero, stop. */
if ((count = vbox_get_u32(¶m[7])) == 0) {
free(block);
break;
}
/*
* Add the block to the list. We could realloc() the block to
* free unused tail space, but this is not likely to gain us
* much in general.
*/
block->count = count;
STAILQ_INSERT_TAIL(&dirdata->blocks, block, next);
/* Continue until a zero resume point is returned. */
} while (vbox_get_u32(¶m[6]) != 0);
if (r != OK) {
free_dir(dirdata);
return r;
}
dirdata->block = STAILQ_FIRST(&dirdata->blocks);
*dirp = (sffs_dir_t) dirdata;
return OK;
}
/*
* Open a directory.
*/
int
vboxfs_opendir(const char *path, sffs_dir_t *handle)
{
vboxfs_handle_t h;
int r;
/* Open the directory. */
if ((r = vboxfs_open_file(path, O_RDONLY, S_IFDIR, &h, NULL)) != OK)
return r;
/*
* Upon success, read in the full contents of the directory right away.
* If it succeeds, this will also set the caller's directory handle.
*/
r = read_dir(h, handle);
/* We do not need the directory to be open anymore now. */
vboxfs_close_file(h);
return r;
}
/*
* Read one entry from a directory. On success, copy the name into buf, and
* return the requested attributes. If the name (including terminating null)
* exceeds size, return ENAMETOOLONG. Do not return dot and dot-dot entries.
* Return ENOENT if the index exceeds the number of files.
*/
int
vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, size_t size,
struct sffs_attr *attr)
{
vboxfs_dirdata_t *dirdata;
vboxfs_dirinfo_t *dirinfo;
int r;
dirdata = (vboxfs_dirdata_t *) handle;
/*
* If the saved index exceeds the requested index, start from the
* beginning.
*/
if (dirdata->index > index) {
dirdata->index = 0;
dirdata->bindex = 0;
dirdata->bpos = 0;
dirdata->block = STAILQ_FIRST(&dirdata->blocks);
}
/* Loop until we find the requested entry or we run out of entries. */
while (dirdata->block != NULL) {
dirinfo =
(vboxfs_dirinfo_t *) &dirdata->block->data[dirdata->bpos];
/* Consider all entries that are not dot or dot-dot. */
if (dirinfo->name.len > 2 || dirinfo->name.data[0] != '.' ||
(dirinfo->name.len == 2 && dirinfo->name.data[1] != '.')) {
if (dirdata->index == index)
break;
dirdata->index++;
}
/* Advance to the next entry. */
dirdata->bpos += offsetof(vboxfs_dirinfo_t, name) +
offsetof(vboxfs_path_t, data) + dirinfo->name.size;
if (++dirdata->bindex >= dirdata->block->count) {
dirdata->block = STAILQ_NEXT(dirdata->block, next);
dirdata->bindex = 0;
dirdata->bpos = 0;
}
}
/* Not enough files to satisfy the request? */
if (dirdata->block == NULL)
return ENOENT;
/* Return the information for the file we found. */
if ((r = vboxfs_get_path(&dirinfo->name, buf, size)) != OK)
return r;
vboxfs_get_attr(attr, &dirinfo->info);
return OK;
}
/*
* Close a directory.
*/
int
vboxfs_closedir(sffs_dir_t handle)
{
vboxfs_dirdata_t *dirdata;
dirdata = (vboxfs_dirdata_t *) handle;
free_dir(dirdata);
return OK;
}
|