From 87822b72296e4b1c7e2dc28dce76659791374dd0 Mon Sep 17 00:00:00 2001
From: Jorge Camarero Vera <jorge.camarero@cern.ch>
Date: Tue, 4 Oct 2022 15:59:21 +0200
Subject: [PATCH] Resolve "Problem with handling of the non-native formats by
 the CleanerSession"

---
 .gitlab/ci/kube-tests.gitlab-ci.yml           |  44 +-
 ReleaseNotes.md                               |   1 +
 catalogue/Catalogue.hpp                       |   2 +
 catalogue/CatalogueRetryWrapper.hpp           |   4 +
 catalogue/CatalogueTest.cpp                   |  24 +
 catalogue/DummyCatalogue.hpp                  |   2 +
 catalogue/RdbmsCatalogue.cpp                  |  27 ++
 catalogue/RdbmsCatalogue.hpp                  |   2 +
 .../cc7/opt/run/bin/externaltapetests.sh      |  33 ++
 .../orchestration/create_instance.sh          |  28 +-
 .../orchestration/pod-externaltapetests.yaml  |  81 ++++
 .../orchestration/run_systemtest.sh           |   6 +-
 .../tests/external_tapes_test.sh              | 111 +++++
 cta.spec.in                                   |   1 +
 .../tape/tapeserver/daemon/CleanerSession.cpp | 152 +++----
 .../tape/tapeserver/daemon/CleanerSession.hpp | 426 +++++++++---------
 .../tape/tapeserver/file/CMakeLists.txt       |  16 +
 .../tape/tapeserver/file/HeaderChecker.cpp    |  61 +++
 .../tape/tapeserver/file/HeaderChecker.hpp    |  20 +
 .../tape/tapeserver/file/OsmFileReader.cpp    |  32 +-
 .../tape/tapeserver/file/OsmFileStructure.hpp |  28 +-
 .../tape/tapeserver/file/OsmReadSession.cpp   |  44 +-
 .../tape/tapeserver/file/OsmReaderTest.cpp    | 196 ++++++++
 23 files changed, 969 insertions(+), 372 deletions(-)
 create mode 100755 continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh
 create mode 100644 continuousintegration/orchestration/pod-externaltapetests.yaml
 create mode 100755 continuousintegration/orchestration/tests/external_tapes_test.sh
 create mode 100644 tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp

diff --git a/.gitlab/ci/kube-tests.gitlab-ci.yml b/.gitlab/ci/kube-tests.gitlab-ci.yml
index cd4220c281..c92e990986 100644
--- a/.gitlab/ci/kube-tests.gitlab-ci.yml
+++ b/.gitlab/ci/kube-tests.gitlab-ci.yml
@@ -1,8 +1,19 @@
+.manual_rules:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "web"
+      when: manual
+      allow_failure: true
+    - if: $CI_PIPELINE_SOURCE == "push"
+      when: manual
+      allow_failure: true
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+      when: on_success
+      allow_failure: false
+
 .kubernetes_test:
   script:
     - export NAMESPACE="${JOB_NAME}-${CTA_BUILD_ID}-$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 4 | head -n 1)"
     - cd continuousintegration/orchestration/
-    - echo "${EXTENDED_OPTIONS}"
     - ./run_systemtest.sh -n ${NAMESPACE} -p ${CI_PIPELINE_ID} -s ${TEST_SCRIPT} ${EXTENDED_OPTIONS}
   variables:
     TEST_SCRIPT:
@@ -21,15 +32,7 @@
   extends:
     - .kubernetes_test
   rules:
-    - if: $CI_PIPELINE_SOURCE == "web"
-      when: manual
-      allow_failure: true
-    - if: $CI_PIPELINE_SOURCE == "push"
-      when: manual
-      allow_failure: true
-    - if: $CI_PIPELINE_SOURCE == "schedule"
-      when: on_success
-      allow_failure: false
+    - !reference [.manual_rules, rules]
   variables:
     EXTENDED_OPTIONS: "-O -D -t 2400 -C -e eos5-config-quarkdb.yaml"
 
@@ -85,19 +88,22 @@ liquibase-update:
   extends:
     - .kubernetes_test
   rules:
-    - if: $CI_PIPELINE_SOURCE == "web"
-      when: manual
-      allow_failure: true
-    - if: $CI_PIPELINE_SOURCE == "push"
-      when: manual
-      allow_failure: true
-    - if: $CI_PIPELINE_SOURCE == "schedule"
-      when: on_success
-      allow_failure: false
+    - !reference [.manual_rules, rules]
   variables:
     TEST_SCRIPT: "tests/update_db_test.sh"
     EXTENDED_OPTIONS: "-O -D -t 2400 -C -u"
 
+external-tape-formats:
+  stage: test
+  extends:
+    - .kubernetes_test
+  rules:
+    - !reference [.manual_rules, rules]
+  variables:
+    TEST_SCRIPT: "/usr/bin/true"
+    EXTENDED_OPTIONS: "-O -D -t 2400 -C -T"
+    JOB_NAME: "external-tape"
+
 nightly1:
   stage: test
   extends:
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index a128f03747..f5cb36a8d9 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -2,6 +2,7 @@
 
 ## Summary
 ### Bug fixes
+- cta/CTA#122 - Problem with handling of the non-native formats by the CleanerSession
 - cta/CTA#160 - Improve DB access to get all the Tape Drive States
 - cta/CTA#164 - Cppcheck job is not failing when there are errors
 - cta/CTA#165 - Fix oracle dbunittests
diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index e7c6178677..be6c5e83fe 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -224,6 +224,8 @@ public:
    */
   virtual std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const = 0;
 
+  virtual common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const = 0;
+
   /**
    * Notifies the catalogue that the specified files have been written to tape.
    *
diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp
index 8bd0e67608..33547358a8 100644
--- a/catalogue/CatalogueRetryWrapper.hpp
+++ b/catalogue/CatalogueRetryWrapper.hpp
@@ -79,6 +79,10 @@ public:
     return retryOnLostConnection(m_log, [&]{return m_catalogue->getTapesForWriting(logicalLibraryName);}, m_maxTriesToConnect);
   }
 
+  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override {
+    return retryOnLostConnection(m_log, [&]{return m_catalogue->getTapeLabelFormat(vid);}, m_maxTriesToConnect);
+  }
+
   void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override {
     return retryOnLostConnection(m_log, [&]{return m_catalogue->filesWrittenToTape(event);}, m_maxTriesToConnect);
   }
diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index 482c880343..77ff7875ce 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -7290,6 +7290,30 @@ TEST_P(cta_catalogue_CatalogueTest, getTapesForWriting) {
   ASSERT_EQ(0, tape.dataOnTapeInBytes);
 }
 
+TEST_P(cta_catalogue_CatalogueTest, getTapeLabelFormat) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->createMediaType(m_admin, m_mediaType);
+  m_catalogue->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->createTape(m_admin, m_tape1);
+
+  // Get Tape
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->getTapes();
+  ASSERT_EQ(1, tapes.size());
+  const cta::common::dataStructures::Tape tape = tapes.front();
+  ASSERT_EQ(m_tape1.vid, tape.vid);
+
+  // Get label format and compare
+  const auto labelFormat = m_catalogue->getTapeLabelFormat(m_tape1.vid);
+  ASSERT_EQ(tape.labelFormat, labelFormat);
+}
+
 TEST_P(cta_catalogue_CatalogueTest, getTapesForWritingOrderedByDataInBytesDesc) {
   using namespace cta;
 
diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp
index daa1559a29..570623fec8 100644
--- a/catalogue/DummyCatalogue.hpp
+++ b/catalogue/DummyCatalogue.hpp
@@ -21,6 +21,7 @@
 #include <list>
 
 #include "Catalogue.hpp"
+#include "common/threading/MutexLocker.hpp"
 
 namespace cta {
 
@@ -120,6 +121,7 @@ public:
   std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
  // getTapesByVid is implemented below (and works).
   std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override { throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented"); }
+  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override { throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented"); }
   std::list<TapeForWriting> getTapesForWriting(const std::string& logicalLibraryName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   bool isAdmin(const common::dataStructures::SecurityIdentity& admin) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 2f38670cd9..f134c9fd15 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -9439,6 +9439,33 @@ std::list<TapeForWriting> RdbmsCatalogue::getTapesForWriting(const std::string &
   }
 }
 
+common::dataStructures::Label::Format RdbmsCatalogue::getTapeLabelFormat(const std::string& vid) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE.LABEL_FORMAT AS LABEL_FORMAT "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID";
+
+    auto conn = m_connPool.getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if(rset.next()) {
+      return common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"), "[RdbmsCatalogue::getTapeLabelFormat()]");
+    } else {
+      throw exception::Exception(std::string("No such tape with vid=") + vid);
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // insertTapeFile
 //------------------------------------------------------------------------------
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index f167f04980..ea76e97d35 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -156,6 +156,8 @@ public:
    */
   std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const override;
 
