/* Returns bytes read on success and a negative value on failure. * If zero bytes are read it will be treated as failure as such * zero cannot be returned from this function. */ int read_proc_entry(char *proc_path, char *buf, int len) { int rc, fd; memset(buf, 0, len); fd = open(proc_path, O_RDONLY); if (fd < 0) { llapi_error(LLAPI_MSG_ERROR, -errno, "cannot open '%s'", proc_path); return -2; } rc = read(fd, buf, len - 1); if (rc < 0) { llapi_error(LLAPI_MSG_ERROR, -errno, "error reading from '%s'", proc_path); rc = -3; } else if (rc == 0) { llapi_err_noerrno(LLAPI_MSG_ERROR, "read zero bytes from '%s'", proc_path); rc = -4; } else if (buf[rc - 1] == '\n') { buf[rc - 1] = '\0'; /* Remove trailing newline */ } close(fd); return rc; }
/* * Give file access advices * * \param fd File to give advice on. * \param ladvise Advice to give. * * \retval 0 on success. * \retval -errno on failure. */ int llapi_ladvise(int fd, unsigned long long flags, int num_advise, struct lu_ladvise *ladvise) { int rc; struct ladvise_hdr *ladvise_hdr; if (num_advise < 1 || num_advise >= LAH_COUNT_MAX) { errno = EINVAL; llapi_error(LLAPI_MSG_ERROR, -EINVAL, "bad advice number %d", num_advise); return -1; } ladvise_hdr = calloc(1, offsetof(typeof(*ladvise_hdr), lah_advise[num_advise])); if (ladvise_hdr == NULL) { errno = ENOMEM; llapi_error(LLAPI_MSG_ERROR, -ENOMEM, "not enough memory"); return -1; } ladvise_hdr->lah_magic = LADVISE_MAGIC; ladvise_hdr->lah_count = num_advise; ladvise_hdr->lah_flags = flags & LF_MASK; memcpy(ladvise_hdr->lah_advise, ladvise, sizeof(*ladvise) * num_advise); rc = ioctl(fd, LL_IOC_LADVISE, ladvise_hdr); if (rc < 0) { llapi_error(LLAPI_MSG_ERROR, -errno, "cannot give advice"); return -1; } return 0; }
/** * Given a path to a FIFO, create a filehandle for nonblocking writes to it. * Intended to be used for copytool monitoring processes that read an * event stream from the FIFO. Events written in the absence of a reader * are lost. * * \param path Path to monitor FIFO. * * \retval 0 on success. * \retval -errno on error. */ int llapi_hsm_register_event_fifo(const char *path) { int rc; int read_fd; struct stat statbuf; /* Create the FIFO if necessary. */ if ((mkfifo(path, 0644) < 0) && (errno != EEXIST)) { llapi_error(LLAPI_MSG_ERROR, errno, "mkfifo(%s) failed", path); return -errno; } if (errno == EEXIST) { if (stat(path, &statbuf) < 0) { llapi_error(LLAPI_MSG_ERROR, errno, "mkfifo(%s) failed", path); return -errno; } if (!S_ISFIFO(statbuf.st_mode) || ((statbuf.st_mode & 0777) != 0644)) { llapi_error(LLAPI_MSG_ERROR, errno, "%s exists but is " "not a pipe or has a wrong mode", path); return -errno; } } else { created_hsm_event_fifo = true; } /* Open the FIFO for read so that the subsequent open for write * doesn't immediately fail. */ read_fd = open(path, O_RDONLY | O_NONBLOCK); if (read_fd < 0) { llapi_error(LLAPI_MSG_ERROR, errno, "cannot open(%s) for read", path); return -errno; } /* Open the FIFO for writes, but don't block on waiting * for a reader. */ llapi_hsm_event_fd = open(path, O_WRONLY | O_NONBLOCK); rc = -errno; /* Now close the reader. An external monitoring process can * now open the FIFO for reads. If no reader comes along the * events are lost. NOTE: Only one reader at a time! */ close(read_fd); if (llapi_hsm_event_fd < 0) { llapi_error(LLAPI_MSG_ERROR, -rc, "cannot open(%s) for write", path); return rc; } /* Ignore SIGPIPEs -- can occur if the reader goes away. */ signal(SIGPIPE, SIG_IGN); return 0; }
/** * Writes a JSON event to the monitor FIFO. Noop if no FIFO has been * registered. * * \param event A list of llapi_json_items comprising a * single JSON-formatted event. * * \retval 0 on success. * \retval -errno on error. */ static int llapi_hsm_write_json_event(struct llapi_json_item_list **event) { int rc; char time_string[40]; char json_buf[PIPE_BUF]; FILE *buf_file; time_t event_time = time(0); struct tm time_components; struct llapi_json_item_list *json_items; /* Noop unless the event fd was initialized */ if (llapi_hsm_event_fd < 0) return 0; if (event == NULL || *event == NULL) return -EINVAL; json_items = *event; localtime_r(&event_time, &time_components); if (strftime(time_string, sizeof(time_string), "%Y-%m-%d %T %z", &time_components) == 0) { rc = -EINVAL; llapi_error(LLAPI_MSG_ERROR, rc, "strftime() failed"); return rc; } rc = llapi_json_add_item(&json_items, "event_time", LLAPI_JSON_STRING, time_string); if (rc < 0) { llapi_error(LLAPI_MSG_ERROR, -rc, "error in " "llapi_json_add_item()"); return rc; } buf_file = fmemopen(json_buf, sizeof(json_buf), "w"); if (buf_file == NULL) return -errno; rc = llapi_json_write_list(event, buf_file); if (rc < 0) { fclose(buf_file); return rc; } fclose(buf_file); if (write(llapi_hsm_event_fd, json_buf, strlen(json_buf)) < 0) { /* Ignore write failures due to missing reader. */ if (errno != EPIPE) return -errno; } return 0; }
/** * Import an existing hsm-archived file into Lustre. * * Caller must access file by (returned) newfid value from now on. * * \param dst path to Lustre destination (e.g. /mnt/lustre/my/file). * \param archive archive number. * \param st struct stat buffer containing file ownership, perm, etc. * \param stripe_* Striping options. Currently ignored, since the restore * operation will set the striping. In V2, this striping might * be used. * \param newfid[out] Filled with new Lustre fid. */ int llapi_hsm_import(const char *dst, int archive, const struct stat *st, unsigned long long stripe_size, int stripe_offset, int stripe_count, int stripe_pattern, char *pool_name, lustre_fid *newfid) { struct hsm_user_import hui; int fd; int rc = 0; if (stripe_pattern == 0) stripe_pattern = LOV_PATTERN_RAID0; /* Create a non-striped file */ fd = llapi_file_open_pool(dst, O_CREAT | O_WRONLY, st->st_mode, stripe_size, stripe_offset, stripe_count, stripe_pattern | LOV_PATTERN_F_RELEASED, pool_name); if (fd < 0) { llapi_error(LLAPI_MSG_ERROR, fd, "cannot create '%s' for import", dst); return fd; } /* Get the new fid in Lustre. Caller needs to use this fid from now on. */ rc = llapi_fd2fid(fd, newfid); if (rc != 0) { llapi_error(LLAPI_MSG_ERROR, rc, "cannot get fid of '%s' for import", dst); goto out_unlink; } hui.hui_uid = st->st_uid; hui.hui_gid = st->st_gid; hui.hui_mode = st->st_mode; hui.hui_size = st->st_size; hui.hui_archive_id = archive; hui.hui_atime = st->st_atime; hui.hui_atime_ns = st->st_atim.tv_nsec; hui.hui_mtime = st->st_mtime; hui.hui_mtime_ns = st->st_mtim.tv_nsec; rc = ioctl(fd, LL_IOC_HSM_IMPORT, &hui); if (rc != 0) { rc = -errno; llapi_error(LLAPI_MSG_ERROR, rc, "cannot import '%s'", dst); goto out_unlink; } out_unlink: if (fd >= 0) close(fd); if (rc) unlink(dst); return rc; }
/** Create the destination volatile file for a restore operation. * * \param hcp Private copyaction handle. * \param mdt_index MDT index where to create the volatile file. * \param flags Volatile file creation flags. * \return 0 on success. */ static int create_restore_volatile(struct hsm_copyaction_private *hcp, int mdt_index, int open_flags) { int rc; int fd; char parent[PATH_MAX + 1]; const char *mnt = hcp->ct_priv->mnt; struct hsm_action_item *hai = &hcp->copy.hc_hai; rc = fid_parent(mnt, &hai->hai_fid, parent, sizeof(parent)); if (rc < 0) { /* fid_parent() failed, try to keep on going */ llapi_error(LLAPI_MSG_ERROR, rc, "cannot get parent path to restore "DFID" " "using '%s'", PFID(&hai->hai_fid), mnt); snprintf(parent, sizeof(parent), "%s", mnt); } fd = llapi_create_volatile_idx(parent, mdt_index, open_flags); if (fd < 0) return fd; rc = fchown(fd, hcp->stat.st_uid, hcp->stat.st_gid); if (rc < 0) goto err_cleanup; rc = llapi_fd2fid(fd, &hai->hai_dfid); if (rc < 0) goto err_cleanup; hcp->data_fd = fd; return 0; err_cleanup: hcp->data_fd = -1; close(fd); return rc; }
/** * Get metadata attributes of file by FID. * * Use the IOC_MDC_GETFILEINFO ioctl (to send a MDS_GETATTR_NAME RPC) * to get the attributes of the file identified by \a fid. This * returns only the attributes stored on the MDT and avoids taking * layout locks or accessing OST objects. It also bypasses the inode * cache. Attributes are returned in \a st. */ static int ct_md_getattr(const struct hsm_copytool_private *ct, const struct lu_fid *fid, lstat_t *st) { struct lov_user_mds_data *lmd; size_t lmd_size; int rc; lmd_size = sizeof(lmd->lmd_st) + lov_user_md_size(LOV_MAX_STRIPE_COUNT, LOV_USER_MAGIC_V3); if (lmd_size < sizeof(lmd->lmd_st) + XATTR_SIZE_MAX) lmd_size = sizeof(lmd->lmd_st) + XATTR_SIZE_MAX; if (lmd_size < FID_NOBRACE_LEN + 1) lmd_size = FID_NOBRACE_LEN + 1; lmd = malloc(lmd_size); if (lmd == NULL) return -ENOMEM; snprintf((char *)lmd, lmd_size, DFID_NOBRACE, PFID(fid)); rc = ioctl(ct->open_by_fid_fd, IOC_MDC_GETFILEINFO, lmd); if (rc != 0) { rc = -errno; llapi_error(LLAPI_MSG_ERROR, rc, "cannot get metadata attributes of "DFID" in '%s'", PFID(fid), ct->mnt); goto out; } *st = lmd->lmd_st; out: free(lmd); return rc; }
/** Register a copytool * \param[out] priv Opaque private control structure * \param mnt Lustre filesystem mount point * \param archive_count Number of valid archive IDs in \a archives * \param archives Which archive numbers this copytool is * responsible for * \param rfd_flags flags applied to read fd of pipe * (e.g. O_NONBLOCK) * * \retval 0 on success. * \retval -errno on error. */ int llapi_hsm_copytool_register(struct hsm_copytool_private **priv, const char *mnt, int archive_count, int *archives, int rfd_flags) { struct hsm_copytool_private *ct; int rc; if (archive_count > 0 && archives == NULL) { llapi_err_noerrno(LLAPI_MSG_ERROR, "NULL archive numbers"); return -EINVAL; } if (archive_count > LL_HSM_MAX_ARCHIVE) { llapi_err_noerrno(LLAPI_MSG_ERROR, "%d requested when maximum " "of %zu archives supported", archive_count, LL_HSM_MAX_ARCHIVE); return -EINVAL; } ct = calloc(1, sizeof(*ct)); if (ct == NULL) return -ENOMEM; ct->magic = CT_PRIV_MAGIC; ct->mnt_fd = -1; ct->open_by_fid_fd = -1; ct->kuc.lk_rfd = LK_NOFD; ct->kuc.lk_wfd = LK_NOFD; ct->mnt = strdup(mnt); if (ct->mnt == NULL) { rc = -ENOMEM; goto out_err; } ct->kuch = malloc(HAL_MAXSIZE + sizeof(*ct->kuch)); if (ct->kuch == NULL) { rc = -ENOMEM; goto out_err; } ct->mnt_fd = open(ct->mnt, O_RDONLY); if (ct->mnt_fd < 0) { rc = -errno; goto out_err; } ct->open_by_fid_fd = openat(ct->mnt_fd, OPEN_BY_FID_PATH, O_RDONLY); if (ct->open_by_fid_fd < 0) { rc = -errno; goto out_err; } /* no archives specified means "match all". */ ct->archives = 0; for (rc = 0; rc < archive_count; rc++) { if ((archives[rc] > LL_HSM_MAX_ARCHIVE) || (archives[rc] < 0)) { llapi_err_noerrno(LLAPI_MSG_ERROR, "%d requested when " "archive id [0 - %zu] is supported", archives[rc], LL_HSM_MAX_ARCHIVE); rc = -EINVAL; goto out_err; } /* in the list we have an all archive wildcard * so move to all archives mode */ if (archives[rc] == 0) { ct->archives = 0; archive_count = 0; break; } ct->archives |= (1 << (archives[rc] - 1)); } rc = libcfs_ukuc_start(&ct->kuc, KUC_GRP_HSM, rfd_flags); if (rc < 0) goto out_err; /* Storing archive(s) in lk_data; see mdc_ioc_hsm_ct_start */ ct->kuc.lk_data = ct->archives; rc = ioctl(ct->mnt_fd, LL_IOC_HSM_CT_START, &ct->kuc); if (rc < 0) { rc = -errno; llapi_error(LLAPI_MSG_ERROR, rc, "cannot start copytool on '%s'", mnt); goto out_kuc; } llapi_hsm_log_ct_registration(&ct, CT_REGISTER); /* Only the kernel reference keeps the write side open */ close(ct->kuc.lk_wfd); ct->kuc.lk_wfd = LK_NOFD; *priv = ct; return 0; out_kuc: /* cleanup the kuc channel */ libcfs_ukuc_stop(&ct->kuc); out_err: if (!(ct->mnt_fd < 0)) close(ct->mnt_fd); if (!(ct->open_by_fid_fd < 0)) close(ct->open_by_fid_fd); free(ct->mnt); free(ct->kuch); free(ct); return rc; }
/** * Given a copytool progress update, construct a JSON event suitable for * consumption by a copytool monitoring process. * * Examples of various events generated here and written by * llapi_hsm_write_json_event: * * Copytool registration and deregistration: * {"event_time": "2014-02-26 14:58:01 -0500", "event_type": "REGISTER", * "archive": 0, "mount_point": "/mnt/lustre", * "uuid": "80379a60-1f8a-743f-daf2-307cde793ec2"} * {"event_time": "2014-02-26 14:58:01 -0500", "event_type": "UNREGISTER", * "archive": 0, "mount_point": "/mnt/lustre", * "uuid": "80379a60-1f8a-743f-daf2-307cde793ec2"} * * An archive action, start to completion: * {"event_time": "2014-02-26 14:50:13 -0500", "event_type": "ARCHIVE_START", * "total_bytes": 0, "lustre_path": "d71.sanity-hsm/f71.sanity-hsm", * "source_fid": "0x2000013a1:0x2:0x0", "data_fid": "0x2000013a1:0x2:0x0"} * {"event_time": "2014-02-26 14:50:18 -0500", "event_type": "ARCHIVE_RUNNING", * "current_bytes": 5242880, "total_bytes": 39000000, * "lustre_path": "d71.sanity-hsm/f71.sanity-hsm", * "source_fid": "0x2000013a1:0x2:0x0", "data_fid": "0x2000013a1:0x2:0x0"} * {"event_time": "2014-02-26 14:50:50 -0500", "event_type": "ARCHIVE_FINISH", * "source_fid": "0x2000013a1:0x2:0x0", "data_fid": "0x2000013a1:0x2:0x0"} * * A log message: * {"event_time": "2014-02-26 14:50:13 -0500", "event_type": "LOGGED_MESSAGE", * "level": "INFO", * "message": "lhsmtool_posix[42]: copytool fs=lustre archive#=2 item_count=1"} * * \param hcp Opaque action handle returned by * llapi_hsm_action_start. * \param hai The hsm_action_item describing the request. * \param progress_type The ct_progress_type describing the update. * \param total The total expected bytes for the request. * \param current The current copied byte count for the request. * * \retval 0 on success. * \retval -errno on error. */ static int llapi_hsm_log_ct_progress(struct hsm_copyaction_private **phcp, const struct hsm_action_item *hai, __u32 progress_type, __u64 total, __u64 current) { int rc; int linkno = 0; long long recno = -1; char lustre_path[PATH_MAX]; char strfid[FID_NOBRACE_LEN + 1]; struct hsm_copyaction_private *hcp; struct llapi_json_item_list *json_items; /* Noop unless the event fd was initialized */ if (llapi_hsm_event_fd < 0) return 0; if (phcp == NULL || *phcp == NULL) return -EINVAL; hcp = *phcp; rc = llapi_json_init_list(&json_items); if (rc < 0) goto err; snprintf(strfid, sizeof(strfid), DFID_NOBRACE, PFID(&hai->hai_dfid)); rc = llapi_json_add_item(&json_items, "data_fid", LLAPI_JSON_STRING, strfid); if (rc < 0) goto err; snprintf(strfid, sizeof(strfid), DFID_NOBRACE, PFID(&hai->hai_fid)); rc = llapi_json_add_item(&json_items, "source_fid", LLAPI_JSON_STRING, strfid); if (rc < 0) goto err; if (hcp->copy.hc_errval == ECANCELED) { progress_type = CT_CANCEL; goto cancel; } if (hcp->copy.hc_errval != 0) { progress_type = CT_ERROR; rc = llapi_json_add_item(&json_items, "errno", LLAPI_JSON_INTEGER, &hcp->copy.hc_errval); if (rc < 0) goto err; rc = llapi_json_add_item(&json_items, "error", LLAPI_JSON_STRING, strerror(hcp->copy.hc_errval)); if (rc < 0) goto err; goto cancel; } /* lustre_path isn't available after a restore completes */ /* total_bytes isn't available after a restore or archive completes */ if (progress_type != CT_FINISH) { rc = llapi_fid2path(hcp->ct_priv->mnt, strfid, lustre_path, sizeof(lustre_path), &recno, &linkno); if (rc < 0) goto err; rc = llapi_json_add_item(&json_items, "lustre_path", LLAPI_JSON_STRING, lustre_path); if (rc < 0) goto err; rc = llapi_json_add_item(&json_items, "total_bytes", LLAPI_JSON_BIGNUM, &total); if (rc < 0) goto err; } if (progress_type == CT_RUNNING) rc = llapi_json_add_item(&json_items, "current_bytes", LLAPI_JSON_BIGNUM, ¤t); if (rc < 0) goto err; cancel: rc = llapi_json_add_item(&json_items, "event_type", LLAPI_JSON_STRING, (char *)llapi_hsm_ct_ev2str(hai->hai_action + progress_type)); if (rc < 0) goto err; rc = llapi_hsm_write_json_event(&json_items); if (rc < 0) goto err; goto out_free; err: llapi_error(LLAPI_MSG_ERROR, rc, "error in " "llapi_hsm_log_ct_progress()"); out_free: if (json_items != NULL) llapi_json_destroy_list(&json_items); return rc; }
/** * Hook for llapi_hsm_copytool_register and llapi_hsm_copytool_unregister * to generate JSON events suitable for consumption by a copytool * monitoring process. * * \param priv Opaque private control structure. * \param event_type The type of event (register or unregister). * * \retval 0 on success. * \retval -errno on error. */ static int llapi_hsm_log_ct_registration(struct hsm_copytool_private **priv, __u32 event_type) { int rc; char agent_uuid[UUID_MAX]; struct hsm_copytool_private *ct; struct llapi_json_item_list *json_items; /* Noop unless the event fd was initialized */ if (llapi_hsm_event_fd < 0) return 0; if (priv == NULL || *priv == NULL) return -EINVAL; ct = *priv; if (ct->magic != CT_PRIV_MAGIC) return -EINVAL; if (event_type != CT_REGISTER && event_type != CT_UNREGISTER) return -EINVAL; rc = llapi_json_init_list(&json_items); if (rc < 0) goto err; rc = llapi_get_agent_uuid(ct->mnt, agent_uuid, sizeof(agent_uuid)); if (rc < 0) goto err; llapi_chomp_string(agent_uuid); rc = llapi_json_add_item(&json_items, "uuid", LLAPI_JSON_STRING, agent_uuid); if (rc < 0) goto err; rc = llapi_json_add_item(&json_items, "mount_point", LLAPI_JSON_STRING, ct->mnt); if (rc < 0) goto err; rc = llapi_json_add_item(&json_items, "archive", LLAPI_JSON_INTEGER, &ct->archives); if (rc < 0) goto err; rc = llapi_json_add_item(&json_items, "event_type", LLAPI_JSON_STRING, (char *)llapi_hsm_ct_ev2str(event_type)); if (rc < 0) goto err; rc = llapi_hsm_write_json_event(&json_items); if (rc < 0) goto err; goto out_free; err: llapi_error(LLAPI_MSG_ERROR, rc, "error in " "llapi_hsm_log_ct_registration()"); out_free: if (json_items != NULL) llapi_json_destroy_list(&json_items); return rc; }
int main(int argc, char **argv) { struct lov_user_md *lum_dir, *lum_file1 = NULL, *lum_file2 = NULL; struct obd_uuid uuid; int lum_size, rc; DIR *dir; if (argc < 3) { llapi_err_noerrno(LLAPI_MSG_ERROR, "Usage: %s <dirname> <filename1> [filename2]\n", argv[0]); return 1; } dir = opendir(argv[1]); if (dir == NULL) { rc = -errno; llapi_error(LLAPI_MSG_ERROR, rc, "error: %s opendir failed", argv[1]); return rc; } lum_size = lov_user_md_size(MAX_LOV_UUID_COUNT, LOV_USER_MAGIC); lum_dir = (struct lov_user_md *)malloc(lum_size); if (lum_dir == NULL) { rc = -ENOMEM; llapi_error(LLAPI_MSG_ERROR, rc, "error: can't allocate %d bytes " "for dir EA", lum_size); goto cleanup; } rc = llapi_file_get_stripe(argv[1], lum_dir); if (rc == -ENODATA) { char root[PATH_MAX], path[PATH_MAX + 2]; rc = llapi_search_mounts(argv[1], 0, root, NULL); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get " "root path for %s\n", argv[1]); goto cleanup; } snprintf(path, sizeof(path), "%s/.", root); rc = llapi_file_get_stripe(path, lum_dir); if (rc == -ENODATA) { free(lum_dir); lum_dir = NULL; } else if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: cant't get " "root's LOVEA for %s\n", path); goto cleanup; } } else if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get LOVEA for " "%s", argv[1]); goto cleanup; } /* XXX should be llapi_lov_getname() */ rc = llapi_file_get_lov_uuid(argv[1], &uuid); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get lov name for %s", argv[1]); return rc; } lum_file1 = malloc(lum_size); if (lum_file1 == NULL) { rc = -ENOMEM; llapi_error(LLAPI_MSG_ERROR, rc, "error: can't allocate %d bytes for EA", lum_size); goto cleanup; } rc = llapi_file_get_stripe(argv[2], lum_file1); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: unable to get EA for %s", argv[2]); goto cleanup; } if (argc == 4) { lum_file2 = (struct lov_user_md *)malloc(lum_size); if (lum_file2 == NULL) { rc = -ENOMEM; llapi_error(LLAPI_MSG_ERROR, rc, "error: can't allocate %d " "bytes for file2 EA", lum_size); goto cleanup; } rc = llapi_file_get_stripe(argv[3], lum_file2); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get EA for %s", argv[3]); goto cleanup; } } rc = compare_lum(&uuid, lum_dir, lum_file1, lum_file2); cleanup: closedir(dir); if (lum_dir != NULL) free(lum_dir); if (lum_file1 != NULL) free(lum_file1); if (lum_file2 != NULL) free(lum_file2); return rc; }
int compare(struct lov_user_md *lum_dir, struct lov_user_md *lum_file1, struct lov_user_md *lum_file2) { int stripe_count = 0, min_stripe_count = 0, def_stripe_count = 1; int stripe_size = 0; int stripe_offset = -1; int ost_count; char buf[128]; char lov_path[PATH_MAX]; char tmp_path[PATH_MAX]; int i; FILE *fp; fp = popen("\\ls -d /proc/fs/lustre/lov/*clilov* | head -1", "r"); if (fp == NULL) { llapi_error(LLAPI_MSG_ERROR, -errno, "open(lustre/lov/*clilov*) failed"); return 2; } if (fscanf(fp, "%s", lov_path) < 1) { llapi_error(LLAPI_MSG_ERROR, -EINVAL, "read(lustre/lov/*clilov*) failed"); pclose(fp); return 3; } pclose(fp); snprintf(tmp_path, sizeof(tmp_path) - 1, "%s/stripecount", lov_path); if (read_proc_entry(tmp_path, buf, sizeof(buf)) < 0) return 5; def_stripe_count = (short)atoi(buf); snprintf(tmp_path, sizeof(tmp_path) - 1, "%s/numobd", lov_path); if (read_proc_entry(tmp_path, buf, sizeof(buf)) < 0) return 6; ost_count = atoi(buf); if (lum_dir == NULL) { stripe_count = def_stripe_count; min_stripe_count = -1; } else { stripe_count = (signed short)lum_dir->lmm_stripe_count; printf("dir stripe %d, ", stripe_count); min_stripe_count = 1; } printf("default stripe %d, ost count %d\n", def_stripe_count, ost_count); if (stripe_count == 0) { min_stripe_count = -1; stripe_count = 1; } stripe_count = (stripe_count > 0 && stripe_count <= ost_count) ? stripe_count : ost_count; min_stripe_count = min_stripe_count > 0 ? stripe_count : ((stripe_count + 1) / 2); if (lum_file1->lmm_stripe_count != stripe_count || lum_file1->lmm_stripe_count < min_stripe_count) { llapi_err_noerrno(LLAPI_MSG_ERROR, "file1 stripe count %d != dir %d\n", lum_file1->lmm_stripe_count, stripe_count); return 7; } if (lum_file1->lmm_stripe_count < stripe_count) llapi_err_noerrno(LLAPI_MSG_WARN, "warning: file1 used fewer stripes" " %d < dir %d (likely due to bug 4900)\n", lum_file1->lmm_stripe_count, stripe_count); if (lum_dir != NULL) stripe_size = (int)lum_dir->lmm_stripe_size; if (stripe_size == 0) { snprintf(tmp_path, sizeof(tmp_path) - 1, "%s/stripesize", lov_path); if (read_proc_entry(tmp_path, buf, sizeof(buf)) < 0) return 5; stripe_size = atoi(buf); } if (lum_file1->lmm_stripe_size != stripe_size) { llapi_err_noerrno(LLAPI_MSG_ERROR, "file1 stripe size %d != dir %d\n", lum_file1->lmm_stripe_size, stripe_size); return 8; } if (lum_dir != NULL) stripe_offset = (short int)lum_dir->lmm_stripe_offset; if (stripe_offset != -1) { for (i = 0; i < stripe_count; i++) if (lum_file1->lmm_objects[i].l_ost_idx != (stripe_offset + i) % ost_count) { llapi_err_noerrno(LLAPI_MSG_WARN, "warning: file1 non-sequential " "stripe[%d] %d != %d\n", i, lum_file1->lmm_objects[i].l_ost_idx, (stripe_offset + i) % ost_count); } } else if (lum_file2 != NULL) { int next, idx, stripe = stripe_count - 1; next = (lum_file1->lmm_objects[stripe].l_ost_idx + 1) % ost_count; idx = lum_file2->lmm_objects[0].l_ost_idx; if (idx != next) { llapi_err_noerrno(LLAPI_MSG_WARN, "warning: non-sequential " "file1 stripe[%d] %d != file2 stripe[0] %d\n", stripe, lum_file1->lmm_objects[stripe].l_ost_idx, idx); } } return 0; }
int main(int argc, char **argv) { DIR * dir; struct lov_user_md *lum_dir, *lum_file1 = NULL, *lum_file2 = NULL; int rc; int lum_size; if (argc < 3) { llapi_err_noerrno(LLAPI_MSG_ERROR, "Usage: %s <dirname> <filename1> [filename2]\n", argv[0]); return 1; } dir = opendir(argv[1]); if (dir == NULL) { rc = -errno; llapi_error(LLAPI_MSG_ERROR, rc, "error: %s opendir failed", argv[1]); return rc; } lum_size = lov_user_md_size(MAX_LOV_UUID_COUNT, LOV_USER_MAGIC); lum_dir = (struct lov_user_md *)malloc(lum_size); if (lum_dir == NULL) { rc = -ENOMEM; llapi_error(LLAPI_MSG_ERROR, rc, "error: can't allocate %d bytes " "for dir EA", lum_size); goto cleanup; } rc = llapi_file_get_stripe(argv[1], lum_dir); if (rc) { if (rc == -ENODATA) { free(lum_dir); lum_dir = NULL; } else { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get EA for %s", argv[1]); goto cleanup; } } /* XXX should be llapi_lov_getname() */ rc = llapi_file_get_lov_uuid(argv[1], &lov_uuid); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get lov name for %s", argv[1]); return rc; } lum_file1 = malloc(lum_size); if (lum_file1 == NULL) { rc = -ENOMEM; llapi_error(LLAPI_MSG_ERROR, rc, "error: can't allocate %d bytes for EA", lum_size); goto cleanup; } rc = llapi_file_get_stripe(argv[2], lum_file1); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: unable to get EA for %s", argv[2]); goto cleanup; } if (argc == 4) { lum_file2 = (struct lov_user_md *)malloc(lum_size); if (lum_file2 == NULL) { rc = -ENOMEM; llapi_error(LLAPI_MSG_ERROR, rc, "error: can't allocate %d " "bytes for file2 EA", lum_size); goto cleanup; } rc = llapi_file_get_stripe(argv[3], lum_file2); if (rc) { llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get EA for %s", argv[3]); goto cleanup; } } rc = compare(lum_dir, lum_file1, lum_file2); cleanup: closedir(dir); if (lum_dir != NULL) free(lum_dir); if (lum_file1 != NULL) free(lum_file1); if (lum_file2 != NULL) free(lum_file2); return rc; }