-/* -*- 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 2008 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/
#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;
+ 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,
#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[] = {
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);
}
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;
}
/*
}
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)) {
"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:
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;
}