VirtualBox

Ticket #4154: vbox.sf.sol.readdir.patch

File vbox.sf.sol.readdir.patch, 10.7 KB (added by foobar42, 14 years ago)

Fix out-of-space errors when listing large directories.

  • src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c

    diff a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
    a b  
    2828#include <sys/sysmacros.h>
    2929#include <sys/ddi.h>
    3030#include <sys/sunddi.h>
     31#include <sys/dirent.h>
    3132#include "vboxfs_prov.h"
    3233#ifdef u
    3334#undef u
    sfprov_rename(sfp_mount_t *mnt, char *from, char *to, uint_t is_dir)  
    606607 * - ENOENT - Couldn't open the directory for reading
    607608 * - EINVAL - Internal error of some kind
    608609 *
    609  * On successful return, buffer[0] is the start of an array of "char *"
    610  * pointers to the filenames. The array ends with a NULL pointer.
    611  * The remaining storage in buffer after that NULL pointer is where the
    612  * filename strings actually are.
    613  *
    614  * On input nents is the max number of filenames the requestor can handle.
    615  * On output nents is the number of entries at buff[0]
    616  *
    617  * The caller is responsible for freeing the returned buffer.
     610 * On successful return, *dirents points to a list of sffs_dirents_t;
     611 * for each dirent, all fields except the d_ino will be set appropriately.
     612 * The caller is responsible for freeing the dirents buffer.
    618613 */
    619614int
    620615sfprov_readdir(
    621616        sfp_mount_t *mnt,
    622617        char *path,
    623         void **buffer,
    624         size_t *buffersize,
    625         uint32_t *nents)
     618        sffs_dirents_t **dirents)
    626619{
    627620        int error;
    628621        char *cp;
    sfprov_readdir(  
    630623        SHFLSTRING *mask_str = NULL;    /* must be path with "/*" appended */
    631624        int mask_size;
    632625        sfp_file_t *fp;
    633         void *buff_start = NULL;
    634         size_t buff_size;
    635626        static char infobuff[2 * MAXNAMELEN];   /* not on stack!! */
    636627        SHFLDIRINFO *info = (SHFLDIRINFO *)&infobuff;
    637         uint32_t numbytes = sizeof (infobuff);
     628        uint32_t numbytes;
    638629        uint32_t justone;
    639630        uint32_t cnt;
    640         char **name_ptrs;
     631        sffs_dirents_t *cur_buf;
     632        struct dirent64 *dirent;
     633        unsigned short reclen;
     634
     635        *dirents = NULL;
    641636
    642         *buffer = NULL;
    643         *buffersize = 0;
    644         if (*nents == 0)
    645                 return (EINVAL);
    646637        error = sfprov_open(mnt, path, &fp);
    647638        if (error != 0)
    648639                return (ENOENT);
    649640
    650641        /*
     642         * Allocate the first dirents buffer.
     643         */
     644        *dirents = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
     645        if (*dirents == NULL) {
     646                error = (ENOSPC);
     647                goto done;
     648        }
     649        cur_buf = *dirents;
     650        cur_buf->sf_next = NULL;
     651        cur_buf->sf_len = 0;
     652
     653        /*
    651654         * Create mask that VBox expects. This needs to be the directory path,
    652655         * plus a "*" wildcard to get all files.
    653656         */
    654657        len = strlen(path) + 3;
    655658        cp = kmem_alloc(len, KM_SLEEP);
     659        if (cp == NULL) {
     660                error = (ENOSPC);
     661                goto done;
     662        }
    656663        strcpy(cp, path);
    657664        strcat(cp, "/*");
    658665        mask_str = sfprov_string(cp, &mask_size);
    659666        kmem_free(cp, len);
    660667
    661668        /*
    662          * Allocate the buffer to use for return values. Each entry
    663          * in the buffer will have a pointer and the string itself.
    664          * The pointers go in the front of the buffer, the strings
    665          * at the end.
    666          */
    667         buff_size = *nents * (sizeof(char *) + MAXNAMELEN);
    668         name_ptrs = buff_start = kmem_alloc(buff_size, KM_SLEEP);
    669         cp = (char *)buff_start + buff_size;
    670 
    671         /*
    672669         * Now loop using vboxCallDirInfo to get one file name at a time
    673670         */
    674671        cnt = 0;
    sfprov_readdir(  
    690687                }
    691688
    692689                /*
    693                  * Put this name in the buffer, stop if we run out of room.
     690                 * Put this name in the buffer, expand if we run out of room.
    694691                 */
    695                 cp -= strlen(info->name.String.utf8) + 1;
    696                 if (cp < (char *)(&name_ptrs[cnt + 2]))
    697                         break;
    698                 strcpy(cp, info->name.String.utf8);
    699                 name_ptrs[cnt] = cp;
     692                reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
     693                if (SFFS_DIRENTS_OFF + cur_buf->sf_len + reclen > SFFS_DIRENTS_SIZE) {
     694                        cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
     695                        if (cur_buf->sf_next == NULL) {
     696                                error = ENOSPC;
     697                                goto done;
     698                        }
     699                        cur_buf = cur_buf->sf_next;
     700                        cur_buf->sf_next = NULL;
     701                        cur_buf->sf_len = 0;
     702                }
     703
     704                dirent = (dirent64_t *)
     705                        (((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
     706                strcpy(&dirent->d_name[0], info->name.String.utf8);
     707                dirent->d_reclen = reclen;
     708                dirent->d_off = cnt;
     709
     710                cur_buf->sf_len += reclen;
    700711                ++cnt;
    701712        }
    702713        error = 0;
    703         name_ptrs[cnt] = NULL;
    704         *nents = cnt;
    705         *buffer = buff_start;
    706         *buffersize = buff_size;
     714
    707715done:
    708         if (error != 0)
    709                 kmem_free(buff_start, buff_size);
    710         kmem_free(mask_str, mask_size);
     716        if (error != 0) {
     717                while (*dirents) {
     718                        cur_buf = (*dirents)->sf_next;
     719                        kmem_free(*dirents, SFFS_DIRENTS_SIZE);
     720                        *dirents = cur_buf;
     721                }
     722        }
     723        if (mask_str != NULL)
     724                kmem_free(mask_str, mask_size);
    711725        sfprov_close(fp);
    712726        return (error);
    713727}
  • src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h

    diff a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
    a b extern int sfprov_rename(sfp_mount_t *, char *from, char *to, uint_t is_dir);  
    106106/*
    107107 * Read directory entries.
    108108 */
    109 extern int sfprov_readdir(sfp_mount_t *mnt, char *path, void **buffer,
    110         size_t *buffersize, uint32_t *nents);
     109/*
     110 * a singly linked list of buffers, each containing an array of dirent's.
     111 * sf_len is length of the sf_entries array, in bytes.
     112 */
     113typedef struct sffs_dirents {
     114        struct sffs_dirents     *sf_next;
     115        len_t                   sf_len;
     116        dirent64_t              sf_entries[1];
     117} sffs_dirents_t;
     118
     119extern int sfprov_readdir(sfp_mount_t *mnt, char *path, sffs_dirents_t **dirents);
     120
     121#define SFFS_DIRENTS_SIZE       8192
     122#define SFFS_DIRENTS_OFF        (offsetof(sffs_dirents_t, sf_entries[0]))
    111123
    112124#ifdef  __cplusplus
    113125}
  • src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c

    diff a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
    a b sfnode_make(  
    217217        node->sf_parent = parent;
    218218        if (parent)
    219219                ++parent->sf_children;
     220        node->sf_dir_list = NULL;
    220221
    221222        /*
    222223         * add the new node to our cache
    sffs_readdir(  
    581582        sfnode_t *dir = VN2SFN(vp);
    582583        sfnode_t *node;
    583584        struct dirent64 *dirent;
     585        sffs_dirents_t *cur_buf;
     586        offset_t offset;
    584587        int dummy_eof;
    585588        int error = 0;
    586         int namelen;
    587         void *prov_buff = NULL;
    588         size_t prov_buff_size;
    589         char **names;
    590         uint32_t nents;
    591         uint32_t index;
    592589
    593590        if (uiop->uio_iovcnt != 1)
    594591                return (EINVAL);
    sffs_readdir(  
    605602                return (0);
    606603        }
    607604
    608         dirent = kmem_zalloc(DIRENT64_RECLEN(MAXNAMELEN), KM_SLEEP);
    609 
    610605        /*
    611606         * Get the directory entry names from the host. This gets all
    612          * entries, so add in starting offset. Max the caller can expect
    613          * would be the size of the UIO buffer / sizeof of a dirent for
    614          * file with name of length 1
     607         * entries. These are stored in a linked list of sffs_dirents_t
     608         * buffers, each of which contains a list of dirent64_t's.
    615609         */
    616610        mutex_enter(&sffs_lock);
    617         index = uiop->uio_loffset;
    618         nents = index + (uiop->uio_resid / DIRENT64_RECLEN(1));
    619         error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
    620             &prov_buff, &prov_buff_size, &nents);
    621         if (error != 0)
    622                 goto done;
    623         if (nents <= index) {
    624                 *eofp = 1;
    625                 goto done;
     611
     612        if (dir->sf_dir_list == NULL) {
     613                error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
     614                                        &dir->sf_dir_list);
     615                if (error != 0)
     616                        goto done;
    626617        }
    627         names = (void *)prov_buff;
    628618
    629619        /*
    630          * Lookup each of the names, so that we have ino's.
     620         * Lookup each of the names, so that we have ino's, and copy to
     621         * result buffer.
    631622         */
    632         for (; index < nents; ++index) {
    633                 if (strcmp(names[index], ".") == 0) {
     623        offset = 0;
     624        cur_buf = dir->sf_dir_list;
     625        while (cur_buf != NULL) {
     626                if (offset + cur_buf->sf_len <= uiop->uio_loffset) {
     627                        offset += cur_buf->sf_len;
     628                        cur_buf = cur_buf->sf_next;
     629                        continue;
     630                }
     631
     632                dirent = (dirent64_t *)
     633                        (((char *) &cur_buf->sf_entries[0]) +
     634                         (uiop->uio_loffset - offset));
     635                if (dirent->d_reclen > uiop->uio_resid)
     636                        break;
     637
     638                if (strcmp(dirent->d_name, ".") == 0) {
    634639                        node = dir;
    635                 } else if (strcmp(names[index], "..") == 0) {
     640                } else if (strcmp(dirent->d_name, "..") == 0) {
    636641                        node = dir->sf_parent;
    637642                        if (node == NULL)
    638643                                node = dir;
    639644                } else {
    640                         node = sfnode_lookup(dir, names[index], VNON);
     645                        node = sfnode_lookup(dir, dirent->d_name, VNON);
    641646                        if (node == NULL)
    642647                                panic("sffs_readdir() lookup failed");
    643648                }
    644                 namelen = strlen(names[index]);
    645                 strcpy(&dirent->d_name[0], names[index]);
    646                 dirent->d_reclen = DIRENT64_RECLEN(namelen);
    647                 dirent->d_off = index;
    648649                dirent->d_ino = node->sf_ino;
    649                 if (dirent->d_reclen > uiop->uio_resid) {
    650                         error = ENOSPC;
    651                         break;
    652                 }
     650
    653651                error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
    654652                if (error != 0)
    655653                        break;
    656                 bzero(&dirent->d_name[0], namelen);
    657654        }
    658         if (error == 0 && index >= nents)
     655        if (error == 0 && cur_buf == NULL)
    659656                *eofp = 1;
    660657done:
    661658        mutex_exit(&sffs_lock);
    662         if (prov_buff != NULL)
    663                 kmem_free(prov_buff, prov_buff_size);
    664         kmem_free(dirent, DIRENT64_RECLEN(MAXNAMELEN));
    665659        return (error);
    666660}
    667661
    sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)  
    14181412        }
    14191413
    14201414        /*
     1415         * Free the directory entries for the node. This should normally
     1416         * have been taken care of in sffs_close(), but better safe than
     1417         * sorry.
     1418         */
     1419        while (node->sf_dir_list != NULL) {
     1420                sffs_dirents_t *next = node->sf_dir_list->sf_next;
     1421                kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
     1422                node->sf_dir_list = next;
     1423        }
     1424
     1425        /*
    14211426         * If the node is stale, we can also destroy it.
    14221427         */
    14231428        if (node->sf_is_stale && node->sf_children == 0)
    sffs_close(  
    14501455        cred_t *cr,
    14511456        caller_context_t *ct)
    14521457{
     1458        sfnode_t *node;
     1459
     1460        mutex_enter(&sffs_lock);
     1461        node = VN2SFN(vp);
     1462
     1463        /*
     1464         * Free the directory entries for the node. We do this on this call
     1465         * here because the directory node may not become inactive for a long
     1466         * time after the readdir is over. Case in point, if somebody cd's into
     1467         * the directory then it won't become inactive until they cd away again.
     1468         * In such a case we would end up with the directory listing not getting
     1469         * updated (i.e. the result of 'ls' always being the same) until they
     1470         * change the working directory.
     1471         */
     1472        while (node->sf_dir_list != NULL) {
     1473                sffs_dirents_t *next = node->sf_dir_list->sf_next;
     1474                kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
     1475                node->sf_dir_list = next;
     1476        }
     1477
     1478        mutex_exit(&sffs_lock);
    14531479        return (0);
    14541480}
    14551481
  • src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h

    diff a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
    a b typedef struct sfnode {  
    4747        uint16_t        sf_children;    /* number of children sfnodes */
    4848        uint8_t         sf_type;        /* VDIR or VREG */
    4949        uint8_t         sf_is_stale;    /* this is stale and should be purged */
     50        sffs_dirents_t  *sf_dir_list;   /* list of entries for this directory */
    5051} sfnode_t;
    5152
    5253#define VN2SFN(vp) ((sfnode_t *)(vp)->v_data)

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy