public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Laszlo Ersek" <lersek@redhat.com>
To: edk2-devel-groups-io <devel@edk2.groups.io>
Cc: "Philippe Mathieu-Daudé" <philmd@redhat.com>,
	"Ray Ni" <ray.ni@intel.com>,
	"Zhichao Gao" <zhichao.gao@intel.com>
Subject: [PATCH 4/8] ShellPkg/ShellCommandLib: add ShellSortFileList()
Date: Mon,  4 Jan 2021 16:42:31 +0100	[thread overview]
Message-ID: <20210104154235.31785-5-lersek@redhat.com> (raw)
In-Reply-To: <20210104154235.31785-1-lersek@redhat.com>

Introduce the ShellSortFileList() function, for sorting an
EFI_SHELL_FILE_INFO list, by FileName or by FullName.

Duplicates can be kept in the same list, or separated out to a new list.
In either case, the relative order between duplicates does not change (the
sorting is stable).

For the sorting, use OrderedCollectionLib rather than SortLib:

- The PerformQuickSort() function from the latter has quadratic worst-case
  time complexity, plus it is implemented recursively (see
  "MdeModulePkg/Library/UefiSortLib/UefiSortLib.c"). It can also not
  return an error on memory allocation failure.

- In comparison, the Red-Black Tree instance of OrderedCollectionLib sorts
  in O(n*log(n)) worst-case time, contains no recursion with the default
  PcdValidateOrderedCollection=FALSE setting, and the OrderedCollectionLib
  class APIs return errors appropriately.

The OrderedCollectionLib APIs do not permit duplicates natively, but by
using lists as collection entries, stable sorting of duplicates can be
achieved.

Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Zhichao Gao <zhichao.gao@intel.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3151
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 ShellPkg/ShellPkg.dsc                                        |   1 +
 ShellPkg/Include/Library/ShellCommandLib.h                   |  81 +++++
 ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf |   1 +
 ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.h   |  19 ++
 ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c   | 312 ++++++++++++++++++++
 5 files changed, 414 insertions(+)

diff --git a/ShellPkg/ShellPkg.dsc b/ShellPkg/ShellPkg.dsc
index c42bc9464a0f..a8b6de334284 100644
--- a/ShellPkg/ShellPkg.dsc
+++ b/ShellPkg/ShellPkg.dsc
@@ -47,6 +47,7 @@ [LibraryClasses.common]
   ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
   ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf
   HandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf
+  OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
 
   PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
   BcfgCommandLib|ShellPkg/Library/UefiShellBcfgCommandLib/UefiShellBcfgCommandLib.inf
diff --git a/ShellPkg/Include/Library/ShellCommandLib.h b/ShellPkg/Include/Library/ShellCommandLib.h
index 63fcac82a2de..bc1afed5e7f5 100644
--- a/ShellPkg/Include/Library/ShellCommandLib.h
+++ b/ShellPkg/Include/Library/ShellCommandLib.h
@@ -714,4 +714,85 @@ CatSDumpHex (
   IN UINTN   DataSize,
   IN VOID    *UserData
   );
