Whamcloud - gitweb
LU-8009 utils: Lustre_rsync does not sync files on MDT1
[fs/lustre-release.git] / lustre / utils / lustre_rsync.c
index 67b53ff..3e196d5 100644 (file)
@@ -1,6 +1,4 @@
-/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
- * vim:expandtab:shiftwidth=8:tabstop=8:
- *
+/*
  * GPL HEADER START
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  * GPL HEADER END
  */
 /*
- * Copyright  2009 Sun Microsystems, Inc. All rights reserved
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, 2014, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
  *  tfid - The FID of the target file
  *  pfid - The FID of the parent of the target file (at the time of
  *         the operation)
- *  name - The name of the target file (at the time of the operation)
+ *  sfid - The FID of the source file
+ *  spfid - The FID of the parent of the source file
+ *  name - The name of the target file (at the time of the operation), the name
+ *         of the source file is appended (delimited with '\0') if this
+ *         operation involves a source
  *
  * With just this information, it is not alwasy possible to determine
  * the file paths for each operation. For instance, if pfid does not
@@ -72,7 +76,7 @@
  * changelog are replayed, all the files in this special directory
  * will get moved to the location as in the source-fs.
  *
- * Shorthand used: f2p(tfid) = fid2path(tfid)
+ * Shorthand used: f2p(fid) = fid2path(fid)
  *
  * The following are the metadata operations of interest.
  * 1. creat
  *      rm .lustrerepl/[tfid]
  *    Else if pfid is present on the source-fs,
  *      if f2p(pfid)+name is present,
- *        rm f2p(pfid)+name(pfid,name)
+ *        rm f2p(pfid)+name
  *
- * 3. move (pfid1,name1) to (pfid2,name2)
- *    If pfid2 is present
- *      if pfid1 is also present, mv (pfid1,name1) to (pfid2,name2)
- *      else mv .lustrerepl/[tfid] to (pfid2,name2)
- *    If pfid2 is not present,
- *      if pfid1 is present, mv (pfid1,name1) .lustrerepl/[tfid]
+ * 3. move (spfid,sname) to (pfid,name)
+ *    If pfid is present
+ *      if spfid is also present, mv (spfid,sname) to (pfid,name)
+ *      else mv .lustrerepl/[sfid] to (pfid,name)
+ *    Else if pfid is not present,
+ *      if spfid is present, mv (spfid,sname) .lustrerepl/[sfid]
  *    If moving out of .lustrerepl
  *      move out all its children in .lustrerepl.
  *      [pfid,tfid,name] tracked from (1) is used for this.
 #include <getopt.h>
 #include <stdarg.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <limits.h>
 #include <utime.h>
+#include <time.h>
 #include <sys/xattr.h>
 
-#include <libcfs/libcfs.h>
-#include <libcfs/libcfsutil.h>
-#include <lustre/liblustreapi.h>
+#include <libcfs/util/string.h>
+#include <libcfs/util/parser.h>
+#include <lustre/lustreapi.h>
 #include <lustre/lustre_idl.h>
 #include "lustre_rsync.h"
 
@@ -148,10 +154,14 @@ extern int obd_initialize(int argc, char **argv);
 struct lr_info {
         long long recno;
         int target_no;
+       unsigned int is_extended:1;
         enum changelog_rec_type type;
-        char pfid[LR_FID_STR_LEN];
-        char tfid[LR_FID_STR_LEN];
-        char name[PATH_MAX + 1];
+       char tfid[LR_FID_STR_LEN];
+       char pfid[LR_FID_STR_LEN];
+       char sfid[LR_FID_STR_LEN];
+       char spfid[LR_FID_STR_LEN];
+       char sname[NAME_MAX + 1];
+       char name[NAME_MAX + 1];
         char src[PATH_MAX + 1];
         char dest[PATH_MAX + 1];
         char path[PATH_MAX + 1];
@@ -194,6 +204,8 @@ char rsync[PATH_MAX];
 char rsync_ver[PATH_MAX];
 struct lr_parent_child_list *parents;
 
+FILE *debug_log;
+
 /* Command line options */
 struct option long_opts[] = {
         {"source",      required_argument, 0, 's'},
@@ -211,7 +223,8 @@ struct option long_opts[] = {
         {"start-recno", required_argument, 0, 'n'},
         {"abort-on-err",no_argument,       0, 'a'},
         {"debug",       required_argument, 0, 'd'},
-        {0, 0, 0, 0}
+       {"debuglog",    required_argument, 0, 'D'},
+       {0, 0, 0, 0}
 };
 
 /* Command line usage */
@@ -229,18 +242,31 @@ void lr_usage()
                 "\t--dry-run        don't write anything\n");
 }
 
