X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lustre%2Futils%2Fll_recover_lost_found_objs.c;h=cd9a6f4e9d587a26d9794f4590af71ffa20dd37e;hp=8f24e9a89e092deca3956f2787906e074f33bba0;hb=8f1c8dbe2389e1ef1e1d3387e343fb9a1bb84198;hpb=e3a7c58aebafce40323db54bf6056029e5af4a70 diff --git a/lustre/utils/ll_recover_lost_found_objs.c b/lustre/utils/ll_recover_lost_found_objs.c index 8f24e9a..cd9a6f4 100644 --- a/lustre/utils/ll_recover_lost_found_objs.c +++ b/lustre/utils/ll_recover_lost_found_objs.c @@ -26,6 +26,8 @@ /* * 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/ @@ -55,25 +57,43 @@ #include #include -#include +#include +#include #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; + struct list_head grp_list; }; -struct obd_group_info grp_info[MAX_GROUPS]; + +struct list_head grp_info_list; + +static void grp_info_list_destroy(struct list_head *list) +{ + struct obd_group_info *grp, *tmp; + + list_for_each_entry_safe(grp, tmp, list, grp_list) { + 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, @@ -99,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(struct list_head *list, __u64 seq, + const char *mount) +{ + struct obd_group_info *grp; + struct list_head *entry; + char tmp_path[PATH_MAX]; + char seq_name[32]; + int retval; + __u64 tmp_last_id; + + list_for_each(entry, list) { + grp = (struct obd_group_info *)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; + + list_add(&grp->grp_list, list); + return grp; } static unsigned filetype_dir_table[] = { @@ -184,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); @@ -247,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; } /* @@ -433,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)) { @@ -497,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: @@ -519,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; - } + 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; }