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(¶m[0], vboxfs_root); 96*433d6423SLionel Sambuc vbox_set_u64(¶m[1], handle); 97*433d6423SLionel Sambuc vbox_set_u32(¶m[2], 0); /* flags */ 98*433d6423SLionel Sambuc vbox_set_u32(¶m[3], sizeof(block->data)); 99*433d6423SLionel Sambuc vbox_set_ptr(¶m[4], NULL, 0, VBOX_DIR_OUT); 100*433d6423SLionel Sambuc vbox_set_ptr(¶m[5], block->data, sizeof(block->data), 101*433d6423SLionel Sambuc VBOX_DIR_IN); 102*433d6423SLionel Sambuc vbox_set_u32(¶m[6], 0); /* resume point */ 103*433d6423SLionel Sambuc vbox_set_u32(¶m[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(¶m[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(¶m[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