+#define DEBUG_ENTRY(info)                                                     \
+       lr_debug(D_TRACE, "***** Start %lld %s (%d) %s %s %s *****\n",         \
+                (info)->recno, changelog_type2str((info)->type),              \
+                (info)->type, (info)->tfid, (info)->pfid, (info)->name);
+
+#define DEBUG_EXIT(info, rc)                                                  \
+       lr_debug(D_TRACE, "##### End %lld %s (%d) %s %s %s rc=%d #####\n",     \
+                (info)->recno, changelog_type2str((info)->type),              \
+                (info)->type, (info)->tfid, (info)->pfid, (info)->name, rc);
+
 /* Print debug information. This is controlled by the value of the
    global variable 'debug' */
 void lr_debug(int level, const char *fmt, ...)
 {
-        va_list ap;
+       va_list ap;
 
-        if (level > debug)
-                return;
+       if (level > debug)
+               return;
 
-        va_start(ap, fmt);
-        vprintf(fmt, ap);
-        va_end(ap);
+       va_start(ap, fmt);
+       if (debug_log != NULL)
+               vfprintf(debug_log, fmt, ap);
+       else
+               vfprintf(stdout, fmt, ap);
+       va_end(ap);
 }
 
 
@@ -366,31 +392,39 @@ int lr_copy_data(struct lr_info *info)
                 info->bufsize = bufsize;
         }
 
-        while (1) {
-                rsize = read(fd_src, info->buf, bufsize);
-                if (rsize == 0) {
-                        break;
-                } else if (rsize < 0) {
-                        rc = -errno;
-                        goto out;
-                }
-                errno = 0;
-                if (write(fd_dest, info->buf, rsize) != rsize) {
-                        if (errno != 0)
-                                rc = -errno;
-                        else
-                                rc = -EINTR;
-                }
-        }
-        fsync(fd_dest);
+       while (1) {
+               char *buf;
+               int wsize;
+
+               buf = info->buf;
+               rsize = read(fd_src, buf, bufsize);
+               if (rsize == 0) {
+                       rc = 0;
+                       break;
+               }
+               if (rsize < 0) {
+                       rc = -errno;
+                       break;
+               }
+               do {
+                       wsize = write(fd_dest, buf, rsize);
+                       if (wsize <= 0) {
+                               rc = -errno;
+                               break;
+                       }
+                       rsize -= wsize;
+                       buf += wsize;
+               } while (rsize > 0);
+       }
+       fsync(fd_dest);
 
 out:
-        if (fd_src != -1)
-                close(fd_src);
-        if (fd_dest != -1)
-                close(fd_dest);
+       if (fd_src != -1)
+               close(fd_src);
+       if (fd_dest != -1)
+               close(fd_dest);
 
-        return rc;
+       return rc;
 }
 
 /* Copy data from source to destination */
@@ -546,10 +580,9 @@ int lr_get_symlink(struct lr_info *info)
         } else {
                 link = info->linktmp;
         }
-        strncpy(info->link, link, PATH_MAX);
-        info->link[PATH_MAX] = '\0';
+       strlcpy(info->link, link, sizeof(info->link));
 
-        return rc;
+       return rc;
 }
 
 /* Create file/directory/device file/symlink. */
@@ -605,18 +638,29 @@ int lr_mkfile(struct lr_info *info)
 
 int lr_add_pc(const char *pfid, const char *tfid, const char *name)
 {
-        struct lr_parent_child_list *p;
-
-        p = calloc(1, sizeof(*p));
-        if (!p)
-                return -ENOMEM;
-        strcpy(p->pc_log.pcl_pfid, pfid);
-        strcpy(p->pc_log.pcl_tfid, tfid);
-        strcpy(p->pc_log.pcl_name, name);
-
-        p->pc_next = parents;
-        parents = p;
-        return 0;
+       struct lr_parent_child_list *p;
+       size_t len;
+
+       p = calloc(1, sizeof(*p));
+       if (p == NULL)
+               return -ENOMEM;
+       len = strlcpy(p->pc_log.pcl_pfid, pfid, sizeof(p->pc_log.pcl_pfid));
+       if (len >= sizeof(p->pc_log.pcl_pfid))
+               goto out_err;
+       len = strlcpy(p->pc_log.pcl_tfid, tfid, sizeof(p->pc_log.pcl_tfid));
+       if (len >= sizeof(p->pc_log.pcl_tfid))
+               goto out_err;
+       len = strlcpy(p->pc_log.pcl_name, name, sizeof(p->pc_log.pcl_name));
+       if (len >= sizeof(p->pc_log.pcl_name))
+               goto out_err;
+
+       p->pc_next = parents;
+       parents = p;
+       return 0;
+
+out_err:
+       free(p);
+       return -E2BIG;
 }
 
 void lr_cascade_move(const char *fid, const char *dest, struct lr_info *info)