+
+//
+// Determines the ordering operation for ShellSortFileList().
+//
+typedef enum {
+  //
+  // Sort the EFI_SHELL_FILE_INFO objects by the FileName field, in increasing
+  // order, using gUnicodeCollation->StriColl().
+  //
+  ShellSortFileListByFileName,
+  //
+  // Sort the EFI_SHELL_FILE_INFO objects by the FullName field, in increasing
+  // order, using gUnicodeCollation->StriColl().
+  //
+  ShellSortFileListByFullName,
+  ShellSortFileListMax
+} SHELL_SORT_FILE_LIST;
+
+/**
+  Sort an EFI_SHELL_FILE_INFO list, optionally moving duplicates to a separate
+  list.
+
+  @param[in,out] FileList  The list of EFI_SHELL_FILE_INFO objects to sort.
+
+                           If FileList is NULL on input, then FileList is
+                           considered an empty, hence already sorted, list.
+
+                           Otherwise, if (*FileList) is NULL on input, then
+                           EFI_INVALID_PARAMETER is returned.
+
+                           Otherwise, the caller is responsible for having
+                           initialized (*FileList)->Link with
+                           InitializeListHead(). No other fields in the
+                           (**FileList) head element are accessed by this
+                           function.
+
+                           On output, (*FileList) is sorted according to Order.
+                           If Duplicates is NULL on input, then duplicate
+                           elements are preserved, sorted stably, on
+                           (*FileList). If Duplicates is not NULL on input,
+                           then duplicates are moved (stably sorted) to the
+                           new, dynamically allocated (*Duplicates) list.
+
+  @param[out] Duplicates   If Duplicates is NULL on input, (*FileList) will be
+                           a monotonically ordered list on output, with
+                           duplicates stably sorted.
+
+                           If Duplicates is not NULL on input, (*FileList) will
+                           be a strictly monotonically oredered list on output,
+                           with duplicates separated (stably sorted) to
+                           (*Duplicates). All fields except Link will be
+                           zero-initialized in the (**Duplicates) head element.
+                           If no duplicates exist, then (*Duplicates) is set to
+                           NULL on output.
+
+  @param[in] Order         Determines the comparison operation between
+                           EFI_SHELL_FILE_INFO objects.
+
+  @retval EFI_INVALID_PARAMETER  (UINTN)Order is greater than or equal to
+                                 (UINTN)ShellSortFileListMax. Neither the
+                                 (*FileList) nor the (*Duplicates) list has
+                                 been modified.
+
+  @retval EFI_INVALID_PARAMETER  (*FileList) was NULL on input. Neither the
+                                 (*FileList) nor the (*Duplicates) list has
+                                 been modified.
+
+  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed. Neither the
+                                 (*FileList) nor the (*Duplicates) list has
+                                 been modified.
+
+  @retval EFI_SUCCESS            Sorting successful, including the case when
+                                 FileList is NULL on input.
+**/
+EFI_STATUS
+EFIAPI
+ShellSortFileList (
+  IN OUT EFI_SHELL_FILE_INFO  **FileList,
+     OUT EFI_SHELL_FILE_INFO  **Duplicates OPTIONAL,
+  IN     SHELL_SORT_FILE_LIST Order
+  );
 #endif //_SHELL_COMMAND_LIB_
diff --git a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
index a1b5601c96b6..08ca7cb78842 100644
--- a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
+++ b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
@@ -42,6 +42,7 @@ [LibraryClasses]
   ShellLib
   HiiLib
   HandleParsingLib
+  OrderedCollectionLib
 
 [Protocols]
   gEfiUnicodeCollation2ProtocolGuid                       ## CONSUMES
diff --git a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.h b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.h
index 8ecc2f6bf5a2..0ca291e4f9bf 100644
--- a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.h
+++ b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.h
@@ -39,6 +39,7 @@
 #include <Library/HiiLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
