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; }
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; }