@@ -641,11 +685,11 @@ void lr_cascade_move(const char *fid, const char *dest, struct lr_info *info)
                                         info->src, d, errno);
                                 errors++;
                         }
-                        lr_cascade_move(curr->pc_log.pcl_tfid, d, info);
                         if (curr == parents)
                                 parents = curr->pc_next;
                         else
                                 prev->pc_next = curr->pc_next;
+                       lr_cascade_move(curr->pc_log.pcl_tfid, d, info);
                         free(curr);
                         prev = curr = parents;
 
@@ -658,7 +702,7 @@ void lr_cascade_move(const char *fid, const char *dest, struct lr_info *info)
         free(d);
 }
 
-/* remove [info->pfid, ext->tfid] from parents */
+/* remove [info->spfid, info->sfid] from parents */
 int lr_remove_pc(const char *pfid, const char *tfid)
 {
         struct lr_parent_child_list *curr, *prev;
@@ -769,7 +813,9 @@ int lr_create(struct lr_info *info)
 
         /* Is f2p(pfid)+name != f2p(tfid)? If not the file has moved. */
         len = strlen(info->path);
-        if (len - 1 >= 0 && info->path[len - 1] == '/')
+       if (len == 1 && info->path[0] == '/')
+               snprintf(info->dest, PATH_MAX, "%s", info->name);
+       else if (len - 1 > 0 && info->path[len - 1] == '/')
                 snprintf(info->dest, PATH_MAX, "%s%s", info->path, info->name);
         else
                 snprintf(info->dest, PATH_MAX, "%s/%s", info->path, info->name);
@@ -785,10 +831,13 @@ int lr_create(struct lr_info *info)
         /* Is f2p(pfid) present on the target? If not, the parent has
            moved */
         if (!mkspecial) {
-                snprintf(info->dest, PATH_MAX, "%s/%s", status->ls_targets[0],
-                        info->path);
-                if (access(info->dest, F_OK) != 0)
-                        mkspecial = 1;
+               snprintf(info->dest, PATH_MAX, "%s/%s", status->ls_targets[0],
+                       info->path);
+               if (access(info->dest, F_OK) != 0) {
+                       lr_debug(DTRACE, "create: parent %s not found\n",
+                               info->dest);
+                       mkspecial = 1;
+               }
         }
         for (info->target_no = 0; info->target_no < status->ls_num_targets;
              info->target_no++) {
@@ -805,7 +854,7 @@ int lr_create(struct lr_info *info)
                 if (rc1)
                         rc = rc1;
         }
-        return rc;
+       return rc;
 }
 
 /* Replicate a file remove (rmdir/unlink) operation */
@@ -846,26 +895,29 @@ int lr_remove(struct lr_info *info)
                         continue;
                 }
         }
-        return rc;
+       return rc;
 }
 