+  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override;
+
   /**
    * Notifies the CTA catalogue that the specified tape has been mounted in
    * order to archive files.
diff --git a/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh b/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh
new file mode 100755
index 0000000000..60411e8e24
--- /dev/null
+++ b/continuousintegration/docker/ctafrontend/cc7/opt/run/bin/externaltapetests.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+#               option) any later version.
+#
+#               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+#               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+#               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+. /opt/run/bin/init_pod.sh
+
+set -e
+
+if [ ! -e /etc/buildtreeRunner ]; then
+  yum-config-manager --enable cta-artifacts
+  yum-config-manager --enable ceph
+  yum-config-manager --enable castor
+
+  # Install missing RPMs
+  yum -y install mt-st lsscsi sg3_utils cta-taped cta-tape-label cta-debuginfo ceph-common
+fi
+
+yum -y install cta-systemtests
+
+2>&1 cta-osmReaderTest ${DEVICE_NAME} ${DRIVE_DEVICE}
diff --git a/continuousintegration/orchestration/create_instance.sh b/continuousintegration/orchestration/create_instance.sh
index 87e22b5e23..757db5abf7 100755
--- a/continuousintegration/orchestration/create_instance.sh
+++ b/continuousintegration/orchestration/create_instance.sh
@@ -46,6 +46,9 @@ runoracleunittests=0
 # By default doesn't prepare the images with the previous schema version
 updatedatabasetest=0
 
+# By default doesn't run the tests for external tape formats
+runexternaltapetests=0
+
 usage() { cat <<EOF 1>&2
 Usage: $0 -n <namespace> [-o <objectstore_configmap>] [-d <database_configmap>] \
       [-e <eos_configmap>] [-a <additional_k8_resources>]\
@@ -63,13 +66,14 @@ Options:
   -a    additional kubernetes resources added to the kubernetes namespace
   -U    Run database unit test only
   -u    Prepare the pods to run the liquibase test
+  -T    Execute tests for external tape formats
 EOF
 exit 1
 }
 
 die() { echo "$@" 1>&2 ; exit 1; }
 
-while getopts "n:o:d:e:a:p:b:B:E:SDOUum" o; do
+while getopts "n:o:d:e:a:p:b:B:E:SDOUumT" o; do
     case "${o}" in
         o)
             config_objectstore=${OPTARG}
@@ -94,7 +98,7 @@ while getopts "n:o:d:e:a:p:b:B:E:SDOUum" o; do
         n)
             instance=${OPTARG}
             ;;
-	p)
+	      p)
             pipelineid=${OPTARG}
             ;;
         b)
@@ -118,6 +122,9 @@ while getopts "n:o:d:e:a:p:b:B:E:SDOUum" o; do
         U)
             runoracleunittests=1
             ;;
+        T)
+            runexternaltapetests=1
+            ;;
         u)
             updatedatabasetest=1
             ;;
@@ -463,4 +470,21 @@ fi
 echo "Instance ${instance} successfully created:"
 kubectl get pods -a --namespace=${instance}
 
+if [ $runexternaltapetests == 1 ] ; then
+  echo "Running database unit-tests"
+  ./tests/external_tapes_test.sh -n ${instance} -P ${poddir}
+
+  kubectl --namespace=${instance} logs externaltapetests
+
+  # database unit-tests went wrong => exit now with error
+  if $(kubectl get pod externaltapetests -a --namespace=${instance} | grep -q Error); then
+    echo "init pod in Error status here are its last log lines:"
+    kubectl --namespace=${instance} logs externaltapetests --tail 10
+    die "ERROR: externaltapetests pod in Error state. Initialization failed."
+  fi
+
+  # database unit-tests were successful => exit now with success
+  exit 0
+fi
+
 exit 0
diff --git a/continuousintegration/orchestration/pod-externaltapetests.yaml b/continuousintegration/orchestration/pod-externaltapetests.yaml
new file mode 100644
index 0000000000..0de217d569
--- /dev/null
+++ b/continuousintegration/orchestration/pod-externaltapetests.yaml
@@ -0,0 +1,81 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: externaltapetests
+spec:
+  restartPolicy: Never
+  containers:
+  - name: externaltapetests
+    image: gitlab-registry.cern.ch/cta/ctageneric:78673git921a9300
+    stdin: true
+    env:
+    - name: MY_CONTAINER
+      value: "externaltapetests"
+    - name: MY_NAME
+      valueFrom:
+        fieldRef:
+          fieldPath: metadata.name
+    - name: MY_NAMESPACE
+      valueFrom:
+        fieldRef:
+          fieldPath: metadata.namespace
+    - name: INSTANCE_NAME
+      value: "$(MY_NAMESPACE)"
+    - name: DEVICE_NAME
+      value: "DEVICE_NAME_VALUE"
+    - name: DRIVE_DEVICE
+      value: "DEVICE_PATH"
+    - name: BUILDTREE_BASE
+      valueFrom:
+        configMapKeyRef:
+          name: buildtree
+          key: base
+    - name: CTA_BUILDTREE_SUBDIR
+      valueFrom:
+        configMapKeyRef:
+          name: buildtree
+          key: cta_subdir
+    - name: EOS_BUILDTREE_SUBDIR
+      valueFrom:
+        configMapKeyRef:
+          name: buildtree
+          key: eos_subdir
+    - name: TERM
+      value: "xterm"
+    - name: driveslot
+      value: "0"
+    command: ['/opt/run/bin/externaltapetests.sh']
+    args: ["none"]
+    volumeMounts:
+    - mountPath: /shared
+      name: shared
+    - mountPath: /etc/config/objectstore
+      name: myobjectstore
+    - mountPath: /etc/config/database
+      name: mydatabase
+    - mountPath: /etc/config/library
+      name: mylibrary
+    - mountPath: /mnt/logs
+      name: logstorage
+    securityContext:
+      privileged: true
+
+  volumes:
+  - name: shared
+    hostPath:
+      path: /opt/cta
+  - name: myobjectstore
+    configMap:
+      name: objectstore-config
+  - name: mydatabase
+    configMap:
+      name: database-config
+  - name: mylibrary
+    configMap:
+      name: library-config
+  - name: logstorage
+    persistentVolumeClaim:
+      claimName: claimlogs
+
+  imagePullSecrets:
+  - name: ctaregsecret
diff --git a/continuousintegration/orchestration/run_systemtest.sh b/continuousintegration/orchestration/run_systemtest.sh
index 427a5bdf71..bd031a85d3 100755
--- a/continuousintegration/orchestration/run_systemtest.sh
+++ b/continuousintegration/orchestration/run_systemtest.sh
@@ -61,6 +61,7 @@ Options:
   -U    Run database unit test only
   -C    Cleanup leftover kubernetes namespaces
   -u    Prepare the pods to run the liquibase test
+  -T    Execute tests for external tape formats
 
 Create a kubernetes instance and launch the system test script specified.
 Makes sure the created instance is cleaned up at the end and return the status of the system test.
@@ -73,7 +74,7 @@ exit 1
 # always delete DB and OBJECTSTORE for tests
 CREATE_OPTS="-D -O"
 
-while getopts "n:d:s:p:b:e:a:B:t:ukDOSUC" o; do
+while getopts "n:d:s:p:b:e:a:B:t:ukDOSUCT" o; do
     case "${o}" in
         s)
             systemtest_script=${OPTARG}
@@ -126,6 +127,9 @@ while getopts "n:d:s:p:b:e:a:B:t:ukDOSUC" o; do
         u)
             CREATE_OPTS="${CREATE_OPTS} -u"
             ;;
+        T)
+            CREATE_OPTS="${CREATE_OPTS} -T"
+            ;;
         *)
             usage
             ;;
diff --git a/continuousintegration/orchestration/tests/external_tapes_test.sh b/continuousintegration/orchestration/tests/external_tapes_test.sh
new file mode 100755
index 0000000000..34b7b99bb3
--- /dev/null
+++ b/continuousintegration/orchestration/tests/external_tapes_test.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+#               option) any later version.
+#
+#               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+#               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+#               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+usage() { cat <<EOF 1>&2
+Usage: $0 -n <namespace>
+EOF
+exit 1
+}
+
+NAMESPACE=""
+tempdir=$(mktemp -d)
+poddir=""
+READ_OSM_SCRIPT="${tempdir}/read_osm_tape.sh"
+
+while getopts "n:P:" o; do
+  case "${o}" in
+    n)
+      NAMESPACE=${OPTARG}
+      ;;
+    P)
+      poddir=${OPTARG}
+      ;;
+    *)
+      usage
+      ;;
+  esac
+done
+shift $((OPTIND-1))
+
+if [ -z "${NAMESPACE}" ]; then
+  usage
+fi
+
+echo "
+device=\"DEVICE\"
+echo \"Device is \$device\"
+
+# Load tape in a tapedrive
+mtx -f /dev/smc status
+mtx -f /dev/smc load 1 0
+mtx -f /dev/smc status
+
+# Get the device status where the tape is loaded and rewind it.
+mt -f \$device status
+mt -f \$device rewind
+
+# The header files have 4 more bytes in the git file
+truncate -s -4 /osm-mhvtl/L08033/L1
+touch /osm-tape.img
+dd if=/osm-mhvtl/L08033/L1 of=/osm-tape.img bs=32768
+dd if=/osm-mhvtl/L08033/L2 of=/osm-tape.img bs=32768 seek=1
+dd if=/osm-tape.img of=\$device bs=32768 count=2
+dd if=/osm-mhvtl/L08033/file1 of=\$device bs=262144 count=202
+dd if=/osm-mhvtl/L08033/file2 of=\$device bs=262144 count=202
+
+mt -f \$device rewind
+" &> ${READ_OSM_SCRIPT}
+
+# Download OSM Tape from git
+echo "${tempdir}/osm-mhvtl"
+if [ ! -d "${tempdir}/osm-mhvtl" ] ; then
+  yum install -y git git-lfs
+  git lfs install || git lfs update --force
+  git lfs clone https://gitlab.desy.de/mwai.karimi/osm-mhvtl.git ${tempdir}/osm-mhvtl
+fi
+
+# Get the device to be used.
+device_name=$(kubectl -n ${NAMESPACE} exec tpsrv01 -c taped -- cat /etc/cta/TPCONFIG | grep smc0 | awk '{ print $1 }')
+device=$(kubectl -n ${NAMESPACE} exec tpsrv01 -c taped -- cat /etc/cta/TPCONFIG | grep smc0 | awk '{ print $3 }')
+sed -i "s#DEVICE#${device}#g" ${READ_OSM_SCRIPT}
+sed -i "s#DEVICE_NAME_VALUE#${device_name}#g" ${poddir}/pod-externaltapetests.yaml
+sed -i "s#DEVICE_PATH#${device}#g" ${poddir}/pod-externaltapetests.yaml
+
+# Copy the above script to the pod rmcd to be run
+kubectl -n ${NAMESPACE} cp ${tempdir}/osm-mhvtl tpsrv01:/osm-mhvtl -c rmcd
+kubectl -n ${NAMESPACE} cp ${READ_OSM_SCRIPT} tpsrv01:/root/read_osm_tape.sh -c rmcd
+kubectl -n ${NAMESPACE} exec tpsrv01 -c rmcd -- /bin/bash /root/read_osm_tape.sh
+
+# Creation of the pod to run the tests
+kubectl create -f "${poddir}/pod-externaltapetests.yaml" --namespace=${NAMESPACE}
+
+echo -n "Waiting for externaltapetests"
+for ((i=0; i<400; i++)); do
+  echo -n "."
+  kubectl get pod externaltapetests -a --namespace=${NAMESPACE} | egrep -q 'Completed|Error' && break
+  sleep 1
+done
+echo "\n"
+
+# Copy the logs outside
+kubectl -n ${NAMESPACE} logs externaltapetests &> "../../pod_logs/${NAMESPACE}/externaltapetests.log"
+
+# Cleanning
+rm -rf ${tempdir}/osm-mhvtl
+rm ${READ_OSM_SCRIPT}
+
+exit 0
diff --git a/cta.spec.in b/cta.spec.in
index edf6b8fb71..a93c6c3d04 100644
--- a/cta.spec.in
+++ b/cta.spec.in
@@ -329,6 +329,7 @@ Unit tests and system tests with virtual tape drives
 %{_bindir}/cta-unitTests-multiProcess
 %{_bindir}/cta-valgrindUnitTests.sh
 %{_bindir}/cta-unitPlusSystemTests.sh
+%{_bindir}/cta-osmReaderTest
 %{_libdir}/libctacataloguecmdlineunittests.so*
 %{_libdir}/libctacommonunittests.so*
 %{_libdir}/libctadbconfigcatalogueunittests.so*
diff --git a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp
index dd3efded2e..fbc5171c05 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp
@@ -15,9 +15,13 @@
  *               submit itself to any jurisdiction.
  */
 
