xref: /minix3/minix/lib/libvboxfs/dir.c (revision 94e65446c4f963450ea94b8becc02cec062675cd)
1433d6423SLionel Sambuc /* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc #include "inc.h"
4433d6423SLionel Sambuc 
5433d6423SLionel Sambuc /*
6433d6423SLionel Sambuc  * Directories will generally be accessed sequentially, but there is no
7433d6423SLionel Sambuc  * guarantee that this is actually the case.  In particular, multiple user
8433d6423SLionel Sambuc  * processes may iterate over the same directory concurrently, and since
9433d6423SLionel Sambuc  * process information is lost in the VFS/FS protocol, the result is a
10433d6423SLionel Sambuc  * nonsequential pattern on the same single directory handle.  Therefore, we
11433d6423SLionel Sambuc  * must support random access.  Since the VirtualBox shared folders interface
12433d6423SLionel Sambuc  * does not allow for random access (the resume point cannot be used for this),
13433d6423SLionel Sambuc  * we choose to read in the contents of the directory upon open, and cache it
14433d6423SLionel Sambuc  * until the directory is closed again.  The risk is that the cached contents
15433d6423SLionel Sambuc  * will go stale.
16433d6423SLionel Sambuc  *
17433d6423SLionel Sambuc  * The directory will always be closed once one reader finishes going through
18433d6423SLionel Sambuc  * the entire directory, so the problem is rather limited anyway.  Ideally, the
19433d6423SLionel Sambuc  * directory contents would be refreshed upon any invalidating local action as
20433d6423SLionel Sambuc  * well as after a certain time span (since the file system can be changed from
21433d6423SLionel Sambuc  * the host as well).  This is currently not implemented, because the odds of
22433d6423SLionel Sambuc  * things going wrong are pretty small, and the effects are not devastating.
23433d6423SLionel Sambuc  *
24433d6423SLionel Sambuc  * The calling functions may also request the same directory entry twice in a
25433d6423SLionel Sambuc  * row, because the entry does not fit in the user buffer the first time.  The
26433d6423SLionel Sambuc  * routines here optimize for repeat-sequential access, while supporting fully
27433d6423SLionel Sambuc  * random access as well.
28433d6423SLionel Sambuc  */
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc #define VBOXFS_DIRDATA_SIZE	8192	/* data allocation granularity */
31433d6423SLionel Sambuc 
32433d6423SLionel Sambuc typedef struct vboxfs_dirblock_s {
33433d6423SLionel Sambuc 	STAILQ_ENTRY(vboxfs_dirblock_s) next;
34433d6423SLionel Sambuc 	unsigned int count;
35433d6423SLionel Sambuc 	char data[VBOXFS_DIRDATA_SIZE];
36433d6423SLionel Sambuc } vboxfs_dirblock_t;
37433d6423SLionel Sambuc 
38433d6423SLionel Sambuc typedef struct {
39433d6423SLionel Sambuc 	STAILQ_HEAD(blocks, vboxfs_dirblock_s) blocks;
40433d6423SLionel Sambuc 	unsigned int index;
41433d6423SLionel Sambuc 	vboxfs_dirblock_t *block;
42433d6423SLionel Sambuc 	unsigned int bindex;
43433d6423SLionel Sambuc 	unsigned int bpos;
44433d6423SLionel Sambuc } vboxfs_dirdata_t;
45433d6423SLionel Sambuc 
46433d6423SLionel Sambuc /*
47433d6423SLionel Sambuc  * Free the memory allocated for the given directory contents storage.
48433d6423SLionel Sambuc  */
49433d6423SLionel Sambuc static void
free_dir(vboxfs_dirdata_t * dirdata)50433d6423SLionel Sambuc free_dir(vboxfs_dirdata_t *dirdata)
51433d6423SLionel Sambuc {
52433d6423SLionel Sambuc 	vboxfs_dirblock_t *block;
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc 	while (!STAILQ_EMPTY(&dirdata->blocks)) {
55433d6423SLionel Sambuc 		block = STAILQ_FIRST(&dirdata->blocks);
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc 		STAILQ_REMOVE_HEAD(&dirdata->blocks, next);
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc 		free(block);
60433d6423SLionel Sambuc 	}
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc 	free(dirdata);
63433d6423SLionel Sambuc }
64433d6423SLionel Sambuc 
65433d6423SLionel Sambuc /*
66433d6423SLionel Sambuc  * Read all the contents of the given directory, allocating memory as needed to
67433d6423SLionel Sambuc  * store the data.
68433d6423SLionel Sambuc  */
69433d6423SLionel Sambuc static int
read_dir(vboxfs_handle_t handle,sffs_dir_t * dirp)70433d6423SLionel Sambuc read_dir(vboxfs_handle_t handle, sffs_dir_t *dirp)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc 	vboxfs_dirdata_t *dirdata;
73433d6423SLionel Sambuc 	vboxfs_dirblock_t *block;
74433d6423SLionel Sambuc 	vbox_param_t param[8];
75433d6423SLionel Sambuc 	unsigned int count;
76433d6423SLionel Sambuc 	int r;
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc 	dirdata = (vboxfs_dirdata_t *) malloc(sizeof(vboxfs_dirdata_t));
79433d6423SLionel Sambuc 	if (dirdata == NULL)
80433d6423SLionel Sambuc 		return ENOMEM;
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc 	memset(dirdata, 0, sizeof(*dirdata));
83433d6423SLionel Sambuc 	STAILQ_INIT(&dirdata->blocks);
84433d6423SLionel Sambuc 
85433d6423SLionel Sambuc 	r = OK;
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc 	do {
88433d6423SLionel Sambuc 		block =
89433d6423SLionel Sambuc 		    (vboxfs_dirblock_t *) malloc(sizeof(vboxfs_dirblock_t));
90433d6423SLionel Sambuc 		if (block == NULL) {
91433d6423SLionel Sambuc 			r = ENOMEM;
92433d6423SLionel Sambuc 			break;
93433d6423SLionel Sambuc 		}
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc 		vbox_set_u32(&param[0], vboxfs_root);
96433d6423SLionel Sambuc 		vbox_set_u64(&param[1], handle);
97433d6423SLionel Sambuc 		vbox_set_u32(&param[2], 0);		/* flags */
98433d6423SLionel Sambuc 		vbox_set_u32(&param[3], sizeof(block->data));
99433d6423SLionel Sambuc 		vbox_set_ptr(&param[4], NULL, 0, VBOX_DIR_OUT);
100433d6423SLionel Sambuc 		vbox_set_ptr(&param[5], block->data, sizeof(block->data),
101433d6423SLionel Sambuc 		    VBOX_DIR_IN);
102433d6423SLionel Sambuc 		vbox_set_u32(&param[6], 0);		/* resume point */
103433d6423SLionel Sambuc 		vbox_set_u32(&param[7], 0);		/* number of files */
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc 		/* If the call fails, stop. */
106433d6423SLionel Sambuc 		if ((r = vbox_call(vboxfs_conn, VBOXFS_CALL_LIST, param, 8,
107433d6423SLionel Sambuc 		    NULL)) != OK) {
108433d6423SLionel Sambuc 			free(block);
109433d6423SLionel Sambuc 			break;
110433d6423SLionel Sambuc 		}
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc 		/* If the number of returned files is zero, stop. */
113433d6423SLionel Sambuc 		if ((count = vbox_get_u32(&param[7])) == 0) {
114433d6423SLionel Sambuc 			free(block);
115433d6423SLionel Sambuc 			break;
116433d6423SLionel Sambuc 		}
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc 		/*
119433d6423SLionel Sambuc 		 * Add the block to the list. We could realloc() the block to
120433d6423SLionel Sambuc 		 * free unused tail space, but this is not likely to gain us
121433d6423SLionel Sambuc 		 * much in general.
122433d6423SLionel Sambuc 		 */
123433d6423SLionel Sambuc 		block->count = count;
124433d6423SLionel Sambuc 		STAILQ_INSERT_TAIL(&dirdata->blocks, block, next);
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc 		/* Continue until a zero resume point is returned. */
127433d6423SLionel Sambuc 	} while (vbox_get_u32(&param[6]) != 0);
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc 	if (r != OK) {
130433d6423SLionel Sambuc 		free_dir(dirdata);
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc 		return r;
133433d6423SLionel Sambuc 	}
134433d6423SLionel Sambuc 
135433d6423SLionel Sambuc 	dirdata->block = STAILQ_FIRST(&dirdata->blocks);
136433d6423SLionel Sambuc 
137433d6423SLionel Sambuc 	*dirp = (sffs_dir_t) dirdata;
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc 	return OK;
140433d6423SLionel Sambuc }
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc /*
143433d6423SLionel Sambuc  * Open a directory.
144433d6423SLionel Sambuc  */
145433d6423SLionel Sambuc int
vboxfs_opendir(const char * path,sffs_dir_t * handle)146*94e65446SDavid van Moolenbroek vboxfs_opendir(const char *path, sffs_dir_t *handle)
147433d6423SLionel Sambuc {
148433d6423SLionel Sambuc 	vboxfs_handle_t h;
149433d6423SLionel Sambuc 	int r;
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc 	/* Open the directory. */
152433d6423SLionel Sambuc 	if ((r = vboxfs_open_file(path, O_RDONLY, S_IFDIR, &h, NULL)) != OK)
153433d6423SLionel Sambuc 		return r;
154433d6423SLionel Sambuc 
155433d6423SLionel Sambuc 	/*
156433d6423SLionel Sambuc 	 * Upon success, read in the full contents of the directory right away.
157433d6423SLionel Sambuc 	 * If it succeeds, this will also set the caller's directory handle.
158433d6423SLionel Sambuc 	 */
159433d6423SLionel Sambuc 	r = read_dir(h, handle);
160433d6423SLionel Sambuc 
161433d6423SLionel Sambuc 	/* We do not need the directory to be open anymore now. */
162433d6423SLionel Sambuc 	vboxfs_close_file(h);
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc 	return r;
165433d6423SLionel Sambuc }
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc /*
168433d6423SLionel Sambuc  * Read one entry from a directory.  On success, copy the name into buf, and
169433d6423SLionel Sambuc  * return the requested attributes.  If the name (including terminating null)
170433d6423SLionel Sambuc  * exceeds size, return ENAMETOOLONG.  Do not return dot and dot-dot entries.
171433d6423SLionel Sambuc  * Return ENOENT if the index exceeds the number of files.
172433d6423SLionel Sambuc  */
173433d6423SLionel Sambuc int
vboxfs_readdir(sffs_dir_t handle,unsigned int index,char * buf,size_t size,struct sffs_attr * attr)174433d6423SLionel Sambuc vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, size_t size,
175433d6423SLionel Sambuc 	struct sffs_attr *attr)
176433d6423SLionel Sambuc {
177433d6423SLionel Sambuc 	vboxfs_dirdata_t *dirdata;
178433d6423SLionel Sambuc 	vboxfs_dirinfo_t *dirinfo;
179433d6423SLionel Sambuc 	int r;
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc 	dirdata = (vboxfs_dirdata_t *) handle;
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc 	/*
184433d6423SLionel Sambuc 	 * If the saved index exceeds the requested index, start from the
185433d6423SLionel Sambuc 	 * beginning.
186433d6423SLionel Sambuc 	 */
187433d6423SLionel Sambuc 	if (dirdata->index > index) {
188433d6423SLionel Sambuc 		dirdata->index = 0;
189433d6423SLionel Sambuc 		dirdata->bindex = 0;
190433d6423SLionel Sambuc 		dirdata->bpos = 0;
191433d6423SLionel Sambuc 		dirdata->block = STAILQ_FIRST(&dirdata->blocks);
192433d6423SLionel Sambuc 	}
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc 	/* Loop until we find the requested entry or we run out of entries. */
195433d6423SLionel Sambuc 	while (dirdata->block != NULL) {
196433d6423SLionel Sambuc 		dirinfo =
197433d6423SLionel Sambuc 		    (vboxfs_dirinfo_t *) &dirdata->block->data[dirdata->bpos];
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 		/* Consider all entries that are not dot or dot-dot. */
200433d6423SLionel Sambuc 		if (dirinfo->name.len > 2 || dirinfo->name.data[0] != '.' ||
201433d6423SLionel Sambuc 		    (dirinfo->name.len == 2 && dirinfo->name.data[1] != '.')) {
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc 			if (dirdata->index == index)
204433d6423SLionel Sambuc 				break;
205433d6423SLionel Sambuc 
206433d6423SLionel Sambuc 			dirdata->index++;
207433d6423SLionel Sambuc 		}
208433d6423SLionel Sambuc 
209433d6423SLionel Sambuc 		/* Advance to the next entry. */
210433d6423SLionel Sambuc 		dirdata->bpos += offsetof(vboxfs_dirinfo_t, name) +
211433d6423SLionel Sambuc 		    offsetof(vboxfs_path_t, data) + dirinfo->name.size;
212433d6423SLionel Sambuc 		if (++dirdata->bindex >= dirdata->block->count) {
213433d6423SLionel Sambuc 			dirdata->block = STAILQ_NEXT(dirdata->block, next);
214433d6423SLionel Sambuc 			dirdata->bindex = 0;
215433d6423SLionel Sambuc 			dirdata->bpos = 0;
216433d6423SLionel Sambuc 		}
217433d6423SLionel Sambuc 	}
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc 	/* Not enough files to satisfy the request? */
220433d6423SLionel Sambuc 	if (dirdata->block == NULL)
221433d6423SLionel Sambuc 		return ENOENT;
222433d6423SLionel Sambuc 
223433d6423SLionel Sambuc 	/* Return the information for the file we found. */
224433d6423SLionel Sambuc 	if ((r = vboxfs_get_path(&dirinfo->name, buf, size)) != OK)
225433d6423SLionel Sambuc 		return r;
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc 	vboxfs_get_attr(attr, &dirinfo->info);
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc 	return OK;
230433d6423SLionel Sambuc }
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc /*
233433d6423SLionel Sambuc  * Close a directory.
234433d6423SLionel Sambuc  */
235433d6423SLionel Sambuc int
vboxfs_closedir(sffs_dir_t handle)236433d6423SLionel Sambuc vboxfs_closedir(sffs_dir_t handle)
237433d6423SLionel Sambuc {
238433d6423SLionel Sambuc 	vboxfs_dirdata_t *dirdata;
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc 	dirdata = (vboxfs_dirdata_t *) handle;
241433d6423SLionel Sambuc 
242433d6423SLionel Sambuc 	free_dir(dirdata);
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc 	return OK;
245433d6423SLionel Sambuc }
246