-/* Replicate a rename/move operation. This operations are tracked by
-   two changelog records. */
-int lr_move(struct lr_info *info, struct lr_info *ext)
+/* Replicate a rename/move operation. */
+int lr_move(struct lr_info *info)
 {
         int rc = 0;
         int rc1;
         int rc_dest, rc_src;
         int special_src = 0;
         int special_dest = 0;
+       char srcpath[PATH_MAX + 1] = "";
 
-        rc_dest = lr_get_path(ext, ext->pfid);
-        if (rc_dest < 0 && rc_dest != -ENOENT)
-                return rc_dest;
+       LASSERT(info->is_extended);
 
-        rc_src = lr_get_path(info, info->pfid);
-        if (rc_src < 0 && rc_src != -ENOENT)
-                return rc_src;
+       rc_src = lr_get_path(info, info->spfid);
+       if (rc_src < 0 && rc_src != -ENOENT)
+               return rc_src;
+       memcpy(srcpath, info->path, strlen(info->path));
+
+       rc_dest = lr_get_path(info, info->pfid);
+       if (rc_dest < 0 && rc_dest != -ENOENT)
+               return rc_dest;
 
         for (info->target_no = 0; info->target_no < status->ls_num_targets;
              info->target_no++) {
@@ -873,55 +925,64 @@ int lr_move(struct lr_info *info, struct lr_info *ext)
                 if (!rc_dest) {
                         snprintf(info->dest, PATH_MAX, "%s/%s",
                                 status->ls_targets[info->target_no],
-                                ext->path);
+                               info->path);
                         if (access(info->dest, F_OK) != 0) {
                                 rc_dest = -errno;
                         } else {
                                 snprintf(info->dest, PATH_MAX, "%s/%s/%s",
                                         status->ls_targets[info->target_no],
-                                        ext->path, ext->name);
+                                       info->path, info->name);
                         }
+                       lr_debug(DINFO, "dest path %s rc_dest=%d\n", info->dest,
+                                rc_dest);
                 }
                 if (rc_dest == -ENOENT) {
                         snprintf(info->dest, PATH_MAX, "%s/%s/%s",
                                 status->ls_targets[info->target_no],
-                                SPECIAL_DIR, info->tfid);
+                               SPECIAL_DIR, info->sfid);
                         special_dest = 1;
+                       lr_debug(DINFO, "special dest %s\n", info->dest);
                 }
 
-                if (!rc_src)
-                        snprintf(info->src, PATH_MAX, "%s/%s/%s",
-                                status->ls_targets[info->target_no],
-                                info->path, info->name);
+               if (!rc_src) {
+                       snprintf(info->src, PATH_MAX, "%s/%s/%s",
+                               status->ls_targets[info->target_no],
+                               srcpath, info->sname);
+                       lr_debug(DINFO, "src path %s rc_src=%d\n", info->src,
+                                rc_src);
+               }
                 if (rc_src == -ENOENT || (access(info->src, F_OK) != 0 &&
                                           errno == ENOENT)) {
                         snprintf(info->src, PATH_MAX, "%s/%s/%s",
                                 status->ls_targets[info->target_no],
-                                SPECIAL_DIR, info->tfid);
+                               SPECIAL_DIR, info->sfid);
                         special_src = 1;
+                       lr_debug(DINFO, "special src %s\n", info->src);
                 }
 
                 rc1 = 0;
+               errno = 0;
                 if (strcmp(info->src, info->dest) != 0) {
                         rc1 = rename(info->src, info->dest);
                         if (rc1 == -1)
                                 rc1 = -errno;
+                       lr_debug(DINFO, "rename returns %d\n", rc1);
                 }
 
-                if (special_src) {
-                        lr_remove_pc(info->pfid, info->tfid);
-                        if (!special_dest)
-                                lr_cascade_move(info->tfid, info->dest, info);
-                }
-                if (special_dest)
-                        lr_add_pc(ext->pfid, info->tfid, ext->name);
+               if (special_src)
+                       rc1 = lr_remove_pc(info->spfid, info->sfid);
+
+               if (!special_dest)
+                       lr_cascade_move(info->sfid, info->dest, info);
+               else
+                       rc1 = lr_add_pc(info->pfid, info->sfid, info->name);
 
                 lr_debug(DINFO, "move: %s [to] %s rc1=%d, errno=%d\n",
                          info->src, info->dest, rc1, errno);
                 if (rc1)
                         rc = rc1;
         }
-        return rc;
+       return rc;
 }
 
 /* Replicate a hard link */
@@ -955,7 +1016,7 @@ int lr_link(struct lr_info *info)
                                 break;
 
                         len = strlen(info->path) - strlen(info->name);
-                        if (len > 0 && strcmp(info->path + len,
+                       if (len >= 0 && strcmp(info->path + len,
                                               info->name) == 0)
                                 snprintf(info->dest, PATH_MAX, "%s/%s",
                                         status->ls_targets[info->target_no],
@@ -993,7 +1054,7 @@ int lr_link(struct lr_info *info)
                 if (rc1)
                         rc = rc1;
         }
-        return rc;
+       return rc;
 }
 
 /* Replicate file attributes */
@@ -1025,7 +1086,7 @@ int lr_setattr(struct lr_info *info)
                 if (rc1)
                         rc = rc1;
         }