+#include <exception>
+#include <list>
+#include <vector>
+
 #include "castor/tape/tapeserver/daemon/CleanerSession.hpp"
+#include "castor/tape/tapeserver/file/HeaderChecker.hpp"
 #include "catalogue/Catalogue.hpp"
-#include <exception>
 
 //------------------------------------------------------------------------------
 // constructor
@@ -64,14 +68,14 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction
     errorMessage = "Caught an unknown exception";
   }
 
-  //Reaching this point means the cleaner failed and an exception was thrown
+  // Reaching this point means the cleaner failed and an exception was thrown
   std::list<cta::log::Param> params = {
     cta::log::Param("tapeVid", m_vid),
     cta::log::Param("tapeDrive", m_driveConfig.unitName),
     cta::log::Param("message", errorMessage)};
   m_log(cta::log::ERR, "Cleaner failed. Putting the drive down.", params);
-  
-  //Putting the drive down  
+
+  // Putting the drive down
   try {
     setDriveDownAfterCleanerFailed(std::string("Cleaner failed. ") + errorMessage);
   } catch(const cta::exception::Exception &ex) {
@@ -81,29 +85,30 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction
     cta::log::Param("message", ex.getMessageValue())};
     m_log(cta::log::ERR, "Cleaner failed. Failed to put the drive down", params);
   }
-  
+
   return MARK_DRIVE_AS_DOWN;
 }
 
 void castor::tape::tapeserver::daemon::CleanerSession::setDriveDownAfterCleanerFailed(const std::string & errorMsg) {
-  
+
   std::string logicalLibrary =  m_driveConfig.logicalLibrary;
-  std::string hostname=cta::utils::getShortHostname();
+  std::string hostname = cta::utils::getShortHostname();
   std::string driveName = m_driveConfig.unitName;
-  
+
   cta::common::dataStructures::DriveInfo driveInfo;
   driveInfo.driveName = driveName;
   driveInfo.logicalLibrary = logicalLibrary;
   driveInfo.host = hostname;
-  
+
   cta::log::LogContext lc(m_log);
-  
-  m_scheduler.reportDriveStatus(driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, lc);
+
+  m_scheduler.reportDriveStatus(driveInfo, cta::common::dataStructures::MountType::NoMount,
+    cta::common::dataStructures::DriveStatus::Down, lc);
   cta::common::dataStructures::SecurityIdentity cliId;
   cta::common::dataStructures::DesiredDriveState driveState;
   driveState.up = false;
   driveState.forceDown = false;
-  driveState.setReasonFromLogMsg(cta::log::ERR,errorMsg);
+  driveState.setReasonFromLogMsg(cta::log::ERR, errorMsg);
   m_scheduler.setDesiredDriveState(cliId, m_driveConfig.unitName, driveState, lc);
 }
 
