add opendir alias
[minix.git] / lib / libvboxfs / dir.c
blob00c7f379985e4539f7995b3593eb489f7273fe3b
1 /* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
3 #include "inc.h"
5 /*
6 * Directories will generally be accessed sequentially, but there is no
7 * guarantee that this is actually the case. In particular, multiple user
8 * processes may iterate over the same directory concurrently, and since
9 * process information is lost in the VFS/FS protocol, the result is a
10 * nonsequential pattern on the same single directory handle. Therefore, we
11 * must support random access. Since the VirtualBox shared folders interface
12 * does not allow for random access (the resume point cannot be used for this),
13 * we choose to read in the contents of the directory upon open, and cache it
14 * until the directory is closed again. The risk is that the cached contents
15 * will go stale.
17 * The directory will always be closed once one reader finishes going through
18 * the entire directory, so the problem is rather limited anyway. Ideally, the
19 * directory contents would be refreshed upon any invalidating local action as
20 * well as after a certain time span (since the file system can be changed from
21 * the host as well). This is currently not implemented, because the odds of
22 * things going wrong are pretty small, and the effects are not devastating.
24 * The calling functions may also request the same directory entry twice in a
25 * row, because the entry does not fit in the user buffer the first time. The
26 * routines here optimize for repeat-sequential access, while supporting fully
27 * random access as well.
30 #define VBOXFS_DIRDATA_SIZE 8192 /* data allocation granularity */
32 typedef struct vboxfs_dirblock_s {
33 STAILQ_ENTRY(vboxfs_dirblock_s) next;
34 unsigned int count;
35 char data[VBOXFS_DIRDATA_SIZE];
36 } vboxfs_dirblock_t;
38 typedef struct {
39 STAILQ_HEAD(blocks, vboxfs_dirblock_s) blocks;
40 unsigned int index;
41 vboxfs_dirblock_t *block;
42 unsigned int bindex;
43 unsigned int bpos;
44 } vboxfs_dirdata_t;
47 * Free the memory allocated for the given directory contents storage.
49 static void
50 free_dir(vboxfs_dirdata_t *dirdata)
52 vboxfs_dirblock_t *block;
54 while (!STAILQ_EMPTY(&dirdata->blocks)) {
55 block = STAILQ_FIRST(&dirdata->blocks);
57 STAILQ_REMOVE_HEAD(&dirdata->blocks, next);
59 free(block);
62 free(dirdata);
66 * Read all the contents of the given directory, allocating memory as needed to
67 * store the data.
69 static int
70 read_dir(vboxfs_handle_t handle, sffs_dir_t *dirp)
72 vboxfs_dirdata_t *dirdata;
73 vboxfs_dirblock_t *block;
74 vbox_param_t param[8];
75 unsigned int count;
76 int r;
78 dirdata = (vboxfs_dirdata_t *) malloc(sizeof(vboxfs_dirdata_t));
79 if (dirdata == NULL)
80 return ENOMEM;
82 memset(dirdata, 0, sizeof(*dirdata));
83 STAILQ_INIT(&dirdata->blocks);
85 r = OK;
87 do {
88 block =
89 (vboxfs_dirblock_t *) malloc(sizeof(vboxfs_dirblock_t));
90 if (block == NULL) {
91 r = ENOMEM;
92 break;
95 vbox_set_u32(&param[0], vboxfs_root);
96 vbox_set_u64(&param[1], handle);
97 vbox_set_u32(&param[2], 0); /* flags */
98 vbox_set_u32(&param[3], sizeof(block->data));
99 vbox_set_ptr(&param[4], NULL, 0, VBOX_DIR_OUT);
100 vbox_set_ptr(&param[5], block->data, sizeof(block->data),
101 VBOX_DIR_IN);
102 vbox_set_u32(&param[6], 0); /* resume point */
103 vbox_set_u32(&param[7], 0); /* number of files */
105 /* If the call fails, stop. */
106 if ((r = vbox_call(vboxfs_conn, VBOXFS_CALL_LIST, param, 8,
107 NULL)) != OK) {
108 free(block);
109 break;
112 /* If the number of returned files is zero, stop. */
113 if ((count = vbox_get_u32(&param[7])) == 0) {
114 free(block);
115 break;
119 * Add the block to the list. We could realloc() the block to
120 * free unused tail space, but this is not likely to gain us
121 * much in general.
123 block->count = count;
124 STAILQ_INSERT_TAIL(&dirdata->blocks, block, next);
126 /* Continue until a zero resume point is returned. */
127 } while (vbox_get_u32(&param[6]) != 0);
129 if (r != OK) {
130 free_dir(dirdata);
132 return r;
135 dirdata->block = STAILQ_FIRST(&dirdata->blocks);
137 *dirp = (sffs_dir_t) dirdata;
139 return OK;
143 * Open a directory.
146 vboxfs_opendir(char *path, sffs_dir_t *handle)
148 vboxfs_handle_t h;
149 int r;
151 /* Open the directory. */
152 if ((r = vboxfs_open_file(path, O_RDONLY, S_IFDIR, &h, NULL)) != OK)
153 return r;
156 * Upon success, read in the full contents of the directory right away.
157 * If it succeeds, this will also set the caller's directory handle.
159 r = read_dir(h, handle);
161 /* We do not need the directory to be open anymore now. */
162 vboxfs_close_file(h);
164 return r;
168 * Read one entry from a directory. On success, copy the name into buf, and
169 * return the requested attributes. If the name (including terminating null)
170 * exceeds size, return ENAMETOOLONG. Do not return dot and dot-dot entries.
171 * Return ENOENT if the index exceeds the number of files.
174 vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, size_t size,
175 struct sffs_attr *attr)
177 vboxfs_dirdata_t *dirdata;
178 vboxfs_dirinfo_t *dirinfo;
179 int r;
181 dirdata = (vboxfs_dirdata_t *) handle;
184 * If the saved index exceeds the requested index, start from the
185 * beginning.
187 if (dirdata->index > index) {
188 dirdata->index = 0;
189 dirdata->bindex = 0;
190 dirdata->bpos = 0;
191 dirdata->block = STAILQ_FIRST(&dirdata->blocks);
194 /* Loop until we find the requested entry or we run out of entries. */
195 while (dirdata->block != NULL) {
196 dirinfo =
197 (vboxfs_dirinfo_t *) &dirdata->block->data[dirdata->bpos];
199 /* Consider all entries that are not dot or dot-dot. */
200 if (dirinfo->name.len > 2 || dirinfo->name.data[0] != '.' ||
201 (dirinfo->name.len == 2 && dirinfo->name.data[1] != '.')) {
203 if (dirdata->index == index)
204 break;
206 dirdata->index++;
209 /* Advance to the next entry. */
210 dirdata->bpos += offsetof(vboxfs_dirinfo_t, name) +
211 offsetof(vboxfs_path_t, data) + dirinfo->name.size;
212 if (++dirdata->bindex >= dirdata->block->count) {
213 dirdata->block = STAILQ_NEXT(dirdata->block, next);
214 dirdata->bindex = 0;
215 dirdata->bpos = 0;
219 /* Not enough files to satisfy the request? */
220 if (dirdata->block == NULL)
221 return ENOENT;
223 /* Return the information for the file we found. */
224 if ((r = vboxfs_get_path(&dirinfo->name, buf, size)) != OK)
225 return r;
227 vboxfs_get_attr(attr, &dirinfo->info);
229 return OK;
233 * Close a directory.
236 vboxfs_closedir(sffs_dir_t handle)
238 vboxfs_dirdata_t *dirdata;
240 dirdata = (vboxfs_dirdata_t *) handle;
242 free_dir(dirdata);
244 return OK;