-        return rc;
+       return rc;
 }
 
 /* Replicate xattrs */
@@ -1055,26 +1116,53 @@ int lr_setxattr(struct lr_info *info)
                         rc = rc1;
         }
 
-        return rc;
+       return rc;
 }
 
 /* Parse a line of changelog entry */
 int lr_parse_line(void *priv, struct lr_info *info)
 {
-        struct changelog_rec *rec;
-
-        if (llapi_changelog_recv(priv, &rec) != 0)
-                return -1;
-
-        info->recno = rec->cr_index;
-        info->type = rec->cr_type;
-        sprintf(info->tfid, DFID, PFID(&rec->cr_tfid));
-        sprintf(info->pfid, DFID, PFID(&rec->cr_pfid));
-        strncpy(info->name, rec->cr_name, rec->cr_namelen);
-        info->name[rec->cr_namelen] = '\0';
-
-        if (verbose > 1)
-                printf("Rec %lld: %d %s\n", info->recno, info->type,info->name);
+       struct changelog_rec            *rec;
+       struct changelog_ext_rename     *rnm;
+       size_t                           namelen;
+       size_t                           copylen = sizeof(info->name);
+
+       if (llapi_changelog_recv(priv, &rec) != 0)
+               return -1;
+
+       info->is_extended = !!(rec->cr_flags & CLF_RENAME);
+       info->recno = rec->cr_index;
+       info->type = rec->cr_type;
+       snprintf(info->tfid, sizeof(info->tfid), DFID, PFID(&rec->cr_tfid));
+       snprintf(info->pfid, sizeof(info->pfid), DFID, PFID(&rec->cr_pfid));
+
+       namelen = strnlen(changelog_rec_name(rec), rec->cr_namelen);
+       if (copylen > namelen + 1)
+               copylen = namelen + 1;
+       strlcpy(info->name, changelog_rec_name(rec), copylen);
+
+       /* Don't use rnm if CLF_RENAME isn't set */
+       rnm = changelog_rec_rename(rec);
+       if (rec->cr_flags & CLF_RENAME && !fid_is_zero(&rnm->cr_sfid)) {
+               copylen = sizeof(info->sname);
+
+               snprintf(info->sfid, sizeof(info->sfid), DFID,
+                        PFID(&rnm->cr_sfid));
+               snprintf(info->spfid, sizeof(info->spfid), DFID,
+                        PFID(&rnm->cr_spfid));
+               namelen = changelog_rec_snamelen(rec);
+               if (copylen > namelen + 1)
+                       copylen = namelen + 1;
+               strlcpy(info->sname, changelog_rec_sname(rec), copylen);
+
+               if (verbose > 1)
+                       printf("Rec %lld: %d %s %s\n", info->recno, info->type,
+                               info->name, info->sname);
+       } else {
+               if (verbose > 1)
+                       printf("Rec %lld: %d %s\n", info->recno, info->type,
+                               info->name);
+       }
 
         llapi_changelog_free(&rec);
 
@@ -1085,7 +1173,7 @@ int lr_parse_line(void *priv, struct lr_info *info)
 /* Initialize the replication parameters */
 int lr_init_status()
 {
-        size_t size = sizeof(struct lustre_rsync_status) + PATH_MAX;
+        size_t size = sizeof(struct lustre_rsync_status) + PATH_MAX + 1;
 
         if (status != NULL)
                 return 0;
@@ -1164,38 +1252,57 @@ int lr_read_log()
         struct lustre_rsync_status *s;
         int fd = -1;
         size_t size;
-        size_t read_size = sizeof(struct lustre_rsync_status) + PATH_MAX;
+        size_t read_size = sizeof(struct lustre_rsync_status) + PATH_MAX + 1;
         int rc = 0;
 
         if (statuslog == NULL)
                 return 0;
 
         s = calloc(1, read_size);
-        if (s == NULL)
-                GOTO(out, rc = -ENOMEM);
-
-        fd = open(statuslog, O_RDONLY);
-        if (fd == -1)
-                GOTO(out, rc = -errno);
-        size = read(fd, s, read_size);
-        if (size != read_size)
-                GOTO(out, rc = -EINVAL);
-        if (read_size < s->ls_size) {
-                read_size = s->ls_size;
-                s = lr_grow_buf(s, read_size);
-                if (s == NULL)
-                        GOTO(out, rc = -ENOMEM);
-                if (lseek(fd, 0, SEEK_SET) == -1)
-                        GOTO(out, rc = -errno);
-                size = read(fd, s, read_size);
-                if (size != read_size)
-                        GOTO(out, rc = -EINVAL);
-        }
+       if (s == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       fd = open(statuslog, O_RDONLY);
+       if (fd == -1) {
+               rc = -errno;
+               goto out;
+       }
+
+       size = read(fd, s, read_size);
+       if (size != read_size) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       if (read_size < s->ls_size) {
+               read_size = s->ls_size;
+               s = lr_grow_buf(s, read_size);
+               if (s == NULL) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
+
+               if (lseek(fd, 0, SEEK_SET) == -1) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
+
+               size = read(fd, s, read_size);
+               if (size != read_size) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+       }
+
+       while (read(fd, &rec, sizeof(rec)) != 0) {
+               tmp = calloc(1, sizeof(*tmp));
+               if (!tmp) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
 
-        while (read(fd, &rec, sizeof(rec)) != 0) {
-                tmp = calloc(1, sizeof(*tmp));
-                if (!tmp)
-                        GOTO(out, rc = -ENOMEM);
                 tmp->pc_log = rec;
                 tmp->pc_next = parents;
                 parents = tmp;
@@ -1205,31 +1312,35 @@ int lr_read_log()
         if (status->ls_num_targets == 0) {
                 if (status->ls_size != s->ls_size) {
                         status = lr_grow_buf(status, s->ls_size);
-                        if (status == NULL)
-                                GOTO(out, rc = -ENOMEM);
+                       if (status == NULL) {
+                               rc = -ENOMEM;
+                               goto out;
+                       }
+
                         status->ls_size = s->ls_size;
                 }
                 status->ls_num_targets = s->ls_num_targets;
                 memcpy(status->ls_targets, s->ls_targets,
-                       PATH_MAX * s->ls_num_targets);
+                       (PATH_MAX + 1) * s->ls_num_targets);
         }
         if (status->ls_last_recno == -1)
                 status->ls_last_recno = s->ls_last_recno;
 
-        if (status->ls_registration[0] == '\0')
-                strncpy(status->ls_registration, s->ls_registration,
-                        LR_NAME_MAXLEN);
+       if (status->ls_registration[0] == '\0')
+               strlcpy(status->ls_registration, s->ls_registration,
+                       sizeof(status->ls_registration));
 
-        if (status->ls_mdt_device[0] == '\0')
-                strncpy(status->ls_mdt_device, s->ls_mdt_device,
-                        LR_NAME_MAXLEN);
+       if (status->ls_mdt_device[0] == '\0')
+               strlcpy(status->ls_mdt_device, s->ls_mdt_device,
+                       sizeof(status->ls_mdt_device));
 
-        if (status->ls_source_fs[0] == '\0')
-                strncpy(status->ls_source_fs, s->ls_source_fs,
-                        LR_NAME_MAXLEN);
+       if (status->ls_source_fs[0] == '\0')
+               strlcpy(status->ls_source_fs, s->ls_source_fs,
+                       sizeof(status->ls_source_fs));
 
-        if (status->ls_source[0] == '\0')
-                strncpy(status->ls_source, s->ls_source, PATH_MAX);
+       if (status->ls_source[0] == '\0')
+               strlcpy(status->ls_source, s->ls_source,
+                       sizeof(status->ls_source));
 
  out:
         if (fd != -1)
@@ -1243,9 +1354,9 @@ int lr_read_log()
    processing. */
 int lr_clear_cl(struct lr_info *info, int force)
 {
-        char    mdt_device[LR_NAME_MAXLEN + 1];
-        long long rec;
-        int rc = 0;
+       char            mdt_device[LR_NAME_MAXLEN + 1];
+       long long       rec;
+       int             rc = 0;
 
         if (force || info->recno > status->ls_last_recno + CLEAR_INTERVAL) {
                 if (info->type == CL_RENAME)
@@ -1257,8 +1368,8 @@ int lr_clear_cl(struct lr_info *info, int force)
                          * device name so make a copy of it until this
                          * is fixed.
                         */
-                        strncpy(mdt_device, status->ls_mdt_device,
-                                LR_NAME_MAXLEN);
+                       strlcpy(mdt_device, status->ls_mdt_device,
+                               sizeof(mdt_device));
                         rc = llapi_changelog_clear(mdt_device,
                                                    status->ls_registration,
                                                    rec);
@@ -1335,7 +1446,8 @@ void lr_print_status(struct lr_info *info)
         if (statuslog != NULL)
                 printf("Statuslog: %s\n", statuslog);
         printf("Changelog registration: %s\n", status->ls_registration);
-        printf("Starting changelog record: "LPD64"\n", status->ls_last_recno);
+       printf("Starting changelog record: %jd\n",
+              (uintmax_t)status->ls_last_recno);
         if (noxattr)
                 printf("Replicate xattrs: no\n");
         if (noclear)
@@ -1357,7 +1469,7 @@ int lr_replicate()
 {
         void *changelog_priv;
         struct lr_info *info;
-        struct lr_info *ext;
+       struct lr_info *ext = NULL;
         time_t start;
         int xattr_not_supp;
         int i;
@@ -1373,16 +1485,17 @@ int lr_replicate()
         if (rc) {
                 fprintf(stderr, "Source path is not a valid Lustre client "
                         "mountpoint.\n");
-                return rc;
+               goto out;
         }
         if (status->ls_mdt_device[0] == '\0')
                 snprintf(status->ls_mdt_device, LR_NAME_MAXLEN, "%s%s",
                         status->ls_source_fs, DEFAULT_MDT);
 
         ext = calloc(1, sizeof(struct lr_info));
-        if (ext == NULL)
-                return -ENOMEM;
-        memcpy(ext, info, sizeof(struct lr_info));
+       if (ext == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
 
         for (i = 0, xattr_not_supp = 0; i < status->ls_num_targets; i++) {
                 snprintf(info->dest, PATH_MAX, "%s/%s", status->ls_targets[i],
@@ -1391,7 +1504,8 @@ int lr_replicate()
                 if (rc == -1 && errno != EEXIST) {
                         fprintf(stderr, "Error writing to target path %s.\n",
                                 status->ls_targets[i]);
-                        return -errno;
+                       rc = -errno;
+                       goto out;
                 }
                 rc = llistxattr(info->src, info->xlist, info->xsize);
                 if (rc == -1 && errno == ENOTSUP) {
@@ -1406,25 +1520,39 @@ int lr_replicate()
 
         lr_print_status(info);
 
-        /* Open changelogs for consumption*/
-        rc = llapi_changelog_start(&changelog_priv, CHANGELOG_FLAG_BLOCK,
-                                   status->ls_source_fs, status->ls_last_recno);
+       /* Open changelogs for consumption*/
+       rc = llapi_changelog_start(&changelog_priv,
+                               CHANGELOG_FLAG_BLOCK | CHANGELOG_FLAG_JOBID,
+                               status->ls_mdt_device, status->ls_last_recno);
         if (rc < 0) {
                 fprintf(stderr, "Error opening changelog file for fs %s.\n",
                         status->ls_source_fs);
-                return rc;
+               goto out;
         }
 
         while (!quit && lr_parse_line(changelog_priv, info) == 0) {
                 rc = 0;
-                if (info->type == CL_RENAME)
-                        /* Rename operations have an additional changelog
-                           record of information. */
-                        lr_parse_line(changelog_priv, ext);
+               if (info->type == CL_RENAME && !info->is_extended) {
+                       /* Newer rename operations extends changelog to store
+                        * source file information, but old changelog has
+                        * another record.
+                        */
+                       if (lr_parse_line(changelog_priv, ext) != 0)
+                               break;
+                       memcpy(info->sfid, info->tfid, sizeof(info->sfid));
+                       memcpy(info->spfid, info->pfid, sizeof(info->spfid));
+                       memcpy(info->tfid, ext->tfid, sizeof(info->tfid));
+                       memcpy(info->pfid, ext->pfid, sizeof(info->pfid));
+                       strlcpy(info->sname, info->name, sizeof(info->sname));
+                       strlcpy(info->name, ext->name, sizeof(info->name));
+                       info->is_extended = 1;
+               }
 
                 if (dryrun)
                         continue;
 
+               DEBUG_ENTRY(info);
+
                 switch(info->type) {
                 case CL_CREATE:
                 case CL_MKDIR:
@@ -1437,7 +1565,7 @@ int lr_replicate()
                         rc = lr_remove(info);
                         break;
                 case CL_RENAME:
-                        rc = lr_move(info, ext);
+                       rc = lr_move(info);
                         break;
                 case CL_HARDLINK:
                         rc = lr_link(info);
@@ -1449,15 +1577,17 @@ int lr_replicate()
                 case CL_XATTR:
                         rc = lr_setxattr(info);
                         break;
-                case CL_CLOSE:
-                case CL_EXT:
-                case CL_OPEN:
-                case CL_IOCTL:
-                case CL_MARK:
-                        /* Nothing needs to be done for these entries */
-                default:
-                        break;
-                }
+               case CL_CLOSE:
+               case CL_EXT:
+               case CL_OPEN:
+               case CL_LAYOUT:
+               case CL_MARK:
+                       /* Nothing needs to be done for these entries */
+                       /* fallthrough */
+               default:
+                       break;
+               }
+               DEBUG_EXIT(info, rc);
                 if (rc && rc != -ENOENT) {
                         lr_print_failure(info, rc);
                         errors++;
@@ -1484,7 +1614,15 @@ int lr_replicate()
                 printf("Changelog records consumed: %lld\n", rec_count);
         }
 
-        return 0;
+       rc = 0;
+
+out:
+       if (info != NULL)
+               free(info);
+       if (ext != NULL)
+               free(ext);
+
+       return rc;
 }
 
 void
@@ -1504,8 +1642,8 @@ int main(int argc, char *argv[])
         if ((rc = lr_init_status()) != 0)
                 return rc;
 
-        while ((rc = getopt_long(argc, argv, "as:t:m:u:l:vx:zc:ry:n:d:",
-                                long_opts, NULL)) >= 0) {
+       while ((rc = getopt_long(argc, argv, "as:t:m:u:l:vx:zc:ry:n:d:D:",
+                                long_opts, NULL)) >= 0) {
                 switch (rc) {
                 case 'a':
                         /* Assume absolute paths */
@@ -1513,7 +1651,8 @@ int main(int argc, char *argv[])
                         break;
                 case 's':
                         /* Assume absolute paths */
-                        strncpy(status->ls_source, optarg, PATH_MAX);
+                       strlcpy(status->ls_source, optarg,
+                               sizeof(status->ls_source));
                         break;
                 case 't':
                         status->ls_num_targets++;
@@ -1527,23 +1666,23 @@ int main(int argc, char *argv[])
                                 status->ls_num_targets = numtargets;
                         }
                         newsize = sizeof (struct lustre_rsync_status) +
-                                (status->ls_num_targets * PATH_MAX);
+                                (status->ls_num_targets * (PATH_MAX + 1));
                         if (status->ls_size != newsize) {
                                 status->ls_size = newsize;
                                 status = lr_grow_buf(status, newsize);
                                 if (status == NULL)
                                         return -ENOMEM;
                         }
-                        strncpy(status->ls_targets[status->ls_num_targets - 1],
-                                optarg,
-                                PATH_MAX);
-                        break;
-                case 'm':
-                        strncpy(status->ls_mdt_device, optarg, LR_NAME_MAXLEN);
-                        break;
-                case 'u':
-                        strncpy(status->ls_registration, optarg,
-                                LR_NAME_MAXLEN);
+                       strlcpy(status->ls_targets[status->ls_num_targets - 1],
+                               optarg, sizeof(status->ls_targets[0]));
+                       break;
+               case 'm':
+                       strlcpy(status->ls_mdt_device, optarg,
+                               sizeof(status->ls_mdt_device));
+                       break;
+               case 'u':
+                       strlcpy(status->ls_registration, optarg,
+                               sizeof(status->ls_registration));
                         break;
                 case 'l':
                         statuslog = optarg;
@@ -1595,6 +1734,17 @@ int main(int argc, char *argv[])
                         if (debug < 0 || debug > 2)
                                 debug = 0;
                         break;
+               case 'D':
+                       /* Undocumented option debug log file */
+                       if (debug_log != NULL)
+                               fclose(debug_log);
+                       debug_log = fopen(optarg, "a");
+                       if (debug_log == NULL) {
+                               printf("Cannot open %s for debug log\n",
+                                      optarg);
+                               return -1;
+                       }
+                       break;
                 default:
                         fprintf(stderr, "error: %s: option '%s' "
                                 "unrecognized.\n", argv[0], argv[optind - 1]);
@@ -1641,5 +1791,7 @@ int main(int argc, char *argv[])
 
         rc = lr_replicate();
 
-        return rc;
+       if (debug_log != NULL)
+               fclose(debug_log);
+       return rc;
 }