@@ -112,7 +117,6 @@ void castor::tape::tapeserver::daemon::CleanerSession::setDriveDownAfterCleanerF
 //------------------------------------------------------------------------------
 castor::tape::tapeserver::daemon::Session::EndOfSessionAction
   castor::tape::tapeserver::daemon::CleanerSession::exceptionThrowingExecute() {
-
   setProcessCapabilities("cap_sys_rawio+ep");
 
   std::unique_ptr<drive::DriveInterface> drivePtr = createDrive();
@@ -122,50 +126,49 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction
     cleanDrive(drive);
   } catch(...) {
     logAndClearTapeAlerts(drive);
-    //As we failed to clean the drive (unmount the tape or rewinding impossible), 
-    //we set the tape as disabled so that it will not be mounted for future retrieves
-    //otherwise, we will go in an infinite loop of mounting with errors.
-    //Gitlab ticket reference : https://gitlab.cern.ch/cta/CTA/issues/224
-    if(!m_vid.empty()){
-      
-      //Get the message exception to log it into the comment section of the tape table
+    // As we failed to clean the drive (unmount the tape or rewinding impossible),
+    // we set the tape as disabled so that it will not be mounted for future retrieves
+    // otherwise, we will go in an infinite loop of mounting with errors.
+    // Gitlab ticket reference : https://gitlab.cern.ch/cta/CTA/issues/224
+    if (!m_vid.empty()) {
+      // Get the message exception to log it into the comment section of the tape table
       auto currException = std::current_exception();
       std::string currentExceptionMsg = "";
       try {
         std::rethrow_exception(currException);
-      } catch(cta::exception::Exception &ex){
+      } catch (cta::exception::Exception &ex) {
         currentExceptionMsg = ex.getMessageValue();
-      } catch(std::exception & ex){
+      } catch (std::exception & ex) {
         currentExceptionMsg = ex.what();
       } catch (...) {
         currentExceptionMsg = "Unknown exception";
       }
-      
+
       std::string hostname = cta::utils::getShortHostname();
       std::string tapeDrive = m_driveConfig.unitName;
       std::list<cta::log::Param> params = {
         cta::log::Param("tapeVid", m_vid),
         cta::log::Param("tapeDrive", tapeDrive),
         cta::log::Param("logicalLibrary", m_driveConfig.logicalLibrary),
-        cta::log::Param("host",hostname),
+        cta::log::Param("host", hostname),
         cta::log::Param("exceptionMsg", currentExceptionMsg)};
-      m_log(cta::log::ERR,"In CleanerSession::exceptionThrowingExecute(), failed to clean the Drive with a tape mounted. Disabling the tape.",params);
+      m_log(cta::log::ERR, "In CleanerSession::exceptionThrowingExecute(), failed to clean the Drive with a tape mounted. Disabling the tape.", params);
       cta::common::dataStructures::SecurityIdentity admin;
       admin.username = c_defaultUserNameUpdate + " " + tapeDrive;
       admin.host = hostname;
-      
-      try{
-        std::string disabledReason = cta::utils::getCurrentLocalTime("%F %T") + ":" + currentExceptionMsg; 
+
+      try {
+        std::string disabledReason = cta::utils::getCurrentLocalTime("%F %T") + ":" + currentExceptionMsg;
         m_catalogue.setTapeDisabled(admin, m_vid, disabledReason);
       } catch(cta::exception::Exception &ex) {
-        cta::log::Param param("exceptionMsg",ex.getMessageValue());
+        cta::log::Param param("exceptionMsg", ex.getMessageValue());
         params.push_back(param);
-        m_log(cta::log::ERR,"In CleanerSession::exceptionThrowingExecute(), failed to disable the tape.",params);
+        m_log(cta::log::ERR, "In CleanerSession::exceptionThrowingExecute(), failed to disable the tape.", params);
       }
     }
     throw;
   }
-  
+
   logAndClearTapeAlerts(drive);
   return MARK_DRIVE_AS_UP;
 }
@@ -174,16 +177,16 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction
 // cleanDrive
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::CleanerSession::cleanDrive(drive::DriveInterface &drive) {
-  if(m_waitMediaInDrive) {
+  if (m_waitMediaInDrive) {
     waitUntilMediaIsReady(drive);
   }
 
-  if(!drive.hasTapeInPlace()) {
+  if (!drive.hasTapeInPlace()) {
     std::list<cta::log::Param> params;
     params.push_back(cta::log::Param("tapeVid", m_vid));
     params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
     m_log(cta::log::INFO, "Cleaner found tape drive empty", params);
-    return; //return immediately if there is no tape
+    return;  // return immediately if there is no tape
   }
 
   m_encryptionControl.disable(drive);
@@ -205,19 +208,18 @@ void castor::tape::tapeserver::daemon::CleanerSession::cleanDrive(drive::DriveIn
 //------------------------------------------------------------------------------
 void castor::tape::tapeserver::daemon::CleanerSession::logAndClearTapeAlerts(drive::DriveInterface &drive) throw() {
   std::string errorMessage;
-  try{
+  try {
     std::vector<uint16_t> tapeAlertCodes = drive.getTapeAlertCodes();
     if (!tapeAlertCodes.empty()) {
       size_t alertNumber = 0;
       // Log tape alerts in the logs.
       std::vector<std::string> tapeAlerts = drive.getTapeAlerts(tapeAlertCodes);
-      for (std::vector<std::string>::iterator ta=tapeAlerts.begin(); ta!=tapeAlerts.end();++ta)
-      {
+      for (std::vector<std::string>::iterator ta = tapeAlerts.begin(); ta != tapeAlerts.end(); ++ta) {
         std::list<cta::log::Param> params = {
-          cta::log::Param("tapeAlert",*ta),
+          cta::log::Param("tapeAlert", *ta),
           cta::log::Param("tapeAlertNumber", alertNumber++),
           cta::log::Param("tapeAlertCount", tapeAlerts.size())};
-        m_log(cta::log::WARNING, "Tape alert detected",params);
+        m_log(cta::log::WARNING, "Tape alert detected", params);
       }
     }
     return;
@@ -234,7 +236,7 @@ void castor::tape::tapeserver::daemon::CleanerSession::logAndClearTapeAlerts(dri
     cta::log::Param("tapeVid", m_vid),
     cta::log::Param("tapeDrive", m_driveConfig.unitName),
     cta::log::Param("message", errorMessage)};
-  m_log(cta::log::ERR, "Cleaner failed getting tape alerts from the drive", params);  
+  m_log(cta::log::ERR, "Cleaner failed getting tape alerts from the drive", params);
 }
 
 //------------------------------------------------------------------------------
@@ -258,25 +260,24 @@ std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface>
   castor::tape::tapeserver::daemon::CleanerSession::createDrive() {
   SCSI::DeviceVector dv(m_sysWrapper);
   SCSI::DeviceInfo driveInfo = dv.findBySymlink(m_driveConfig.devFilename);
-  
+
   // Instantiate the drive object
   std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface>
     drive(drive::createDrive(driveInfo, m_sysWrapper));
 
-  if(nullptr == drive.get()) {
+  if (nullptr == drive.get()) {
     cta::exception::Exception ex;
     ex.getMessage() << "Failed to instantiate drive object";
     throw ex;
-  } 
-    
+  }
+
   return drive;
 }
 
 //------------------------------------------------------------------------------
 // waitUntilDriveIsReady
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady(
-  drive::DriveInterface &drive) {  
+void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady(drive::DriveInterface &drive) {
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("tapeVid", m_vid));
   params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
@@ -297,8 +298,7 @@ void castor::tape::tapeserver::daemon::CleanerSession::waitUntilMediaIsReady(
 //------------------------------------------------------------------------------
 // rewindDrive
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive(
-  drive::DriveInterface &drive) {
+void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive(drive::DriveInterface &drive) {
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("tapeVid", m_vid));
   params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
@@ -311,14 +311,13 @@ void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive(
 //------------------------------------------------------------------------------
 // checkTapeContainsData
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData(
-  drive::DriveInterface &drive) {
+void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData(drive::DriveInterface &drive) {
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("tapeVid", m_vid));
   params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
 
   m_log(cta::log::INFO, "Cleaner checking tape contains data", params);
-  if(drive.isTapeBlank()) {
+  if (drive.isTapeBlank()) {
     cta::exception::Exception ex;
     ex.getMessage() << "Tape is completely blank when it should be labeled";
     throw ex;
@@ -331,45 +330,35 @@ void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData(
 //------------------------------------------------------------------------------
 std::string castor::tape::tapeserver::daemon::CleanerSession::checkVolumeLabel(
   drive::DriveInterface &drive) {
-  tapeFile::VOL1 vol1;
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("tapeVid", m_vid));
   params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
-  
-  try {
-    drive.readExactBlock((void * )&vol1, sizeof(vol1),
-      "[CleanerSession::clean()] - Reading header VOL1");
-    vol1.verify();
-
-    const std::string &volumeLabelVSN = vol1.getVSN();
-    params.push_back(cta::log::Param("volumeLabelVSN", volumeLabelVSN));
-
-    m_log(cta::log::INFO, "Cleaner read VSN from volume label", params);
-
-    // If the cleaner was given a VID
-    if(!m_vid.empty()) {
-      if(m_vid == volumeLabelVSN) {
-        m_log(cta::log::INFO, "Cleaner detected volume label contains expected VSN",
-          params);
-      } else {
-        m_log(cta::log::WARNING,
-          "Cleaner detected volume label does not contain expected VSN", params);
-      }
-    }
 
-    return volumeLabelVSN;
-  } catch(cta::exception::Exception &ne) {
-    cta::exception::Exception ex;
-    ex.getMessage() << "Failed to check volume label: " << ne.getMessage().str();
-    throw ex;
+  using LabelFormat = cta::common::dataStructures::Label::Format;
+  const LabelFormat labelFormat = m_catalogue.getTapeLabelFormat(m_vid);
+
+  const std::string volumeLabelVSN = tapeFile::HeaderChecker::checkVolumeLabel(drive, labelFormat);
+
+  params.push_back(cta::log::Param("volumeLabelVSN", volumeLabelVSN));
+
+  m_log(cta::log::INFO, "Cleaner read VSN from volume label", params);
+
+  // If the cleaner was given a VID
+  if (!m_vid.empty()) {
+    if (m_vid == volumeLabelVSN) {
+      m_log(cta::log::INFO, "Cleaner detected volume label contains expected VSN", params);
+    } else {
+      m_log(cta::log::WARNING, "Cleaner detected volume label does not contain expected VSN", params);
+    }
   }
+  return volumeLabelVSN;
 }
 
 //------------------------------------------------------------------------------
 // unloadTape
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::CleanerSession::unloadTape(
-  const std::string &vid, drive::DriveInterface &drive) {
+void castor::tape::tapeserver::daemon::CleanerSession::unloadTape(const std::string &vid,
+  drive::DriveInterface &drive) {
   const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot();
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("tapeVid", vid));
@@ -391,8 +380,7 @@ void castor::tape::tapeserver::daemon::CleanerSession::unloadTape(
 //------------------------------------------------------------------------------
 // dismountTape
 //------------------------------------------------------------------------------
-void castor::tape::tapeserver::daemon::CleanerSession::dismountTape(
-  const std::string &vid) {
+void castor::tape::tapeserver::daemon::CleanerSession::dismountTape(const std::string &vid) {
   const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot();
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("tapeVid", vid));
diff --git a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp
index c609e71a1b..7cb05873a1 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.hpp
@@ -17,230 +17,228 @@
 
 #pragma once
 
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
 #include "common/log/LogContext.hpp"
 #include "common/log/Logger.hpp"
 #include "common/processCap/ProcessCap.hpp"
 #include "mediachanger/MediaChangerFacade.hpp"
-#include "Session.hpp"
-#include "tapeserver/daemon/TpconfigLine.hpp"
+#include "scheduler/Scheduler.hpp"
+#include "tapeserver/castor/tape/tapeserver/daemon/EncryptionControl.hpp"
+#include "tapeserver/castor/tape/tapeserver/daemon/Session.hpp"
 #include "tapeserver/castor/tape/tapeserver/drive/DriveInterface.hpp"
 #include "tapeserver/castor/tape/tapeserver/file/Structures.hpp"
 #include "tapeserver/castor/tape/tapeserver/SCSI/Device.hpp"
-#include "tapeserver/castor/tape/tapeserver/daemon/EncryptionControl.hpp"
-#include "catalogue/Catalogue.hpp"
-#include "scheduler/Scheduler.hpp"
-
-#include <memory>
+#include "tapeserver/daemon/TpconfigLine.hpp"
 
 namespace castor {
 namespace tape {
 namespace tapeserver {
 namespace daemon {
+/**
+  * Class responsible for cleaning up a tape drive left in a (possibly) dirty state.
+  */
+class CleanerSession : public Session {
+public:
+  /**
+    * Constructor
+    *
+    * @param capUtils Object providing support for UNIX capabilities.
+    * @param mc Object representing the media changer.
+    * @param log Object representing the API to the CASTOR logging system.
+    * @param driveConfig Configuration of the tape drive to be cleaned.
+    * @param sysWrapper Object representing the operating system.
+    * @param vid The volume identifier of the mounted tape if known,
+    * else the empty string.
+    * @param waitMediaInDrive true if we want to check the presence of the media in the drive before cleaning,
+    * false otherwise.
+    * @param waitMediaInDriveTimeout The maximum number of seconds to wait for
+    * the media to be ready for operations inside the drive.
+    * @param externalEncryptionKeyScript path to the operator provided script
+    * for encryption control.
+    * @param catalogue the CTA catalogue
+    */
+  CleanerSession(
+    cta::server::ProcessCap &capUtils,
+    cta::mediachanger::MediaChangerFacade &mc,
+    cta::log::Logger &log,
+    const cta::tape::daemon::TpconfigLine &driveConfig,
+    System::virtualWrapper &sysWrapper,
+    const std::string &vid,
+    const bool waitMediaInDrive,
+    const uint32_t waitMediaInDriveTimeout,
+    const std::string & externalEncryptionKeyScript,
+    cta::catalogue::Catalogue & catalogue,
+    cta::Scheduler & scheduler);
+
+  /**
+    * Execute the session and return the type of action to be performed
+    * immediately after the session has completed.
+    *
+    * @return Returns the type of action to be performed after the session has
+    * completed.
+    */
+  EndOfSessionAction execute() throw();
+
+private:
+  /**
+    * Object providing support for UNIX capabilities.
+    */
+  cta::server::ProcessCap &m_capUtils;
+
+  /**
+    * The object representing the media changer.
+    */
+  cta::mediachanger::MediaChangerFacade &m_mc;
+
+  /**
+    * The logging object
+    */
+  cta::log::Logger & m_log;
+
+  /**
+    * The configuration of the tape drive to be cleaned.
+    */
+  const cta::tape::daemon::TpconfigLine m_driveConfig;
+
+  /**
+    * The system wrapper used to find the device and instantiate the drive object
+    */
+  System::virtualWrapper & m_sysWrapper;
+
+  /**
+    * The volume identifier of the mounted tape if known, else the empty
+    * string.
+    */
+  const std::string m_vid;
+
+  /**
+    * true if we want to check the presence of the media in the drive before cleaning,
+    * false otherwise.
+    */
+  const bool m_waitMediaInDrive;
+
+  /**
+    * The maximum number of seconds to wait for
+    * the media to be ready for operations inside the drive.
+    */
+  const uint32_t m_tapeLoadTimeout;
+
+  /**
+    * Encryption helper object
+    */
+  EncryptionControl m_encryptionControl;
+
+  /**
+    * CTA catalogue
+    */
+  cta::catalogue::Catalogue & m_catalogue;
+
+  /**
+    * CTA scheduler
+    */
+  cta::Scheduler & m_scheduler;
+
+  /**
+    * Variable used to log UPDATE_USER_NAME in the DB
+    */
+  const std::string c_defaultUserNameUpdate = "cta-taped";
+
+  /**
+    * Execute the session and return the type of action to be performed
+    * immediately after the session has completed.
+    *
+    * @return Returns the type of action to be performed after the session has
+    * completed.
+    */
+  EndOfSessionAction exceptionThrowingExecute();
+
+  /**
+    * Logs and clears (just by reading them...) any outstanding tape alerts
+    *
+    * @param drive The tape drive.
+    */
+  void logAndClearTapeAlerts(drive::DriveInterface &drive) throw();
+
+  /**
+    * Does the actual steps to clean the drive
+    *
+    * @param drive The tape drive.
+    */
+  void cleanDrive(drive::DriveInterface &drive);
+
+  /**
+    * Sets the capabilities of the process and logs the result.
+    *
+    * @param capabilities The string representation of the capabilities.
+    */
+  void setProcessCapabilities(const std::string &capabilities);
+
+  /**
+    * Creates and returns the object that represents the tape drive to be
+    * cleaned.
+    *
+    * @return The tape drive.
+    */
+  std::unique_ptr<drive::DriveInterface> createDrive();
+
+  /**
+    * Waits for the specified drive to be ready.
+    *
+    * @param drive The tape drive.
+    */
+  void waitUntilMediaIsReady(drive::DriveInterface &drive);
+
+  /**
+    * Rewinds the specified tape drive.
+    *
+    * @param drive The tape drive.
+    */
+  void rewindDrive(drive::DriveInterface &drive);
+
+  /**
+    * Checks the tape in the specified tape drive contains some data where no
+    * data means the tape does not even contain a volume label.
+    *
+    * @param drive The tape drive.
+    */
+  void checkTapeContainsData(drive::DriveInterface &drive);
+
+  /**
+    * Checks that the tape in the specified drive contains a valid volume
+    * label.
+    *
+    * @param drive The tape drive for which it is assumed the tape to be
+    * tested is present and rewound to the beginning.
+    * @return The VSN stored within the colue label.
+    */
+  std::string checkVolumeLabel(drive::DriveInterface &drive);
+
+  /**
+    * Unloads the specified tape from the specified tape drive.
+    *
+    * @param vid The volume identifier of the tape to be unloaded.  Please note
+    * that the value of this field is only used for logging purposes.
+    * @param drive The tape drive.
+    */
+  void unloadTape(const std::string &vid, drive::DriveInterface &drive);
+
+  /**
+    * Dismounts the specified tape.
+    *
+    * @param vid The volume identifier of the tape to be dismounted.
+    */
+  void dismountTape(const std::string &vid);
+
   /**
-   * Class responsible for cleaning up a tape drive left in a (possibly) dirty state.
-   */
-  class CleanerSession : public Session {
-    
-  public:
-    /**
-     * Constructor
-     * 
-     * @param capUtils Object providing support for UNIX capabilities.
-     * @param mc Object representing the media changer.
-     * @param log Object representing the API to the CASTOR logging system.
-     * @param driveConfig Configuration of the tape drive to be cleaned.
-     * @param sysWrapper Object representing the operating system.
-     * @param vid The volume identifier of the mounted tape if known,
-     * else the empty string.
-     * @param waitMediaInDrive true if we want to check the presence of the media in the drive before cleaning,
-     * false otherwise.
-     * @param waitMediaInDriveTimeout The maximum number of seconds to wait for
-     * the media to be ready for operations inside the drive.
-     * @param externalEncryptionKeyScript path to the operator provided script
-     * for encryption control.
-     * @param catalogue the CTA catalogue
-     */
-    CleanerSession(
-      cta::server::ProcessCap &capUtils,
-      cta::mediachanger::MediaChangerFacade &mc,
-      cta::log::Logger &log,
-      const cta::tape::daemon::TpconfigLine &driveConfig,
-      System::virtualWrapper &sysWrapper,
-      const std::string &vid,
-      const bool waitMediaInDrive,
-      const uint32_t waitMediaInDriveTimeout,
-      const std::string & externalEncryptionKeyScript,
-      cta::catalogue::Catalogue & catalogue,
-      cta::Scheduler & scheduler);
-
-    /** 
-     * Execute the session and return the type of action to be performed
-     * immediately after the session has completed.
-     *
-     * @return Returns the type of action to be performed after the session has
-     * completed.
-     */
-    EndOfSessionAction execute() throw();
-    
-  private:
-
-    /**
-     * Object providing support for UNIX capabilities.
-     */
-    cta::server::ProcessCap &m_capUtils;
-
-    /**
-     * The object representing the media changer.
-     */
-    cta::mediachanger::MediaChangerFacade &m_mc;
-    
-    /**
-     * The logging object     
-     */
-    cta::log::Logger & m_log;
-    
-    /**
-     * The configuration of the tape drive to be cleaned.
-     */
-    const cta::tape::daemon::TpconfigLine m_driveConfig;
-    
-    /**
-     * The system wrapper used to find the device and instantiate the drive object
-     */
-    System::virtualWrapper & m_sysWrapper;
-
-    /**
-     * The volume identifier of the mounted tape if known, else the empty
-     * string.
-     */
-    const std::string m_vid;
-
-    /**
-     * true if we want to check the presence of the media in the drive before cleaning,
-     * false otherwise.
-     */    
-    const bool m_waitMediaInDrive;
-    
-    /**
-     * The maximum number of seconds to wait for
-     * the media to be ready for operations inside the drive.
-     */
-    const uint32_t m_tapeLoadTimeout;
-
-    /** 
-     * Encryption helper object 
-     */
-    EncryptionControl m_encryptionControl;
-    
-    /**
-     * CTA catalogue
-     */
-    cta::catalogue::Catalogue & m_catalogue;
-    
-    /**
-     * CTA scheduler
-     */
-    cta::Scheduler & m_scheduler;
-    
-    /**
-     * Variable used to log UPDATE_USER_NAME in the DB
-     */
-    const std::string c_defaultUserNameUpdate = "cta-taped";
-
-    /** 
-     * Execute the session and return the type of action to be performed
-     * immediately after the session has completed.
-     *
-     * @return Returns the type of action to be performed after the session has
-     * completed.
-     */
-    EndOfSessionAction exceptionThrowingExecute();
-    
-    /**
-     * Logs and clears (just by reading them...) any outstanding tape alerts
-     *
-     * @param drive The tape drive.
-     */
-    void logAndClearTapeAlerts(drive::DriveInterface &drive) throw();
-    
-    /**
-     * Does the actual steps to clean the drive
-     *
-     * @param drive The tape drive.
-     */
-    void cleanDrive(drive::DriveInterface &drive);
-
-    /**
-     * Sets the capabilities of the process and logs the result.
-     *
-     * @param capabilities The string representation of the capabilities.
-     */
-    void setProcessCapabilities(const std::string &capabilities);
-
-    /**
-     * Creates and returns the object that represents the tape drive to be
-     * cleaned.
-     *
-     * @return The tape drive.
-     */
-    std::unique_ptr<drive::DriveInterface> createDrive();
-
-    /**
-     * Waits for the specified drive to be ready.
-     *
-     * @param drive The tape drive.
-     */
-    void waitUntilMediaIsReady(drive::DriveInterface &drive);
-
-    /**
-     * Rewinds the specified tape drive.
-     *
-     * @param drive The tape drive.
-     */
-    void rewindDrive(drive::DriveInterface &drive);
-
-    /**
-     * Checks the tape in the specified tape drive contains some data where no
-     * data means the tape does not even contain a volume label.
-     *
-     * @param drive The tape drive.
-     */
-    void checkTapeContainsData(drive::DriveInterface &drive);
-
-    /**
-     * Checks that the tape in the specified drive contains a valid volume
-     * label.
-     *
-     * @param drive The tape drive for which it is assumed the tape to be
-     * tested is present and rewound to the beginning.
-     * @return The VSN stored within the colue label.
-     */
-    std::string checkVolumeLabel(drive::DriveInterface &drive);
-
-    /**
-     * Unloads the specified tape from the specified tape drive.
-     *
-     * @param vid The volume identifier of the tape to be unloaded.  Please note
-     * that the value of this field is only used for logging purposes.
-     * @param drive The tape drive.
-     */
-    void unloadTape(const std::string &vid, drive::DriveInterface &drive);
-
-    /**
-     * Dismounts the specified tape.
-     *
-     * @param vid The volume identifier of the tape to be dismounted.
-     */
-    void dismountTape(const std::string &vid);
-    
-    /**
-     * Put the drive down in case the Cleaner has failed
-     */
-    void setDriveDownAfterCleanerFailed(const std::string & errorMsg);
-    
-  }; // class CleanerSession
-
-} // namespace daemon
-} // namespace tapeserver
-} // namespace tape
-} // namespace castor
+    * Put the drive down in case the Cleaner has failed
+    */
+  void setDriveDownAfterCleanerFailed(const std::string & errorMsg);
+};  // class CleanerSession
+
+}  // namespace daemon
+}  // namespace tapeserver
+}  // namespace tape
+}  // namespace castor
diff --git a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt
index 3e42441adf..103df19e08 100644
--- a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt
+++ b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt
@@ -20,6 +20,8 @@ include_directories(/usr/include/shift)
 include_directories(${CMAKE_SOURCE_DIR}/tapeserver)
 include_directories (${XROOTD_INCLUDE_DIR})
 
+find_package(Protobuf3 REQUIRED)
+
 set(TAPESERVER_FILE_LIBRARY_SRCS
   CtaFileReader.cpp
   CtaReadSession.cpp
@@ -69,3 +71,17 @@ set_property(TARGET ctatapeserverfileunittests PROPERTY SOVERSION "${CTA_SOVERSI
 set_property(TARGET ctatapeserverfileunittests PROPERTY   VERSION "${CTA_LIBVERSION}")
 
 install(TARGETS ctatapeserverfileunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
+
+include_directories(${PROTOBUF3_INCLUDE_DIRS})
+add_executable(cta-osmReaderTest
+  OsmReaderTest.cpp)
+
+target_link_libraries(cta-osmReaderTest
+  ctacommon
+  ctaTapeServerDaemon
+  File
+  gtest
+)
+set_property(TARGET cta-osmReaderTest APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
+
+install(TARGETS cta-osmReaderTest DESTINATION usr/bin)
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp
index 0fc6a30f08..95f710c3eb 100644
--- a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp
+++ b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.cpp
@@ -19,8 +19,10 @@
 #include <string>
 
 #include "castor/tape/tapeserver/daemon/VolumeInfo.hpp"
+#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
 #include "castor/tape/tapeserver/file/Exceptions.hpp"
 #include "castor/tape/tapeserver/file/HeaderChecker.hpp"
+#include "castor/tape/tapeserver/file/OsmFileStructure.hpp"
 #include "castor/tape/tapeserver/file/Structures.hpp"
 #include "scheduler/RetrieveJob.hpp"
 
@@ -37,6 +39,15 @@ void HeaderChecker::checkVOL1(const VOL1 &vol1, const std::string &volId)  {
   }
 }
 
+void HeaderChecker::checkOSM(const osm::LABEL &osmLabel, const std::string &volId) {
+  if (osmLabel.name().compare(volId) != 0) {
+    std::stringstream ex_str;
+    ex_str << "[OsmReadSession::OsmReadSession()] - VSN of tape (" << osmLabel.name()
+            << ") is not the one requested (" << volId << ")";
+    throw TapeFormatError(ex_str.str());
+  }
+}
+
 bool HeaderChecker::checkHeaderNumericalField(const std::string &headerField,
   const uint64_t value, const HeaderBase& base)  {
   uint64_t res = 0;
@@ -103,6 +114,56 @@ void HeaderChecker::checkUTL1(const UTL1 &utl1, const uint32_t fSeq)  {
   }
 }
 
+std::string HeaderChecker::checkVolumeLabel(tapeserver::drive::DriveInterface &drive, LabelFormat labelFormat) {
+  std::string volumeLabelVSN;
+  auto vol1Label = [](tapeserver::drive::DriveInterface &drive, const std::string &expectedLblStandard) {
+    tapeFile::VOL1 vol1;
+    drive.readExactBlock(reinterpret_cast<void *>(&vol1), sizeof(vol1),
+      "[HeaderChecker::checkVolumeLabel()] - Reading header VOL1");
+    vol1.verify(expectedLblStandard.c_str());
+    return vol1.getVSN();
+  };
+
+  auto osmLabel = [](tapeserver::drive::DriveInterface &drive) {
+    tapeFile::osm::LABEL osmLabel;
+    drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()),
+    tapeFile::osm::LIMITS::MAXMRECSIZE,
+    "[HeaderChecker::checkVolumeLabel()] - Reading OSM label - part 1");
+    drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + tapeFile::osm::LIMITS::MAXMRECSIZE),
+    tapeFile::osm::LIMITS::MAXMRECSIZE,
+    "[HeaderChecker::checkVolumeLabel()] - Reading OSM label - part 2");
+    osmLabel.decode();
+    return osmLabel.name();
+  };
+
+  try {
+    switch (labelFormat) {
+      case LabelFormat::CTA:
+        volumeLabelVSN = vol1Label(drive, "3");
+        break;
+      case LabelFormat::OSM:
+        volumeLabelVSN = osmLabel(drive);
+        break;
+      case LabelFormat::Enstore:
+        volumeLabelVSN = vol1Label(drive, "0");
+        break;
+      default: {
+        cta::exception::Exception ex;
+        ex.getMessage() << "In HeaderChecker::checkVolumeLabel(): unknown label format: ";
+        ex.getMessage() << std::showbase << std::internal << std::setfill('0') << std::hex << std::setw(4)
+                        << static_cast<unsigned int>(labelFormat);
+        throw ex;
+      }
+    }
+  } catch(cta::exception::Exception &ne) {
+    std::ostringstream ex_str;
+    ex_str << "Failed to check volume label: " << ne.getMessageValue();
+    throw TapeFormatError(ex_str.str());
+  }
+
+  return volumeLabelVSN;
+}
+
 }  // namespace tapeFile
 }  // namespace tape
 }  // namespace castor
diff --git a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp
index 894e594616..81ae2d4b05 100644
--- a/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp
+++ b/tapeserver/castor/tape/tapeserver/file/HeaderChecker.hpp
@@ -19,6 +19,8 @@
 
 #include <string>
 
+#include "common/dataStructures/LabelFormat.hpp"
+
 namespace cta {
 class RetrieveJob;
 }
@@ -30,6 +32,10 @@ namespace tapeserver {
 namespace daemon {
 class VolumeInfo;
 }
+
+namespace drive {
+class DriveInterface;
+}
 }
 
 namespace tapeFile {
@@ -39,6 +45,10 @@ class UHL1;
 class UTL1;
 class VOL1;
 
+namespace osm {
+class LABEL;
+}
+
 class HeaderChecker {
 public:
   /**
@@ -72,6 +82,16 @@ public:
     */
   static void checkVOL1(const VOL1 &vol1, const std::string &volId);
 
+  /**
+    * Checks the osm::LABEL
+    * @param osm: the osm header of the current file
+    * @param volId: the volume id of the tape in the drive
+    */
+  static void checkOSM(const osm::LABEL &osmLabel, const std::string &volId);
+
+  using LabelFormat = cta::common::dataStructures::Label::Format;
+  static std::string checkVolumeLabel(tapeserver::drive::DriveInterface &drive, LabelFormat labelFormat);
+
 private:
   enum class HeaderBase {
     octal,
diff --git a/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp b/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp
index 54675a14f5..9c5cefa790 100644
--- a/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp
+++ b/tapeserver/castor/tape/tapeserver/file/OsmFileReader.cpp
@@ -21,7 +21,6 @@
 #include <string>
 
 #include "castor/tape/tapeserver/drive/DriveInterface.hpp"
-#include "castor/tape/tapeserver/file/HeaderChecker.hpp"
 #include "castor/tape/tapeserver/file/OsmFileReader.hpp"
 #include "castor/tape/tapeserver/file/OsmReadSession.hpp"
 #include "castor/tape/tapeserver/file/Structures.hpp"
@@ -80,9 +79,9 @@ void OsmFileReader::positionByBlockID(const cta::RetrieveJob &fileToRecall) {
 }
 
 void OsmFileReader::moveToFirstFile() {
-  // special case: we can rewind the tape to be faster 
-  //(TODO: in the future we could also think of a threshold above 
-  //which we rewind the tape anyway and then space forward)       
+  // special case: we can rewind the tape to be faster
+  // (TODO: in the future we could also think of a threshold above
+  // which we rewind the tape anyway and then space forward)
   m_session->m_drive.rewind();
   osm::LABEL osmLabel;
   m_session->m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), osm::LIMITS::MAXMRECSIZE, "[FileReader::position] - Reading OSM label - part 1");
@@ -145,22 +144,24 @@ size_t OsmFileReader::readNextDataBlock(void *data, const size_t size) {
    * Cpio filter for OSM file
    * - caclulate the file size and position of the trailer
    */
-  if(size < CPIO::MAXHEADERSIZE) {
+  if (size < CPIO::MAXHEADERSIZE) {
     std::ostringstream ex_str;
-    ex_str << "Invalid block size: " << size << " - " << "the block size is smaller then max size of a CPIO header: " << CPIO::MAXHEADERSIZE;
+    ex_str << "Invalid block size: " << size << " - "
+           << "the block size is smaller then max size of a CPIO header: "
+           << CPIO::MAXHEADERSIZE;
     throw TapeFormatError(ex_str.str());
   }
-  if(!m_cpioHeader.valid()) {
+  if (!m_cpioHeader.valid()) {
     size_t uiHeaderSize = 0;
     size_t uiResiduesSize = 0;
     uint8_t* pucTmpData = new uint8_t[size];
-    
+
     bytes_read = m_session->m_drive.readBlock(pucTmpData, size);
     uiHeaderSize = m_cpioHeader.decode(pucTmpData, size);
     uiResiduesSize = bytes_read - uiHeaderSize;
-    
+
     // Copy the rest of data to the buffer
-    if(uiResiduesSize >= m_cpioHeader.m_ui64FileSize) {
+    if (uiResiduesSize >= m_cpioHeader.m_ui64FileSize) {
       bytes_read = m_cpioHeader.m_ui64FileSize;
       m_ui64CPIODataSize = bytes_read;
       memcpy(data, pucTmpData + uiHeaderSize, bytes_read);
@@ -170,26 +171,25 @@ size_t OsmFileReader::readNextDataBlock(void *data, const size_t size) {
       m_ui64CPIODataSize = bytes_read >= m_cpioHeader.m_ui64FileSize ? m_cpioHeader.m_ui64FileSize : bytes_read;
     }
     delete[] pucTmpData;
-    
   } else {
     bytes_read = m_session->m_drive.readBlock(data, size);
     m_ui64CPIODataSize += bytes_read;
-    if(m_ui64CPIODataSize > m_cpioHeader.m_ui64FileSize && bytes_read > 0) {
+    if (m_ui64CPIODataSize > m_cpioHeader.m_ui64FileSize && bytes_read > 0) {
       // File is ready
       // size_t uiOldSize = bytes_read;
       bytes_read = bytes_read - (m_ui64CPIODataSize - m_cpioHeader.m_ui64FileSize);
       // m_ui64CPIODataSize += bytes_read - uiOldSize;
     }
   }
-  
+
   // end of file reached! keep reading until the header of the next file
-  if(!bytes_read) {
-    m_session->setCurrentFseq(m_session->getCurrentFseq() + 1); // moving on to the header of the next file 
+  if (!bytes_read) {
+    m_session->setCurrentFseq(m_session->getCurrentFseq() + 1); // moving on to the header of the next file
     m_session->setCurrentFilePart(PartOfFile::Header);
     // the following is a normal day exception: end of files exceptions are thrown at the end of each file being read
     throw EndOfFile();
   }
-  
+
   return bytes_read;
 }
 
diff --git a/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp b/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp
index cde57f3bbe..3d4bda8157 100644
--- a/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp
+++ b/tapeserver/castor/tape/tapeserver/file/OsmFileStructure.hpp
@@ -16,12 +16,13 @@
  */
 
 #pragma once
- 
+
+#include <string>
+
 #include "tapeserver/castor/tape/tapeserver/SCSI/Structures.hpp"
 #include "common/Constants.hpp"
 
-#include <string>
- 
+
 namespace castor {
 namespace tape {
 /**
@@ -38,10 +39,10 @@ namespace osm {
 struct LIMITS {
 public:
   static const uint64_t MVOLMAGIC = 0x070460;
-  static const size_t LABELVERSIONLEN = 9; // label version string
-  static const size_t VOLNAMELEN = 16;     // volume name
-  static const size_t CIDLEN = 33;         // client id length
-  static const size_t MAXMRECSIZE = 32768; // half of label block
+  static const size_t LABELVERSIONLEN = 9;  // label version string
+  static const size_t VOLNAMELEN = 16;      // volume name
+  static const size_t CIDLEN = 33;          // client id length
+  static const size_t MAXMRECSIZE = 32768;  // half of label block
   static const size_t MMAXCHK = 2048;
 
 // prevent the generation of default public constructor and destructor
@@ -63,8 +64,8 @@ public:
   inline char* rawLabel() {
     return m_tcRawLabel;
   }
-  
-  void decode(); // can throw
+
+  void decode();  // can throw
 
   inline std::string owner() {
     return std::string(m_tcOwner);
@@ -72,8 +73,8 @@ public:
   inline std::string version() {
     return std::string(m_tcVersion);
   }
-  inline std::string name() {
-    // Returns truncated name of the label 
+  inline std::string name() const {
+    // Returns truncated name of the label
     return std::string(m_tcName, cta::CA_MAXVIDLEN);
   }
   inline uint64_t createTime() {
@@ -96,7 +97,6 @@ public:
     // SCSI::logicBlockProtectionMethod::DoNotUse
     // SCSI::logicBlockProtectionMethod::ReedSolomon
     // SCSI::logicBlockProtectionMethod::CRC32C
-    
     return SCSI::logicBlockProtectionMethod::CRC32C;
   }
 
@@ -106,11 +106,11 @@ protected:
   char m_tcName[LIMITS::VOLNAMELEN];
   char m_tcRawLabel[2 * LIMITS::MAXMRECSIZE];
   uint64_t m_ulCreateTime;
-  uint64_t m_ulExpireTime; 
+  uint64_t m_ulExpireTime;
   uint64_t m_ulRecSize;
   uint64_t m_ulVolId;
 };
-  
+
 } /* namespace osm */
 } /* namespace tapeFile */
 } /* namespace tape */
diff --git a/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp b/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp
index 237a3e3aab..fb9f8cd86c 100644
--- a/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp
+++ b/tapeserver/castor/tape/tapeserver/file/OsmReadSession.cpp
@@ -19,9 +19,10 @@
 #include <string>
 
 #include "castor/tape/tapeserver/file/Exceptions.hpp"
+#include "castor/tape/tapeserver/file/HeaderChecker.hpp"
+#include "castor/tape/tapeserver/file/OsmFileStructure.hpp"
 #include "castor/tape/tapeserver/file/OsmReadSession.hpp"
 #include "castor/tape/tapeserver/file/Structures.hpp"
-#include "castor/tape/tapeserver/file/OsmFileStructure.hpp"
 
 namespace castor {
 namespace tape {
@@ -30,24 +31,18 @@ namespace tapeFile {
 OsmReadSession::OsmReadSession(tapeserver::drive::DriveInterface &drive,
   const tapeserver::daemon::VolumeInfo &volInfo, const bool useLbp)
   : ReadSession(drive, volInfo, useLbp) {
-    
-  if (!m_vid.compare("")) {
-    throw cta::exception::InvalidArgument();
-  }
-  if (m_drive.isTapeBlank()) {
-    cta::exception::Exception ex;
-    ex.getMessage() << "[OsmReadSession::OsmReadSession()] - Tape is blank, cannot proceed with constructing the ReadSession";
-    throw ex;
-  }
-
   m_drive.rewind();
   m_drive.disableLogicalBlockProtection();
 
   uint8_t uiLLBPMethod = SCSI::logicBlockProtectionMethod::DoNotUse;
   osm::LABEL osmLabel;
 
-  m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1");
-  m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2");
+  m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()),
+    osm::LIMITS::MAXMRECSIZE,
+    "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1");
+  m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE),
+    osm::LIMITS::MAXMRECSIZE,
+    "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2");
 
   try {
     osmLabel.decode();
@@ -77,18 +72,19 @@ OsmReadSession::OsmReadSession(tapeserver::drive::DriveInterface &drive,
   }
   // from this point the right LBP mode should be set or not set
   m_drive.rewind();
-  m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1");
-  m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE), osm::LIMITS::MAXMRECSIZE, "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2");
-
-  try {
-    osmLabel.decode();
-    if (osmLabel.name().compare(m_vid) != 0) {
-      std::stringstream ex_str;
-      ex_str<<"[OsmReadSession::OsmReadSession()] - VSN of tape ("<<osmLabel.name()<<") is not the one requested ("<<m_vid<<")";
-      throw TapeFormatError(ex_str.str());
+  {
+    m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel()),
+      osm::LIMITS::MAXMRECSIZE,
+      "[OsmReadSession::OsmReadSession] - Reading OSM label - part 1");
+    m_drive.readExactBlock(reinterpret_cast<void*>(osmLabel.rawLabel() + osm::LIMITS::MAXMRECSIZE),
+      osm::LIMITS::MAXMRECSIZE,
+      "[OsmReadSession::OsmReadSession] - Reading OSM label - part 2");
+    try {
+      osmLabel.decode();
+    } catch (const std::exception& e) {
+      throw TapeFormatError(e.what());
     }
-  } catch (const std::exception& osmExc) {
-    throw TapeFormatError(osmExc.what());
+    HeaderChecker::checkOSM(osmLabel, volInfo.vid);
   }
 }
 
diff --git a/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp b/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp
new file mode 100644
index 0000000000..6b456b9c2b
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp
@@ -0,0 +1,196 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "castor/tape/tapeserver/daemon/CleanerSession.hpp"
+#include "castor/tape/tapeserver/daemon/VolumeInfo.hpp"
+#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
+#include "castor/tape/tapeserver/file/Exceptions.hpp"
+#include "castor/tape/tapeserver/file/FileReader.hpp"
+#include "castor/tape/tapeserver/file/FileReaderFactory.hpp"
+#include "castor/tape/tapeserver/file/ReadSession.hpp"
+#include "castor/tape/tapeserver/file/ReadSessionFactory.hpp"
+#include "castor/tape/tapeserver/SCSI/Device.hpp"
+#include "castor/tape/tapeserver/system/Wrapper.hpp"
+#include "scheduler/RetrieveJob.hpp"
+#include "catalogue/DummyCatalogue.hpp"
+#include "common/processCap/ProcessCapDummy.hpp"
+#include "common/log/StdoutLogger.hpp"
+#include "common/log/StringLogger.hpp"
+#include "scheduler/OStoreDB/OStoreDBFactory.hpp"
+
+namespace {
+std::string g_device_name;
+std::string g_device_path;
+}
+
+class OSMCatalogue: public cta::catalogue::DummyCatalogue {
+public:
+  using LabelFormat = cta::common::dataStructures::Label::Format;
+  OSMCatalogue() = default;
+
+  LabelFormat getTapeLabelFormat(const std::string& vid) const override {
+    return LabelFormat::OSM;
+  }
+};
+
+class BasicRetrieveJob: public cta::RetrieveJob {
+public:
+  BasicRetrieveJob() : cta::RetrieveJob(nullptr,
+  cta::common::dataStructures::RetrieveRequest(),
+  cta::common::dataStructures::ArchiveFile(), 1,
+  cta::PositioningMethod::ByBlock) {}
+};
+
+class OsmReaderTest : public ::testing::Test {
+protected:
+  OsmReaderTest() : m_sWrapper(), m_dev(), m_catalogue(std::make_unique<OSMCatalogue>()) {
+    cta::OStoreDBFactory<cta::objectstore::BackendVFS> factory;
+  }
+
+  void SetUp() override {
+    m_vid = "L08033";
+    m_nstDev = g_device_path;
+    m_devName = g_device_name;
+  }
+
+  void TearDown() override {
+    m_db.reset();
+    m_catalogue.reset();
+  }
+
+  void createDrive() {
+    // Create drive object and open tape device
+    m_dev.product = "MHVTL";
+    m_dev.nst_dev = m_nstDev;
+    m_drive.reset(castor::tape::tapeserver::drive::createDrive(m_dev, m_sWrapper));
+
+    try {
+      /**
+        * Gets generic device info for the drive object.
+        */
+      castor::tape::tapeserver::drive::deviceInfo devInfo;
+      devInfo = m_drive->getDeviceInfo();
+      auto removeWhiteSpaces = [](std::string *str) -> std::string {
+        str->erase(std::remove_if(str->begin(), str->end(), [](uint8_t x){return std::isspace(x);}), str->end());
+        return *str;
+      };
+      ASSERT_EQ("STK", removeWhiteSpaces(&devInfo.vendor));
+      ASSERT_EQ("MHVTL", removeWhiteSpaces(&devInfo.product));
+      ASSERT_EQ("0105", devInfo.productRevisionLevel);
+      ASSERT_EQ(m_devName, removeWhiteSpaces(&devInfo.serialNumber));
+    } catch (cta::exception::Exception & ex) {
+      FAIL() << "The drive couldn't be created. " << ex.getMessageValue();
+    }
+
+    try {
+      /**
+        * Checks if the drive ready to use the tape installed loaded into it.
+        */
+      m_drive->waitUntilReady(5);
+    } catch(cta::exception::Exception &ex) {
+      FAIL() << "The drive is not ready to use. " << ex.getMessageValue();
+    }
+
+    m_drive->rewind();
+  }
+
+  castor::tape::System::realWrapper m_sWrapper;
+  castor::tape::SCSI::DeviceInfo m_dev;
+  std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface> m_drive;
+  std::unique_ptr<cta::SchedulerDatabase> m_db;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+  std::string m_vid;
+  std::string m_nstDev;
+  std::string m_devName;
+};
+
+TEST_F(OsmReaderTest, ReadOsmTape) {
+  createDrive();
+  try {
+    castor::tape::tapeserver::daemon::VolumeInfo m_volInfo;
+    m_volInfo.vid = m_vid;
+    m_volInfo.nbFiles = 1;
+    m_volInfo.labelFormat = m_catalogue->getTapeLabelFormat(m_volInfo.vid);
+    m_volInfo.mountType = cta::common::dataStructures::MountType::Retrieve;
+
+    // Now read a random file
+    // Create Read Session OSM
+    auto readSession = castor::tape::tapeFile::ReadSessionFactory::create(*m_drive, m_volInfo, false);
+    BasicRetrieveJob fileToRecall;
+    fileToRecall.selectedCopyNb = 0;
+    fileToRecall.archiveFile.tapeFiles.push_back(cta::common::dataStructures::TapeFile());
+    fileToRecall.selectedTapeFile().blockId = 1;  // here should be the block ID of HDR1
+    fileToRecall.selectedTapeFile().fSeq = 1;
+    fileToRecall.retrieveRequest.archiveFileID = 1;
+    fileToRecall.positioningMethod = cta::PositioningMethod::ByBlock;
+
+    // Create Read File OSM
+    auto reader = castor::tape::tapeFile::FileReaderFactory::create(readSession, fileToRecall);
+    size_t bs = reader->getBlockSize();
+    char *data = new char[bs+1];
+    size_t j = 0;
+    while (j < 100) {
+        reader->readNextDataBlock(data, bs);
+        j++;
+    }
+  } catch(cta::exception::Exception &ex) {
+    FAIL() << "Problem to read the OSM Tape. " << ex.getMessageValue();
+  }
+}
+
+TEST_F(OsmReaderTest, CleanDrive) {
+  cta::log::DummyLogger dummylogger("dummy", "unitTest");
+  cta::log::StringLogger strlogger("string", "unitTest", cta::log::DEBUG);
+  cta::log::StdoutLogger stdoutlogger("stdout", "unitTest");
+  cta::server::ProcessCapDummy capUtils;
+  cta::mediachanger::MediaChangerFacade mc(dummylogger);
+  cta::tape::daemon::TpconfigLine driveConfig(m_devName, "TestLogicalLibrary", m_nstDev, "dummy");
+
+  auto scheduler = std::make_unique<cta::Scheduler>(*m_catalogue, *m_db, 5, 2 * 1000 * 1000);
+
+  castor::tape::tapeserver::daemon::CleanerSession cleanerSession(
+    capUtils,
+    mc,
+    strlogger,
+    driveConfig,
+    m_sWrapper,
+    m_vid,
+    false,
+    0,
+    "",
+    *m_catalogue,
+    *scheduler);
+
+    cleanerSession.execute();
+
+  const auto logToCheck = strlogger.getLog();
+  ASSERT_NE(std::string::npos, logToCheck.find("Cleaner successfully detected tape contains data"));
+  ASSERT_NE(std::string::npos, logToCheck.find("Cleaner detected volume label contains expected VSN"));
+  ASSERT_NE(std::string::npos, logToCheck.find("Cleaner unloaded tape"));
+  ASSERT_NE(std::string::npos, logToCheck.find("Cleaner dismounted tape"));
+}
+
+int main(int argc, char **argv) {
+  g_device_name = argv[1];
+  g_device_path = argv[2];
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
\ No newline at end of file
-- 
GitLab