+#include <Library/OrderedCollectionLib.h>
 
 typedef struct{
   LIST_ENTRY                  Link;
@@ -60,6 +61,24 @@ typedef struct {
   CHAR16            *Path;
 } SHELL_COMMAND_FILE_HANDLE;
 
+//
+// Collects multiple EFI_SHELL_FILE_INFO objects that share the same name.
+//
+typedef struct {
+  //
+  // A string that compares equal to either the FileName or the FullName fields
+  // of all EFI_SHELL_FILE_INFO objects on SameNameList, according to
+  // gUnicodeCollation->StriColl(). The string is not dynamically allocated;
+  // instead, it *aliases* the FileName or FullName field of the
+  // EFI_SHELL_FILE_INFO object that was first encountered with this name.
+  //
+  CONST CHAR16 *Alias;
+  //
+  // A list of EFI_SHELL_FILE_INFO objects whose FileName or FullName fields
+  // compare equal to Alias, according to gUnicodeCollation->StriColl().
+  //
+  LIST_ENTRY SameNameList;
+} SHELL_SORT_UNIQUE_NAME;
 
 #endif //_UEFI_COMMAND_LIB_INTERNAL_HEADER_
 
diff --git a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
index 345808a1eac6..b06d22592d33 100644
--- a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
+++ b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
@@ -1909,3 +1909,315 @@ CatSDumpHex (
 
   return RetVal;
 }
+
+/**
+  ORDERED_COLLECTION_USER_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
+
+  @param[in] Unique1AsVoid  The first SHELL_SORT_UNIQUE_NAME object (Unique1),
+                            passed in as a pointer-to-VOID.
+
+  @param[in] Unique2AsVoid  The second SHELL_SORT_UNIQUE_NAME object (Unique2),
+                            passed in as a pointer-to-VOID.
+
+  @retval <0  If Unique1 compares less than Unique2.
+
+  @retval  0  If Unique1 compares equal to Unique2.
+
+  @retval >0  If Unique1 compares greater than Unique2.
+**/
+STATIC
+INTN
+EFIAPI
+UniqueNameCompare (
+  IN CONST VOID *Unique1AsVoid,
+  IN CONST VOID *Unique2AsVoid
+  )
+{
+  CONST SHELL_SORT_UNIQUE_NAME *Unique1;
+  CONST SHELL_SORT_UNIQUE_NAME *Unique2;
+
+  Unique1 = Unique1AsVoid;
+  Unique2 = Unique2AsVoid;
+
+  //
+  // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
+  //
+  return gUnicodeCollation->StriColl (
+                              gUnicodeCollation,
+                              (CHAR16 *)Unique1->Alias,
+                              (CHAR16 *)Unique2->Alias
+                              );
+}
+
+/**
+  ORDERED_COLLECTION_KEY_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
+
+  @param[in] UniqueAliasAsVoid  The CHAR16 string UniqueAlias, passed in as a
+                                pointer-to-VOID.
+
+  @param[in] UniqueAsVoid       The SHELL_SORT_UNIQUE_NAME object (Unique),
+                                passed in as a pointer-to-VOID.
+
+  @retval <0  If UniqueAlias compares less than Unique->Alias.
+
+  @retval  0  If UniqueAlias compares equal to Unique->Alias.
+
+  @retval >0  If UniqueAlias compares greater than Unique->Alias.
+**/
+STATIC
+INTN
+EFIAPI
+UniqueNameAliasCompare (
+  IN CONST VOID *UniqueAliasAsVoid,
+  IN CONST VOID *UniqueAsVoid
+  )
+{
+  CONST CHAR16                 *UniqueAlias;
+  CONST SHELL_SORT_UNIQUE_NAME *Unique;
+
+  UniqueAlias = UniqueAliasAsVoid;
+  Unique      = UniqueAsVoid;
+
+  //
+  // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
+  //
+  return gUnicodeCollation->StriColl (
+                              gUnicodeCollation,
+                              (CHAR16 *)UniqueAlias,
+                              (CHAR16 *)Unique->Alias
+                              );
+}
+
+/**
+  Sort an EFI_SHELL_FILE_INFO list, optionally moving duplicates to a separate
+  list.
+
+  @param[in,out] FileList  The list of EFI_SHELL_FILE_INFO objects to sort.
+
+                           If FileList is NULL on input, then FileList is
+                           considered an empty, hence already sorted, list.
+
+                           Otherwise, if (*FileList) is NULL on input, then
+                           EFI_INVALID_PARAMETER is returned.
+
+                           Otherwise, the caller is responsible for having
+                           initialized (*FileList)->Link with
+                           InitializeListHead(). No other fields in the
+                           (**FileList) head element are accessed by this
+                           function.
+
+                           On output, (*FileList) is sorted according to Order.
+                           If Duplicates is NULL on input, then duplicate
+                           elements are preserved, sorted stably, on
+                           (*FileList). If Duplicates is not NULL on input,
+                           then duplicates are moved (stably sorted) to the
+                           new, dynamically allocated (*Duplicates) list.
+
+  @param[out] Duplicates   If Duplicates is NULL on input, (*FileList) will be
+                           a monotonically ordered list on output, with
+                           duplicates stably sorted.
+
+                           If Duplicates is not NULL on input, (*FileList) will
+                           be a strictly monotonically oredered list on output,
+                           with duplicates separated (stably sorted) to
+                           (*Duplicates). All fields except Link will be
+                           zero-initialized in the (**Duplicates) head element.
+                           If no duplicates exist, then (*Duplicates) is set to
+                           NULL on output.
+
+  @param[in] Order         Determines the comparison operation between
+                           EFI_SHELL_FILE_INFO objects.
+
+  @retval EFI_INVALID_PARAMETER  (UINTN)Order is greater than or equal to
+                                 (UINTN)ShellSortFileListMax. Neither the
+                                 (*FileList) nor the (*Duplicates) list has
+                                 been modified.
+
+  @retval EFI_INVALID_PARAMETER  (*FileList) was NULL on input. Neither the
+                                 (*FileList) nor the (*Duplicates) list has
+                                 been modified.
+
+  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed. Neither the
+                                 (*FileList) nor the (*Duplicates) list has
+                                 been modified.
+
+  @retval EFI_SUCCESS            Sorting successful, including the case when
+                                 FileList is NULL on input.
+**/
+EFI_STATUS
+EFIAPI
+ShellSortFileList (
+  IN OUT EFI_SHELL_FILE_INFO  **FileList,
+     OUT EFI_SHELL_FILE_INFO  **Duplicates OPTIONAL,
+  IN     SHELL_SORT_FILE_LIST Order
+  )
+{
+  LIST_ENTRY               *FilesHead;
+  ORDERED_COLLECTION       *Sort;
+  LIST_ENTRY               *FileEntry;
+  EFI_SHELL_FILE_INFO      *FileInfo;
+  SHELL_SORT_UNIQUE_NAME   *Unique;
+  EFI_STATUS               Status;
+  EFI_SHELL_FILE_INFO      *Dupes;
+  LIST_ENTRY               *NextFileEntry;
+  CONST CHAR16             *Alias;
+  ORDERED_COLLECTION_ENTRY *SortEntry;
+  LIST_ENTRY               *TargetFileList;
+  ORDERED_COLLECTION_ENTRY *NextSortEntry;
+  VOID                     *UniqueAsVoid;
+
+  if ((UINTN)Order >= (UINTN)ShellSortFileListMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (FileList == NULL) {
+    //
+    // FileList is considered empty, hence already sorted, with no duplicates.
+    //
+    if (Duplicates != NULL) {
+      *Duplicates = NULL;
+    }
+    return EFI_SUCCESS;
+  }
+
+  if (*FileList == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  FilesHead = &(*FileList)->Link;
+
+  //
+  // Collect all the unique names.
+  //
+  Sort = OrderedCollectionInit (UniqueNameCompare, UniqueNameAliasCompare);
+  if (Sort == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  BASE_LIST_FOR_EACH (FileEntry, FilesHead) {
+    FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
+
+    //
+    // Try to record the name of this file as a unique name.
+    //
+    Unique = AllocatePool (sizeof (*Unique));
+    if (Unique == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto UninitSort;
+    }
+    Unique->Alias = ((Order == ShellSortFileListByFileName) ?
+                     FileInfo->FileName :
+                     FileInfo->FullName);
+    InitializeListHead (&Unique->SameNameList);
+
+    Status = OrderedCollectionInsert (Sort, NULL, Unique);
+    if (EFI_ERROR (Status)) {
+      //
+      // Only two errors are possible: memory allocation failed, or this name
+      // has been encountered before. In either case, the
+      // SHELL_SORT_UNIQUE_NAME object being constructed has to be released.
+      //
+      FreePool (Unique);
+      //
+      // Memory allocation failure is fatal, while having seen the same name
+      // before is normal.
+      //
+      if (Status == EFI_OUT_OF_RESOURCES) {
+        goto UninitSort;
+      }
+      ASSERT (Status == EFI_ALREADY_STARTED);
+    }
+  }
+
+  //
+  // If separation of duplicates has been requested, allocate the list for
+  // them.
+  //
+  if (Duplicates != NULL) {
+    Dupes = AllocateZeroPool (sizeof (*Dupes));
+    if (Dupes == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto UninitSort;
+    }
+    InitializeListHead (&Dupes->Link);
+  }
+
+  //
+  // No memory allocation beyond this point; thus, no chance to fail. We can
+  // now migrate the EFI_SHELL_FILE_INFO objects from (*FileList) to Sort.
+  //
+  BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, FilesHead) {
+    FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
+    //
+    // Look up the SHELL_SORT_UNIQUE_NAME that matches FileInfo's name.
+    //
+    Alias = ((Order == ShellSortFileListByFileName) ?
+             FileInfo->FileName :
+             FileInfo->FullName);
+    SortEntry = OrderedCollectionFind (Sort, Alias);
+    ASSERT (SortEntry != NULL);
+    Unique = OrderedCollectionUserStruct (SortEntry);
+    //
+    // Move FileInfo from (*FileList) to the end of the list of files whose
+    // names all compare identical to FileInfo's name.
+    //
+    RemoveEntryList (&FileInfo->Link);
+    InsertTailList (&Unique->SameNameList, &FileInfo->Link);
+  }
+
+  //
+  // All EFI_SHELL_FILE_INFO objects originally in (*FileList) have been
+  // distributed to Sort. Now migrate them back to (*FileList), advancing in
+  // unique name order.
+  //
+  for (SortEntry = OrderedCollectionMin (Sort);
+       SortEntry != NULL;
+       SortEntry = OrderedCollectionNext (SortEntry)) {
+    Unique = OrderedCollectionUserStruct (SortEntry);
+    //
+    // The first FileInfo encountered for each unique name goes back on
+    // (*FileList) unconditionally. Further FileInfo instances for the same
+    // unique name -- that is, duplicates -- are either returned to (*FileList)
+    // or separated, dependent on the caller's request.
+    //
+    TargetFileList = FilesHead;
+    BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, &Unique->SameNameList) {
+      RemoveEntryList (FileEntry);
+      InsertTailList (TargetFileList, FileEntry);
+      if (Duplicates != NULL) {
+        TargetFileList = &Dupes->Link;
+      }
+    }
+  }
+
+  //
+  // We're done. If separation of duplicates has been requested, output the
+  // list of duplicates -- and free that list at once, if it's empty (i.e., if
+  // no duplicates have been found).
+  //
+  if (Duplicates != NULL) {
+    if (IsListEmpty (&Dupes->Link)) {
+      FreePool (Dupes);
+      *Duplicates = NULL;
+    } else {
+      *Duplicates = Dupes;
+    }
+  }
+  Status = EFI_SUCCESS;
+
+  //
+  // Fall through.
+  //
+UninitSort:
+  for (SortEntry = OrderedCollectionMin (Sort);
+       SortEntry != NULL;
+       SortEntry = NextSortEntry) {
+    NextSortEntry = OrderedCollectionNext (SortEntry);
+    OrderedCollectionDelete (Sort, SortEntry, &UniqueAsVoid);
+    Unique = UniqueAsVoid;
+    ASSERT (IsListEmpty (&Unique->SameNameList));
+    FreePool (Unique);
+  }
+  OrderedCollectionUninit (Sort);
+
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



  parent reply	other threads:[~2021-01-04 15:42 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-04 15:42 [PATCH 0/8] ShellPkg, ArmVirtPkg, OvmfPkg: shell usability improvements Laszlo Ersek
2021-01-04 15:42 ` [PATCH 1/8] ShellPkg/Comp: add file buffering Laszlo Ersek
2021-01-11  1:24   ` Gao, Zhichao
2021-01-04 15:42 ` [PATCH 2/8] OvmfPkg: raise PcdShellFileOperationSize to 128KB Laszlo Ersek
2021-01-04 15:42 ` [PATCH 3/8] ArmVirtPkg: " Laszlo Ersek
2021-01-04 15:42 ` Laszlo Ersek [this message]
2021-01-13  3:03   ` [PATCH 4/8] ShellPkg/ShellCommandLib: add ShellSortFileList() Gao, Zhichao
2021-01-04 15:42 ` [PATCH 5/8] ShellPkg/Ls: sort output by FileName in non-SFO mode Laszlo Ersek
2021-01-13  3:04   ` Gao, Zhichao
2021-01-04 15:42 ` [PATCH 6/8] ShellPkg/ShellProtocol: sort files by FullName in RemoveDupInFileList() Laszlo Ersek
2021-01-13  3:04   ` Gao, Zhichao
2021-01-04 15:42 ` [PATCH 7/8] OvmfPkg: disable list length checks in NOOPT and DEBUG builds Laszlo Ersek
2021-01-04 15:42 ` [PATCH 8/8] ArmVirtPkg: " Laszlo Ersek
2021-01-04 16:12 ` [PATCH 0/8] ShellPkg, ArmVirtPkg, OvmfPkg: shell usability improvements Ard Biesheuvel
2021-01-08  8:34 ` [edk2-devel] " Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210104154235.31785-5-lersek@redhat.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox