All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion - Resend
@ 2010-09-07 13:59 Thomas Gleixner
  2010-09-07 14:00 ` [patch 1/3] audit: Call tty_audit_push_task() outside preempt disabled Thomas Gleixner
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Thomas Gleixner @ 2010-09-07 13:59 UTC (permalink / raw)
  To: LKML; +Cc: Al Viro, Peter Zijlstra, Andrew Morton

Hi,

the following patches result from auditing read_lock(&tasklist_lock)
sites whether they can be converted to RCU.

Resend of http://lkml.org/lkml/2009/12/9/216

Thanks,

	tglx




^ permalink raw reply	[flat|nested] 8+ messages in thread

* [patch 1/3] audit: Call tty_audit_push_task() outside preempt disabled
  2010-09-07 13:59 [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion - Resend Thomas Gleixner
@ 2010-09-07 14:00 ` Thomas Gleixner
  2010-09-07 14:00 ` [patch 2/3] audit: Do not send uninitialized data for AUDIT_TTY_GET Thomas Gleixner
  2010-09-07 14:00 ` [patch 3/3] audit: Use rcu for task lookup protection Thomas Gleixner
  2 siblings, 0 replies; 8+ messages in thread
From: Thomas Gleixner @ 2010-09-07 14:00 UTC (permalink / raw)
  To: LKML; +Cc: Al Viro, Peter Zijlstra, Andrew Morton, Eric Paris, Oleg Nesterov

