overlay: getdents: support lseek
Refs: #1421 Change-Id: Ife7ab1b50159a5897552ff695bb001ada27ec934
This commit is contained in:
@ -3003,12 +3003,16 @@ int close_cloexec_fds(int mcos_fd)
|
||||
|
||||
struct overlay_fd {
|
||||
int fd; /* associated fd, points to mckernel side */
|
||||
int getdents_fd; /* non-seekable mckernel fd */
|
||||
int linux_fd; /* linux fd, -1 if not opened */
|
||||
struct list_head link;
|
||||
char path[PATH_MAX]; /* linux path */
|
||||
char linux_path[PATH_MAX]; /* linux path */
|
||||
char mck_path[PATH_MAX]; /* mckernel path */
|
||||
size_t pathlen;
|
||||
void *dirents; /* copy of mckernel dirents to filter duplicates */
|
||||
size_t dirents_size;
|
||||
void *mck_dirents; /* cache of mckernel dirents to filter duplicates */
|
||||
size_t mck_dirents_size;
|
||||
void *linux_dirents; /* cache of filtered Linux dirents */
|
||||
size_t linux_dirents_size;
|
||||
};
|
||||
LIST_HEAD(overlay_fd_list);
|
||||
|
||||
@ -3039,10 +3043,15 @@ void overlay_addfd(int fd, const char *path)
|
||||
}
|
||||
|
||||
ofd->fd = fd;
|
||||
ofd->getdents_fd = -1;
|
||||
ofd->linux_fd = -1;
|
||||
ofd->dirents = NULL;
|
||||
ofd->dirents_size = 0;
|
||||
ofd->pathlen = snprintf(ofd->path, PATH_MAX, "%s%s", prefix, real_path);
|
||||
ofd->mck_dirents = NULL;
|
||||
ofd->mck_dirents_size = 0;
|
||||
ofd->linux_dirents = NULL;
|
||||
ofd->linux_dirents_size = 0;
|
||||
ofd->pathlen = snprintf(ofd->linux_path, PATH_MAX, "%s%s",
|
||||
prefix, real_path);
|
||||
strncpy(ofd->mck_path, path, PATH_MAX);
|
||||
|
||||
pthread_spin_lock(&overlay_fd_lock);
|
||||
list_add(&ofd->link, &overlay_fd_list);
|
||||
@ -3057,9 +3066,12 @@ void overlay_delfd(int fd)
|
||||
list_for_each_entry(ofd, &overlay_fd_list, link) {
|
||||
if (ofd->fd == fd) {
|
||||
list_del(&ofd->link);
|
||||
if (ofd->getdents_fd != -1)
|
||||
close(ofd->getdents_fd);
|
||||
if (ofd->linux_fd != -1)
|
||||
close(ofd->linux_fd);
|
||||
free(ofd->dirents);
|
||||
free(ofd->mck_dirents);
|
||||
free(ofd->linux_dirents);
|
||||
free(ofd);
|
||||
break;
|
||||
}
|
||||
@ -3401,122 +3413,370 @@ static inline char *dirent_name(int sysnum, void *_dirp)
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static inline void *dirent_off(int sysnum, void *_dirp)
|
||||
{
|
||||
|
||||
#ifdef __NR_getdents
|
||||
if (sysnum == __NR_getdents) {
|
||||
struct linux_dirent *dirp = _dirp;
|
||||
|
||||
return &(dirp->d_off);
|
||||
}
|
||||
#endif
|
||||
if (sysnum == __NR_getdents64) {
|
||||
struct linux_dirent64 *dirp = _dirp;
|
||||
|
||||
return &(dirp->d_off);
|
||||
}
|
||||
fprintf(stderr, "%s: unexpected syscall number %d\n",
|
||||
__func__, sysnum);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int copy_dirents(void *_dirp, void *dirents, size_t dirents_size,
|
||||
off_t offset, unsigned int *count, int sysnum)
|
||||
{
|
||||
off_t max_len;
|
||||
int len;
|
||||
void *dirp_iter;
|
||||
unsigned short reclen;
|
||||
|
||||
max_len = dirents_size - offset > *count ?
|
||||
*count : dirents_size - offset;
|
||||
__dprintf("max_len: %ld\n", max_len);
|
||||
for (len = 0; len < max_len;) {
|
||||
dirp_iter = dirents + offset + len;
|
||||
reclen = dirent_reclen(sysnum, dirp_iter);
|
||||
|
||||
/* early exit on record boundary */
|
||||
if (len + reclen > max_len) {
|
||||
/* don't try to copy lower */
|
||||
*count = 0;
|
||||
__dprintf("early exit: len: %d, reclen: %d, max_len: %ld\n",
|
||||
len, reclen, max_len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(_dirp + len, dirp_iter, reclen);
|
||||
len += reclen;
|
||||
}
|
||||
*count -= len;
|
||||
out:
|
||||
return len;
|
||||
}
|
||||
|
||||
int overlay_getdents(int sysnum, int fd, void *_dirp, unsigned int count)
|
||||
{
|
||||
void *dirp, *mcdirp;
|
||||
int ret = 0, pos, linux_ret, mcpos;
|
||||
void *dirp = NULL;
|
||||
void *linux_dirp_iter, *mck_dirp_iter;
|
||||
int ret, ret_before_edit;
|
||||
int mck_ret = 0, pos;
|
||||
int linux_ret = 0, mcpos;
|
||||
unsigned short reclen;
|
||||
struct overlay_fd *ofd = NULL, *ofd_iter;
|
||||
int hide_orig = 0;
|
||||
off_t offset;
|
||||
char ofd_path[PATH_MAX];
|
||||
int mck_len, linux_len;
|
||||
|
||||
pthread_spin_lock(&overlay_fd_lock);
|
||||
list_for_each_entry(ofd_iter, &overlay_fd_list, link) {
|
||||
if (ofd_iter->fd == fd) {
|
||||
ofd = ofd_iter;
|
||||
__dprintf("found overlay cache entry (%s)\n",
|
||||
ofd->path);
|
||||
ofd->linux_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&overlay_fd_lock);
|
||||
|
||||
/* special case for /proc/N/task */
|
||||
if (ofd && !strncmp(ofd->path, "/proc", 5) &&
|
||||
!strncmp(ofd->path + strlen(ofd->path) - 4,
|
||||
"task", 4)) {
|
||||
if (ofd && !strncmp(ofd->linux_path, "/proc", 5) &&
|
||||
!strncmp(ofd->linux_path + strlen(ofd->linux_path) - 4,
|
||||
"task", 4)) {
|
||||
hide_orig = 1;
|
||||
}
|
||||
|
||||
/* not a directory we overlay, or not there yet */
|
||||
if (ofd == NULL || ofd->linux_fd == -1 || hide_orig) {
|
||||
/* not a directory we overlay or hiding lower fs */
|
||||
if (ofd == NULL || hide_orig) {
|
||||
ret = syscall(sysnum, fd, _dirp, count);
|
||||
if (ret == -1)
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
}
|
||||
if (ofd == NULL || ret < 0 || hide_orig)
|
||||
return ret;
|
||||
|
||||
/* copy mckernel dirents to our buffer, in case of split getdents */
|
||||
if (ret > 0) {
|
||||
void *newbuf = realloc(ofd->dirents, ofd->dirents_size + ret);
|
||||
|
||||
if (!newbuf) {
|
||||
fprintf(stderr, "%s: not enough memory (%zd)",
|
||||
__func__, ofd->dirents_size + ret);
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
ofd->dirents = newbuf;
|
||||
memcpy(ofd->dirents + ofd->dirents_size, _dirp, ret);
|
||||
ofd->dirents_size += ret;
|
||||
goto out_mck_only;
|
||||
}
|
||||
|
||||
/* return first directory result unless it is empty or there
|
||||
* is obvious room for more elements.
|
||||
* The second check could have false positives depending on
|
||||
* the fs, but should not be for filesystems we overlay
|
||||
*/
|
||||
if (ret > 0 && count - ret < 500)
|
||||
return ret;
|
||||
dirp = malloc(count);
|
||||
if (!dirp) {
|
||||
fprintf(stderr, "%s: out of memory\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ofd->linux_fd == -1) {
|
||||
ofd->linux_fd = open(ofd->path, O_RDONLY|O_DIRECTORY);
|
||||
if (ofd->linux_fd < 0) {
|
||||
offset = lseek(fd, 0, SEEK_CUR);
|
||||
if (offset == (off_t)-1) {
|
||||
ret = -errno;
|
||||
goto err;
|
||||
}
|
||||
__dprintf("offset: %ld\n", offset);
|
||||
|
||||
if (ofd->getdents_fd == -1) {
|
||||
ofd->getdents_fd = open(ofd->mck_path, O_RDONLY | O_DIRECTORY);
|
||||
if (ofd->getdents_fd < 0) {
|
||||
ret = -errno;
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "%s: could not open %s: %d\n",
|
||||
__func__, ofd->path, errno);
|
||||
__func__, ofd->mck_path, errno);
|
||||
}
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
again:
|
||||
linux_ret = syscall(sysnum, ofd->linux_fd, _dirp + ret, count - ret);
|
||||
if (linux_ret < 0) {
|
||||
mck_again:
|
||||
/* Use "count" to simplify the handling of
|
||||
* "Result buffer is too small" case
|
||||
*/
|
||||
ret = syscall(sysnum, ofd->getdents_fd, dirp, count);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto err;
|
||||
}
|
||||
mck_ret += ret;
|
||||
__dprintf("getdents from upper: mck_ret: %d, ret: %d, count: %d\n",
|
||||
mck_ret, ret, count);
|
||||
|
||||
/* cache mckernel dirents to our buffer, in case of split getdents */
|
||||
if (ret > 0) {
|
||||
void *newbuf = realloc(ofd->mck_dirents,
|
||||
ofd->mck_dirents_size + ret);
|
||||
|
||||
if (!newbuf) {
|
||||
ret = -ENOMEM;
|
||||
fprintf(stderr, "%s: not enough memory (%zd)",
|
||||
__func__, ofd->mck_dirents_size + ret);
|
||||
goto err;
|
||||
}
|
||||
ofd->mck_dirents = newbuf;
|
||||
memcpy(ofd->mck_dirents + ofd->mck_dirents_size, dirp, ret);
|
||||
|
||||
/* Rewrite d_off to match the packed data layout.
|
||||
* (EOF of fd) >= (EOF of upper + lower) is assumed.
|
||||
* See generic_file_llseek_size().
|
||||
*/
|
||||
for (mcpos = ofd->mck_dirents_size;
|
||||
mcpos < ofd->mck_dirents_size + ret;) {
|
||||
mck_dirp_iter = ofd->mck_dirents + mcpos;
|
||||
reclen = dirent_reclen(sysnum, mck_dirp_iter);
|
||||
#ifdef DEBUG
|
||||
printf("<%s,%d,%ld> ",
|
||||
dirent_name(sysnum, mck_dirp_iter),
|
||||
dirent_reclen(sysnum, mck_dirp_iter),
|
||||
*((unsigned long *)
|
||||
dirent_off(sysnum, mck_dirp_iter)));
|
||||
#endif
|
||||
*((unsigned long *)
|
||||
dirent_off(sysnum, mck_dirp_iter)) = mcpos + reclen;
|
||||
|
||||
mcpos += reclen;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("\n");
|
||||
#endif
|
||||
ofd->mck_dirents_size += ret;
|
||||
}
|
||||
|
||||
/* Fill as many entries as possbile to avoid
|
||||
* upper entries appear to be inserted in the
|
||||
* following getdents
|
||||
*/
|
||||
if (ret > 0 && mck_ret < count) {
|
||||
goto mck_again;
|
||||
}
|
||||
|
||||
if (ofd->linux_fd == -1) {
|
||||
ofd->linux_fd = open(ofd->linux_path, O_RDONLY | O_DIRECTORY);
|
||||
if (ofd->linux_fd < 0) {
|
||||
ret = -errno;
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "%s: could not open %s: %d\n",
|
||||
__func__, ofd->linux_path, errno);
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* lower fs path for blacklist check */
|
||||
strncpy(ofd_path, ofd->linux_path, PATH_MAX - ofd->pathlen);
|
||||
|
||||
linux_again:
|
||||
/* greedy-fetch because the results would be blacklisted */
|
||||
ret = syscall(sysnum, ofd->linux_fd, dirp, count);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "%s: linux getdents failed: %d\n",
|
||||
__func__, errno);
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
if (linux_ret == 0)
|
||||
return ret;
|
||||
ret_before_edit = ret;
|
||||
|
||||
for (pos = ret; pos < ret + linux_ret;) {
|
||||
dirp = _dirp + pos;
|
||||
reclen = dirent_reclen(sysnum, dirp);
|
||||
snprintf(ofd->path + ofd->pathlen, PATH_MAX - ofd->pathlen,
|
||||
"/%s", dirent_name(sysnum, dirp));
|
||||
for (pos = 0; pos < ret;) {
|
||||
linux_dirp_iter = dirp + pos;
|
||||
reclen = dirent_reclen(sysnum, linux_dirp_iter);
|
||||
snprintf(ofd_path + ofd->pathlen, PATH_MAX - ofd->pathlen,
|
||||
"/%s", dirent_name(sysnum, linux_dirp_iter));
|
||||
/* remove blacklist */
|
||||
if (overlay_blacklist(ofd->path)) {
|
||||
__dprintf("blacklisted %s\n", ofd->path);
|
||||
memmove(_dirp + pos,
|
||||
_dirp + pos + reclen,
|
||||
ret + linux_ret - pos - reclen);
|
||||
linux_ret -= reclen;
|
||||
if (overlay_blacklist(ofd_path)) {
|
||||
__dprintf("blacklisted: %s\n", ofd_path);
|
||||
memmove(dirp + pos,
|
||||
dirp + pos + reclen,
|
||||
ret - pos - reclen);
|
||||
ret -= reclen;
|
||||
continue;
|
||||
}
|
||||
/* remove duplicates */
|
||||
for (mcpos = 0; mcpos < ofd->dirents_size;) {
|
||||
mcdirp = ofd->dirents + mcpos;
|
||||
if (!strcmp(dirent_name(sysnum, mcdirp),
|
||||
dirent_name(sysnum, dirp))) {
|
||||
memmove(_dirp + pos,
|
||||
_dirp + pos + reclen,
|
||||
ret + linux_ret - pos - reclen);
|
||||
linux_ret -= reclen;
|
||||
for (mcpos = 0; mcpos < ofd->mck_dirents_size;) {
|
||||
mck_dirp_iter = ofd->mck_dirents + mcpos;
|
||||
if (!strcmp(dirent_name(sysnum, mck_dirp_iter),
|
||||
dirent_name(sysnum, linux_dirp_iter))) {
|
||||
__dprintf("dupe: %s\n",
|
||||
dirent_name(sysnum, mck_dirp_iter));
|
||||
memmove(dirp + pos,
|
||||
dirp + pos + reclen,
|
||||
ret - pos - reclen);
|
||||
ret -= reclen;
|
||||
break;
|
||||
}
|
||||
mcpos += dirent_reclen(sysnum, mcdirp);
|
||||
mcpos += dirent_reclen(sysnum, mck_dirp_iter);
|
||||
}
|
||||
if (mcpos >= ofd->dirents_size)
|
||||
pos += reclen;
|
||||
}
|
||||
if (mcpos < ofd->mck_dirents_size)
|
||||
continue;
|
||||
|
||||
ret += linux_ret;
|
||||
pos += reclen;
|
||||
}
|
||||
linux_ret += ret;
|
||||
__dprintf("getdents from lower: linux_ret: %d, ret: %d, count: %d\n",
|
||||
linux_ret, ret, count);
|
||||
|
||||
/* cache Linux dirents to our buffer, in case of split getdents */
|
||||
if (ret > 0) {
|
||||
void *newbuf = realloc(ofd->linux_dirents,
|
||||
ofd->linux_dirents_size + ret);
|
||||
|
||||
if (!newbuf) {
|
||||
fprintf(stderr, "%s: not enough memory (%zd)",
|
||||
__func__, ofd->linux_dirents_size + ret);
|
||||
return ret;
|
||||
}
|
||||
ofd->linux_dirents = newbuf;
|
||||
memcpy(ofd->linux_dirents + ofd->linux_dirents_size,
|
||||
dirp, ret);
|
||||
|
||||
ofd->linux_dirents_size += ret;
|
||||
|
||||
/* Rewrite d_off to match the packed data layout.
|
||||
* Rewrite all because ofd->mck_dirents_size might
|
||||
* have changed.
|
||||
*/
|
||||
for (pos = 0; pos < ofd->linux_dirents_size;) {
|
||||
linux_dirp_iter = ofd->linux_dirents + pos;
|
||||
reclen = dirent_reclen(sysnum, linux_dirp_iter);
|
||||
#ifdef DEBUG
|
||||
printf("<%s,%d,%ld> ",
|
||||
dirent_name(sysnum, linux_dirp_iter),
|
||||
dirent_reclen(sysnum, linux_dirp_iter),
|
||||
*((unsigned long *)
|
||||
dirent_off(sysnum, linux_dirp_iter)));
|
||||
#endif
|
||||
*((unsigned long *)
|
||||
dirent_off(sysnum, linux_dirp_iter)) =
|
||||
ofd->mck_dirents_size + pos + reclen;
|
||||
|
||||
pos += reclen;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* It's possible we filtered everything out, but there is more
|
||||
* available. Keep trying!
|
||||
*/
|
||||
if (linux_ret == 0 || count - ret > 500)
|
||||
goto again;
|
||||
if (ret_before_edit > 0 && mck_ret + linux_ret < count) {
|
||||
goto linux_again;
|
||||
}
|
||||
|
||||
/* concatenate cached upper and lower and lseek */
|
||||
|
||||
/* TODO: this error should be detected by lseek */
|
||||
if (offset > ofd->mck_dirents_size + ofd->linux_dirents_size) {
|
||||
fprintf(stderr, "%s: offset (%ld) is too large (upper: %ld, lower: %ld)\n",
|
||||
__func__, offset, ofd->mck_dirents_size,
|
||||
ofd->linux_dirents_size);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
mck_len = 0;
|
||||
linux_len = 0;
|
||||
if (count > 0 && offset < ofd->mck_dirents_size) {
|
||||
mck_len = copy_dirents(_dirp, ofd->mck_dirents,
|
||||
ofd->mck_dirents_size, offset,
|
||||
&count, sysnum);
|
||||
/* Result buffer is too small */
|
||||
if (mck_len == 0) {
|
||||
__dprintf("upper: Result buffer is too small\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
offset = 0;
|
||||
} else {
|
||||
offset -= ofd->mck_dirents_size;
|
||||
}
|
||||
__dprintf("mck_dirents_size: %ld, offset: %ld, mck_len: %d, count: %d\n",
|
||||
ofd->mck_dirents_size, offset,
|
||||
mck_len, count);
|
||||
if (count > 0 && offset < ofd->linux_dirents_size) {
|
||||
linux_len = copy_dirents(_dirp + mck_len, ofd->linux_dirents,
|
||||
ofd->linux_dirents_size, offset,
|
||||
&count, sysnum);
|
||||
/* Result buffer is too small */
|
||||
if (mck_len == 0 && linux_len == 0) {
|
||||
__dprintf("lower: Result buffer is too small\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
__dprintf("linux_dirents_size: %ld, offset: %ld, linux_len: %d, count: %d\n",
|
||||
ofd->linux_dirents_size, offset,
|
||||
linux_len, count);
|
||||
}
|
||||
|
||||
ret = mck_len + linux_len;
|
||||
lseek(fd, ret, SEEK_CUR);
|
||||
|
||||
out_mck_only:
|
||||
|
||||
err:
|
||||
free(dirp);
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
void *dirp_iter;
|
||||
|
||||
printf("ret: %d, {<d_name,d_reclen,d_off>}: ", ret);
|
||||
for (pos = 0; pos < ret;
|
||||
pos += dirent_reclen(sysnum, dirp_iter)) {
|
||||
dirp_iter = _dirp + pos;
|
||||
printf("<%s,%d,%ld> ",
|
||||
dirent_name(sysnum, dirp_iter),
|
||||
dirent_reclen(sysnum, dirp_iter),
|
||||
*((unsigned long *)dirent_off(sysnum, dirp_iter))
|
||||
);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
129
test/issues/1421/1421.c
Normal file
129
test/issues/1421/1421.c
Normal file
@ -0,0 +1,129 @@
|
||||
// test getdents d_off and lseek are coherent
|
||||
#define _GNU_SOURCE
|
||||
#include <dirent.h> /* Defines DT_* constants */
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#define handle_error(msg) \
|
||||
do { perror(msg); exit(EXIT_FAILURE); } while (0)
|
||||
struct linux_dirent {
|
||||
long d_ino;
|
||||
off_t d_off;
|
||||
unsigned short d_reclen;
|
||||
char d_name[];
|
||||
};
|
||||
#define OFF_TABLE_SIZE (1UL << 20)
|
||||
#define RECLEN_TABLE_SIZE (1UL << 20)
|
||||
|
||||
void print_dirent(char *buf, int bpos)
|
||||
{
|
||||
struct linux_dirent *d;
|
||||
char d_type;
|
||||
|
||||
d = (struct linux_dirent *) (buf + bpos);
|
||||
printf("%8ld ", d->d_ino);
|
||||
d_type = *(buf + bpos + d->d_reclen - 1);
|
||||
printf("%-10s ", (d_type == DT_REG) ? "regular" :
|
||||
(d_type == DT_DIR) ? "directory" :
|
||||
(d_type == DT_FIFO) ? "FIFO" :
|
||||
(d_type == DT_SOCK) ? "socket" :
|
||||
(d_type == DT_LNK) ? "symlink" :
|
||||
(d_type == DT_BLK) ? "block dev" :
|
||||
(d_type == DT_CHR) ? "char dev" : "???");
|
||||
printf("%4d %10lld %s\n", d->d_reclen,
|
||||
(long long) d->d_off, (char *) d->d_name);
|
||||
}
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd, nread;
|
||||
int buf_size = argc > 2 ? atoi(argv[2]) : 40;
|
||||
char *buf;
|
||||
struct linux_dirent *d;
|
||||
int bpos;
|
||||
off_t *off_table;
|
||||
int off_table_size = 0;
|
||||
unsigned short *reclen_table;
|
||||
int reclen_table_size = 0;
|
||||
int i;
|
||||
|
||||
buf = malloc(buf_size);
|
||||
if (!buf)
|
||||
handle_error("allocating buf");
|
||||
|
||||
off_table = malloc(OFF_TABLE_SIZE * sizeof(off_t));
|
||||
if (!off_table)
|
||||
handle_error("allocating off_table");
|
||||
off_table[off_table_size++] = 0;
|
||||
|
||||
reclen_table = malloc(RECLEN_TABLE_SIZE * sizeof(unsigned short));
|
||||
if (!reclen_table)
|
||||
handle_error("allocating reclen_table");
|
||||
|
||||
fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
|
||||
if (fd == -1)
|
||||
handle_error("open");
|
||||
for ( ; ; ) {
|
||||
nread = syscall(SYS_getdents, fd, buf, buf_size);
|
||||
if (nread == -1)
|
||||
handle_error("getdents");
|
||||
if (nread == 0)
|
||||
break;
|
||||
printf("--------------- nread=%d ---------------\n", nread);
|
||||
printf("i-node# file type d_reclen d_off d_name\n");
|
||||
for (bpos = 0; bpos < nread;) {
|
||||
d = (struct linux_dirent *) (buf + bpos);
|
||||
print_dirent(buf, bpos);
|
||||
off_table[off_table_size++] = d->d_off;
|
||||
reclen_table[reclen_table_size++] = d->d_reclen;
|
||||
bpos += d->d_reclen;
|
||||
}
|
||||
printf("at end of getdents: lseek %10lld\n",
|
||||
lseek(fd, 0, SEEK_CUR));
|
||||
}
|
||||
|
||||
for (i = 0; i < off_table_size; i++) {
|
||||
lseek(fd, off_table[i], SEEK_SET);
|
||||
printf("lseek to %ld: lseek %10lld\n",
|
||||
off_table[i], lseek(fd, 0, SEEK_CUR));
|
||||
|
||||
nread = syscall(SYS_getdents, fd, buf, reclen_table[i] - 1);
|
||||
if (i != off_table_size - 1) {
|
||||
if (nread == -1 && errno == EINVAL) {
|
||||
printf("[ OK ] EINVAL for too small count\n");
|
||||
} else {
|
||||
printf("[ NG ] EINVAL for too small count\n");
|
||||
handle_error("Test failed");
|
||||
}
|
||||
}
|
||||
|
||||
nread = syscall(SYS_getdents, fd, buf, buf_size);
|
||||
if (nread == -1)
|
||||
handle_error("getdents");
|
||||
if (nread == 0) {
|
||||
printf("--------------- nread=%d (EOF) ---------\n",
|
||||
nread);
|
||||
if (i != off_table_size - 1)
|
||||
handle_error("unexpected EOF");
|
||||
} else {
|
||||
printf("--------------- nread=%d ---------------\n",
|
||||
nread);
|
||||
printf("i-node# file type d_reclen d_off d_name\n");
|
||||
for (bpos = 0; bpos < nread;) {
|
||||
d = (struct linux_dirent *) (buf + bpos);
|
||||
print_dirent(buf, bpos);
|
||||
bpos += d->d_reclen;
|
||||
}
|
||||
}
|
||||
printf("at end of getdents: lseek %10lld\n",
|
||||
lseek(fd, 0, SEEK_CUR));
|
||||
}
|
||||
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
11
test/issues/1421/Makefile
Normal file
11
test/issues/1421/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
include $(HOME)/.mck_test_config.mk
|
||||
|
||||
TARGET=1421
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
test: $(TARGET)
|
||||
@echo "===== getdents count: 40"
|
||||
$(MCK_DIR)/bin/mcexec ./$(TARGET) /sys/devices/system/node 40
|
||||
@printf "\n===== getdents count: 200\n"
|
||||
$(MCK_DIR)/bin/mcexec ./$(TARGET) /sys/devices/system/node 200
|
||||
13
test/issues/1421/README
Normal file
13
test/issues/1421/README
Normal file
@ -0,0 +1,13 @@
|
||||
============
|
||||
What to test
|
||||
============
|
||||
(1) lseek value after a getdents matches the d_off of the last entry
|
||||
received.
|
||||
(2) lseek using d_off and then getdents gets the proper entry.
|
||||
(3) lseek using d_off and then getdents with (d_reclen -1) returns EINVAL.
|
||||
|
||||
===========
|
||||
How to test
|
||||
===========
|
||||
(1) make test
|
||||
(2) Check the result manually.
|
||||
179
test/issues/1421/result_x86_64.txt
Normal file
179
test/issues/1421/result_x86_64.txt
Normal file
@ -0,0 +1,179 @@
|
||||
cc 1421.c -o 1421
|
||||
===== getdents count: 40
|
||||
/work/gg10/e29005/project/os/install/bin/mcexec ./1421 /sys/devices/system/node 40
|
||||
--------------- nread=24 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61145 directory 24 24 .
|
||||
at end of getdents: lseek 24
|
||||
--------------- nread=24 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
46533 directory 24 48 ..
|
||||
at end of getdents: lseek 48
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61156 directory 32 80 node0
|
||||
at end of getdents: lseek 80
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61148 directory 32 112 node1
|
||||
at end of getdents: lseek 112
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61147 regular 32 144 possible
|
||||
at end of getdents: lseek 144
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61146 regular 32 176 online
|
||||
at end of getdents: lseek 176
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18112 directory 32 208 power
|
||||
at end of getdents: lseek 208
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 0: lseek 0
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=24 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61145 directory 24 24 .
|
||||
at end of getdents: lseek 24
|
||||
lseek to 24: lseek 24
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=24 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
46533 directory 24 48 ..
|
||||
at end of getdents: lseek 48
|
||||
lseek to 48: lseek 48
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61156 directory 32 80 node0
|
||||
at end of getdents: lseek 80
|
||||
lseek to 80: lseek 80
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61148 directory 32 112 node1
|
||||
at end of getdents: lseek 112
|
||||
lseek to 112: lseek 112
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61147 regular 32 144 possible
|
||||
at end of getdents: lseek 144
|
||||
lseek to 144: lseek 144
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61146 regular 32 176 online
|
||||
at end of getdents: lseek 176
|
||||
lseek to 176: lseek 176
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18112 directory 32 208 power
|
||||
at end of getdents: lseek 208
|
||||
lseek to 208: lseek 208
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 240: lseek 240
|
||||
--------------- nread=0 (EOF) ---------
|
||||
at end of getdents: lseek 240
|
||||
|
||||
===== getdents count: 200
|
||||
/work/gg10/e29005/project/os/install/bin/mcexec ./1421 /sys/devices/system/node 200
|
||||
--------------- nread=176 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61145 directory 24 24 .
|
||||
46533 directory 24 48 ..
|
||||
61156 directory 32 80 node0
|
||||
61148 directory 32 112 node1
|
||||
61147 regular 32 144 possible
|
||||
61146 regular 32 176 online
|
||||
at end of getdents: lseek 176
|
||||
--------------- nread=64 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18112 directory 32 208 power
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 0: lseek 0
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=176 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61145 directory 24 24 .
|
||||
46533 directory 24 48 ..
|
||||
61156 directory 32 80 node0
|
||||
61148 directory 32 112 node1
|
||||
61147 regular 32 144 possible
|
||||
61146 regular 32 176 online
|
||||
at end of getdents: lseek 176
|
||||
lseek to 24: lseek 24
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=184 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
46533 directory 24 48 ..
|
||||
61156 directory 32 80 node0
|
||||
61148 directory 32 112 node1
|
||||
61147 regular 32 144 possible
|
||||
61146 regular 32 176 online
|
||||
18112 directory 32 208 power
|
||||
at end of getdents: lseek 208
|
||||
lseek to 48: lseek 48
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=192 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61156 directory 32 80 node0
|
||||
61148 directory 32 112 node1
|
||||
61147 regular 32 144 possible
|
||||
61146 regular 32 176 online
|
||||
18112 directory 32 208 power
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 80: lseek 80
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=160 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61148 directory 32 112 node1
|
||||
61147 regular 32 144 possible
|
||||
61146 regular 32 176 online
|
||||
18112 directory 32 208 power
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 112: lseek 112
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=128 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61147 regular 32 144 possible
|
||||
61146 regular 32 176 online
|
||||
18112 directory 32 208 power
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 144: lseek 144
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=96 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
61146 regular 32 176 online
|
||||
18112 directory 32 208 power
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 176: lseek 176
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=64 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18112 directory 32 208 power
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 208: lseek 208
|
||||
[ OK ] EINVAL for too small count
|
||||
--------------- nread=32 ---------------
|
||||
i-node# file type d_reclen d_off d_name
|
||||
18106 regular 32 240 uevent
|
||||
at end of getdents: lseek 240
|
||||
lseek to 240: lseek 240
|
||||
--------------- nread=0 (EOF) ---------
|
||||
at end of getdents: lseek 240
|
||||
Reference in New Issue
Block a user