Ejemplo n.º 1
0
size_t shim_do_getdents64 (int fd, struct linux_dirent64 * buf, size_t count)
{
    if (!buf || test_user_memory(buf, count, true))
        return -EFAULT;

    struct shim_handle * hdl = get_fd_handle(fd, NULL, NULL);
    if (!hdl)
        return -EBADF;

    int ret = -EACCES;

    if (hdl->type != TYPE_DIR) {
        ret = -ENOTDIR;
        goto out;
    }

    /* DEP 3/3/17: Properly handle an unlinked directory */
    if (hdl->dentry->state & DENTRY_NEGATIVE) {
        ret = -ENOENT;
        goto out;
    }

    lock(&hdl->lock);

    struct shim_dir_handle * dirhdl = &hdl->info.dir;
    struct shim_dentry * dent = hdl->dentry;
    struct linux_dirent64 * b = buf;
    int bytes = 0;

    /* If we haven't listed the directory, do this first */
    if (!(dent->state & DENTRY_LISTED)) {
        ret = list_directory_dentry(dent);
        if (ret) goto out;
    }

#define DIRENT_SIZE(len)  (sizeof(struct linux_dirent64) + (len) + 1)

#define ASSIGN_DIRENT(dent, name, type)                                 \
        do {                                                            \
            int len = strlen(name);                                     \
            if (bytes + DIRENT_SIZE(len) > count)                       \
                goto done;                                              \
                                                                        \
            b->d_ino = (dent)->ino;                                     \
            b->d_off = ++dirhdl->offset;                                \
            b->d_reclen = DIRENT_SIZE(len);                             \
            b->d_type = (type);                                         \
                                                                        \
            memcpy(b->d_name, name, len + 1);                           \
                                                                        \
            b = (void *) b + DIRENT_SIZE(len);                          \
            bytes += DIRENT_SIZE(len);                                  \
        } while(0)

    if (dirhdl->dot) {
        ASSIGN_DIRENT(dirhdl->dot, ".", LINUX_DT_DIR);
        put_dentry(dirhdl->dot);
        dirhdl->dot = NULL;
    }

    if (dirhdl->dotdot) {
        ASSIGN_DIRENT(dirhdl->dotdot, "..", LINUX_DT_DIR);
        put_dentry(dirhdl->dotdot);
        dirhdl->dotdot = NULL;
    }

    if (dirhdl->ptr == (void *) -1) {
        ret = list_directory_handle(dent, hdl);
        if (ret) goto out;
    }

    while (dirhdl->ptr && *dirhdl->ptr) {
        dent = *dirhdl->ptr;
        /* DEP 3/3/17: We need to filter negative dentries */
        if (!(dent->state & DENTRY_NEGATIVE))
            ASSIGN_DIRENT(dent, dentry_get_name(dent), get_dirent_type(dent->type));
        put_dentry(dent);
        *(dirhdl->ptr++) = NULL;
    }

#undef DIRENT_SIZE
#undef ASSIGN_DIRENT

done:
    ret = bytes;
    /* DEP 3/3/17: Properly detect EINVAL case, where buffer is too small to
     * hold anything */
    if (bytes == 0 && (dirhdl->dot || dirhdl->dotdot ||
                       (dirhdl->ptr && *dirhdl->ptr)))
        ret = -EINVAL;
    unlock(&hdl->lock);
out:
    put_handle(hdl);
    return ret;
}
Ejemplo n.º 2
0
size_t shim_do_getdents64 (int fd, struct linux_dirent64 * buf, size_t count)
{
    struct shim_handle * hdl = get_fd_handle(fd, NULL, NULL);
    if (!hdl)
        return -EBADF;

    int ret = -EACCES;

    if (hdl->type != TYPE_DIR) {
        ret = -ENOTDIR;
        goto out;
    }

    lock(hdl->lock);

    struct shim_dir_handle * dirhdl = &hdl->info.dir;
    struct shim_dentry * dent = hdl->dentry;
    struct linux_dirent64 * b = buf;
    int bytes = 0;

#define DIRENT_SIZE(len)  (sizeof(struct linux_dirent64) + (len) + 1)

#define ASSIGN_DIRENT(dent, name, type)                                 \
        do {                                                            \
            int len = strlen(name);                                     \
            if (bytes + DIRENT_SIZE(len) > count)                       \
                goto done;                                              \
                                                                        \
            b->d_ino = dent->ino;                                       \
            b->d_off = ++dirhdl->offset;                                \
            b->d_reclen = DIRENT_SIZE(len);                             \
            b->d_type = type ? : get_dirent_type(dent->mode);           \
                                                                        \
            memcpy(b->d_name, name, len + 1);                           \
                                                                        \
            b = (void *) b + DIRENT_SIZE(len);                          \
            bytes += DIRENT_SIZE(len);                                  \
        } while(0)

    if (dirhdl->dot) {
        ASSIGN_DIRENT(dirhdl->dot, ".", LINUX_DT_DIR);
        put_dentry(dirhdl->dot);
        dirhdl->dot = NULL;
    }

    if (dirhdl->dotdot) {
        ASSIGN_DIRENT(dirhdl->dotdot, "..", LINUX_DT_DIR);
        put_dentry(dirhdl->dotdot);
        dirhdl->dotdot = NULL;
    }

    while (*dirhdl->ptr) {
        dent = *dirhdl->ptr;
        ASSIGN_DIRENT(dent, dentry_get_name(dent), 0);
        put_dentry(dent);
        *(dirhdl->ptr++) = NULL;
    }

#undef DIRENT_SIZE
#undef ASSIGN_DIRENT

done:
    ret = bytes;
    unlock(hdl->lock);
out:
    put_handle(hdl);
    return ret;
}