Hello Charles P. Wright wrote: > Vladimir, > > We actually came across this while working on Unionfs in the kernel, > which uses lseek to inquire about directory positions and then resume > directory reading operations (much like nfsd does). >
Ok, please try whether the attached patch makes reiser4 to behave similar to others. > I've attached a user space program that demonstrates the behavior. > > The relevant strace entries are: > open("/mnt/r4/", O_RDONLY) = 3 > getdents(3, /* 5 entries */, 268) = 80 > lseek(3, 0, SEEK_CUR) = -1 ENOENT > lseek(3, 4294967295, SEEK_SET) = -1 EINVAL > > The third line is the one of concern. > > The same program run on another file system yields a trace like: > open("/mnt", O_RDONLY) = 3 > getdents(3, /* 4 entries */, 268) = 68 > lseek(3, 0, SEEK_CUR) = 2147483647 > lseek(3, 2147483647, SEEK_SET) = 2147483647 > > Charles > > On Mon, 2005-08-22 at 20:19 +0400, Vladimir V. Saveliev wrote: >>Hello >> >>Charles P. Wright wrote: >>>Hello, >>> >>>I've noticed that Reiser4's behavior deviates from other file systems >>>when seeking with directories. After reading a directory, if you run >>>vfs_lseek(dir, 0, SEEK_CUR), then -ENOENT is returned. This means that >>>you can't pass the identifier back to vfs_lseek with SEEK_SET. >>> >>Would you please send your test program? >> >>------------------------------------------------------------------------ >> >>#include <stdio.h> >>#include <linux/types.h> >>#include <linux/unistd.h> >>#include <linux/dirent.h> >>#include <sys/types.h> >>#include <fcntl.h> >>#include <sys/errno.h> >>#include <unistd.h> >>#include <stdlib.h> >> >>_syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count); >> >>int main(int argc, char *argv[]) >>{ >> struct dirent dirent; >> int fd; >> int nread; >> struct dirent *p; >> off_t offset; >> >> if (argc != 2) { >> fprintf(stderr, "No dir specified\n"); >> exit(1); >> } >> >> if ((fd = open(argv[1], O_RDONLY)) < 0) { >> fprintf(stderr, "Could not open directory %s\n", argv[1]); >> exit(2); >> } >> >> while ((nread = getdents(fd, &dirent, sizeof(dirent))) > 0) { >> p = &dirent; >> while (nread > 0) { >> printf("d_reclen = %d, d_name = %s, d_off = %lu\n", >> p->d_reclen, p->d_name, p->d_off); >> nread -= p->d_reclen; >> p = (struct dirent *)((char *)p + p->d_reclen); >> } >> offset = lseek(fd, 0, SEEK_CUR); >> printf("Offset is %llu\n", offset); offset is off_t. off_t is long int in this program. You should have therefore printf("Offset is %ld\n", offset); >> if (lseek(fd, offset, SEEK_SET) < 0) { >> perror("lseek"); >> exit(1); >> } >> } >> exit(0); >>}
This patch changes lseek part the end of reiser4 directory to not return ENOENT. fs/reiser4/plugin/dir/dir.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff -puN fs/reiser4/plugin/dir/dir.c~reiser4-fix-llseek fs/reiser4/plugin/dir/dir.c --- linux-2.6.12-rc6-mm1/fs/reiser4/plugin/dir/dir.c~reiser4-fix-llseek 2005-08-23 15:22:43.363256896 +0400 +++ linux-2.6.12-rc6-mm1-vs/fs/reiser4/plugin/dir/dir.c 2005-08-23 15:59:29.523105778 +0400 @@ -854,7 +854,10 @@ dir_rewind(struct file *dir, readdir_pos memset(pos, 0, sizeof *pos); return dir_go_to(dir, pos, tap); } else if (destination >= inode->i_size) - return RETERR(-ENOENT); + /* seek past the end of directory */ + dir->f_pos = inode->i_size; + return 0; + } if (shift < 0) { /* I am afraid of negative numbers */ @@ -1629,7 +1632,7 @@ readdir_common(struct file *f /* directo repeat: result = dir_readdir_init(f, &tap, &pos); - if (result == 0) { + if (result == 0 && f->f_pos != inode->i_size) { result = tap_load(&tap); /* scan entries one by one feeding them to @filld */ while (result == 0) { @@ -1669,7 +1672,7 @@ readdir_common(struct file *f /* directo if (result >= 0) f->f_version = inode->i_version; - } else if (result == -E_NO_NEIGHBOR || result == -ENOENT) + } else if (result == -E_NO_NEIGHBOR || f->f_pos == inode->i_size || result == -ENOENT) result = 0; tap_done(&tap); detach_fsdata(f); _