* [PATCH] fuse: fix occasional dentry leak when readdirplus is used
@ 2013-07-15 12:59 Niels de Vos
2013-07-15 20:08 ` [fuse-devel] " Brian Foster
0 siblings, 1 reply; 8+ messages in thread
From: Niels de Vos @ 2013-07-15 12:59 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: fuse-devel, linux-kernel, Niels de Vos
In case d_lookup() returns a dentry with d_inode == NULL, the dentry is
not returned with dput(). This results in triggering a BUG() in
shrink_dcache_for_umount_subtree():
BUG: Dentry ...{i=0,n=...} still in use (1) [unmount of fuse fuse]
Reported-by: Justin Clift <jclift@redhat.com>
Signed-off-by: Niels de Vos <ndevos@redhat.com>
--
Reproducing the BUG() on kernels with fuse that support READDIRPLUS can
be done with the GlusterFS tests:
- http://www.gluster.org/community/documentation/index.php/Using_the_Gluster_Test_Framework
After some stressing of the VFS and fuse mountpoints, bug-860663.t will
hit the BUG(). It does not happen on running this test stand-alone.
---
fs/fuse/dir.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 0eda527..da67a15 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1246,7 +1246,9 @@ static int fuse_direntplus_link(struct file *file,
if (err)
goto out;
dput(dentry);
- dentry = NULL;
+ } else if (dentry) {
+ /* this dentry does not have a d_inode, just drop it */
+ dput(dentry);
}
dentry = d_alloc(parent, &name);
--
1.7.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-15 12:59 [PATCH] fuse: fix occasional dentry leak when readdirplus is used Niels de Vos @ 2013-07-15 20:08 ` Brian Foster 2013-07-16 10:39 ` Niels de Vos 0 siblings, 1 reply; 8+ messages in thread From: Brian Foster @ 2013-07-15 20:08 UTC (permalink / raw) To: Niels de Vos; +Cc: Miklos Szeredi, fuse-devel, linux-kernel On 07/15/2013 08:59 AM, Niels de Vos wrote: > In case d_lookup() returns a dentry with d_inode == NULL, the dentry is > not returned with dput(). This results in triggering a BUG() in > shrink_dcache_for_umount_subtree(): > > BUG: Dentry ...{i=0,n=...} still in use (1) [unmount of fuse fuse] > > Reported-by: Justin Clift <jclift@redhat.com> > Signed-off-by: Niels de Vos <ndevos@redhat.com> > > -- > Reproducing the BUG() on kernels with fuse that support READDIRPLUS can > be done with the GlusterFS tests: > - http://www.gluster.org/community/documentation/index.php/Using_the_Gluster_Test_Framework > > After some stressing of the VFS and fuse mountpoints, bug-860663.t will > hit the BUG(). It does not happen on running this test stand-alone. Hi Neils, FYI, this is fairly easy to reproduce on-demand with gluster: - mount a volume to two local mountpoints (i.e., I used a single storage/posix translator volume): glusterfs --volfile=./test.vol /mnt/{1,2} --use-readdirp=1 - create a negative dentry in one mountpoint: ls /mnt/1/file (results in ENOENT) - create the file via the second mountpoint: touch /mnt/2/file - run a readdirp on the first mountpoint: ls /mnt/1/ - umount /mnt/2 /mnt/1 > --- > fs/fuse/dir.c | 4 +++- > 1 files changed, 3 insertions(+), 1 deletions(-) > > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > index 0eda527..da67a15 100644 > --- a/fs/fuse/dir.c > +++ b/fs/fuse/dir.c > @@ -1246,7 +1246,9 @@ static int fuse_direntplus_link(struct file *file, > if (err) > goto out; > dput(dentry); > - dentry = NULL; > + } else if (dentry) { > + /* this dentry does not have a d_inode, just drop it */ > + dput(dentry); > } I'm not really familiar with the dcache code, but is it appropriate to also d_invalidate() the dentry in this case (as the previous code block does)? Perhaps Miklos or somebody more familiar with dcache can confirm... Brian > > dentry = d_alloc(parent, &name); > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-15 20:08 ` [fuse-devel] " Brian Foster @ 2013-07-16 10:39 ` Niels de Vos 2013-07-16 13:15 ` Brian Foster 0 siblings, 1 reply; 8+ messages in thread From: Niels de Vos @ 2013-07-16 10:39 UTC (permalink / raw) To: Brian Foster; +Cc: Miklos Szeredi, fuse-devel, linux-kernel On Mon, Jul 15, 2013 at 04:08:22PM -0400, Brian Foster wrote: > On 07/15/2013 08:59 AM, Niels de Vos wrote: > > In case d_lookup() returns a dentry with d_inode == NULL, the dentry is > > not returned with dput(). This results in triggering a BUG() in > > shrink_dcache_for_umount_subtree(): > > > > BUG: Dentry ...{i=0,n=...} still in use (1) [unmount of fuse fuse] > > > > Reported-by: Justin Clift <jclift@redhat.com> > > Signed-off-by: Niels de Vos <ndevos@redhat.com> > > > > -- > > Reproducing the BUG() on kernels with fuse that support READDIRPLUS can > > be done with the GlusterFS tests: > > - http://www.gluster.org/community/documentation/index.php/Using_the_Gluster_Test_Framework > > > > After some stressing of the VFS and fuse mountpoints, bug-860663.t will > > hit the BUG(). It does not happen on running this test stand-alone. > > Hi Neils, > > FYI, this is fairly easy to reproduce on-demand with gluster: > > - mount a volume to two local mountpoints (i.e., I used a single > storage/posix translator volume): > glusterfs --volfile=./test.vol /mnt/{1,2} --use-readdirp=1 > - create a negative dentry in one mountpoint: > ls /mnt/1/file (results in ENOENT) > - create the file via the second mountpoint: > touch /mnt/2/file > - run a readdirp on the first mountpoint: > ls /mnt/1/ > - umount /mnt/2 /mnt/1 Thanks, that definitely makes it easier to verify the fix. > > --- > > fs/fuse/dir.c | 4 +++- > > 1 files changed, 3 insertions(+), 1 deletions(-) > > > > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > > index 0eda527..da67a15 100644 > > --- a/fs/fuse/dir.c > > +++ b/fs/fuse/dir.c > > @@ -1246,7 +1246,9 @@ static int fuse_direntplus_link(struct file *file, > > if (err) > > goto out; > > dput(dentry); > > - dentry = NULL; > > + } else if (dentry) { > > + /* this dentry does not have a d_inode, just drop it */ > > + dput(dentry); > > } > > I'm not really familiar with the dcache code, but is it appropriate to > also d_invalidate() the dentry in this case (as the previous code block > does)? Perhaps Miklos or somebody more familiar with dcache can confirm... I do not *think* d_invalidate() is needed. The vmcores I have seem where this BUG() happened, only have dentry->d_flags = 0x18 which translates to (DCACHE_OP_DELETE | DCACHE_OP_PRUNE) and d_subdirs as an empty list. d_invalidate() only calls __d_drop(), which only does something when the dentry is hashed. I am not sure if a dentry can be hashed, but still does not have a valid non-NULL d_inode. If that is the case, d_invalidate() should indeed be called. Thanks, Niels > Brian > > > > > dentry = d_alloc(parent, &name); > > > -- Niels de Vos Sr. Software Maintenance Engineer Support Engineering Group Red Hat Global Support Services ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-16 10:39 ` Niels de Vos @ 2013-07-16 13:15 ` Brian Foster 2013-07-16 16:14 ` Miklos Szeredi 0 siblings, 1 reply; 8+ messages in thread From: Brian Foster @ 2013-07-16 13:15 UTC (permalink / raw) To: Niels de Vos; +Cc: Miklos Szeredi, fuse-devel, linux-kernel On 07/16/2013 06:39 AM, Niels de Vos wrote: > On Mon, Jul 15, 2013 at 04:08:22PM -0400, Brian Foster wrote: >> On 07/15/2013 08:59 AM, Niels de Vos wrote: ... > >>> --- >>> fs/fuse/dir.c | 4 +++- >>> 1 files changed, 3 insertions(+), 1 deletions(-) >>> >>> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c >>> index 0eda527..da67a15 100644 >>> --- a/fs/fuse/dir.c >>> +++ b/fs/fuse/dir.c >>> @@ -1246,7 +1246,9 @@ static int fuse_direntplus_link(struct file *file, >>> if (err) >>> goto out; >>> dput(dentry); >>> - dentry = NULL; >>> + } else if (dentry) { >>> + /* this dentry does not have a d_inode, just drop it */ >>> + dput(dentry); >>> } >> >> I'm not really familiar with the dcache code, but is it appropriate to >> also d_invalidate() the dentry in this case (as the previous code block >> does)? Perhaps Miklos or somebody more familiar with dcache can confirm... > > I do not *think* d_invalidate() is needed. The vmcores I have seem where > this BUG() happened, only have dentry->d_flags = 0x18 which translates > to (DCACHE_OP_DELETE | DCACHE_OP_PRUNE) and d_subdirs as an empty list. > d_invalidate() only calls __d_drop(), which only does something when the > dentry is hashed. > I ran a little experiment to invoke a d_lookup() after the fuse_direntplus_link() function has done its work in this particular case. I do receive the newly allocated dentry. Subsequently, I hacked __d_lookup_rcu() to continue iterating the list after finding an entry to check for duplicates and print a message. What I see is that after the dput() (via a subsequent lookup resulting from a getxattr call), the negative dentry is still hashed and on the list (my printk() kicks in after the existing d_unhashed() check). The flags for both dentries are (DCACHE_OP_REVALIDATE|DCACHE_REFERENCED|DCACHE_RCUACCESS). I'm not aware of the steps involved in the original reproducer, but the simple multi-mount test leads to this state. Running a d_invalidate() clears it up. So perhaps it's required in some cases and not all. That said, some of those flags seem to simply specify whether a dentry op handler is defined for the particular operation or not. > I am not sure if a dentry can be hashed, but still does not have a valid > non-NULL d_inode. If that is the case, d_invalidate() should indeed be > called. > I'm not sure why it would need to have a valid inode. A dentry with a NULL inode is valid, no? I think the question is whether the above state (multiple, hashed dentries) can be valid for whatever reason. It certainly looks suspicious. Brian > Thanks, > Niels > >> Brian >> >>> >>> dentry = d_alloc(parent, &name); >>> >> > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-16 13:15 ` Brian Foster @ 2013-07-16 16:14 ` Miklos Szeredi 2013-07-16 17:43 ` Brian Foster 2013-07-17 11:20 ` Niels de Vos 0 siblings, 2 replies; 8+ messages in thread From: Miklos Szeredi @ 2013-07-16 16:14 UTC (permalink / raw) To: Brian Foster; +Cc: Niels de Vos, fuse-devel, linux-kernel On Tue, Jul 16, 2013 at 09:15:16AM -0400, Brian Foster wrote: > I'm not sure why it would need to have a valid inode. A dentry with a > NULL inode is valid, no? It is valid, yes. It's called a "negative" dentry, which caches the information that the file does not exist. > I think the question is whether the above state (multiple, hashed dentries) > can be valid for whatever reason. It certainly looks suspicious. That state is not valid. So we certainly need to unhash the negative dentry first, before hashing a new one. We could also reuse the old dentry, but that is just an optimization. Below patch fixes several issues with that function. Could you please give it a go? Thanks, Miklos diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0eda527..a8208c5 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1223,28 +1223,45 @@ static int fuse_direntplus_link(struct file *file, if (name.name[1] == '.' && name.len == 2) return 0; } + + if (invalid_nodeid(o->nodeid)) + return -EIO; + if (!fuse_valid_type(o->attr.mode)) + return -EIO; + fc = get_fuse_conn(dir); name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); - if (dentry && dentry->d_inode) { + if (dentry) { inode = dentry->d_inode; - if (get_node_id(inode) == o->nodeid) { + if (!inode) { + d_drop(dentry); + } else if (get_node_id(inode) != o->nodeid || + ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { + err = d_invalidate(dentry); + if (err) + goto out; + } else if (is_bad_inode(inode)) { + err = -EIO; + goto out; + } else { struct fuse_inode *fi; fi = get_fuse_inode(inode); spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); + fuse_change_attributes(inode, &o->attr, + entry_attr_timeout(o), + attr_version); + /* * The other branch to 'found' comes via fuse_iget() * which bumps nlookup inside */ goto found; } - err = d_invalidate(dentry); - if (err) - goto out; dput(dentry); dentry = NULL; } @@ -1259,25 +1276,30 @@ static int fuse_direntplus_link(struct file *file, if (!inode) goto out; - alias = d_materialise_unique(dentry, inode); - err = PTR_ERR(alias); - if (IS_ERR(alias)) - goto out; + if (S_ISDIR(inode->i_mode)) { + mutex_lock(&fc->inst_mutex); + alias = fuse_d_add_directory(dentry, inode); + mutex_unlock(&fc->inst_mutex); + err = PTR_ERR(alias); + if (IS_ERR(alias)) { + iput(inode); + goto out; + } + } else { + alias = d_splice_alias(inode, dentry); + } + if (alias) { dput(dentry); dentry = alias; } found: - fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), - attr_version); - fuse_change_entry_timeout(dentry, o); err = 0; out: - if (dentry) - dput(dentry); + dput(dentry); return err; } ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-16 16:14 ` Miklos Szeredi @ 2013-07-16 17:43 ` Brian Foster 2013-07-17 11:20 ` Niels de Vos 1 sibling, 0 replies; 8+ messages in thread From: Brian Foster @ 2013-07-16 17:43 UTC (permalink / raw) To: Miklos Szeredi; +Cc: Niels de Vos, fuse-devel, linux-kernel On 07/16/2013 12:14 PM, Miklos Szeredi wrote: > On Tue, Jul 16, 2013 at 09:15:16AM -0400, Brian Foster wrote: > >> I'm not sure why it would need to have a valid inode. A dentry with a >> NULL inode is valid, no? > > It is valid, yes. It's called a "negative" dentry, which caches the information > that the file does not exist. > >> I think the question is whether the above state (multiple, hashed dentries) >> can be valid for whatever reason. It certainly looks suspicious. > > That state is not valid. So we certainly need to unhash the negative dentry > first, before hashing a new one. We could also reuse the old dentry, but that > is just an optimization. > Thanks Miklos. > Below patch fixes several issues with that function. Could you please give it a > go? > This patch looks good and fixes the issue for me. It might be good to give Neils a chance to beat on it as well, but otherwise: Tested-by: Brian Foster <bfoster@redhat.com> Brian > Thanks, > Miklos > > > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > index 0eda527..a8208c5 100644 > --- a/fs/fuse/dir.c > +++ b/fs/fuse/dir.c > @@ -1223,28 +1223,45 @@ static int fuse_direntplus_link(struct file *file, > if (name.name[1] == '.' && name.len == 2) > return 0; > } > + > + if (invalid_nodeid(o->nodeid)) > + return -EIO; > + if (!fuse_valid_type(o->attr.mode)) > + return -EIO; > + > fc = get_fuse_conn(dir); > > name.hash = full_name_hash(name.name, name.len); > dentry = d_lookup(parent, &name); > - if (dentry && dentry->d_inode) { > + if (dentry) { > inode = dentry->d_inode; > - if (get_node_id(inode) == o->nodeid) { > + if (!inode) { > + d_drop(dentry); > + } else if (get_node_id(inode) != o->nodeid || > + ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { > + err = d_invalidate(dentry); > + if (err) > + goto out; > + } else if (is_bad_inode(inode)) { > + err = -EIO; > + goto out; > + } else { > struct fuse_inode *fi; > fi = get_fuse_inode(inode); > spin_lock(&fc->lock); > fi->nlookup++; > spin_unlock(&fc->lock); > > + fuse_change_attributes(inode, &o->attr, > + entry_attr_timeout(o), > + attr_version); > + > /* > * The other branch to 'found' comes via fuse_iget() > * which bumps nlookup inside > */ > goto found; > } > - err = d_invalidate(dentry); > - if (err) > - goto out; > dput(dentry); > dentry = NULL; > } > @@ -1259,25 +1276,30 @@ static int fuse_direntplus_link(struct file *file, > if (!inode) > goto out; > > - alias = d_materialise_unique(dentry, inode); > - err = PTR_ERR(alias); > - if (IS_ERR(alias)) > - goto out; > + if (S_ISDIR(inode->i_mode)) { > + mutex_lock(&fc->inst_mutex); > + alias = fuse_d_add_directory(dentry, inode); > + mutex_unlock(&fc->inst_mutex); > + err = PTR_ERR(alias); > + if (IS_ERR(alias)) { > + iput(inode); > + goto out; > + } > + } else { > + alias = d_splice_alias(inode, dentry); > + } > + > if (alias) { > dput(dentry); > dentry = alias; > } > > found: > - fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), > - attr_version); > - > fuse_change_entry_timeout(dentry, o); > > err = 0; > out: > - if (dentry) > - dput(dentry); > + dput(dentry); > return err; > } > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-16 16:14 ` Miklos Szeredi 2013-07-16 17:43 ` Brian Foster @ 2013-07-17 11:20 ` Niels de Vos 2013-07-17 13:04 ` Miklos Szeredi 1 sibling, 1 reply; 8+ messages in thread From: Niels de Vos @ 2013-07-17 11:20 UTC (permalink / raw) To: Miklos Szeredi; +Cc: Brian Foster, fuse-devel, linux-kernel On Tue, Jul 16, 2013 at 06:14:58PM +0200, Miklos Szeredi wrote: > On Tue, Jul 16, 2013 at 09:15:16AM -0400, Brian Foster wrote: > > > I'm not sure why it would need to have a valid inode. A dentry with a > > NULL inode is valid, no? > > It is valid, yes. It's called a "negative" dentry, which caches the information > that the file does not exist. > > > I think the question is whether the above state (multiple, hashed dentries) > > can be valid for whatever reason. It certainly looks suspicious. > > That state is not valid. So we certainly need to unhash the negative dentry > first, before hashing a new one. We could also reuse the old dentry, but that > is just an optimization. > > Below patch fixes several issues with that function. Could you please give it a > go? Thanks Miklos! This looks much cleaner and the additional error checking surely reduces the chance of any further problems. I've added only one little comment in the patch below. I can confirm that your patch fixes the original panic/BUG() with both Brian's test-case and the GlusterFS regression tests. Feel free to add my Tested-by/Reviewed-by signoff for this patch. Cheers, Niels > > Thanks, > Miklos > > > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > index 0eda527..a8208c5 100644 > --- a/fs/fuse/dir.c > +++ b/fs/fuse/dir.c > @@ -1223,28 +1223,45 @@ static int fuse_direntplus_link(struct file *file, > if (name.name[1] == '.' && name.len == 2) > return 0; > } > + > + if (invalid_nodeid(o->nodeid)) > + return -EIO; > + if (!fuse_valid_type(o->attr.mode)) > + return -EIO; > + > fc = get_fuse_conn(dir); > > name.hash = full_name_hash(name.name, name.len); > dentry = d_lookup(parent, &name); > - if (dentry && dentry->d_inode) { > + if (dentry) { > inode = dentry->d_inode; > - if (get_node_id(inode) == o->nodeid) { > + if (!inode) { > + d_drop(dentry); > + } else if (get_node_id(inode) != o->nodeid || > + ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { > + err = d_invalidate(dentry); > + if (err) > + goto out; > + } else if (is_bad_inode(inode)) { > + err = -EIO; > + goto out; > + } else { > struct fuse_inode *fi; > fi = get_fuse_inode(inode); > spin_lock(&fc->lock); > fi->nlookup++; > spin_unlock(&fc->lock); > > + fuse_change_attributes(inode, &o->attr, > + entry_attr_timeout(o), > + attr_version); > + > /* > * The other branch to 'found' comes via fuse_iget() > * which bumps nlookup inside > */ > goto found; > } > - err = d_invalidate(dentry); > - if (err) > - goto out; > dput(dentry); > dentry = NULL; You might want to remove that 'dentry = NULL' line, as the result of d_alloc() is assigned to dentry on line below the if-statement. > } > @@ -1259,25 +1276,30 @@ static int fuse_direntplus_link(struct file *file, > if (!inode) > goto out; > > - alias = d_materialise_unique(dentry, inode); > - err = PTR_ERR(alias); > - if (IS_ERR(alias)) > - goto out; > + if (S_ISDIR(inode->i_mode)) { > + mutex_lock(&fc->inst_mutex); > + alias = fuse_d_add_directory(dentry, inode); > + mutex_unlock(&fc->inst_mutex); > + err = PTR_ERR(alias); > + if (IS_ERR(alias)) { > + iput(inode); > + goto out; > + } > + } else { > + alias = d_splice_alias(inode, dentry); > + } > + > if (alias) { > dput(dentry); > dentry = alias; > } > > found: > - fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), > - attr_version); > - > fuse_change_entry_timeout(dentry, o); > > err = 0; > out: > - if (dentry) > - dput(dentry); > + dput(dentry); > return err; > } > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [fuse-devel] [PATCH] fuse: fix occasional dentry leak when readdirplus is used 2013-07-17 11:20 ` Niels de Vos @ 2013-07-17 13:04 ` Miklos Szeredi 0 siblings, 0 replies; 8+ messages in thread From: Miklos Szeredi @ 2013-07-17 13:04 UTC (permalink / raw) To: Niels de Vos; +Cc: Brian Foster, fuse-devel, Kernel Mailing List On Wed, Jul 17, 2013 at 1:20 PM, Niels de Vos <ndevos@redhat.com> wrote: > I can confirm that your patch fixes the original panic/BUG() with both > Brian's test-case and the GlusterFS regression tests. > > Feel free to add my Tested-by/Reviewed-by signoff for this patch. Thanks for the review and testing, patches pushed to git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next Thanks, Miklos ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2013-07-17 13:04 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-07-15 12:59 [PATCH] fuse: fix occasional dentry leak when readdirplus is used Niels de Vos 2013-07-15 20:08 ` [fuse-devel] " Brian Foster 2013-07-16 10:39 ` Niels de Vos 2013-07-16 13:15 ` Brian Foster 2013-07-16 16:14 ` Miklos Szeredi 2013-07-16 17:43 ` Brian Foster 2013-07-17 11:20 ` Niels de Vos 2013-07-17 13:04 ` Miklos Szeredi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).