VirtualBox

Changeset 30446 in vbox


Ignore:
Timestamp:
Jun 25, 2010 9:51:58 AM (15 years ago)
Author:
vboxsync
Message:

Additions/Solaris/SharedFolders: Fix out-of-space errors when listing large directories.

The existing code would fail if the full directory listing could not fit in the buffer passed to the first readdir call. The change here implements the same idea as the linux guest code does: on the first readdir call the full
directory list is read into a dynamically allocated buffer (actually a list of buffers), and the readdir calls then copy from this buffer; the buffer is freed when the vnode is closed. This buffer contains dirent_t entries,
rather than some slightly more compact list-of-names, because readdir provides the offset as a byte offset in a conceptual array of dirent_t's, and hence we can find our place much easier and faster if we keep actual dirent_t
entries around.

Location:
trunk/src/VBox/Additions/solaris/SharedFolders
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c

    r30433 r30446  
    3030#include <sys/ddi.h>
    3131#include <sys/sunddi.h>
     32#include <sys/dirent.h>
    3233#include "vboxfs_prov.h"
    3334#ifdef u
     
    776777 * - EINVAL - Internal error of some kind
    777778 *
    778  * On successful return, buffer[0] is the start of an array of "char *"
    779  * pointers to the filenames. The array ends with a NULL pointer.
    780  * The remaining storage in buffer after that NULL pointer is where the
    781  * filename strings actually are.
    782  *
    783  * On input nents is the max number of filenames the requestor can handle.
    784  * On output nents is the number of entries at buff[0]
    785  *
    786  * The caller is responsible for freeing the returned buffer.
     779 * On successful return, *dirents points to a list of sffs_dirents_t;
     780 * for each dirent, all fields except the d_ino will be set appropriately.
     781 * The caller is responsible for freeing the dirents buffer.
    787782 */
    788783int
     
    790785        sfp_mount_t *mnt,
    791786        char *path,
    792         void **buffer,
    793         size_t *buffersize,
    794         uint32_t *nents)
     787        sffs_dirents_t **dirents)
    795788{
    796789        int error;
     
    800793        int mask_size;
    801794        sfp_file_t *fp;
    802         void *buff_start = NULL;
    803         size_t buff_size;
    804795        static char infobuff[2 * MAXNAMELEN];   /* not on stack!! */
    805796        SHFLDIRINFO *info = (SHFLDIRINFO *)&infobuff;
    806         uint32_t numbytes = sizeof (infobuff);
     797        uint32_t numbytes;
    807798        uint32_t justone;
    808799        uint32_t cnt;
    809         char **name_ptrs;
    810 
    811         *buffer = NULL;
    812         *buffersize = 0;
    813         if (*nents == 0)
    814                 return (EINVAL);
     800        sffs_dirents_t *cur_buf;
     801        struct dirent64 *dirent;
     802        unsigned short reclen;
     803
     804        *dirents = NULL;
     805
    815806        error = sfprov_open(mnt, path, &fp);
    816807        if (error != 0)
    817808                return (ENOENT);
     809
     810        /*
     811         * Allocate the first dirents buffer.
     812         */
     813        *dirents = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
     814        if (*dirents == NULL) {
     815                error = (ENOSPC);
     816                goto done;
     817        }
     818        cur_buf = *dirents;
     819        cur_buf->sf_next = NULL;
     820        cur_buf->sf_len = 0;
    818821
    819822        /*
     
    823826        len = strlen(path) + 3;
    824827        cp = kmem_alloc(len, KM_SLEEP);
     828        if (cp == NULL) {
     829                error = (ENOSPC);
     830                goto done;
     831        }
    825832        strcpy(cp, path);
    826833        strcat(cp, "/*");
    827834        mask_str = sfprov_string(cp, &mask_size);
    828835        kmem_free(cp, len);
    829 
    830         /*
    831          * Allocate the buffer to use for return values. Each entry
    832          * in the buffer will have a pointer and the string itself.
    833          * The pointers go in the front of the buffer, the strings
    834          * at the end.
    835          */
    836         buff_size = *nents * (sizeof(char *) + MAXNAMELEN);
    837         name_ptrs = buff_start = kmem_alloc(buff_size, KM_SLEEP);
    838         cp = (char *)buff_start + buff_size;
    839836
    840837        /*
     
    860857
    861858                /*
    862                  * Put this name in the buffer, stop if we run out of room.
     859                 * Put this name in the buffer, expand if we run out of room.
    863860                 */
    864                 cp -= strlen(info->name.String.utf8) + 1;
    865                 if (cp < (char *)(&name_ptrs[cnt + 2]))
    866                         break;
    867                 strcpy(cp, info->name.String.utf8);
    868                 name_ptrs[cnt] = cp;
     861                reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
     862                if (SFFS_DIRENTS_OFF + cur_buf->sf_len + reclen > SFFS_DIRENTS_SIZE) {
     863                        cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
     864                        if (cur_buf->sf_next == NULL) {
     865                                error = ENOSPC;
     866                                goto done;
     867                        }
     868                        cur_buf = cur_buf->sf_next;
     869                        cur_buf->sf_next = NULL;
     870                        cur_buf->sf_len = 0;
     871                }
     872
     873                dirent = (dirent64_t *)
     874                    (((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
     875                strcpy(&dirent->d_name[0], info->name.String.utf8);
     876                dirent->d_reclen = reclen;
     877                dirent->d_off = cnt;
     878
     879                cur_buf->sf_len += reclen;
    869880                ++cnt;
    870881        }
    871882        error = 0;
    872         name_ptrs[cnt] = NULL;
    873         *nents = cnt;
    874         *buffer = buff_start;
    875         *buffersize = buff_size;
     883
    876884done:
    877         if (error != 0)
    878                 kmem_free(buff_start, buff_size);
    879         kmem_free(mask_str, mask_size);
     885        if (error != 0) {
     886                while (*dirents) {
     887                        cur_buf = (*dirents)->sf_next;
     888                        kmem_free(*dirents, SFFS_DIRENTS_SIZE);
     889                        *dirents = cur_buf;
     890                }
     891        }
     892        if (mask_str != NULL)
     893                kmem_free(mask_str, mask_size);
    880894        sfprov_close(fp);
    881895        return (error);
  • trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h

    r30417 r30446  
    111111 * Read directory entries.
    112112 */
    113 extern int sfprov_readdir(sfp_mount_t *mnt, char *path, void **buffer,
    114         size_t *buffersize, uint32_t *nents);
     113/*
     114 * a singly linked list of buffers, each containing an array of dirent's.
     115 * sf_len is length of the sf_entries array, in bytes.
     116 */
     117typedef struct sffs_dirents {
     118        struct sffs_dirents     *sf_next;
     119        len_t                   sf_len;
     120        dirent64_t              sf_entries[1];
     121} sffs_dirents_t;
     122
     123extern int sfprov_readdir(sfp_mount_t *mnt, char *path, sffs_dirents_t **dirents);
     124
     125#define SFFS_DIRENTS_SIZE       8192
     126#define SFFS_DIRENTS_OFF        (offsetof(sffs_dirents_t, sf_entries[0]))
    115127
    116128#ifdef  __cplusplus
  • trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c

    r30435 r30446  
    144144
    145145/*
     146 * Clears the (cached) directory listing for the node.
     147 */
     148static void
     149sfnode_clear_dir_list(sfnode_t *node)
     150{
     151        ASSERT(MUTEX_HELD(&sffs_lock));
     152
     153        while (node->sf_dir_list != NULL) {
     154                sffs_dirents_t *next = node->sf_dir_list->sf_next;
     155                kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
     156                node->sf_dir_list = next;
     157        }
     158}
     159
     160/*
    146161 * Open the provider file associated with a vnode. Holding the file open is
    147162 * the only way we have of trying to have a vnode continue to refer to the
     
    220235        if (parent)
    221236                ++parent->sf_children;
     237        node->sf_dir_list = NULL;
    222238
    223239        /*
     
    258274
    259275        VFS_RELE(node->sf_sffs->sf_vfsp);
     276        sfnode_clear_dir_list(node);
    260277        kmem_free(node->sf_path, strlen(node->sf_path) + 1);
    261278        kmem_free(node, sizeof (*node));
    262279        if (parent != NULL) {
     280                sfnode_clear_dir_list(parent);
    263281                if (parent->sf_children == 0)
    264282                        panic("sfnode_destroy(%s) parent has no child", node->sf_path);
     
    313331                        } else {
    314332                LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path));
     333                                sfnode_clear_dir_list(n);
    315334                                if (avl_find(&sfnodes, n, &where) == NULL)
    316335                                        panic("sfnode_make_stale(%s)"
     
    333352        } else if (!node->sf_is_stale) {
    334353        LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path));
     354                sfnode_clear_dir_list(node);
     355                if (node->sf_parent)
     356                        sfnode_clear_dir_list(node->sf_parent);
    335357                if (avl_find(&sfnodes, node, &where) == NULL)
    336358                        panic("sfnode_make_stale(%s) not in sfnodes",
     
    433455        if (node->sf_parent->sf_children == 0)
    434456                panic("sfnode_rename(%s) parent has no child", node->sf_path);
     457        sfnode_clear_dir_list(node->sf_parent);
     458        sfnode_clear_dir_list(newparent);
    435459        --node->sf_parent->sf_children;
    436460        node->sf_parent = newparent;
     
    584608        sfnode_t *node;
    585609        struct dirent64 *dirent;
     610        sffs_dirents_t *cur_buf;
     611        offset_t offset;
    586612        int dummy_eof;
    587613        int error = 0;
    588         int namelen;
    589         void *prov_buff = NULL;
    590         size_t prov_buff_size;
    591         char **names;
    592         uint32_t nents;
    593         uint32_t index;
    594614
    595615        if (uiop->uio_iovcnt != 1)
     
    608628        }
    609629
    610         dirent = kmem_zalloc(DIRENT64_RECLEN(MAXNAMELEN), KM_SLEEP);
    611 
    612630        /*
    613631         * Get the directory entry names from the host. This gets all
    614          * entries, so add in starting offset. Max the caller can expect
    615          * would be the size of the UIO buffer / sizeof of a dirent for
    616          * file with name of length 1
     632         * entries. These are stored in a linked list of sffs_dirents_t
     633         * buffers, each of which contains a list of dirent64_t's.
    617634         */
    618635        mutex_enter(&sffs_lock);
    619         index = uiop->uio_loffset;
    620         nents = index + (uiop->uio_resid / DIRENT64_RECLEN(1));
    621         error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
    622             &prov_buff, &prov_buff_size, &nents);
    623         if (error != 0)
    624                 goto done;
    625         if (nents <= index) {
    626                 *eofp = 1;
    627                 goto done;
    628         }
    629         names = (void *)prov_buff;
    630 
    631         /*
    632          * Lookup each of the names, so that we have ino's.
    633          */
    634         for (; index < nents; ++index) {
    635                 if (strcmp(names[index], ".") == 0) {
     636
     637        if (dir->sf_dir_list == NULL) {
     638                error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
     639                    &dir->sf_dir_list);
     640                if (error != 0)
     641                        goto done;
     642        }
     643
     644        /*
     645         * Lookup each of the names, so that we have ino's, and copy to
     646         * result buffer.
     647         */
     648        offset = 0;
     649        cur_buf = dir->sf_dir_list;
     650        while (cur_buf != NULL) {
     651                if (offset + cur_buf->sf_len <= uiop->uio_loffset) {
     652                        offset += cur_buf->sf_len;
     653                        cur_buf = cur_buf->sf_next;
     654                        continue;
     655                }
     656
     657                dirent = (dirent64_t *)
     658                    (((char *) &cur_buf->sf_entries[0]) +
     659                     (uiop->uio_loffset - offset));
     660                if (dirent->d_reclen > uiop->uio_resid)
     661                        break;
     662
     663                if (strcmp(dirent->d_name, ".") == 0) {
    636664                        node = dir;
    637                 } else if (strcmp(names[index], "..") == 0) {
     665                } else if (strcmp(dirent->d_name, "..") == 0) {
    638666                        node = dir->sf_parent;
    639667                        if (node == NULL)
    640668                                node = dir;
    641669                } else {
    642                         node = sfnode_lookup(dir, names[index], VNON);
     670                        node = sfnode_lookup(dir, dirent->d_name, VNON);
    643671                        if (node == NULL)
    644672                                panic("sffs_readdir() lookup failed");
    645673                }
    646                 namelen = strlen(names[index]);
    647                 strcpy(&dirent->d_name[0], names[index]);
    648                 dirent->d_reclen = DIRENT64_RECLEN(namelen);
    649                 dirent->d_off = index;
    650674                dirent->d_ino = node->sf_ino;
    651                 if (dirent->d_reclen > uiop->uio_resid) {
    652                         error = ENOSPC;
    653                         break;
    654                 }
     675
    655676                error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
    656677                if (error != 0)
    657678                        break;
    658                 bzero(&dirent->d_name[0], namelen);
    659         }
    660         if (error == 0 && index >= nents)
     679        }
     680        if (error == 0 && cur_buf == NULL)
    661681                *eofp = 1;
    662682done:
    663683        mutex_exit(&sffs_lock);
    664         if (prov_buff != NULL)
    665                 kmem_free(prov_buff, prov_buff_size);
    666         kmem_free(dirent, DIRENT64_RECLEN(MAXNAMELEN));
    667684        return (error);
    668685}
     
    11881205        }
    11891206
     1207        if (node->sf_parent)
     1208                sfnode_clear_dir_list(node->sf_parent);
     1209
    11901210        mutex_exit(&sffs_lock);
    11911211        if (node == NULL)
     
    12501270        }
    12511271
     1272        if (node->sf_parent)
     1273                sfnode_clear_dir_list(node->sf_parent);
     1274
    12521275        mutex_exit(&sffs_lock);
    12531276        if (node == NULL)
     
    13281351        if (error == ENOENT || error == 0)
    13291352                sfnode_make_stale(node);
     1353
     1354        if (node->sf_parent)
     1355                sfnode_clear_dir_list(node->sf_parent);
    13301356done:
    13311357        mutex_exit(&sffs_lock);
     
    13871413        if (error == ENOENT || error == 0)
    13881414                sfnode_make_stale(node);
     1415
     1416        if (node->sf_parent)
     1417                sfnode_clear_dir_list(node->sf_parent);
    13891418done:
    13901419        mutex_exit(&sffs_lock);
     
    15231552
    15241553        /*
     1554         * Free the directory entries for the node. This should normally
     1555         * have been taken care of in sffs_close(), but better safe than
     1556         * sorry.
     1557         */
     1558        sfnode_clear_dir_list(node);
     1559
     1560        /*
    15251561         * If the node is stale, we can also destroy it.
    15261562         */
     
    15551591        caller_context_t *ct)
    15561592{
     1593        sfnode_t *node;
     1594
     1595        mutex_enter(&sffs_lock);
     1596        node = VN2SFN(vp);
     1597
     1598        /*
     1599         * Free the directory entries for the node. We do this on this call
     1600         * here because the directory node may not become inactive for a long
     1601         * time after the readdir is over. Case in point, if somebody cd's into
     1602         * the directory then it won't become inactive until they cd away again.
     1603         * In such a case we would end up with the directory listing not getting
     1604         * updated (i.e. the result of 'ls' always being the same) until they
     1605         * change the working directory.
     1606         */
     1607        sfnode_clear_dir_list(node);
     1608
     1609        mutex_exit(&sffs_lock);
    15571610        return (0);
    15581611}
     
    17161769                }
    17171770        }
    1718 done:
    17191771        mutex_exit(&sffs_lock);
    17201772        return (0);
  • trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h

    r28800 r30446  
    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
Note: See TracChangeset for help on using the changeset viewer.

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