1 /* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
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
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
;
35 char data
[VBOXFS_DIRDATA_SIZE
];
39 STAILQ_HEAD(blocks
, vboxfs_dirblock_s
) blocks
;
41 vboxfs_dirblock_t
*block
;
47 * Free the memory allocated for the given directory contents storage.
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
);
66 * Read all the contents of the given directory, allocating memory as needed to
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];
78 dirdata
= (vboxfs_dirdata_t
*) malloc(sizeof(vboxfs_dirdata_t
));
82 memset(dirdata
, 0, sizeof(*dirdata
));
83 STAILQ_INIT(&dirdata
->blocks
);
89 (vboxfs_dirblock_t
*) malloc(sizeof(vboxfs_dirblock_t
));
95 vbox_set_u32(¶m
[0], vboxfs_root
);
96 vbox_set_u64(¶m
[1], handle
);
97 vbox_set_u32(¶m
[2], 0); /* flags */
98 vbox_set_u32(¶m
[3], sizeof(block
->data
));
99 vbox_set_ptr(¶m
[4], NULL
, 0, VBOX_DIR_OUT
);
100 vbox_set_ptr(¶m
[5], block
->data
, sizeof(block
->data
),
102 vbox_set_u32(¶m
[6], 0); /* resume point */
103 vbox_set_u32(¶m
[7], 0); /* number of files */
105 /* If the call fails, stop. */
106 if ((r
= vbox_call(vboxfs_conn
, VBOXFS_CALL_LIST
, param
, 8,
112 /* If the number of returned files is zero, stop. */
113 if ((count
= vbox_get_u32(¶m
[7])) == 0) {
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
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(¶m
[6]) != 0);
135 dirdata
->block
= STAILQ_FIRST(&dirdata
->blocks
);
137 *dirp
= (sffs_dir_t
) dirdata
;
146 vboxfs_opendir(char *path
, sffs_dir_t
*handle
)
151 /* Open the directory. */
152 if ((r
= vboxfs_open_file(path
, O_RDONLY
, S_IFDIR
, &h
, NULL
)) != OK
)
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
);
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
;
181 dirdata
= (vboxfs_dirdata_t
*) handle
;
184 * If the saved index exceeds the requested index, start from the
187 if (dirdata
->index
> index
) {
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
) {
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
)
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
);
219 /* Not enough files to satisfy the request? */
220 if (dirdata
->block
== NULL
)
223 /* Return the information for the file we found. */
224 if ((r
= vboxfs_get_path(&dirinfo
->name
, buf
, size
)) != OK
)
227 vboxfs_get_attr(attr
, &dirinfo
->info
);
236 vboxfs_closedir(sffs_dir_t handle
)
238 vboxfs_dirdata_t
*dirdata
;
240 dirdata
= (vboxfs_dirdata_t
*) handle
;