Whamcloud - gitweb
LU-3811 hsm: handle file ownership and timestamps 61/7461/5
authorJohn L. Hammond <john.hammond@intel.com>
Thu, 29 Aug 2013 19:41:49 +0000 (14:41 -0500)
committerOleg Drokin <oleg.drokin@intel.com>
Wed, 4 Sep 2013 04:39:07 +0000 (04:39 +0000)
In hsm_init_ucred() set the capability mask to CFS_CAP_FS_MASK thereby
allowing the coordinator to swap layouts of files not owned by
root. In mdt_orphan_open() raise CFS_CAP_DAC_OVERRIDE before creating
the orphan filee in the MDT's local root. In mdt_hsm_release() set
inode size and timestamps before getting the LOV xattr and ensure that
the orphan file is created with the same UID and GID of the original.
In lhsmtool_posix.c similalrly ensure that the volatile file used for
restore has the same ownership, access and modification timestamps as
the original.

Strengthen sanity-hsm test 24 to check that none of archive, release,
or restore will change the access or modified timestamps. Add 24b to
check that root can archive, release, and restore an ordinary user's
files and that the user will be able to read a released file even if
the parent directory is unwritable.

Signed-off-by: John L. Hammond <john.hammond@intel.com>
Change-Id: Id08429cbfed1bbab719f52ed101e61d5e629ccd2
Reviewed-on: http://review.whamcloud.com/7461
Reviewed-by: Aurelien Degremont <aurelien.degremont@cea.fr>
Tested-by: Hudson
Tested-by: Maloo <whamcloud.maloo@gmail.com>
Reviewed-by: jacques-Charles Lafoucriere <jacques-charles.lafoucriere@cea.fr>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/include/lustre/lustreapi.h
lustre/mdt/mdt_coordinator.c
lustre/mdt/mdt_open.c
lustre/tests/sanity-hsm.sh
lustre/utils/lhsmtool_posix.c
lustre/utils/liblustreapi_hsm.c

