Whamcloud - gitweb
LU-4961 lustre: remove liblustre.h and obd.h from userspace
[fs/lustre-release.git] / lustre / utils / ll_recover_lost_found_objs.c
index 4ac685d..3cafd7c 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.
@@ -28,6 +26,8 @@
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, 2013, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
 #include <sys/xattr.h>
 #include <sys/stat.h>
 
-#include <liblustre.h>
+#include <lustre/lustre_idl.h>
+#include <libcfs/list.h>
 
 #define MAX_GROUPS 64
 
-int verbose = 0;
+int verbose;
+bool dry_run;
 
 struct obd_group_info {
-        int dir_exists;
+       __u64           grp_last_id;
+       __u64           grp_seq;
+       cfs_list_t      grp_list;
 };
-struct obd_group_info grp_info[MAX_GROUPS];
+
+cfs_list_t grp_info_list;
+
+static void grp_info_list_destroy(cfs_list_t *list)
+{
+       struct obd_group_info *grp, *tmp;
+
+       cfs_list_for_each_entry_safe(grp, tmp, list, grp_list) {
+               cfs_list_del_init(&grp->grp_list);
+               free(grp);
+       }
+}
 
 static void usage(char *progname)
 {
-        fprintf(stderr, "Usage: %s [-hv] -d lost+found_directory\n", progname);
-        fprintf(stderr, "You need to mount the corrupted OST filesystem and "
-                "provide the path for the lost+found directory as the -d "
-                "option, for example:\n"
-                "ll_recover_lost_found_objs -d /mnt/ost/lost+found\n");
-        exit(1);
+       fprintf(stderr, "Usage: %s [-hnv] -d directory\n"
+               "Recover Lustre OST objects put in lost+found by e2fsck.\n"
+               "\t-d: Check directory, usually '/lost+found' (required).\n"
+               "\t    Alternately, verify object directories under '/O'.\n"
+               "\t-n: Do not modify filesystem, only report changes.\n"
+               "\t-h: Print help message.\n"
+               "\t-v: Print changes verbosely.\n",
+               progname);
+       exit(1);
 }
 
 static int _ll_sprintf(char *buf, size_t size, const char *func, int line,
@@ -101,73 +119,136 @@ static int _ll_sprintf(char *buf, size_t size, const char *func, int line,
 #define ll_sprintf(buf, size, format, ...) \
         _ll_sprintf(buf, size, __FUNCTION__, __LINE__, format, ## __VA_ARGS__)
 
-static int mkdir_p(const char *dest_path, const char *mount, __u64 ff_group)
+static int mkdir_p(const char *dest_path, mode_t mode)
 {
-        struct stat stat_buf;
-        int retval;
-        mode_t mode = 0700;
-        char tmp_path[PATH_MAX];
-
-        if (stat(dest_path, &stat_buf) == 0)
-                return 0;
-
-        if (grp_info[ff_group].dir_exists == 0) {
-                if (ll_sprintf(tmp_path, PATH_MAX, "%s/O/"LPU64,
-                               mount, ff_group))
-                        return 1;
-
-                if (stat(tmp_path, &stat_buf) != 0) {
-                        retval = mkdir(tmp_path, mode);
-                        if (retval < 0) {
-                                fprintf(stderr, "error: creating directory %s: "
-                                        "%s\n", tmp_path, strerror(errno));
-                                return 1;
-                        }
-                        grp_info[ff_group].dir_exists = 1;
-                }
-        }
-
-        retval = mkdir(dest_path, mode);
-        if (retval < 0) {
-                fprintf(stderr, "error: creating directory %s: "
-                        "%s\n", dest_path, strerror(errno));
-                return 1;
-        }
-
-        return 0;
+       struct stat stat_buf;
+       int rc;
+
+       rc = stat(dest_path, &stat_buf);
+       if (rc == 0) {
+               if (S_ISDIR(stat_buf.st_mode))
+                       goto out;
+               if (!S_ISDIR(stat_buf.st_mode)) {
+                       fprintf(stderr, "error: '%s' is not a directory (%o)\n",
+                               dest_path, stat_buf.st_mode);
+                       rc = -ENOTDIR;
+                       goto out;
+               }
+       } else if (errno != ENOENT) {
+               rc = -errno;
+               fprintf(stderr, "error: error checking directory '%s': %s\n",
+                       dest_path, strerror(errno));
+               goto out;
+       }
+
+       if (dry_run) {
+               fprintf(stderr, "dry_run: not creating directory '%s'\n",
+                       dest_path);
+               rc = 0;
+               goto out;
+       }
+
+       rc = mkdir(dest_path, mode);
+       if (rc != 0)
+               fprintf(stderr, "error: creating directory '%s': %s\n",
+                       dest_path, strerror(errno));
+out:
+       return rc;
 }
 
 /* This is returning 0 for an error */
 static __u64 read_last_id(char *file_path)
 {
-        __u64 last_id;
-        int fd;
-        int count;
-
-        fd = open(file_path, O_RDONLY);
-        if (fd < 0) {
-                if (errno != ENOENT)
-                        fprintf(stderr, "error: opening %s: %s\n",
-                                        file_path, strerror(errno));
-                return 0;
-        }
-
-        count = read(fd, &last_id, sizeof(last_id));
-        if (count < 0) {
-                fprintf(stderr, "error: reading file %s: %s\n", file_path,
-                        strerror(errno));
-                close(fd);
-                return 0;
-        }
-        if (count != sizeof(last_id)) {
-                fprintf(stderr, "error: Could not read full last_id from %s\n",
-                        file_path);
-                close(fd);
-                return 0;
-        }
+       __u64 last_id;
+       int fd;
+       int count;
+
+       fd = open(file_path, O_RDONLY);
+       if (fd < 0) {
+               if (errno != ENOENT)
+                       fprintf(stderr, "error: opening '%s': %s\n",
+                               file_path, strerror(errno));
+               return 0;
+       }
+
+       count = read(fd, &last_id, sizeof(last_id));
+       if (count < 0) {
+               fprintf(stderr, "error: reading file '%s': %s\n",
+                       file_path, strerror(errno));
+               close(fd);
+               return 0;
+       }
+       if (count != sizeof(last_id)) {
+               fprintf(stderr, "error: only read %d bytes from '%s'\n",
+                       count, file_path);
+               close(fd);
+               return 0;
+       }
+
+       close(fd);
+       return le64_to_cpu(last_id);
+}
 
-        close(fd);
-        return le64_to_cpu(last_id);
+struct obd_group_info *find_or_create_grp(cfs_list_t *list, __u64 seq,
+                                         const char *mount)
+{
+       struct obd_group_info   *grp;
+       cfs_list_t              *entry;
+       char                    tmp_path[PATH_MAX];
+       char                    seq_name[32];
+       int                     retval;
+       __u64                   tmp_last_id;
+
+       cfs_list_for_each(entry, list) {
+               grp = (struct obd_group_info *)cfs_list_entry(entry,
+                                               struct obd_group_info,
+                                               grp_list);
+               if (grp->grp_seq == seq)
+                       return grp;
+       }
+
+       grp = malloc(sizeof(struct obd_group_info));
+       if (grp == NULL)
+               return NULL;
+
+       sprintf(seq_name, (fid_seq_is_rsvd(seq) ||
+                          fid_seq_is_mdt0(seq)) ? LPU64 : LPX64i,
+                          fid_seq_is_idif(seq) ? 0 : seq);
+
+       /* Check whether the obj dir has been created */
+       if (ll_sprintf(tmp_path, PATH_MAX, "%s/O/%s", mount, seq_name)) {
+               free(grp);
+               return NULL;
+       }
+
+       retval = mkdir_p(tmp_path, 0700);
+       if (retval < 0) {
+               free(grp);
+               fprintf(stderr, "error: creating directory %s: %s\n",
+                       tmp_path, strerror(errno));
+               return NULL;
+       }
+
+       if (ll_sprintf(tmp_path, PATH_MAX, "%s/O/%s/LAST_ID",
+                      mount, seq_name)) {
+               free(grp);
+               return NULL;
+       }
+
+       /*
+        * Object ID needs to be verified against last_id.
+        * LAST_ID file may not be present in the group directory
+        * due to corruption. In case of any error try to recover
+        * as many objects as possible by setting last_id to ~0ULL.
+        */
+       tmp_last_id = read_last_id(tmp_path);
+       if (tmp_last_id == 0)
+               tmp_last_id = ~0ULL;
+       grp->grp_last_id = tmp_last_id;
+       grp->grp_seq = seq;
+
+       cfs_list_add(&grp->grp_list, list);
+       return grp;
 }
 
 static unsigned filetype_dir_table[] = {
@@ -186,18 +267,18 @@ static unsigned filetype_dir_table[] = {
 
 static int traverse_lost_found(char *src_dir, const char *mount_path)
 {
-        DIR *dir_ptr;
-        struct filter_fid parent_fid;
-        struct dirent64 *dirent;
-        __u64 ff_group, ff_objid;
+       DIR *dir_ptr;
+       struct lustre_mdt_attrs lma;
+       struct dirent64 *dirent;
+       __u64 ff_seq, ff_objid;
         char *file_path;
         char dest_path[PATH_MAX];
-        char last_id_file[PATH_MAX];
-        __u64 last_id[MAX_GROUPS] = {0};
-        __u64 tmp_last_id;
         struct stat st;
         int obj_exists, xattr_len;
         int len, ret = 0, error = 0;
+       char seq_name[32];
+       char obj_name[32];
+       struct obd_group_info *grp_info;
 
         len = strlen(src_dir);
 
@@ -249,114 +330,134 @@ static int traverse_lost_found(char *src_dir, const char *mount_path)
                 }
                 break;
 
-                case DT_REG:
-                file_path = src_dir;
-                xattr_len = getxattr(file_path, "trusted.fid",
-                                     (void *)&parent_fid,
-                                     sizeof(parent_fid));
-
-                if (xattr_len == -1 || xattr_len < sizeof(parent_fid))
-                        /*
-                         * Its very much possible that we dont find fid
-                         * on precreated files, LAST_ID
-                         */
-                        continue;
-
-                ff_group = le64_to_cpu(parent_fid.ff_seq);
-                if (ff_group >= FID_SEQ_OST_MAX) {
-                        fprintf(stderr, "error: invalid group "LPU64" likely"
-                                "indicates a corrupt xattr for file %s.\n",
-                                ff_group, file_path);
-                        continue;
-                }
-                ff_objid = le64_to_cpu(parent_fid.ff_objid);
-
-                /* might need to create the parent directories for
-                   this object */
-                if (ll_sprintf(dest_path, PATH_MAX, "%s/O/"LPU64"/d"LPU64,
-                               mount_path, ff_group, ff_objid % 32)) {
-                        closedir(dir_ptr);
-                        return 1;
-                }
-
-                ret = mkdir_p(dest_path, mount_path, ff_group);
-                if (ret) {
-                        closedir(dir_ptr);
-                        return ret;
-                }
-
-                /*
-                 * Object ID needs to be verified against last_id.
-                 * LAST_ID file may not be present in the group directory
-                 * due to corruption. In case of any error tyr to recover
-                 * as many objects as possible by setting last_id to ~0ULL.
-                 */
-                if (last_id[ff_group] == 0) {
-                        if (ll_sprintf(last_id_file, PATH_MAX,
-                                       "%s/O/"LPU64"/LAST_ID",
-                                       mount_path, ff_group)) {
-                                closedir(dir_ptr);
-                                return 1;
-                        }
-
-                        tmp_last_id = read_last_id(last_id_file);
-                        if (tmp_last_id == 0)
-                                tmp_last_id = ~0ULL;
-                        last_id[ff_group] = tmp_last_id;
-                }
-
-                if (ff_objid > last_id[ff_group]) {
-                        fprintf(stderr, "error: file skipped because object ID "
-                                "greater than LAST_ID\nFilename: %s\n"
-                                "Group: "LPU64"\nObjectid: "LPU64"\n"
-                                "LAST_ID: "LPU64, file_path, ff_group, ff_objid,
-                                last_id[ff_group]);
-                        continue;
-                }
-
-                /* move file from lost+found to proper object
-                   directory */
-                if (ll_sprintf(dest_path, PATH_MAX,
-                               "%s/O/"LPU64"/d"LPU64"/"LPU64, mount_path,
-                               ff_group, ff_objid % 32, ff_objid)) {
-                        closedir(dir_ptr);
-                        return 1;
-                }
-
-                obj_exists = 1;
-                ret = stat(dest_path, &st);
-                if (ret == 0) {
-                        if (st.st_size == 0)
-                                obj_exists = 0;
-                } else {
-                        if (errno != ENOENT)
-                                fprintf(stderr,
-                                        "warning: stat for %s: %s\n",
-                                        dest_path, strerror(errno));
-                        obj_exists = 0;
-                }
-
-                if (obj_exists) {
-                        fprintf(stderr, "error: target object %s already "
-                                "exists and will not be replaced.\n",dest_path);
-                        continue;
-                }
-
-                if (rename(file_path, dest_path) < 0) {
-                        fprintf(stderr, "error: rename failed for file %s: %s\n",
-                                file_path, strerror(errno));
-                        error++;
-                        continue;
-                }
-
-                printf("Object %s restored.\n", dest_path);
-                break;
-                }
-        }
-
-        closedir(dir_ptr);
-
-        return error;
+               case DT_REG:
+               file_path = src_dir;
+               xattr_len = getxattr(file_path, "trusted.lma",
+                                    (void *)&lma, sizeof(lma));
+               if (xattr_len == -1 || xattr_len < sizeof(lma)) {
+                       struct filter_fid_old   ff;
+
+                       /* try old filter_fid EA */
+                       xattr_len = getxattr(file_path, "trusted.fid",
+                                            (void *)&ff, sizeof(ff));
+                       /* It's very much possible that we don't find any
+                        * FID on precreated or unused objects or LAST_ID.
+                        * The xattr needs to hold the full filter_fid_old
+                        * with the OID/parent to be useful. */
+                       if (xattr_len == -1 || xattr_len < sizeof(ff))
+                               continue;
+
+                       ff_seq = le64_to_cpu(ff.ff_seq);
+                       ff_objid = le64_to_cpu(ff.ff_objid);
+                       if (verbose)
+                               printf(DOSTID": ", ff_seq, ff_objid);
+               } else {
+                       if (verbose)
+                               printf(DFID": ", PFID(&lma.lma_self_fid));
+                       ff_seq = le64_to_cpu(lma.lma_self_fid.f_seq);
+                       ff_objid = le32_to_cpu(lma.lma_self_fid.f_oid);
+               }
+
+               sprintf(seq_name, (fid_seq_is_rsvd(ff_seq) ||
+                                  fid_seq_is_mdt0(ff_seq)) ? LPU64 : LPX64i,
+                       fid_seq_is_idif(ff_seq) ? 0 : ff_seq);
+
+               /* LAST_ID uses OID = 0.  It will be regenerated later. */
+               if (ff_objid == 0) {
+                       if (verbose)
+                               printf("'%s': LAST_ID\n", file_path);
+                       continue;
+               }
+
+               sprintf(obj_name, (fid_seq_is_rsvd(ff_seq) ||
+                                  fid_seq_is_mdt0(ff_seq) ||
+                                  fid_seq_is_idif(ff_seq)) ?
+                                  LPU64 : LPX64i, ff_objid);
+
+               grp_info = find_or_create_grp(&grp_info_list, ff_seq,
+                                             mount_path);
+               if (grp_info == NULL) {
+                       closedir(dir_ptr);
+                       return 1;
+               }
+
+               /* Might need to create the parent directory for this object */
+               if (ll_sprintf(dest_path, PATH_MAX, "%s/O/%s/d"LPU64,
+                               mount_path, seq_name, ff_objid % 32)) {
+                       closedir(dir_ptr);
+                       return 1;
+               }
+
+               /* The O/{seq} directory was created in find_or_create_grp() */
+               ret = mkdir_p(dest_path, 0700);
+               if (ret < 0) {
+                       closedir(dir_ptr);
+                       return ret;
+               }
+
+               if (ff_objid > grp_info->grp_last_id) {
+                       fprintf(stderr, "error: file skipped because object ID "
+                               "greater than LAST_ID\nFilename: %s\n"
+                               "Group: "LPU64"\nObjectid: "LPU64"\n"
+                               "LAST_ID: "LPU64, file_path, ff_seq, ff_objid,
+                               grp_info->grp_last_id);
+                       continue;
+               }
+
+               /* move file from lost+found to proper object directory */
+               if (ll_sprintf(dest_path, PATH_MAX,
+                               "%s/O/%s/d"LPU64"/%s", mount_path,
+                               seq_name, ff_objid % 32, obj_name)) {
+                       closedir(dir_ptr);
+                       return 1;
+               }
+
+               /* Source and destination are the same file, do nothing. */
+               if (strcmp(file_path, dest_path) == 0) {
+                       if (verbose)
+                               printf("'%s': OK\n", file_path);
+                       continue;
+               }
+
+               obj_exists = 1;
+               ret = stat(dest_path, &st);
+               if (ret == 0) {
+                       if (st.st_size == 0)
+                               obj_exists = 0;
+               } else {
+                       if (errno != ENOENT)
+                               fprintf(stderr, "warning: stat for %s: %s\n",
+                                       dest_path, strerror(errno));
+                       obj_exists = 0;
+               }
+
+               if (obj_exists) {
+                       fprintf(dry_run ? stdout : stderr,
+                               "%s: '%s' exists, will not replace with '%s'\n",
+                               dry_run ? "dry_run" : "error",
+                               dest_path, file_path);
+                       continue;
+               }
+               if (dry_run) {
+                       printf("dry_run: not renaming '%s' to '%s'\n",
+                              file_path, dest_path);
+                       continue;
+               }
+               if (rename(file_path, dest_path) < 0) {
+                       fprintf(stderr, "error: rename failed for '%s': %s\n",
+                               file_path, strerror(errno));
+                       error++;
+                       continue;
+               }
+
+               printf("object '%s' restored.\n", dest_path);
+               break;
+               }
+       }
+
+       closedir(dir_ptr);
+
+       return error;
 }
 
 /*
@@ -435,41 +536,47 @@ static int check_last_id(const char *mount_path)
                 }
                 closedir(groupdir);
 
-                fd = open(lastid_path, O_RDWR | O_CREAT, 0700);
-                if (fd < 0) {
-                        fprintf(stderr, "error: open \"%s\" failed: %s\n",
-                                lastid_path, strerror(errno));
-                        return 1;
-                }
-
-                max_objid = cpu_to_le64(max_objid);
-                ret = write(fd, &max_objid, sizeof(__u64));
-                if (ret < sizeof(__u64)) {
-                        fprintf(stderr, "error: write \"%s\" failed: %s\n",
-                                lastid_path, strerror(errno));
-                        close(fd);
-                        return 1;
-                }
-
-                close(fd);
-        }
-
-        return 0;
+               if (dry_run) {
+                       fprintf(stderr, "dry_run: not updating '%s' to "
+                               LPU64"\n", lastid_path, max_objid);
+                       return 0;
+               }
+               fd = open(lastid_path, O_RDWR | O_CREAT, 0700);
+               if (fd < 0) {
+                       fprintf(stderr, "error: open '%s' failed: %s\n",
+                               lastid_path, strerror(errno));
+                       return 1;
+               }
+
+               max_objid = cpu_to_le64(max_objid);
+               ret = write(fd, &max_objid, sizeof(__u64));
+               if (ret < sizeof(__u64)) {
+                       fprintf(stderr, "error: write '%s' failed: %s\n",
+                               lastid_path, strerror(errno));
+                       close(fd);
+                       return 1;
+               }
+
+               close(fd);
+       }
+
+       return 0;
 }
 
 int main(int argc, char **argv)
 {
-        char *progname;
-        struct stat stat_buf;
-        char src_dir[PATH_MAX] = "";
-        char mount_path[PATH_MAX];
-        char tmp_path[PATH_MAX];
-        int c;
-        int retval;
-
-        progname = argv[0];
-
-        while ((c = getopt(argc, argv, "d:hv")) != EOF) {
+       char *progname;
+       char src_dir[PATH_MAX] = "";
+       char mount_path[PATH_MAX];
+       char tmp_path[PATH_MAX];
+       int rc;
+       int c;
+
+       progname = strrchr(argv[0], '/');
+       if (progname++ == NULL)
+               progname = argv[0];
+
+       while ((c = getopt(argc, argv, "d:hnv")) != EOF) {
                 switch (c) {
                 case 'd':
                         if (chdir(optarg)) {
@@ -499,12 +606,15 @@ int main(int argc, char **argv)
                                         "error: root directory is detected\n");
                                 return 1;
                         }
-                        fprintf(stdout, "\"lost+found\" directory path: %s\n",
-                                src_dir);
-                        break;
-                case 'v':
-                        verbose = 1;
-                        break;
+                       fprintf(stdout, "%s: %sscan directory path: %s\n",
+                               progname, dry_run ? "read_only " : "", src_dir);
+                       break;
+               case 'n':
+                       dry_run = true;
+                       break;
+               case 'v':
+                       verbose = true;
+                       break;
                 case 'h':
                         usage(progname);
                 default:
@@ -521,25 +631,28 @@ int main(int argc, char **argv)
         if (ll_sprintf(tmp_path, PATH_MAX, "%s/O",  mount_path))
                 return 1;
 
-        if (stat(tmp_path, &stat_buf) != 0) {
-                retval = mkdir(tmp_path, 0700);
-                if (retval == -1) {
-                        fprintf(stderr, "error: creating objects directory %s:"
-                                " %s\n", tmp_path, strerror(errno));
-                        return 1;
-                }
+       rc = mkdir_p(tmp_path, 0700);
+       if (rc == -1) {
+               fprintf(stderr, "error: creating objects directory %s:"
+                       " %s\n", tmp_path, strerror(errno));
+               return 1;
         }
 
-        retval = traverse_lost_found(src_dir, mount_path);
-        if (retval) {
-                fprintf(stderr, "error: traversing lost+found looking for "
-                        "orphan objects.\n");
-                return retval;
-        }
+       CFS_INIT_LIST_HEAD(&grp_info_list);
+       rc = traverse_lost_found(src_dir, mount_path);
+       if (rc) {
+               fprintf(stderr, "error: traversing lost+found looking for "
+                       "orphan objects.\n");
+               goto grp_destory;
+       }
+
+       rc = check_last_id(mount_path);
+       if (rc)
+               fprintf(stderr, "error: while checking/restoring LAST_ID.\n");
 
-        retval = check_last_id(mount_path);
-        if (retval)
-                fprintf(stderr, "error: while checking/restoring LAST_ID.\n");
+grp_destory:
+       grp_info_list_destroy(&grp_info_list);
 
-        return retval;
+       printf("%s: scan finished: rc = %d\n", progname, rc);
+       return rc;
 }