[-- Attachment #1: audit-call-tty_audit_push_task-outside-preempt-disabled.patch --]
[-- Type: text/plain, Size: 4508 bytes --]

While auditing all tasklist_lock read_lock sites I stumbled over the
following call chain:

audit_prepare_user_tty()
  read_lock(&tasklist_lock);
  tty_audit_push_task();
     mutex_lock(&buf->mutex);

     --> buf->mutex is locked with preemption disabled.

Solve this by acquiring a reference to the task struct under
rcu_read_lock and call tty_audit_push_task outside of the preempt
disabled region.

Move all code which needs to be protected by sighand lock into
tty_audit_push_task() and use lock/unlock_sighand as we do not hold
tasklist_lock.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Paris <eparis@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Oleg Nesterov <oleg@redhat.com>

---
drivers/char/tty_audit.c |   38 ++++++++++++++++++++++++++++----------
 drivers/char/tty_audit.c |   38 ++++++++++++++++++++++++++++----------
 include/linux/tty.h      |    8 ++------
 kernel/audit.c           |   25 +++++++++----------------
 3 files changed, 39 insertions(+), 32 deletions(-)
Index: linux-2.6/drivers/char/tty_audit.c
===================================================================
--- linux-2.6.orig/drivers/char/tty_audit.c
+++ linux-2.6/drivers/char/tty_audit.c
@@ -188,25 +188,43 @@ void tty_audit_tiocsti(struct tty_struct
 }
 
 /**
- *	tty_audit_push_task	-	Flush task's pending audit data
+ * tty_audit_push_task	-	Flush task's pending audit data
+ * @tsk:		task pointer
+ * @loginuid:		sender login uid
+ * @sessionid:		sender session id
+ *
+ * Called with a ref on @tsk held. Try to lock sighand and get a
+ * reference to the tty audit buffer if available.
+ * Flush the buffer or return an appropriate error code.
  */
-void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
+int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
 {
-	struct tty_audit_buf *buf;
+	struct tty_audit_buf *buf = ERR_PTR(-EPERM);
+	unsigned long flags;
 
-	spin_lock_irq(&tsk->sighand->siglock);
-	buf = tsk->signal->tty_audit_buf;
-	if (buf)
-		atomic_inc(&buf->count);
-	spin_unlock_irq(&tsk->sighand->siglock);
-	if (!buf)
-		return;
+	if (!lock_task_sighand(tsk, &flags))
+		return -ESRCH;
+
+	if (tsk->signal->audit_tty) {
+		buf = tsk->signal->tty_audit_buf;
+		if (buf)
+			atomic_inc(&buf->count);
+	}
+	unlock_task_sighand(tsk, &flags);
+
+	/*
+	 * Return 0 when signal->audit_tty set
+	 * but tsk->signal->tty_audit_buf == NULL.
+	 */
+	if (!buf || IS_ERR(buf))
+		return PTR_ERR(buf);
 
 	mutex_lock(&buf->mutex);
 	tty_audit_buf_push(tsk, loginuid, sessionid, buf);
 	mutex_unlock(&buf->mutex);
 
 	tty_audit_buf_put(buf);
+	return 0;
 }
 
 /**
Index: linux-2.6/include/linux/tty.h
===================================================================
--- linux-2.6.orig/include/linux/tty.h
+++ linux-2.6/include/linux/tty.h
@@ -539,8 +539,8 @@ extern void tty_audit_exit(void);
 extern void tty_audit_fork(struct signal_struct *sig);
 extern void tty_audit_tiocsti(struct tty_struct *tty, char ch);
 extern void tty_audit_push(struct tty_struct *tty);
-extern void tty_audit_push_task(struct task_struct *tsk,
-					uid_t loginuid, u32 sessionid);
+extern int tty_audit_push_task(struct task_struct *tsk,
+			       uid_t loginuid, u32 sessionid);
 #else
 static inline void tty_audit_add_data(struct tty_struct *tty,
 				      unsigned char *data, size_t size)
@@ -558,10 +558,6 @@ static inline void tty_audit_fork(struct
 static inline void tty_audit_push(struct tty_struct *tty)
 {
 }
-static inline void tty_audit_push_task(struct task_struct *tsk,
-					uid_t loginuid, u32 sessionid)
-{
-}
 #endif
 
 /* tty_io.c */
Index: linux-2.6/kernel/audit.c
===================================================================
--- linux-2.6.orig/kernel/audit.c
+++ linux-2.6/kernel/audit.c
@@ -467,23 +467,16 @@ static int audit_prepare_user_tty(pid_t 
 	struct task_struct *tsk;
 	int err;
 
-	read_lock(&tasklist_lock);
+	rcu_read_lock();
 	tsk = find_task_by_vpid(pid);
-	err = -ESRCH;
-	if (!tsk)
-		goto out;
-	err = 0;
-
-	spin_lock_irq(&tsk->sighand->siglock);
-	if (!tsk->signal->audit_tty)
-		err = -EPERM;
-	spin_unlock_irq(&tsk->sighand->siglock);
-	if (err)
-		goto out;
-
-	tty_audit_push_task(tsk, loginuid, sessionid);
-out:
-	read_unlock(&tasklist_lock);
+	if (!tsk) {
+		rcu_read_unlock();
+		return -ESRCH;
+	}
+	get_task_struct(tsk);
+	rcu_read_unlock();
+	err = tty_audit_push_task(tsk, loginuid, sessionid);
+	put_task_struct(tsk);
 	return err;
 }
 



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [patch 2/3] audit: Do not send uninitialized data for AUDIT_TTY_GET
  2010-09-07 13:59 [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion - Resend Thomas Gleixner
  2010-09-07 14:00 ` [patch 1/3] audit: Call tty_audit_push_task() outside preempt disabled Thomas Gleixner
@ 2010-09-07 14:00 ` Thomas Gleixner
  2010-09-07 14:00 ` [patch 3/3] audit: Use rcu for task lookup protection Thomas Gleixner
  2 siblings, 0 replies; 8+ messages in thread
From: Thomas Gleixner @ 2010-09-07 14:00 UTC (permalink / raw)
  To: LKML; +Cc: Al Viro, Peter Zijlstra, Andrew Morton, Eric Paris

[-- Attachment #1: audit-do-not-send-uninitialized-data-for-audit_tty_get.patch --]
[-- Type: text/plain, Size: 953 bytes --]

audit_receive_msg() sends uninitialized data for AUDIT_TTY_GET when
the task was not found.

Send reply only when task was found.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Paris <eparis@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>

---
kernel/audit.c |    6 ++++--
 kernel/audit.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
Index: linux-2.6/kernel/audit.c
===================================================================
--- linux-2.6.orig/kernel/audit.c
+++ linux-2.6/kernel/audit.c
@@ -884,8 +884,10 @@ static int audit_receive_msg(struct sk_b
 			spin_unlock_irq(&tsk->sighand->siglock);
 		}
 		read_unlock(&tasklist_lock);
-		audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_TTY_GET, 0, 0,
-				 &s, sizeof(s));
+
+		if (!err)
+			audit_send_reply(NETLINK_CB(skb).pid, seq,
+					 AUDIT_TTY_GET, 0, 0, &s, sizeof(s));
 		break;
 	}
 	case AUDIT_TTY_SET: {



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [patch 3/3] audit: Use rcu for task lookup protection
  2010-09-07 13:59 [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion - Resend Thomas Gleixner
  2010-09-07 14:00 ` [patch 1/3] audit: Call tty_audit_push_task() outside preempt disabled Thomas Gleixner
  2010-09-07 14:00 ` [patch 2/3] audit: Do not send uninitialized data for AUDIT_TTY_GET Thomas Gleixner
@ 2010-09-07 14:00 ` Thomas Gleixner
  2010-09-07 18:25   ` Oleg Nesterov
  2 siblings, 1 reply; 8+ messages in thread
From: Thomas Gleixner @ 2010-09-07 14:00 UTC (permalink / raw)
  To: LKML; +Cc: Al Viro, Peter Zijlstra, Andrew Morton, Eric Paris, Oleg Nesterov

[-- Attachment #1: audit-use-rcu-for-task-lookup-protection.patch --]
[-- Type: text/plain, Size: 2132 bytes --]

Protect the task lookups in audit_receive_msg() with rcu_read_lock()
instead of tasklist_lock and use lock/unlock_sighand to protect
against the exit race.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Paris <eparis@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Oleg Nesterov <oleg@redhat.com>

---
kernel/audit.c |   30 ++++++++++++++----------------
 kernel/audit.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
Index: linux-2.6/kernel/audit.c
===================================================================
--- linux-2.6.orig/kernel/audit.c
+++ linux-2.6/kernel/audit.c
@@ -873,17 +873,16 @@ static int audit_receive_msg(struct sk_b
 	case AUDIT_TTY_GET: {
 		struct audit_tty_status s;
 		struct task_struct *tsk;
+		unsigned long flags;
 
-		read_lock(&tasklist_lock);
+		rcu_read_lock();
 		tsk = find_task_by_vpid(pid);
-		if (!tsk)
-			err = -ESRCH;
-		else {
-			spin_lock_irq(&tsk->sighand->siglock);
+		if (tsk && lock_task_sighand(tsk, &flags)) {
 			s.enabled = tsk->signal->audit_tty != 0;
-			spin_unlock_irq(&tsk->sighand->siglock);
-		}
-		read_unlock(&tasklist_lock);
+			unlock_task_sighand(tsk, &flags);
+		} else
+			err = -ESRCH;
+		rcu_read_unlock();
 
 		if (!err)
 			audit_send_reply(NETLINK_CB(skb).pid, seq,
@@ -893,22 +892,21 @@ static int audit_receive_msg(struct sk_b
 	case AUDIT_TTY_SET: {
 		struct audit_tty_status *s;
 		struct task_struct *tsk;
+		unsigned long flags;
 
 		if (nlh->nlmsg_len < sizeof(struct audit_tty_status))
 			return -EINVAL;
 		s = data;
 		if (s->enabled != 0 && s->enabled != 1)
 			return -EINVAL;
-		read_lock(&tasklist_lock);
+		rcu_read_lock();
 		tsk = find_task_by_vpid(pid);
-		if (!tsk)
-			err = -ESRCH;
-		else {
-			spin_lock_irq(&tsk->sighand->siglock);
+		if (tsk && lock_task_sighand(tsk, &flags)) {
 			tsk->signal->audit_tty = s->enabled != 0;
-			spin_unlock_irq(&tsk->sighand->siglock);
-		}
-		read_unlock(&tasklist_lock);
+			unlock_task_sighand(tsk, &flags);
+		} else
+			err = -ESRCH;
+		rcu_read_unlock();
 		break;
 	}
 	default:



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch 3/3] audit: Use rcu for task lookup protection
  2010-09-07 14:00 ` [patch 3/3] audit: Use rcu for task lookup protection Thomas Gleixner
@ 2010-09-07 18:25   ` Oleg Nesterov
  2010-09-07 20:25     ` Thomas Gleixner
  0 siblings, 1 reply; 8+ messages in thread
From: Oleg Nesterov @ 2010-09-07 18:25 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, Al Viro, Peter Zijlstra, Andrew Morton, Eric Paris

On 09/07, Thomas Gleixner wrote:
>
> Protect the task lookups in audit_receive_msg() with rcu_read_lock()
> instead of tasklist_lock and use lock/unlock_sighand to protect
> against the exit race.

I do not understand audit, but I belive both 1/3 and 3/3 patches are
fine (I didn't get 2/3).



But, sorry, can't resists ;) off-topic nit.

> @@ -873,17 +873,16 @@ static int audit_receive_msg(struct sk_b
>  	case AUDIT_TTY_GET: {
>  		struct audit_tty_status s;
>  		struct task_struct *tsk;
> +		unsigned long flags;
>
> -		read_lock(&tasklist_lock);
> +		rcu_read_lock();
>  		tsk = find_task_by_vpid(pid);
> -		if (!tsk)
> -			err = -ESRCH;
> -		else {
> -			spin_lock_irq(&tsk->sighand->siglock);
> +		if (tsk && lock_task_sighand(tsk, &flags)) {
>  			s.enabled = tsk->signal->audit_tty != 0;

Yes, this is what original code does, it takes ->siglock every time
around read/write of ->audit_tty. And this looks absolutely bogus.
Say, tty_audit_fork(). Why does it take ->siglock ?

As for ->tty_audit_buf, I am not sure ->siglock is the best choice,
perhaps task_lock() would be better.


Once again, I think the patch is fine. Just it seems to me this code
needs more cleanups.

Oleg.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch 3/3] audit: Use rcu for task lookup protection
  2010-09-07 18:25   ` Oleg Nesterov
@ 2010-09-07 20:25     ` Thomas Gleixner
  2010-09-08 12:44       ` Oleg Nesterov
  0 siblings, 1 reply; 8+ messages in thread
From: Thomas Gleixner @ 2010-09-07 20:25 UTC (permalink / raw)
  To: Oleg Nesterov; +Cc: LKML, Al Viro, Peter Zijlstra, Andrew Morton, Eric Paris



On Tue, 7 Sep 2010, Oleg Nesterov wrote:

> On 09/07, Thomas Gleixner wrote:
> >
> > Protect the task lookups in audit_receive_msg() with rcu_read_lock()
> > instead of tasklist_lock and use lock/unlock_sighand to protect
> > against the exit race.
> 
> I do not understand audit, but I belive both 1/3 and 3/3 patches are
> fine (I didn't get 2/3).
> 
> 
> 
> But, sorry, can't resists ;) off-topic nit.
> 
> > @@ -873,17 +873,16 @@ static int audit_receive_msg(struct sk_b
> >  	case AUDIT_TTY_GET: {
> >  		struct audit_tty_status s;
> >  		struct task_struct *tsk;
> > +		unsigned long flags;
> >
> > -		read_lock(&tasklist_lock);
> > +		rcu_read_lock();
> >  		tsk = find_task_by_vpid(pid);
> > -		if (!tsk)
> > -			err = -ESRCH;
> > -		else {
> > -			spin_lock_irq(&tsk->sighand->siglock);
> > +		if (tsk && lock_task_sighand(tsk, &flags)) {
> >  			s.enabled = tsk->signal->audit_tty != 0;
> 
> Yes, this is what original code does, it takes ->siglock every time
> around read/write of ->audit_tty. And this looks absolutely bogus.
> Say, tty_audit_fork(). Why does it take ->siglock ?
> 
> As for ->tty_audit_buf, I am not sure ->siglock is the best choice,
> perhaps task_lock() would be better.
>
> Once again, I think the patch is fine. Just it seems to me this code
> needs more cleanups.

Yeah, thought about that, but that's not in the scope of what I was
working on. I leave that to the audit folks. :)
 
Thanks,

	tglx

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch 3/3] audit: Use rcu for task lookup protection
  2010-09-07 20:25     ` Thomas Gleixner
@ 2010-09-08 12:44       ` Oleg Nesterov
  0 siblings, 0 replies; 8+ messages in thread
From: Oleg Nesterov @ 2010-09-08 12:44 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, Al Viro, Peter Zijlstra, Andrew Morton, Eric Paris

On 09/07, Thomas Gleixner wrote:
>
> On Tue, 7 Sep 2010, Oleg Nesterov wrote:
>
> > But, sorry, can't resists ;) off-topic nit.
> >
> > > @@ -873,17 +873,16 @@ static int audit_receive_msg(struct sk_b
> > >  	case AUDIT_TTY_GET: {
> > >  		struct audit_tty_status s;
> > >  		struct task_struct *tsk;
> > > +		unsigned long flags;
> > >
> > > -		read_lock(&tasklist_lock);
> > > +		rcu_read_lock();
> > >  		tsk = find_task_by_vpid(pid);
> > > -		if (!tsk)
> > > -			err = -ESRCH;
> > > -		else {
> > > -			spin_lock_irq(&tsk->sighand->siglock);
> > > +		if (tsk && lock_task_sighand(tsk, &flags)) {
> > >  			s.enabled = tsk->signal->audit_tty != 0;
> >
> > Yes, this is what original code does, it takes ->siglock every time
> > around read/write of ->audit_tty. And this looks absolutely bogus.
> > Say, tty_audit_fork(). Why does it take ->siglock ?

Yes, I still think ->audit_tty doesn't need the locking.

> > As for ->tty_audit_buf, I am not sure ->siglock is the best choice,
> > perhaps task_lock() would be better.

OOPS, I misread the code. ->tty_audit_buf is per-process (of course!).
Well, unless I missed something again, tty_audit_push() and
tty_audit_tiocsti() can access ->tty_audit_buf lockless.

> > Once again, I think the patch is fine. Just it seems to me this code
> > needs more cleanups.
>
> Yeah, thought about that, but that's not in the scope of what I was
> working on. I leave that to the audit folks. :)

Yes, yes, sure.

Oleg.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [patch 3/3] audit: Use rcu for task lookup protection
  2009-12-09 14:19 [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion Thomas Gleixner
@ 2009-12-09 14:19 ` Thomas Gleixner
  0 siblings, 0 replies; 8+ messages in thread
From: Thomas Gleixner @ 2009-12-09 14:19 UTC (permalink / raw)
  To: LKML; +Cc: Al Viro, Eric Paris, Ingo Molnar, Peter Zijlstra, Oleg Nesterov

[-- Attachment #1: audit-use-rcu-for-lookup.patch --]
[-- Type: text/plain, Size: 2049 bytes --]

Protect the task lookups in audit_receive_msg() with rcu_read_lock()
instead of tasklist_lock and use lock/unlock_sighand to protect
against the exit race.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Paris <eparis@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
---
 kernel/audit.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

Index: linux-2.6-tip/kernel/audit.c
===================================================================
--- linux-2.6-tip.orig/kernel/audit.c
+++ linux-2.6-tip/kernel/audit.c
@@ -873,17 +873,16 @@ static int audit_receive_msg(struct sk_b
 	case AUDIT_TTY_GET: {
 		struct audit_tty_status s;
 		struct task_struct *tsk;
+		unsigned long flags;
 
-		read_lock(&tasklist_lock);
+		rcu_read_lock();
 		tsk = find_task_by_vpid(pid);
-		if (!tsk)
-			err = -ESRCH;
-		else {
-			spin_lock_irq(&tsk->sighand->siglock);
+		if (tsk && lock_task_sighand(tsk, &flags)) {
 			s.enabled = tsk->signal->audit_tty != 0;
-			spin_unlock_irq(&tsk->sighand->siglock);
-		}
-		read_unlock(&tasklist_lock);
+			unlock_task_sighand(tsk, &flags);
+		} else
+			err = -ESRCH;
+		rcu_read_unlock();
 
 		if (!err)
 			audit_send_reply(NETLINK_CB(skb).pid, seq,
@@ -893,22 +892,21 @@ static int audit_receive_msg(struct sk_b
 	case AUDIT_TTY_SET: {
 		struct audit_tty_status *s;
 		struct task_struct *tsk;
+		unsigned long flags;
 
 		if (nlh->nlmsg_len < sizeof(struct audit_tty_status))
 			return -EINVAL;
 		s = data;
 		if (s->enabled != 0 && s->enabled != 1)
 			return -EINVAL;
-		read_lock(&tasklist_lock);
+		rcu_read_lock();
 		tsk = find_task_by_vpid(pid);
-		if (!tsk)
-			err = -ESRCH;
-		else {
-			spin_lock_irq(&tsk->sighand->siglock);
+		if (tsk && lock_task_sighand(tsk, &flags)) {
 			tsk->signal->audit_tty = s->enabled != 0;
-			spin_unlock_irq(&tsk->sighand->siglock);
-		}
-		read_unlock(&tasklist_lock);
+			unlock_task_sighand(tsk, &flags);
+		} else
+			err = -ESRCH;
+		rcu_read_unlock();
 		break;
 	}
 	default:



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-09-08 12:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-07 13:59 [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion - Resend Thomas Gleixner
2010-09-07 14:00 ` [patch 1/3] audit: Call tty_audit_push_task() outside preempt disabled Thomas Gleixner
2010-09-07 14:00 ` [patch 2/3] audit: Do not send uninitialized data for AUDIT_TTY_GET Thomas Gleixner
2010-09-07 14:00 ` [patch 3/3] audit: Use rcu for task lookup protection Thomas Gleixner
2010-09-07 18:25   ` Oleg Nesterov
2010-09-07 20:25     ` Thomas Gleixner
2010-09-08 12:44       ` Oleg Nesterov
  -- strict thread matches above, loose matches on Subject: below --
2009-12-09 14:19 [patch 0/3] audit: Fixes and tasklist_lock -> RCU conversion Thomas Gleixner
2009-12-09 14:19 ` [patch 3/3] audit: Use rcu for task lookup protection Thomas Gleixner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.