index 98cbf80..3212bfe 100644 (file)
@@ -317,8 +317,8 @@ extern int llapi_hsm_action_begin(struct hsm_copyaction_private **phcp,
                                  int restore_mdt_index, int restore_open_flags,
                                  bool is_error);
 extern int llapi_hsm_action_end(struct hsm_copyaction_private **phcp,
-                               const struct hsm_extent *he, int flags,
-                               int errval);
+                               const struct hsm_extent *he,
+                               int hp_flags, int errval);
 extern int llapi_hsm_action_progress(struct hsm_copyaction_private *hcp,
                                     const struct hsm_extent *he, int hp_flags);
 extern int llapi_hsm_action_get_dfid(const struct hsm_copyaction_private *hcp,
index deb26db..95437e2 100644 (file)
@@ -796,7 +796,7 @@ static int hsm_init_ucred(struct lu_ucred *uc)
        uc->uc_fsgid = 0;
        uc->uc_suppgids[0] = -1;
        uc->uc_suppgids[1] = -1;
-       uc->uc_cap = 0;
+       uc->uc_cap = CFS_CAP_FS_MASK;
        uc->uc_umask = 0777;
        uc->uc_ginfo = NULL;
        uc->uc_identity = NULL;
index 0d4b3d3..5c42b38 100644 (file)
@@ -1377,7 +1377,7 @@ static void mdt_object_open_unlock(struct mdt_thread_info *info,
 /**
  * Check release is permitted for the current HSM flags.
  */
-static bool mdt_hsm_release_allow(struct md_attr *ma)
+static bool mdt_hsm_release_allow(const struct md_attr *ma)
 {
        if (!(ma->ma_valid & MA_HSM))
                return false;
@@ -1887,19 +1887,21 @@ static struct mdt_object *mdt_orphan_open(struct mdt_thread_info *info,
 {
        const struct lu_env *env = info->mti_env;
        struct md_op_spec *spec = &info->mti_spec;
-       struct lu_fid *rootfid = &info->mti_tmp_fid1;
+       struct lu_fid *local_root_fid = &info->mti_tmp_fid1;
        struct mdt_object *obj = NULL;
        struct mdt_object *local_root;
        static const char name[] = "i_am_nobody";
        struct lu_name *lname;
+       struct lu_ucred *uc;
+       cfs_cap_t uc_cap_save;
        int rc;
        ENTRY;
 
-       rc = dt_root_get(env, mdt->mdt_bottom, rootfid);
+       rc = dt_root_get(env, mdt->mdt_bottom, local_root_fid);
        if (rc != 0)
                RETURN(ERR_PTR(rc));
 
-       local_root = mdt_object_find(env, mdt, rootfid);
+       local_root = mdt_object_find(env, mdt, local_root_fid);
        if (IS_ERR(local_root))
                RETURN(local_root);
 
@@ -1920,17 +1922,26 @@ static struct mdt_object *mdt_orphan_open(struct mdt_thread_info *info,
        }
 
        lname = mdt_name(env, (char *)name, sizeof(name) - 1);
+
+       uc = lu_ucred(env);
+       uc_cap_save = uc->uc_cap;
+       uc->uc_cap |= 1 << CFS_CAP_DAC_OVERRIDE;
        rc = mdo_create(env, mdt_object_child(local_root), lname,
                        mdt_object_child(obj), spec, attr);
-       if (rc == 0) {
-               rc = mo_open(env, mdt_object_child(obj), MDS_OPEN_CREATED);
-               if (rc < 0)
-                       CERROR("%s: cannot open volatile file "DFID", orphan "
-                              "file will be left in PENDING directory until "
-                              "next reboot, rc = %d\n", mdt_obd_name(mdt),
-                              PFID(fid), rc);
+       uc->uc_cap = uc_cap_save;
+       if (rc < 0) {
+               CERROR("%s: cannot create volatile file "DFID": rc = %d\n",
+                      mdt_obd_name(mdt), PFID(fid), rc);
+               GOTO(out, rc);
        }
-       EXIT;
+
+       rc = mo_open(env, mdt_object_child(obj), MDS_OPEN_CREATED);
+       if (rc < 0)
+               CERROR("%s: cannot open volatile file "DFID", orphan "
+                      "file will be left in PENDING directory until "
+                      "next reboot, rc = %d\n", mdt_obd_name(mdt),
+                      PFID(fid), rc);
+       GOTO(out, rc);
 
 out:
        if (rc < 0) {
@@ -1993,7 +2004,7 @@ static int mdt_hsm_release(struct mdt_thread_info *info, struct mdt_object *o,
 
        /* ma_need was set before but it seems fine to change it in order to
         * avoid modifying the one from RPC */
-       ma->ma_need = MA_HSM | MA_LOV;
+       ma->ma_need = MA_HSM;
        rc = mdt_attr_get_complex(info, o, ma);
        if (rc != 0)
                GOTO(out_unlock, rc);
@@ -2014,18 +2025,23 @@ static int mdt_hsm_release(struct mdt_thread_info *info, struct mdt_object *o,
        }
 
        ma->ma_valid = MA_INODE;
-       ma->ma_attr.la_valid &= LA_SIZE | LA_MTIME | LA_ATIME;
+       ma->ma_attr.la_valid &= LA_ATIME | LA_MTIME | LA_CTIME | LA_SIZE;
        rc = mo_attr_set(info->mti_env, mdt_object_child(o), ma);
        if (rc < 0)
                GOTO(out_unlock, rc);
 
+       ma->ma_need = MA_INODE | MA_LOV;
+       rc = mdt_attr_get_complex(info, o, ma);
+       if (rc < 0)
+               GOTO(out_unlock, rc);
+
        if (!(ma->ma_valid & MA_LOV)) {
                /* Even empty file are released */
                memset(ma->ma_lmm, 0, sizeof(*ma->ma_lmm));
                ma->ma_lmm->lmm_magic = cpu_to_le32(LOV_MAGIC_V1_DEF);
                ma->ma_lmm->lmm_pattern = cpu_to_le32(LOV_PATTERN_RAID0);
                ma->ma_lmm->lmm_stripe_size = cpu_to_le32(LOV_MIN_STRIPE_SIZE);
-               ma->ma_valid |= MA_LOV;
+               ma->ma_lmm_size = sizeof(*ma->ma_lmm);
        } else {
                /* Magic must be LOV_MAGIC_Vx_DEF otherwise LOD will interpret
                 * ma_lmm as lov_user_md, then it will be confused by union of
@@ -2043,11 +2059,13 @@ static int mdt_hsm_release(struct mdt_thread_info *info, struct mdt_object *o,
 
        /* Hopefully it's not used in this call path */
        orp_ma = &info->mti_u.som.attr;
-       orp_ma->ma_valid = MA_INODE | MA_LOV;
-       orp_ma->ma_attr.la_mode = S_IFREG;
-       orp_ma->ma_attr.la_valid = LA_MODE;
+       orp_ma->ma_attr.la_mode = S_IFREG | S_IWUSR;
+       orp_ma->ma_attr.la_uid = ma->ma_attr.la_uid;
+       orp_ma->ma_attr.la_gid = ma->ma_attr.la_gid;
+       orp_ma->ma_attr.la_valid = LA_MODE | LA_UID | LA_GID;
        orp_ma->ma_lmm = ma->ma_lmm;
        orp_ma->ma_lmm_size = ma->ma_lmm_size;
+       orp_ma->ma_valid = MA_INODE | MA_LOV;
        orphan = mdt_orphan_open(info, info->mti_mdt, &data->cd_fid, orp_ma,
                                 FMODE_WRITE);
        if (IS_ERR(orphan)) {
@@ -2106,6 +2124,7 @@ out_unlock:
 
        ma->ma_valid = 0;
        ma->ma_need = 0;
+
        return rc;
 }
 
index c83e44e..1df1aed 100644 (file)
@@ -14,7 +14,8 @@ ONLY=${ONLY:-"$*"}
 # bug number for skipped test:
 # UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
 # skip test cases failed before landing - Jinshan
-ALWAYS_EXCEPT="$SANITY_HSM_EXCEPT 12a 12b 12n 13 24 30a 31a 34 35 36 58 59"
+
+ALWAYS_EXCEPT="$SANITY_HSM_EXCEPT 12a 12b 12n 13 30a 31a 34 35 36 58 59"
 ALWAYS_EXCEPT="$ALWAYS_EXCEPT 110a 200 201 221 222a 223a 223b 225"
 
 LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)}
@@ -1317,40 +1318,161 @@ test_23() {
 }
 run_test 23 "Release does not change a/mtime (utime)"
 
-test_24() {
+test_24a() {
+       local file=$DIR/$tdir/$tfile
+       local fid
+       local atime0
+       local atime1
+       local mtime0
+       local mtime1
+       local ctime0
+       local ctime1
+
        # test needs a running copytool
        copytool_setup
 
        mkdir -p $DIR/$tdir
-
-       local f=$DIR/$tdir/test_mtime
+       rm -f $file
+       fid=$(make_small $file)
 
        # Create a file and check its states
-       local fid=$(make_small $f)
-       check_hsm_flags $f "0x00000000"
+       check_hsm_flags $file "0x00000000"
 
-       # make mtime is different
+       # Ensure atime is less than mtime and ctime.
        sleep 1
-       echo "append" >> $f
-       local MTIME=$(stat -c "%Y" $f)
-       local ATIME=$(stat -c "%X" $f)
+       echo >> $file
 
-       $LFS hsm_archive $f || error "could not archive file"
+       atime0=$(stat -c "%X" $file)
+       mtime0=$(stat -c "%Y" $file)
+       ctime0=$(stat -c "%Z" $file)
+
+       [ $atime0 -lt $mtime0 ] ||
+               error "atime $atime0 is not less than mtime $mtime0"
+
+       [ $atime0 -lt $ctime0 ] ||
+               error "atime $atime0 is not less than ctime $ctime0"
+
+       # Archive should not change any timestamps.
+       $LFS hsm_archive $file || error "cannot archive '$file'"
        wait_request_state $fid ARCHIVE SUCCEED
 
-       # Release and check states
-       $LFS hsm_release $f || error "could not release file"
-       check_hsm_flags $f "0x0000000d"
+       atime1=$(stat -c "%X" $file)
+       mtime1=$(stat -c "%Y" $file)
+       ctime1=$(stat -c "%Z" $file)
+
+       [ $atime0 -eq $atime1 ] ||
+               error "archive changed atime from $atime0 to $atime1"
+
+       [ $mtime0 -eq $mtime1 ] ||
+               error "archive changed mtime from $mtime0 to $mtime1"
+
+       [ $ctime0 -eq $ctime1 ] ||
+               error "archive changed ctime from $ctime0 to $ctime1"
+
+       # Release should not change any timestamps.
+       $LFS hsm_release $file || error "cannot release '$file'"
+       check_hsm_flags $file "0x0000000d"
+
+       atime1=$(stat -c "%X" $file)
+       mtime1=$(stat -c "%Y" $file)
+       ctime1=$(stat -c "%Z" $file)
+
+       [ $atime0 -eq $atime1 ] ||
+               error "release changed atime from $atime0 to $atime1"
+
+       [ $mtime0 -eq $mtime1 ] ||
+               error "release changed mtime from $mtime0 to $mtime1"
+
+       [ $ctime0 -eq $ctime1 ] ||
+               error "release changed ctime from $ctime0 to $ctime1"
+
+       # Restore should not change atime or mtime and should not
+       # decrease ctime.
+       $LFS hsm_restore $file
+       wait_request_state $fid RESTORE SUCCEED
+
+       atime1=$(stat -c "%X" $file)
+       mtime1=$(stat -c "%Y" $file)
+       ctime1=$(stat -c "%Z" $file)
+
+       [ $atime0 -eq $atime1 ] ||
+               error "restore changed atime from $atime0 to $atime1"
+
+       [ $mtime0 -eq $mtime1 ] ||
+               error "restore changed mtime from $mtime0 to $mtime1"
+
+       [ $ctime0 -le $ctime1 ] ||
+               error "restore changed ctime from $ctime0 to $ctime1"
+
+       copytool_cleanup
+
+       # Once more, after unmount and mount.
+       umount_client $MOUNT || error "cannot unmount '$MOUNT'"
+       mount_client $MOUNT || error "cannot mount '$MOUNT'"
+
+       atime1=$(stat -c "%X" $file)
+       mtime1=$(stat -c "%Y" $file)
+       ctime1=$(stat -c "%Z" $file)
+
+       [ $atime0 -eq $atime1 ] ||
+               error "remount changed atime from $atime0 to $atime1"
+
+       [ $mtime0 -eq $mtime1 ] ||
+               error "remount changed mtime from $mtime0 to $mtime1"
+
+       [ $ctime0 -le $ctime1 ] ||
+               error "remount changed ctime from $ctime0 to $ctime1"
+}
+run_test 24a "Archive, release, and restore does not change a/mtime (i/o)"
+
+test_24b() {
+       local file=$DIR/$tdir/$tfile
+       local fid
+       local sum0
+       local sum1
+       # LU-3811
+
+       # Test needs a running copytool.
+       copytool_setup
+       mkdir -p $DIR/$tdir
+
+       # Check that root can do HSM actions on a ordinary user's file.
+       rm -f $file
+       fid=$(make_small $file)
+       sum0=$(md5sum $file)
+
+       chown $RUNAS_ID:$RUNAS_GID $file ||
+               error "cannot chown '$file' to '$RUNAS_ID'"
+
+       chmod ugo-w $DIR/$tdir ||
+               error "cannot chmod '$DIR/$tdir'"
+
+       $LFS hsm_archive $file
+       wait_request_state $fid ARCHIVE SUCCEED
+
+       $LFS hsm_release $file ||
+               check_hsm_flags $file "0x0000000d"
+
+       $LFS hsm_restore $file
+       wait_request_state $fid RESTORE SUCCEED
+
+       # Check that ordinary user can get HSM state.
+       $RUNAS $LFS hsm_state $file ||
+               error "user '$RUNAS_ID' cannot get HSM state of '$file'"
+
+       $LFS hsm_release $file ||
+               check_hsm_flags $file "0x0000000d"
 
-       [ "$(stat -c "%Y" $f)" -eq "$MTIME" ] ||
-               error "mtime should be $MTIME"
+       # Check that ordinary user can accessed released file.
+       sum1=$($RUNAS md5sum $file) ||
+               error "user '$RUNAS_ID' cannot read '$file'"
 
-       [ "$(stat -c "%X" $f)" -eq "$ATIME" ] ||
-               error "atime should be $ATIME"
+       [ "$sum0" == "$sum1" ] ||
+               error "md5sum mismatch for '$file'"
 
        copytool_cleanup
 }
-run_test 24 "Release does not change a/mtime (i/o)"
+run_test 24b "root can archive, release, and restore user files"
 
 test_25a() {
        # test needs a running copytool
index baf1fac..fe57467 100644 (file)
@@ -92,9 +92,11 @@ struct options opt = {
        .o_chunk_size = ONE_MB,
 };
 
-/* The LLAPI will hold an open FD on lustre for us. Additionally open one on
- * the archive FS root to make sure it doesn't drop out from under us (and
- * remind the admin to shutdown the copytool before unmounting). */
+/* hsm_copytool_private will hold an open FD on the lustre mount point
+ * for us. Additionally open one on the archive FS root to make sure
+ * it doesn't drop out from under us (and remind the admin to shutdown
+ * the copytool before unmounting). */
+
 static int arc_fd = -1;
 
 static int err_major;
@@ -680,16 +682,6 @@ out:
                }
        }
 
-       if (rc == 0) {
-               rc = fsync(dst_fd);
-               if (rc < 0) {
-                       rc = -errno;
-                       CT_ERROR("'%s' fsync failed (%s)\n", dst,
-                                strerror(-rc));
-                       err_major++;
-               }
-       }
-
        free(buf);
 
        return rc;
@@ -814,18 +806,18 @@ static int ct_begin(struct hsm_copyaction_private **phcp,
 }
 
 static int ct_fini(struct hsm_copyaction_private **phcp,
-                  const struct hsm_action_item *hai, int flags, int ct_rc)
+                  const struct hsm_action_item *hai, int hp_flags, int ct_rc)
 {
        char    lstr[PATH_MAX];
        int     rc;
 
        CT_TRACE("Action completed, notifying coordinator "
-                "cookie="LPX64", FID="DFID", flags=%d err=%d\n",
+                "cookie="LPX64", FID="DFID", hp_flags=%d err=%d\n",
                 hai->hai_cookie, PFID(&hai->hai_fid),
-                flags, -ct_rc);
+                hp_flags, -ct_rc);
 
        ct_path_lustre(lstr, sizeof(lstr), opt.o_mnt, &hai->hai_fid);
-       rc = llapi_hsm_action_end(phcp, &hai->hai_extent, flags, abs(ct_rc));
+       rc = llapi_hsm_action_end(phcp, &hai->hai_extent, hp_flags, abs(ct_rc));
        if (rc == -ECANCELED)
                CT_ERROR("'%s' completed action has been canceled: "
                         "cookie="LPX64", FID="DFID"\n", lstr, hai->hai_cookie,
@@ -846,7 +838,7 @@ static int ct_archive(const struct hsm_action_item *hai, const long hal_flags)
        int                              rc;
        int                              rcf = 0;
        bool                             rename_needed = false;
-       int                              ct_flags = 0;
+       int                              hp_flags = 0;
        int                              open_flags;
        int                              src_fd = -1;
        int                              dst_fd = -1;
@@ -870,7 +862,7 @@ static int ct_archive(const struct hsm_action_item *hai, const long hal_flags)
                rename_needed = true;
        }
 
-       CT_TRACE("'%s' archived to %s\n", src, dst);
+       CT_TRACE("'%s' archiving to %s\n", src, dst);
 
        if (opt.o_dry_run) {
                rc = 0;
@@ -883,8 +875,8 @@ static int ct_archive(const struct hsm_action_item *hai, const long hal_flags)
                goto fini_major;
        }
 
-       src_fd = open(src, O_RDONLY | O_NOATIME | O_NONBLOCK | O_NOFOLLOW);
-       if (src_fd == -1) {
+       src_fd = llapi_hsm_action_get_fd(hcp);
+       if (src_fd < 0) {
                CT_ERROR("'%s' open read failed (%s)\n", src, strerror(errno));
                rc = -errno;
                goto fini_major;
@@ -914,6 +906,14 @@ static int ct_archive(const struct hsm_action_item *hai, const long hal_flags)
                goto fini_major;
        }
 
+       rc = fsync(dst_fd);
+       if (rc < 0) {
+               CT_ERROR("'%s' cannot synchronize archive file '%s' (%s)\n",
+                        src, dst, strerror(errno));
+               rc = -errno;
+               goto fini_major;
+       }
+
        CT_TRACE("'%s' data archived to '%s' done\n", src, dst);
 
        /* attrs will remain on the MDS; no need to copy them, except possibly
@@ -1053,7 +1053,7 @@ fini_major:
 
        unlink(dst);
        if (ct_is_retryable(rc))
-               ct_flags |= HP_FLAG_RETRY;
+               hp_flags |= HP_FLAG_RETRY;
 
        rcf = rc;
 
@@ -1065,7 +1065,7 @@ out:
                close(dst_fd);
 
        if (hcp != NULL)
-               rc = ct_fini(&hcp, hai, ct_flags, rcf);
+               rc = ct_fini(&hcp, hai, hp_flags, rcf);
 
        return rc;
 }
@@ -1078,14 +1078,13 @@ static int ct_restore(const struct hsm_action_item *hai, const long hal_flags)
        char                             lov_buf[XATTR_SIZE_MAX];
        size_t                           lov_size = sizeof(lov_buf);
        int                              rc;
-       int                              flags = 0;
+       int                              hp_flags = 0;
        int                              src_fd = -1;
        int                              dst_fd = -1;
        int                              mdt_index = -1; /* Not implemented */
        int                              open_flags = 0;
        bool                             set_lovea;
-       lustre_fid                       dfid;
-
+       struct lu_fid                    dfid;
        /* we fill lustre so:
         * source = lustre FID in the backend
         * destination = data FID = volatile file
@@ -1156,7 +1155,7 @@ static int ct_restore(const struct hsm_action_item *hai, const long hal_flags)
                         strerror(-rc));
                err_major++;
                if (ct_is_retryable(rc))
-                       flags |= HP_FLAG_RETRY;
+                       hp_flags |= HP_FLAG_RETRY;
                goto fini;
        }
 
@@ -1164,7 +1163,7 @@ static int ct_restore(const struct hsm_action_item *hai, const long hal_flags)
 
 fini:
        if (hcp != NULL)
-               rc = ct_fini(&hcp, hai, flags, rc);
+               rc = ct_fini(&hcp, hai, hp_flags, rc);
 
        /* object swaping is done by cdt at copy end, so close of volatile file
         * cannot be done before */
@@ -1814,7 +1813,7 @@ static int ct_setup(void)
        /* set llapi message level */
        llapi_msg_set_level(opt.o_verbose);
 
-       arc_fd = open(opt.o_hsm_root, O_DIRECTORY);
+       arc_fd = open(opt.o_hsm_root, O_RDONLY);
        if (arc_fd < 0) {
                CT_ERROR("cannot open archive at '%s': %s\n", opt.o_hsm_root,
                         strerror(errno));
index 9a36c6b..01ad99a 100644 (file)
 #include <lustre/lustreapi.h>
 #include "lustreapi_internal.h"
 
+#define OPEN_BY_FID_PATH dot_lustre_name"/fid"
+
 /****** HSM Copytool API ********/
 #define CT_PRIV_MAGIC 0xC0BE2001
 struct hsm_copytool_private {
        int                      magic;
        char                    *mnt;
        int                      mnt_fd;
+       int                      open_by_fid_fd;
        lustre_kernelcomm        kuc;
        __u32                    archives;
 };
@@ -75,6 +78,7 @@ struct hsm_copyaction_private {
        __s32                                    data_fd;
        const struct hsm_copytool_private       *ct_priv;
        struct hsm_copy                          copy;
+       struct stat                              stat;
 };
 
 #include <libcfs/libcfs.h>
@@ -103,11 +107,11 @@ int llapi_hsm_copytool_register(struct hsm_copytool_private **priv,
        if (ct == NULL)
                return -ENOMEM;
 
-       ct->mnt_fd = open(mnt, O_DIRECTORY | O_RDONLY | O_NONBLOCK);
-       if (ct->mnt_fd < 0) {
-               rc = -errno;
-               goto out_err;
-       }
+       ct->magic = CT_PRIV_MAGIC;
+       ct->mnt_fd = -1;
+       ct->open_by_fid_fd = -1;
+       ct->kuc.lk_rfd = LK_NOFD;
+       ct->kuc.lk_wfd = LK_NOFD;
 
        ct->mnt = strdup(mnt);
        if (ct->mnt == NULL) {
@@ -115,7 +119,17 @@ int llapi_hsm_copytool_register(struct hsm_copytool_private **priv,
                goto out_err;
        }
 
-       ct->magic = CT_PRIV_MAGIC;
+       ct->mnt_fd = open(ct->mnt, O_RDONLY);
+       if (ct->mnt_fd < 0) {
+               rc = -errno;
+               goto out_err;
+       }
+
+       ct->open_by_fid_fd = openat(ct->mnt_fd, OPEN_BY_FID_PATH, O_RDONLY);
+       if (ct->open_by_fid_fd < 0) {
+               rc = -errno;
+               goto out_err;
+       }
 
        /* no archives specified means "match all". */
        ct->archives = 0;
@@ -169,9 +183,15 @@ out_kuc:
 out_err:
        if (!(ct->mnt_fd < 0))
                close(ct->mnt_fd);
+
+       if (!(ct->open_by_fid_fd < 0))
+               close(ct->open_by_fid_fd);
+
        if (ct->mnt != NULL)
                free(ct->mnt);
+
        free(ct);
+
        return rc;
 }
 
@@ -198,6 +218,7 @@ int llapi_hsm_copytool_unregister(struct hsm_copytool_private **priv)
        /* Shut down the kernelcomms */
        libcfs_ukuc_stop(&ct->kuc);
 
+       close(ct->open_by_fid_fd);
        close(ct->mnt_fd);
        free(ct->mnt);
        free(ct);
@@ -338,6 +359,27 @@ static int fid_parent(const char *mnt, const lustre_fid *fid, char *parent,
        return rc;
 }
 
+static int ct_open_by_fid(const struct hsm_copytool_private *ct,
+                         const struct lu_fid *fid, int open_flags)
+{
+       char fid_name[FID_NOBRACE_LEN + 1];
+
+       snprintf(fid_name, sizeof(fid_name), DFID_NOBRACE, PFID(fid));
+
+       return openat(ct->open_by_fid_fd, fid_name, open_flags);
+}
+
+static int ct_stat_by_fid(const struct hsm_copytool_private *ct,
+                         const struct lu_fid *fid,
+                         struct stat *buf)
+{
+       char fid_name[FID_NOBRACE_LEN + 1];
+
+       snprintf(fid_name, sizeof(fid_name), DFID_NOBRACE, PFID(fid));
+
+       return fstatat(ct->open_by_fid_fd, fid_name, buf, 0);
+}
+
 /** Create the destination volatile file for a restore operation.
  *
  * \param hcp        Private copyaction handle.
@@ -346,7 +388,7 @@ static int fid_parent(const char *mnt, const lustre_fid *fid, char *parent,
  * \return 0 on success.
  */
 static int create_restore_volatile(struct hsm_copyaction_private *hcp,
-                                  int mdt_index, int flags)
+                                  int mdt_index, int open_flags)
 {
        int                      rc;
        int                      fd;
@@ -358,15 +400,19 @@ static int create_restore_volatile(struct hsm_copyaction_private *hcp,
        if (rc < 0) {
                /* fid_parent() failed, try to keep on going */
                llapi_error(LLAPI_MSG_ERROR, rc,
-                           "cannot get parent path to restore "DFID
+                           "cannot get parent path to restore "DFID" "
                            "using '%s'", PFID(&hai->hai_fid), mnt);
                snprintf(parent, sizeof(parent), "%s", mnt);
        }
 
-       fd = llapi_create_volatile_idx(parent, mdt_index, flags);
+       fd = llapi_create_volatile_idx(parent, mdt_index, open_flags);
        if (fd < 0)
                return fd;
 
+       rc = fchown(fd, hcp->stat.st_uid, hcp->stat.st_gid);
+       if (rc < 0)
+               goto err_cleanup;
+
        rc = llapi_fd2fid(fd, &hai->hai_dfid);
        if (rc < 0)
                goto err_cleanup;
@@ -422,6 +468,10 @@ int llapi_hsm_action_begin(struct hsm_copyaction_private **phcp,
                goto ok_out;
 
        if (hai->hai_action == HSMA_RESTORE) {
+               rc = ct_stat_by_fid(hcp->ct_priv, &hai->hai_fid, &hcp->stat);
+               if (rc < 0)
+                       goto err_out;
+
                rc = create_restore_volatile(hcp, restore_mdt_index,
                                             restore_open_flags);
                if (rc < 0)
@@ -459,7 +509,7 @@ err_out:
  * \return 0 on success.
  */
 int llapi_hsm_action_end(struct hsm_copyaction_private **phcp,
-                        const struct hsm_extent *he, int flags, int errval)
+                        const struct hsm_extent *he, int hp_flags, int errval)
 {
        struct hsm_copyaction_private   *hcp;
        struct hsm_action_item          *hai;
@@ -475,6 +525,27 @@ int llapi_hsm_action_end(struct hsm_copyaction_private **phcp,
 
        hai = &hcp->copy.hc_hai;
 
+       if (hai->hai_action == HSMA_RESTORE && errval == 0) {
+               struct timeval tv[2];
+
+               /* Set {a,m}time of volatile file to that of original. */
+               tv[0].tv_sec = hcp->stat.st_atime;
+               tv[0].tv_usec = 0;
+               tv[1].tv_sec = hcp->stat.st_mtime;
+               tv[1].tv_usec = 0;
+               if (futimes(hcp->data_fd, tv) < 0) {
+                       errval = -errno;
+                       goto end;
+               }
+
+               rc = fsync(hcp->data_fd);
+               if (rc < 0) {
+                       errval = -errno;
+                       goto end;
+               }
+       }
+
+end:
        /* In some cases, like restore, 2 FIDs are used.
         * Set the right FID to use here. */
        if (hai->hai_action == HSMA_ARCHIVE || hai->hai_action == HSMA_RESTORE)
@@ -482,7 +553,7 @@ int llapi_hsm_action_end(struct hsm_copyaction_private **phcp,
 
        /* Fill the last missing data that will be needed by
         * kernel to send a hsm_progress. */
-       hcp->copy.hc_flags  = flags;
+       hcp->copy.hc_flags  = hp_flags;
        hcp->copy.hc_errval = abs(errval);
 
        hcp->copy.hc_hai.hai_extent = *he;
@@ -573,10 +644,13 @@ int llapi_hsm_action_get_fd(const struct hsm_copyaction_private *hcp)
        if (hcp->magic != CP_PRIV_MAGIC)
                return -EINVAL;
 
-       if (hai->hai_action != HSMA_RESTORE)
+       if (hai->hai_action == HSMA_ARCHIVE)
+               return ct_open_by_fid(hcp->ct_priv, &hai->hai_dfid,
+                               O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NONBLOCK);
+       else if (hai->hai_action == HSMA_RESTORE)
+               return dup(hcp->data_fd);
+       else
                return -EINVAL;
-
-       return dup(hcp->data_fd);
 }
 
 /**