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 {
|
struct overlay_fd {
|
||||||
int fd; /* associated fd, points to mckernel side */
|
int fd; /* associated fd, points to mckernel side */
|
||||||
|
int getdents_fd; /* non-seekable mckernel fd */
|
||||||
int linux_fd; /* linux fd, -1 if not opened */
|
int linux_fd; /* linux fd, -1 if not opened */
|
||||||
struct list_head link;
|
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;
|
size_t pathlen;
|
||||||
void *dirents; /* copy of mckernel dirents to filter duplicates */
|
void *mck_dirents; /* cache of mckernel dirents to filter duplicates */
|
||||||
size_t dirents_size;
|
size_t mck_dirents_size;
|
||||||
|
void *linux_dirents; /* cache of filtered Linux dirents */
|
||||||
|
size_t linux_dirents_size;
|
||||||
};
|
};
|
||||||
LIST_HEAD(overlay_fd_list);
|
LIST_HEAD(overlay_fd_list);
|
||||||
|
|
||||||
@@ -3039,10 +3043,15 @@ void overlay_addfd(int fd, const char *path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ofd->fd = fd;
|
ofd->fd = fd;
|
||||||
|
ofd->getdents_fd = -1;
|
||||||
ofd->linux_fd = -1;
|
ofd->linux_fd = -1;
|
||||||
ofd->dirents = NULL;
|
ofd->mck_dirents = NULL;
|
||||||
ofd->dirents_size = 0;
|
ofd->mck_dirents_size = 0;
|
||||||
ofd->pathlen = snprintf(ofd->path, PATH_MAX, "%s%s", prefix, real_path);
|
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);
|
pthread_spin_lock(&overlay_fd_lock);
|
||||||
list_add(&ofd->link, &overlay_fd_list);
|
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) {
|
list_for_each_entry(ofd, &overlay_fd_list, link) {
|
||||||
if (ofd->fd == fd) {
|
if (ofd->fd == fd) {
|
||||||
list_del(&ofd->link);
|
list_del(&ofd->link);
|
||||||
|
if (ofd->getdents_fd != -1)
|
||||||
|
close(ofd->getdents_fd);
|
||||||
if (ofd->linux_fd != -1)
|
if (ofd->linux_fd != -1)
|
||||||
close(ofd->linux_fd);
|
close(ofd->linux_fd);
|
||||||
free(ofd->dirents);
|
free(ofd->mck_dirents);
|
||||||
|
free(ofd->linux_dirents);
|
||||||
free(ofd);
|
free(ofd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3401,122 +3413,370 @@ static inline char *dirent_name(int sysnum, void *_dirp)
|
|||||||
exit(-1);
|
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)
|
int overlay_getdents(int sysnum, int fd, void *_dirp, unsigned int count)
|
||||||
{
|
{
|
||||||
void *dirp, *mcdirp;
|
void *dirp = NULL;
|
||||||
int ret = 0, pos, linux_ret, mcpos;
|
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;
|
unsigned short reclen;
|
||||||
struct overlay_fd *ofd = NULL, *ofd_iter;
|
struct overlay_fd *ofd = NULL, *ofd_iter;
|
||||||
int hide_orig = 0;
|
int hide_orig = 0;
|
||||||
|
off_t offset;
|
||||||
|
char ofd_path[PATH_MAX];
|
||||||
|
int mck_len, linux_len;
|
||||||
|
|
||||||
pthread_spin_lock(&overlay_fd_lock);
|
pthread_spin_lock(&overlay_fd_lock);
|
||||||
list_for_each_entry(ofd_iter, &overlay_fd_list, link) {
|
list_for_each_entry(ofd_iter, &overlay_fd_list, link) {
|
||||||
if (ofd_iter->fd == fd) {
|
if (ofd_iter->fd == fd) {
|
||||||
ofd = ofd_iter;
|
ofd = ofd_iter;
|
||||||
__dprintf("found overlay cache entry (%s)\n",
|
__dprintf("found overlay cache entry (%s)\n",
|
||||||
ofd->path);
|
ofd->linux_path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&overlay_fd_lock);
|
pthread_spin_unlock(&overlay_fd_lock);
|
||||||
|
|
||||||
/* special case for /proc/N/task */
|
/* special case for /proc/N/task */
|
||||||
if (ofd && !strncmp(ofd->path, "/proc", 5) &&
|
if (ofd && !strncmp(ofd->linux_path, "/proc", 5) &&
|
||||||
!strncmp(ofd->path + strlen(ofd->path) - 4,
|
!strncmp(ofd->linux_path + strlen(ofd->linux_path) - 4,
|
||||||
"task", 4)) {
|
"task", 4)) {
|
||||||
hide_orig = 1;
|
hide_orig = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not a directory we overlay, or not there yet */
|
/* not a directory we overlay or hiding lower fs */
|
||||||
if (ofd == NULL || ofd->linux_fd == -1 || hide_orig) {
|
if (ofd == NULL || hide_orig) {
|
||||||
ret = syscall(sysnum, fd, _dirp, count);
|
ret = syscall(sysnum, fd, _dirp, count);
|
||||||
if (ret == -1)
|
if (ret == -1) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
goto err;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
ofd->dirents = newbuf;
|
goto out_mck_only;
|
||||||
memcpy(ofd->dirents + ofd->dirents_size, _dirp, ret);
|
|
||||||
ofd->dirents_size += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return first directory result unless it is empty or there
|
dirp = malloc(count);
|
||||||
* is obvious room for more elements.
|
if (!dirp) {
|
||||||
* The second check could have false positives depending on
|
fprintf(stderr, "%s: out of memory\n", __func__);
|
||||||
* the fs, but should not be for filesystems we overlay
|
ret = -ENOMEM;
|
||||||
*/
|
goto err;
|
||||||
if (ret > 0 && count - ret < 500)
|
}
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (ofd->linux_fd == -1) {
|
offset = lseek(fd, 0, SEEK_CUR);
|
||||||
ofd->linux_fd = open(ofd->path, O_RDONLY|O_DIRECTORY);
|
if (offset == (off_t)-1) {
|
||||||
if (ofd->linux_fd < 0) {
|
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) {
|
if (errno != ENOENT) {
|
||||||
fprintf(stderr, "%s: could not open %s: %d\n",
|
fprintf(stderr, "%s: could not open %s: %d\n",
|
||||||
__func__, ofd->path, errno);
|
__func__, ofd->mck_path, errno);
|
||||||
}
|
}
|
||||||
return ret;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
again:
|
mck_again:
|
||||||
linux_ret = syscall(sysnum, ofd->linux_fd, _dirp + ret, count - ret);
|
/* Use "count" to simplify the handling of
|
||||||
if (linux_ret < 0) {
|
* "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",
|
fprintf(stderr, "%s: linux getdents failed: %d\n",
|
||||||
__func__, errno);
|
__func__, errno);
|
||||||
return ret;
|
goto err;
|
||||||
}
|
}
|
||||||
if (linux_ret == 0)
|
ret_before_edit = ret;
|
||||||
return ret;
|
|
||||||
|
|
||||||
for (pos = ret; pos < ret + linux_ret;) {
|
for (pos = 0; pos < ret;) {
|
||||||
dirp = _dirp + pos;
|
linux_dirp_iter = dirp + pos;
|
||||||
reclen = dirent_reclen(sysnum, dirp);
|
reclen = dirent_reclen(sysnum, linux_dirp_iter);
|
||||||
snprintf(ofd->path + ofd->pathlen, PATH_MAX - ofd->pathlen,
|
snprintf(ofd_path + ofd->pathlen, PATH_MAX - ofd->pathlen,
|
||||||
"/%s", dirent_name(sysnum, dirp));
|
"/%s", dirent_name(sysnum, linux_dirp_iter));
|
||||||
/* remove blacklist */
|
/* remove blacklist */
|
||||||
if (overlay_blacklist(ofd->path)) {
|
if (overlay_blacklist(ofd_path)) {
|
||||||
__dprintf("blacklisted %s\n", ofd->path);
|
__dprintf("blacklisted: %s\n", ofd_path);
|
||||||
memmove(_dirp + pos,
|
memmove(dirp + pos,
|
||||||
_dirp + pos + reclen,
|
dirp + pos + reclen,
|
||||||
ret + linux_ret - pos - reclen);
|
ret - pos - reclen);
|
||||||
linux_ret -= reclen;
|
ret -= reclen;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* remove duplicates */
|
/* remove duplicates */
|
||||||
for (mcpos = 0; mcpos < ofd->dirents_size;) {
|
for (mcpos = 0; mcpos < ofd->mck_dirents_size;) {
|
||||||
mcdirp = ofd->dirents + mcpos;
|
mck_dirp_iter = ofd->mck_dirents + mcpos;
|
||||||
if (!strcmp(dirent_name(sysnum, mcdirp),
|
if (!strcmp(dirent_name(sysnum, mck_dirp_iter),
|
||||||
dirent_name(sysnum, dirp))) {
|
dirent_name(sysnum, linux_dirp_iter))) {
|
||||||
memmove(_dirp + pos,
|
__dprintf("dupe: %s\n",
|
||||||
_dirp + pos + reclen,
|
dirent_name(sysnum, mck_dirp_iter));
|
||||||
ret + linux_ret - pos - reclen);
|
memmove(dirp + pos,
|
||||||
linux_ret -= reclen;
|
dirp + pos + reclen,
|
||||||
|
ret - pos - reclen);
|
||||||
|
ret -= reclen;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mcpos += dirent_reclen(sysnum, mcdirp);
|
mcpos += dirent_reclen(sysnum, mck_dirp_iter);
|
||||||
}
|
}
|
||||||
if (mcpos >= ofd->dirents_size)
|
if (mcpos < ofd->mck_dirents_size)
|
||||||
pos += reclen;
|
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
|
/* It's possible we filtered everything out, but there is more
|
||||||
* available. Keep trying!
|
* available. Keep trying!
|
||||||
*/
|
*/
|
||||||
if (linux_ret == 0 || count - ret > 500)
|
if (ret_before_edit > 0 && mck_ret + linux_ret < count) {
|
||||||
goto again;
|
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;
|
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