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