/** Read a message from the link. * Allocates memory, returns handle * * @param link Private descriptor for pipe/socket. * @param buf Buffer to read into, must include size for kuc_hdr * @param maxsize Maximum message size allowed * @param transport Only listen to messages on this transport * (and the generic transport) */ int libcfs_ukuc_msg_get(struct lustre_kernelcomm *link, char *buf, int maxsize, int transport) { struct kuc_hdr *kuch; int rc = 0; memset(buf, 0, maxsize); while (1) { /* Read header first to get message size */ rc = read(link->lk_rfd, buf, lhsz); if (rc <= 0) { rc = -errno; break; } kuch = (struct kuc_hdr *)buf; if (kuch->kuc_magic != KUC_MAGIC) { llapi_err_noerrno(LLAPI_MSG_ERROR, "bad message magic %x != %x\n", kuch->kuc_magic, KUC_MAGIC); rc = -EPROTO; break; } if (kuch->kuc_msglen > maxsize) { rc = -EMSGSIZE; break; } /* Read payload */ rc = read(link->lk_rfd, buf + lhsz, kuch->kuc_msglen - lhsz); if (rc < 0) { rc = -errno; break; } if (rc < (kuch->kuc_msglen - lhsz)) { llapi_err_noerrno(LLAPI_MSG_ERROR, "short read: got %d of %d bytes\n", rc, kuch->kuc_msglen); rc = -EPROTO; break; } if (kuch->kuc_transport == transport || kuch->kuc_transport == KUC_TRANSPORT_GENERIC) { return 0; } /* Drop messages for other transports */ } return rc; }
/* 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; }
int compare_lum(struct obd_uuid *puuid, struct lov_user_md *lum_dir, struct lov_user_md *lum_file1, struct lov_user_md *lum_file2) { struct lov_comp_md_v1 *comp_dir, *comp_file1; struct lov_user_md *sub_dir, *sub_file1; int i, rc = 0; if (lum_dir == NULL || lum_dir->lmm_magic != LOV_MAGIC_COMP_V1) return compare(puuid, lum_dir, lum_file1, lum_file2); comp_dir = (struct lov_comp_md_v1 *)lum_dir; comp_file1 = (struct lov_comp_md_v1 *)lum_file1; if (lum_file1->lmm_magic != lum_dir->lmm_magic) { llapi_err_noerrno(LLAPI_MSG_ERROR, "file1 magic %#x != %#x\n", lum_file1->lmm_magic, lum_dir->lmm_magic); return 10; } if (comp_file1->lcm_entry_count != comp_dir->lcm_entry_count) { llapi_err_noerrno(LLAPI_MSG_ERROR, "file1 comp cnt %d != %d\n", comp_file1->lcm_entry_count, comp_dir->lcm_entry_count); return 11; } for (i = 0; i < comp_dir->lcm_entry_count; i++) { sub_dir = (struct lov_user_md *)((char *)comp_dir + comp_dir->lcm_entries[i].lcme_offset); sub_file1 = (struct lov_user_md *)((char *)comp_file1 + comp_file1->lcm_entries[i].lcme_offset); rc = compare(puuid, sub_dir, sub_file1, NULL); if (rc) break; } return rc; }
static inline const char *llapi_hsm_ct_ev2str(int type) { switch (type) { case CT_REGISTER: return "REGISTER"; case CT_UNREGISTER: return "UNREGISTER"; case CT_ARCHIVE_START: return "ARCHIVE_START"; case CT_ARCHIVE_RUNNING: return "ARCHIVE_RUNNING"; case CT_ARCHIVE_FINISH: return "ARCHIVE_FINISH"; case CT_ARCHIVE_CANCEL: return "ARCHIVE_CANCEL"; case CT_ARCHIVE_ERROR: return "ARCHIVE_ERROR"; case CT_RESTORE_START: return "RESTORE_START"; case CT_RESTORE_RUNNING: return "RESTORE_RUNNING"; case CT_RESTORE_FINISH: return "RESTORE_FINISH"; case CT_RESTORE_CANCEL: return "RESTORE_CANCEL"; case CT_RESTORE_ERROR: return "RESTORE_ERROR"; case CT_REMOVE_START: return "REMOVE_START"; case CT_REMOVE_RUNNING: return "REMOVE_RUNNING"; case CT_REMOVE_FINISH: return "REMOVE_FINISH"; case CT_REMOVE_CANCEL: return "REMOVE_CANCEL"; case CT_REMOVE_ERROR: return "REMOVE_ERROR"; default: llapi_err_noerrno(LLAPI_MSG_ERROR, "Unknown event type: %d", type); return NULL; } }
/** Wait for the next hsm_action_list * \param ct Opaque private control structure * \param halh Action list handle, will be allocated here * \param msgsize Number of bytes in the message, will be set here * \return 0 valid message received; halh and msgsize are set * <0 error code * Note: The application must not call llapi_hsm_copytool_recv until it has * cleared the data in ct->kuch from the previous call. */ int llapi_hsm_copytool_recv(struct hsm_copytool_private *ct, struct hsm_action_list **halh, int *msgsize) { struct kuc_hdr *kuch; struct hsm_action_list *hal; int rc = 0; if (ct == NULL || ct->magic != CT_PRIV_MAGIC) return -EINVAL; if (halh == NULL || msgsize == NULL) return -EINVAL; kuch = ct->kuch; repeat: rc = libcfs_ukuc_msg_get(&ct->kuc, (char *)kuch, HAL_MAXSIZE + sizeof(*kuch), KUC_TRANSPORT_HSM); if (rc < 0) goto out_err; /* Handle generic messages */ if (kuch->kuc_transport == KUC_TRANSPORT_GENERIC && kuch->kuc_msgtype == KUC_MSG_SHUTDOWN) { rc = -ESHUTDOWN; goto out_err; } if (kuch->kuc_transport != KUC_TRANSPORT_HSM || kuch->kuc_msgtype != HMT_ACTION_LIST) { llapi_err_noerrno(LLAPI_MSG_ERROR, "Unknown HSM message type %d:%d\n", kuch->kuc_transport, kuch->kuc_msgtype); rc = -EPROTO; goto out_err; } if (kuch->kuc_msglen < sizeof(*kuch) + sizeof(*hal)) { llapi_err_noerrno(LLAPI_MSG_ERROR, "Short HSM message %d", kuch->kuc_msglen); rc = -EPROTO; goto out_err; } /* Our message is a hsm_action_list. Use pointer math to skip * kuch_hdr and point directly to the message payload. */ hal = (struct hsm_action_list *)(kuch + 1); /* Check that we have registered for this archive # * if 0 registered, we serve any archive */ if (ct->archives && ((1 << (hal->hal_archive_id - 1)) & ct->archives) == 0) { llapi_err_noerrno(LLAPI_MSG_INFO, "This copytool does not service archive #%d," " ignoring this request." " Mask of served archive is 0x%.8X", hal->hal_archive_id, ct->archives); goto repeat; } *halh = hal; *msgsize = kuch->kuc_msglen - sizeof(*kuch); return 0; out_err: *halh = NULL; *msgsize = 0; 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; }
int compare(struct obd_uuid *puuid, 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]; glob_t path; int i; if (cfs_get_param_paths(&path, "lov/%s/stripecount", puuid->uuid) != 0) return 2; if (read_proc_entry(path.gl_pathv[0], buf, sizeof(buf)) < 0) { cfs_free_param_data(&path); return 5; } cfs_free_param_data(&path); def_stripe_count = (short)atoi(buf); if (cfs_get_param_paths(&path, "lov/%s/numobd", puuid->uuid) != 0) return 2; if (read_proc_entry(path.gl_pathv[0], buf, sizeof(buf)) < 0) { cfs_free_param_data(&path); return 6; } cfs_free_param_data(&path); 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) { if (cfs_get_param_paths(&path, "lov/%s/stripesize", puuid->uuid) != 0) return 2; if (read_proc_entry(path.gl_pathv[0], buf, sizeof(buf)) < 0) { cfs_free_param_data(&path); return 5; } cfs_free_param_data(&path); 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) { 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; }