/** * Write the special file which contains the list of llog catalogs IDs * * This function writes the CATALOG file which contains the array of llog * catalogs IDs. It is used mostly to store OSP llogs indexed by OST/MDT * number. * * \param[in] env execution environment * \param[in] d corresponding storage device * \param[in] idx position to start from, usually OST/MDT index * \param[in] count how many catalog IDs to write * \param[out] idarray the buffer with the data to write. * \param[in] fid LLOG_CATALOGS_OID for CATALOG object * * \retval 0 on successful write of catalog IDs * \retval negative value on error */ int llog_osd_put_cat_list(const struct lu_env *env, struct dt_device *d, int idx, int count, struct llog_catid *idarray, const struct lu_fid *fid) { struct llog_thread_info *lgi = llog_info(env); struct dt_object *o = NULL; struct thandle *th; int rc, size; if (count == 0) RETURN(0); LASSERT(d); size = sizeof(*idarray) * count; lgi->lgi_off = idx * sizeof(*idarray); lgi->lgi_fid = *fid; o = dt_locate(env, d, &lgi->lgi_fid); if (IS_ERR(o)) RETURN(PTR_ERR(o)); if (!dt_object_exists(o)) GOTO(out, rc = -ENOENT); rc = dt_attr_get(env, o, &lgi->lgi_attr, BYPASS_CAPA); if (rc) GOTO(out, rc); if (!S_ISREG(lgi->lgi_attr.la_mode)) { CERROR("%s: CATALOGS is not a regular file!: mode = %o\n", o->do_lu.lo_dev->ld_obd->obd_name, lgi->lgi_attr.la_mode); GOTO(out, rc = -ENOENT); } th = dt_trans_create(env, d); if (IS_ERR(th)) GOTO(out, rc = PTR_ERR(th)); lgi->lgi_buf.lb_len = size; lgi->lgi_buf.lb_buf = idarray; rc = dt_declare_record_write(env, o, &lgi->lgi_buf, lgi->lgi_off, th); if (rc) GOTO(out, rc); rc = dt_trans_start_local(env, d, th); if (rc) GOTO(out_trans, rc); rc = dt_record_write(env, o, &lgi->lgi_buf, &lgi->lgi_off, th); if (rc) CDEBUG(D_INODE, "can't write CATALOGS at index %d: rc = %d\n", idx, rc); out_trans: dt_trans_stop(env, d, th); out: lu_object_put(env, &o->do_lu); RETURN(rc); }
int ofd_attr_get(const struct lu_env *env, struct ofd_object *fo, struct lu_attr *la) { int rc = 0; ENTRY; if (ofd_object_exists(fo)) { rc = dt_attr_get(env, ofd_object_child(fo), la, ofd_object_capa(env, fo)); #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 7, 50, 0) /* Try to correct for a bug in 2.1.0 (LU-221) that caused * negative timestamps to appear to be in the far future, * due old timestamp being stored on disk as an unsigned value. * This fixes up any bad values stored on disk before * returning them to the client, and ensures any timestamp * updates are correct. LU-1042 */ if (unlikely(la->la_atime == LU221_BAD_TIME)) la->la_atime = 0; if (unlikely(la->la_mtime == LU221_BAD_TIME)) la->la_mtime = 0; if (unlikely(la->la_ctime == LU221_BAD_TIME)) la->la_ctime = 0; #else #warning "remove old LU-221/LU-1042 workaround code" #endif } else { rc = -ENOENT; } RETURN(rc); }
/** * Prepare buffers for read request processing. * * This function converts remote buffers from client to local buffers * and prepares the latter. * * \param[in] env execution environment * \param[in] exp OBD export of client * \param[in] ofd OFD device * \param[in] fid FID of object * \param[in] la object attributes * \param[in] oa OBDO structure from client * \param[in] niocount number of remote buffers * \param[in] rnb remote buffers * \param[in] nr_local number of local buffers * \param[in] lnb local buffers * \param[in] jobid job ID name * * \retval 0 on successful prepare * \retval negative value on error */ static int ofd_preprw_read(const struct lu_env *env, struct obd_export *exp, struct ofd_device *ofd, const struct lu_fid *fid, struct lu_attr *la, struct obdo *oa, int niocount, struct niobuf_remote *rnb, int *nr_local, struct niobuf_local *lnb, char *jobid) { struct ofd_object *fo; int i, j, rc, tot_bytes = 0; ENTRY; LASSERT(env != NULL); fo = ofd_object_find(env, ofd, fid); if (IS_ERR(fo)) RETURN(PTR_ERR(fo)); LASSERT(fo != NULL); ofd_read_lock(env, fo); if (!ofd_object_exists(fo)) GOTO(unlock, rc = -ENOENT); if (ofd->ofd_lfsck_verify_pfid && oa->o_valid & OBD_MD_FLFID) { rc = ofd_verify_ff(env, fo, oa); if (rc != 0) GOTO(unlock, rc); } *nr_local = 0; for (i = 0, j = 0; i < niocount; i++) { rc = dt_bufs_get(env, ofd_object_child(fo), rnb + i, lnb + j, 0); if (unlikely(rc < 0)) GOTO(buf_put, rc); LASSERT(rc <= PTLRPC_MAX_BRW_PAGES); /* correct index for local buffers to continue with */ j += rc; *nr_local += rc; LASSERT(j <= PTLRPC_MAX_BRW_PAGES); tot_bytes += rnb[i].rnb_len; } LASSERT(*nr_local > 0 && *nr_local <= PTLRPC_MAX_BRW_PAGES); rc = dt_attr_get(env, ofd_object_child(fo), la); if (unlikely(rc)) GOTO(buf_put, rc); rc = dt_read_prep(env, ofd_object_child(fo), lnb, *nr_local); if (unlikely(rc)) GOTO(buf_put, rc); ofd_counter_incr(exp, LPROC_OFD_STATS_READ, jobid, tot_bytes); RETURN(0); buf_put: dt_bufs_put(env, ofd_object_child(fo), lnb, *nr_local); unlock: ofd_read_unlock(env, fo); ofd_object_put(env, fo); return rc; }
static int llog_truncate(const struct lu_env *env, struct dt_object *o) { struct lu_attr la; struct thandle *th; struct dt_device *d; int rc; ENTRY; LASSERT(o); d = lu2dt_dev(o->do_lu.lo_dev); LASSERT(d); rc = dt_attr_get(env, o, &la); if (rc) RETURN(rc); CDEBUG(D_OTHER, "original size "LPU64"\n", la.la_size); rc = sizeof(struct llog_log_hdr) + sizeof(struct llog_mini_rec); if (la.la_size < rc) { CERROR("too small llog: "LPU64"\n", la.la_size); RETURN(0); } /* drop 2 records */ la.la_size = la.la_size - (sizeof(struct llog_mini_rec) * 2); la.la_valid = LA_SIZE; th = dt_trans_create(env, d); if (IS_ERR(th)) RETURN(PTR_ERR(th)); rc = dt_declare_attr_set(env, o, &la, th); if (rc) GOTO(stop, rc); rc = dt_declare_punch(env, o, la.la_size, OBD_OBJECT_EOF, th); rc = dt_trans_start_local(env, d, th); if (rc) GOTO(stop, rc); rc = dt_punch(env, o, la.la_size, OBD_OBJECT_EOF, th); if (rc) GOTO(stop, rc); rc = dt_attr_set(env, o, &la, th); if (rc) GOTO(stop, rc); stop: dt_trans_stop(env, d, th); RETURN(rc); }
static int out_attr_get(struct tgt_session_info *tsi) { const struct lu_env *env = tsi->tsi_env; struct tgt_thread_info *tti = tgt_th_info(env); struct object_update *update = tti->tti_u.update.tti_update; struct obdo *obdo = &tti->tti_u.update.tti_obdo; struct lu_attr *la = &tti->tti_attr; struct dt_object *obj = tti->tti_u.update.tti_dt_object; int idx = tti->tti_u.update.tti_update_reply_index; int rc; ENTRY; if (unlikely(update->ou_result_size < sizeof(*obdo))) return -EPROTO; if (!lu_object_exists(&obj->do_lu)) { /* Usually, this will be called when the master MDT try * to init a remote object(see osp_object_init), so if * the object does not exist on slave, we need set BANSHEE flag, * so the object can be removed from the cache immediately */ set_bit(LU_OBJECT_HEARD_BANSHEE, &obj->do_lu.lo_header->loh_flags); RETURN(-ENOENT); } dt_read_lock(env, obj, MOR_TGT_CHILD); rc = dt_attr_get(env, obj, la); if (rc) GOTO(out_unlock, rc); obdo->o_valid = 0; obdo_from_la(obdo, la, la->la_valid); lustre_set_wire_obdo(NULL, obdo, obdo); out_unlock: dt_read_unlock(env, obj); CDEBUG(D_INFO, "%s: insert attr get reply %p index %d: rc = %d\n", tgt_name(tsi->tsi_tgt), tti->tti_u.update.tti_update_reply, 0, rc); object_update_result_insert(tti->tti_u.update.tti_update_reply, obdo, sizeof(*obdo), idx, rc); RETURN(rc); }
static int llog_osd_declare_write_rec(const struct lu_env *env, struct llog_handle *loghandle, struct llog_rec_hdr *rec, int idx, struct thandle *th) { struct llog_thread_info *lgi = llog_info(env); struct dt_object *o; int rc; ENTRY; LASSERT(env); LASSERT(th); LASSERT(loghandle); o = loghandle->lgh_obj; LASSERT(o); /* each time we update header */ rc = dt_declare_record_write(env, o, sizeof(struct llog_log_hdr), 0, th); if (rc || idx == 0) /* if error or just header */ RETURN(rc); if (dt_object_exists(o)) { rc = dt_attr_get(env, o, &lgi->lgi_attr, BYPASS_CAPA); lgi->lgi_off = lgi->lgi_attr.la_size; LASSERT(ergo(rc == 0, lgi->lgi_attr.la_valid & LA_SIZE)); if (rc) RETURN(rc); rc = dt_declare_punch(env, o, lgi->lgi_off, OBD_OBJECT_EOF, th); if (rc) RETURN(rc); } else { lgi->lgi_off = 0; } /* XXX: implement declared window or multi-chunks approach */ rc = dt_declare_record_write(env, o, 32 * 1024, lgi->lgi_off, th); RETURN(rc); }
/* * If the object still has SUID+SGID bits set (see ofd_precreate_object()) then * we will accept the UID+GID if sent by the client for initializing the * ownership of this object. We only allow this to happen once (so clear these * bits) and later only allow setattr. */ int ofd_attr_handle_ugid(const struct lu_env *env, struct ofd_object *fo, struct lu_attr *la, int is_setattr) { struct ofd_thread_info *info = ofd_info(env); struct lu_attr *ln = &info->fti_attr2; __u32 mask = 0; int rc; ENTRY; if (!(la->la_valid & LA_UID) && !(la->la_valid & LA_GID)) RETURN(0); rc = dt_attr_get(env, ofd_object_child(fo), ln, BYPASS_CAPA); if (rc != 0) RETURN(rc); LASSERT(ln->la_valid & LA_MODE); if (!is_setattr) { if (!(ln->la_mode & S_ISUID)) la->la_valid &= ~LA_UID; if (!(ln->la_mode & S_ISGID)) la->la_valid &= ~LA_GID; } if ((la->la_valid & LA_UID) && (ln->la_mode & S_ISUID)) mask |= S_ISUID; if ((la->la_valid & LA_GID) && (ln->la_mode & S_ISGID)) mask |= S_ISGID; if (mask != 0) { if (!(la->la_valid & LA_MODE) || !is_setattr) { la->la_mode = ln->la_mode; la->la_valid |= LA_MODE; } la->la_mode &= ~mask; } RETURN(0); }
/** * Implementation of the llog_operations::lop_read_header * * This function reads the current llog header from the bottom storage * device. * * \param[in] env execution environment * \param[in] handle llog handle of the current llog * * \retval 0 on successful header read * \retval negative error if read failed */ static int llog_osd_read_header(const struct lu_env *env, struct llog_handle *handle) { struct llog_rec_hdr *llh_hdr; struct dt_object *o; struct llog_thread_info *lgi; int rc; ENTRY; LASSERT(sizeof(*handle->lgh_hdr) == LLOG_CHUNK_SIZE); o = handle->lgh_obj; LASSERT(o); lgi = llog_info(env); rc = dt_attr_get(env, o, &lgi->lgi_attr, NULL); if (rc) RETURN(rc); LASSERT(lgi->lgi_attr.la_valid & LA_SIZE); if (lgi->lgi_attr.la_size == 0) { CDEBUG(D_HA, "not reading header from 0-byte log\n"); RETURN(LLOG_EEMPTY); } lgi->lgi_off = 0; lgi->lgi_buf.lb_buf = handle->lgh_hdr; lgi->lgi_buf.lb_len = LLOG_CHUNK_SIZE; rc = dt_record_read(env, o, &lgi->lgi_buf, &lgi->lgi_off); if (rc) { CERROR("%s: error reading log header from "DFID": rc = %d\n", o->do_lu.lo_dev->ld_obd->obd_name, PFID(lu_object_fid(&o->do_lu)), rc); RETURN(rc); } llh_hdr = &handle->lgh_hdr->llh_hdr; if (LLOG_REC_HDR_NEEDS_SWABBING(llh_hdr)) lustre_swab_llog_hdr(handle->lgh_hdr); if (llh_hdr->lrh_type != LLOG_HDR_MAGIC) { CERROR("%s: bad log %s "DFID" header magic: %#x " "(expected %#x)\n", o->do_lu.lo_dev->ld_obd->obd_name, handle->lgh_name ? handle->lgh_name : "", PFID(lu_object_fid(&o->do_lu)), llh_hdr->lrh_type, LLOG_HDR_MAGIC); RETURN(-EIO); } else if (llh_hdr->lrh_len != LLOG_CHUNK_SIZE) { CERROR("%s: incorrectly sized log %s "DFID" header: " "%#x (expected %#x)\n" "you may need to re-run lconf --write_conf.\n", o->do_lu.lo_dev->ld_obd->obd_name, handle->lgh_name ? handle->lgh_name : "", PFID(lu_object_fid(&o->do_lu)), llh_hdr->lrh_len, LLOG_CHUNK_SIZE); RETURN(-EIO); } handle->lgh_last_idx = handle->lgh_hdr->llh_tail.lrt_index; RETURN(0); }
/** * Read the special file which contains the list of llog catalogs IDs * * This function reads the CATALOGS file which contains the array of llog * catalogs IDs. The main purpose of this file is to store OSP llogs indexed * by OST/MDT number. * * \param[in] env execution environment * \param[in] d corresponding storage device * \param[in] idx position to start from, usually OST/MDT index * \param[in] count how many catalog IDs to read * \param[out] idarray the buffer for the data. If it is NULL then * function returns just number of catalog IDs * in the file. * \param[in] fid LLOG_CATALOGS_OID for CATALOG object * * \retval 0 on successful read of catalog IDs * \retval negative value on error * \retval positive value which is number of records in * the file if \a idarray is NULL */ int llog_osd_get_cat_list(const struct lu_env *env, struct dt_device *d, int idx, int count, struct llog_catid *idarray, const struct lu_fid *fid) { struct llog_thread_info *lgi = llog_info(env); struct dt_object *o = NULL; struct thandle *th; int rc, size; ENTRY; LASSERT(d); size = sizeof(*idarray) * count; lgi->lgi_off = idx * sizeof(*idarray); lgi->lgi_fid = *fid; o = dt_locate(env, d, &lgi->lgi_fid); if (IS_ERR(o)) RETURN(PTR_ERR(o)); if (!dt_object_exists(o)) { th = dt_trans_create(env, d); if (IS_ERR(th)) GOTO(out, rc = PTR_ERR(th)); lgi->lgi_attr.la_valid = LA_MODE; lgi->lgi_attr.la_mode = S_IFREG | S_IRUGO | S_IWUSR; lgi->lgi_dof.dof_type = dt_mode_to_dft(S_IFREG); rc = dt_declare_create(env, o, &lgi->lgi_attr, NULL, &lgi->lgi_dof, th); if (rc) GOTO(out_trans, rc); rc = dt_trans_start_local(env, d, th); if (rc) GOTO(out_trans, rc); dt_write_lock(env, o, 0); if (!dt_object_exists(o)) rc = dt_create(env, o, &lgi->lgi_attr, NULL, &lgi->lgi_dof, th); dt_write_unlock(env, o); out_trans: dt_trans_stop(env, d, th); if (rc) GOTO(out, rc); } rc = dt_attr_get(env, o, &lgi->lgi_attr, BYPASS_CAPA); if (rc) GOTO(out, rc); if (!S_ISREG(lgi->lgi_attr.la_mode)) { CERROR("%s: CATALOGS is not a regular file!: mode = %o\n", o->do_lu.lo_dev->ld_obd->obd_name, lgi->lgi_attr.la_mode); GOTO(out, rc = -ENOENT); } CDEBUG(D_CONFIG, "cat list: disk size=%d, read=%d\n", (int)lgi->lgi_attr.la_size, size); /* return just number of llogs */ if (idarray == NULL) { rc = lgi->lgi_attr.la_size / sizeof(*idarray); GOTO(out, rc); } /* read for new ost index or for empty file */ memset(idarray, 0, size); if (lgi->lgi_attr.la_size <= lgi->lgi_off) GOTO(out, rc = 0); if (lgi->lgi_attr.la_size < lgi->lgi_off + size) size = lgi->lgi_attr.la_size - lgi->lgi_off; lgi->lgi_buf.lb_buf = idarray; lgi->lgi_buf.lb_len = size; rc = dt_record_read(env, o, &lgi->lgi_buf, &lgi->lgi_off); if (rc) { CERROR("%s: error reading CATALOGS: rc = %d\n", o->do_lu.lo_dev->ld_obd->obd_name, rc); GOTO(out, rc); } EXIT; out: lu_object_put(env, &o->do_lu); RETURN(rc); }
static int llog_osd_destroy(const struct lu_env *env, struct llog_handle *loghandle) { struct llog_thread_info *lgi = llog_info(env); struct llog_ctxt *ctxt; struct dt_object *o, *llog_dir = NULL; struct dt_device *d; struct thandle *th; char *name = NULL; int rc; ENTRY; ctxt = loghandle->lgh_ctxt; LASSERT(ctxt); o = loghandle->lgh_obj; LASSERT(o); d = lu2dt_dev(o->do_lu.lo_dev); LASSERT(d); LASSERT(d == ctxt->loc_exp->exp_obd->obd_lvfs_ctxt.dt); th = dt_trans_create(env, d); if (IS_ERR(th)) RETURN(PTR_ERR(th)); if (loghandle->lgh_name) { llog_dir = llog_osd_dir_get(env, ctxt); if (IS_ERR(llog_dir)) GOTO(out_trans, rc = PTR_ERR(llog_dir)); dt_declare_ref_del(env, o, th); name = loghandle->lgh_name; rc = dt_declare_delete(env, llog_dir, (struct dt_key *)name, th); if (rc) GOTO(out_trans, rc); } dt_declare_ref_del(env, o, th); rc = dt_declare_destroy(env, o, th); if (rc) GOTO(out_trans, rc); rc = dt_trans_start_local(env, d, th); if (rc) GOTO(out_trans, rc); dt_write_lock(env, o, 0); if (dt_object_exists(o)) { if (name) { dt_ref_del(env, o, th); dt_read_lock(env, llog_dir, 0); rc = dt_delete(env, llog_dir, (struct dt_key *) name, th, BYPASS_CAPA); dt_read_unlock(env, llog_dir); if (rc) { CERROR("%s: can't remove llog %s: rc = %d\n", o->do_lu.lo_dev->ld_obd->obd_name, name, rc); GOTO(out_unlock, rc); } } /* * XXX: compatibility bits * on old filesystems llogs are referenced by the name * on the new ones they are referenced by OI and by * the name */ rc = dt_attr_get(env, o, &lgi->lgi_attr, NULL); if (rc) GOTO(out_unlock, rc); LASSERT(lgi->lgi_attr.la_nlink < 2); if (lgi->lgi_attr.la_nlink == 1) dt_ref_del(env, o, th); rc = dt_destroy(env, o, th); if (rc) GOTO(out_unlock, rc); } out_unlock: dt_write_unlock(env, o); out_trans: dt_trans_stop(env, d, th); if (llog_dir != NULL) lu_object_put(env, &llog_dir->do_lu); RETURN(rc); }
/* sets: * - cur_offset to the furthest point read in the log file * - cur_idx to the log index preceeding cur_offset * returns -EIO/-EINVAL on error */ static int llog_osd_next_block(const struct lu_env *env, struct llog_handle *loghandle, int *cur_idx, int next_idx, __u64 *cur_offset, void *buf, int len) { struct llog_thread_info *lgi = llog_info(env); struct dt_object *o; struct dt_device *dt; int rc; ENTRY; LASSERT(env); LASSERT(lgi); if (len == 0 || len & (LLOG_CHUNK_SIZE - 1)) RETURN(-EINVAL); CDEBUG(D_OTHER, "looking for log index %u (cur idx %u off "LPU64")\n", next_idx, *cur_idx, *cur_offset); LASSERT(loghandle); LASSERT(loghandle->lgh_ctxt); o = loghandle->lgh_obj; LASSERT(o); LASSERT(dt_object_exists(o)); dt = lu2dt_dev(o->do_lu.lo_dev); LASSERT(dt); rc = dt_attr_get(env, o, &lgi->lgi_attr, BYPASS_CAPA); if (rc) GOTO(out, rc); while (*cur_offset < lgi->lgi_attr.la_size) { struct llog_rec_hdr *rec, *last_rec; struct llog_rec_tail *tail; llog_skip_over(cur_offset, *cur_idx, next_idx); /* read up to next LLOG_CHUNK_SIZE block */ lgi->lgi_buf.lb_len = LLOG_CHUNK_SIZE - (*cur_offset & (LLOG_CHUNK_SIZE - 1)); lgi->lgi_buf.lb_buf = buf; /* Note: read lock is not needed around la_size get above at * the time of dt_attr_get(). There are only two cases that * matter. Either la_size == cur_offset, in which case the * entire read is skipped, or la_size > cur_offset and the loop * is entered and this thread is blocked at dt_read_lock() * until the write is completed. When the write completes, then * the dt_read() will be done with the full length, and will * get the full data. */ dt_read_lock(env, o, 0); rc = dt_read(env, o, &lgi->lgi_buf, cur_offset); dt_read_unlock(env, o); if (rc < 0) { CERROR("%s: can't read llog block from log "DFID " offset "LPU64": rc = %d\n", o->do_lu.lo_dev->ld_obd->obd_name, PFID(lu_object_fid(&o->do_lu)), *cur_offset, rc); GOTO(out, rc); } if (rc < len) { /* signal the end of the valid buffer to * llog_process */ memset(buf + rc, 0, len - rc); } if (rc == 0) /* end of file, nothing to do */ GOTO(out, rc); if (rc < sizeof(*tail)) { CERROR("%s: invalid llog block at log id "LPU64"/%u " "offset "LPU64"\n", o->do_lu.lo_dev->ld_obd->obd_name, loghandle->lgh_id.lgl_oid, loghandle->lgh_id.lgl_ogen, *cur_offset); GOTO(out, rc = -EINVAL); } rec = buf; if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) lustre_swab_llog_rec(rec); tail = (struct llog_rec_tail *)((char *)buf + rc - sizeof(struct llog_rec_tail)); /* get the last record in block */ last_rec = (struct llog_rec_hdr *)((char *)buf + rc - le32_to_cpu(tail->lrt_len)); if (LLOG_REC_HDR_NEEDS_SWABBING(last_rec)) lustre_swab_llog_rec(last_rec); LASSERT(last_rec->lrh_index == tail->lrt_index); *cur_idx = tail->lrt_index; /* this shouldn't happen */ if (tail->lrt_index == 0) { CERROR("%s: invalid llog tail at log id "LPU64"/%u " "offset "LPU64"\n", o->do_lu.lo_dev->ld_obd->obd_name, loghandle->lgh_id.lgl_oid, loghandle->lgh_id.lgl_ogen, *cur_offset); GOTO(out, rc = -EINVAL); } if (tail->lrt_index < next_idx) continue; /* sanity check that the start of the new buffer is no farther * than the record that we wanted. This shouldn't happen. */ if (rec->lrh_index > next_idx) { CERROR("%s: missed desired record? %u > %u\n", o->do_lu.lo_dev->ld_obd->obd_name, rec->lrh_index, next_idx); GOTO(out, rc = -ENOENT); } GOTO(out, rc = 0); } GOTO(out, rc = -EIO); out: return rc; }
/* appends if idx == -1, otherwise overwrites record idx. */ static int llog_osd_write_rec(const struct lu_env *env, struct llog_handle *loghandle, struct llog_rec_hdr *rec, struct llog_cookie *reccookie, int cookiecount, void *buf, int idx, struct thandle *th) { struct llog_thread_info *lgi = llog_info(env); struct llog_log_hdr *llh; int reclen = rec->lrh_len; int index, rc; struct llog_rec_tail *lrt; struct dt_object *o; size_t left; ENTRY; LASSERT(env); llh = loghandle->lgh_hdr; LASSERT(llh); o = loghandle->lgh_obj; LASSERT(o); LASSERT(th); CDEBUG(D_OTHER, "new record %x to "DFID"\n", rec->lrh_type, PFID(lu_object_fid(&o->do_lu))); /* record length should not bigger than LLOG_CHUNK_SIZE */ if (buf) rc = (reclen > LLOG_CHUNK_SIZE - sizeof(struct llog_rec_hdr) - sizeof(struct llog_rec_tail)) ? -E2BIG : 0; else rc = (reclen > LLOG_CHUNK_SIZE) ? -E2BIG : 0; if (rc) RETURN(rc); rc = dt_attr_get(env, o, &lgi->lgi_attr, NULL); if (rc) RETURN(rc); if (buf) /* write_blob adds header and tail to lrh_len. */ reclen = sizeof(*rec) + rec->lrh_len + sizeof(struct llog_rec_tail); if (idx != -1) { /* no header: only allowed to insert record 1 */ if (idx != 1 && lgi->lgi_attr.la_size == 0) LBUG(); if (idx && llh->llh_size && llh->llh_size != rec->lrh_len) RETURN(-EINVAL); if (!ext2_test_bit(idx, llh->llh_bitmap)) CERROR("%s: modify unset record %u\n", o->do_lu.lo_dev->ld_obd->obd_name, idx); if (idx != rec->lrh_index) CERROR("%s: index mismatch %d %u\n", o->do_lu.lo_dev->ld_obd->obd_name, idx, rec->lrh_index); lgi->lgi_off = 0; rc = llog_osd_write_blob(env, o, &llh->llh_hdr, NULL, &lgi->lgi_off, th); /* we are done if we only write the header or on error */ if (rc || idx == 0) RETURN(rc); if (buf) { /* We assume that caller has set lgh_cur_* */ lgi->lgi_off = loghandle->lgh_cur_offset; CDEBUG(D_OTHER, "modify record "LPX64": idx:%d/%u/%d, len:%u " "offset %llu\n", loghandle->lgh_id.lgl_oid, idx, rec->lrh_index, loghandle->lgh_cur_idx, rec->lrh_len, (long long)(lgi->lgi_off - sizeof(*llh))); if (rec->lrh_index != loghandle->lgh_cur_idx) { CERROR("%s: modify idx mismatch %u/%d\n", o->do_lu.lo_dev->ld_obd->obd_name, idx, loghandle->lgh_cur_idx); RETURN(-EFAULT); } } else { /* Assumes constant lrh_len */ lgi->lgi_off = sizeof(*llh) + (idx - 1) * reclen; } rc = llog_osd_write_blob(env, o, rec, buf, &lgi->lgi_off, th); if (rc == 0 && reccookie) { reccookie->lgc_lgl = loghandle->lgh_id; reccookie->lgc_index = idx; rc = 1; } RETURN(rc); } /* Make sure that records don't cross a chunk boundary, so we can * process them page-at-a-time if needed. If it will cross a chunk * boundary, write in a fake (but referenced) entry to pad the chunk. * * We know that llog_current_log() will return a loghandle that is * big enough to hold reclen, so all we care about is padding here. */ LASSERT(lgi->lgi_attr.la_valid & LA_SIZE); lgi->lgi_off = lgi->lgi_attr.la_size; left = LLOG_CHUNK_SIZE - (lgi->lgi_off & (LLOG_CHUNK_SIZE - 1)); /* NOTE: padding is a record, but no bit is set */ if (left != 0 && left != reclen && left < (reclen + LLOG_MIN_REC_SIZE)) { index = loghandle->lgh_last_idx + 1; rc = llog_osd_pad(env, o, &lgi->lgi_off, left, index, th); if (rc) RETURN(rc); loghandle->lgh_last_idx++; /*for pad rec*/ } /* if it's the last idx in log file, then return -ENOSPC */ if (loghandle->lgh_last_idx >= LLOG_BITMAP_SIZE(llh) - 1) RETURN(-ENOSPC); loghandle->lgh_last_idx++; index = loghandle->lgh_last_idx; LASSERT(index < LLOG_BITMAP_SIZE(llh)); rec->lrh_index = index; if (buf == NULL) { lrt = (struct llog_rec_tail *)((char *)rec + rec->lrh_len - sizeof(*lrt)); lrt->lrt_len = rec->lrh_len; lrt->lrt_index = rec->lrh_index; } /* The caller should make sure only 1 process access the lgh_last_idx, * Otherwise it might hit the assert.*/ LASSERT(index < LLOG_BITMAP_SIZE(llh)); spin_lock(&loghandle->lgh_hdr_lock); if (ext2_set_bit(index, llh->llh_bitmap)) { CERROR("%s: index %u already set in log bitmap\n", o->do_lu.lo_dev->ld_obd->obd_name, index); spin_unlock(&loghandle->lgh_hdr_lock); LBUG(); /* should never happen */ } llh->llh_count++; spin_unlock(&loghandle->lgh_hdr_lock); llh->llh_tail.lrt_index = index; lgi->lgi_off = 0; rc = llog_osd_write_blob(env, o, &llh->llh_hdr, NULL, &lgi->lgi_off, th); if (rc) RETURN(rc); rc = dt_attr_get(env, o, &lgi->lgi_attr, NULL); if (rc) RETURN(rc); LASSERT(lgi->lgi_attr.la_valid & LA_SIZE); lgi->lgi_off = lgi->lgi_attr.la_size; rc = llog_osd_write_blob(env, o, rec, buf, &lgi->lgi_off, th); if (rc) RETURN(rc); CDEBUG(D_RPCTRACE, "added record "LPX64": idx: %u, %u\n", loghandle->lgh_id.lgl_oid, index, rec->lrh_len); if (rc == 0 && reccookie) { reccookie->lgc_lgl = loghandle->lgh_id; reccookie->lgc_index = index; if ((rec->lrh_type == MDS_UNLINK_REC) || (rec->lrh_type == MDS_SETATTR64_REC)) reccookie->lgc_subsys = LLOG_MDS_OST_ORIG_CTXT; else if (rec->lrh_type == OST_SZ_REC) reccookie->lgc_subsys = LLOG_SIZE_ORIG_CTXT; else reccookie->lgc_subsys = -1; rc = 1; } RETURN(rc); }
static int out_attr_get(struct tgt_session_info *tsi) { const struct lu_env *env = tsi->tsi_env; struct tgt_thread_info *tti = tgt_th_info(env); struct obdo *obdo = &tti->tti_u.update.tti_obdo; struct lu_attr *la = &tti->tti_attr; struct dt_object *obj = tti->tti_u.update.tti_dt_object; int rc; ENTRY; if (!lu_object_exists(&obj->do_lu)) RETURN(-ENOENT); dt_read_lock(env, obj, MOR_TGT_CHILD); rc = dt_attr_get(env, obj, la, NULL); if (rc) GOTO(out_unlock, rc); /* * If it is a directory, we will also check whether the * directory is empty. * la_flags = 0 : Empty. * = 1 : Not empty. */ la->la_flags = 0; if (S_ISDIR(la->la_mode)) { struct dt_it *it; const struct dt_it_ops *iops; if (!dt_try_as_dir(env, obj)) GOTO(out_unlock, rc = -ENOTDIR); iops = &obj->do_index_ops->dio_it; it = iops->init(env, obj, LUDA_64BITHASH, BYPASS_CAPA); if (!IS_ERR(it)) { int result; result = iops->get(env, it, (const void *)""); if (result > 0) { int i; for (result = 0, i = 0; result == 0 && i < 3; ++i) result = iops->next(env, it); if (result == 0) la->la_flags = 1; } else if (result == 0) /* * Huh? Index contains no zero key? */ rc = -EIO; iops->put(env, it); iops->fini(env, it); } } obdo->o_valid = 0; obdo_from_la(obdo, la, la->la_valid); obdo_cpu_to_le(obdo, obdo); lustre_set_wire_obdo(NULL, obdo, obdo); out_unlock: dt_read_unlock(env, obj); CDEBUG(D_INFO, "%s: insert attr get reply %p index %d: rc = %d\n", tgt_name(tsi->tsi_tgt), tti->tti_u.update.tti_update_reply, 0, rc); update_insert_reply(tti->tti_u.update.tti_update_reply, obdo, sizeof(*obdo), 0, rc); RETURN(rc); }
static int lfsck_namespace_exec_dir(const struct lu_env *env, struct lfsck_component *com, struct dt_object *obj, struct lu_dirent *ent) { struct lfsck_thread_info *info = lfsck_env_info(env); struct lu_attr *la = &info->lti_la; struct lfsck_instance *lfsck = com->lc_lfsck; struct lfsck_bookmark *bk = &lfsck->li_bookmark_ram; struct lfsck_namespace *ns = com->lc_file_ram; struct linkea_data ldata = { 0 }; const struct lu_fid *pfid = lfsck_dto2fid(lfsck->li_obj_dir); const struct lu_fid *cfid = lfsck_dto2fid(obj); const struct lu_name *cname; struct thandle *handle = NULL; bool repaired = false; bool locked = false; bool remove; bool newdata; int count = 0; int rc; ENTRY; cname = lfsck_name_get_const(env, ent->lde_name, ent->lde_namelen); down_write(&com->lc_sem); com->lc_new_checked++; if (ent->lde_attrs & LUDA_UPGRADE) { ns->ln_flags |= LF_UPGRADE; repaired = true; } else if (ent->lde_attrs & LUDA_REPAIR) { ns->ln_flags |= LF_INCONSISTENT; repaired = true; } if (ent->lde_name[0] == '.' && (ent->lde_namelen == 1 || (ent->lde_namelen == 2 && ent->lde_name[1] == '.') || fid_is_dot_lustre(&ent->lde_fid))) GOTO(out, rc = 0); if (!(bk->lb_param & LPF_DRYRUN) && (com->lc_journal || repaired)) { again: LASSERT(!locked); com->lc_journal = 1; handle = dt_trans_create(env, lfsck->li_next); if (IS_ERR(handle)) GOTO(out, rc = PTR_ERR(handle)); rc = lfsck_declare_namespace_exec_dir(env, obj, handle); if (rc != 0) GOTO(stop, rc); rc = dt_trans_start(env, lfsck->li_next, handle); if (rc != 0) GOTO(stop, rc); dt_write_lock(env, obj, MOR_TGT_CHILD); locked = true; } rc = lfsck_namespace_check_exist(env, lfsck, obj, ent->lde_name); if (rc != 0) GOTO(stop, rc); rc = lfsck_links_read(env, obj, &ldata); if (rc == 0) { count = ldata.ld_leh->leh_reccount; rc = linkea_links_find(&ldata, cname, pfid); if ((rc == 0) && (count == 1 || !S_ISDIR(lfsck_object_type(obj)))) goto record; ns->ln_flags |= LF_INCONSISTENT; /* For dir, if there are more than one linkea entries, or the * linkea entry does not match the name entry, then remove all * and add the correct one. */ if (S_ISDIR(lfsck_object_type(obj))) { remove = true; newdata = true; } else { remove = false; newdata = false; } goto nodata; } else if (unlikely(rc == -EINVAL)) { count = 1; ns->ln_flags |= LF_INCONSISTENT; /* The magic crashed, we are not sure whether there are more * corrupt data in the linkea, so remove all linkea entries. */ remove = true; newdata = true; goto nodata; } else if (rc == -ENODATA) { count = 1; ns->ln_flags |= LF_UPGRADE; remove = false; newdata = true; nodata: if (bk->lb_param & LPF_DRYRUN) { repaired = true; goto record; } if (!com->lc_journal) goto again; if (remove) { LASSERT(newdata); rc = dt_xattr_del(env, obj, XATTR_NAME_LINK, handle, BYPASS_CAPA); if (rc != 0) GOTO(stop, rc); } if (newdata) { rc = linkea_data_new(&ldata, &lfsck_env_info(env)->lti_linkea_buf); if (rc != 0) GOTO(stop, rc); } rc = linkea_add_buf(&ldata, cname, pfid); if (rc != 0) GOTO(stop, rc); rc = lfsck_links_write(env, obj, &ldata, handle); if (rc != 0) GOTO(stop, rc); count = ldata.ld_leh->leh_reccount; repaired = true; } else { GOTO(stop, rc); } record: LASSERT(count > 0); rc = dt_attr_get(env, obj, la, BYPASS_CAPA); if (rc != 0) GOTO(stop, rc); if ((count == 1) && (la->la_nlink == 1 || S_ISDIR(lfsck_object_type(obj)))) /* Usually, it is for single linked object or dir, do nothing.*/ GOTO(stop, rc); /* Following modification will be in another transaction. */ if (handle != NULL) { LASSERT(dt_write_locked(env, obj)); dt_write_unlock(env, obj); locked = false; dt_trans_stop(env, lfsck->li_next, handle); handle = NULL; } ns->ln_mlinked_checked++; rc = lfsck_namespace_update(env, com, cfid, count != la->la_nlink ? LLF_UNMATCH_NLINKS : 0, false); GOTO(out, rc); stop: if (locked) dt_write_unlock(env, obj); if (handle != NULL) dt_trans_stop(env, lfsck->li_next, handle); out: if (rc < 0) { ns->ln_items_failed++; if (lfsck_pos_is_zero(&ns->ln_pos_first_inconsistent)) lfsck_pos_fill(env, lfsck, &ns->ln_pos_first_inconsistent, false); if (!(bk->lb_param & LPF_FAILOUT)) rc = 0; } else { if (repaired) { ns->ln_items_repaired++; if (bk->lb_param & LPF_DRYRUN && lfsck_pos_is_zero(&ns->ln_pos_first_inconsistent)) lfsck_pos_fill(env, lfsck, &ns->ln_pos_first_inconsistent, false); } else { com->lc_journal = 0; } rc = 0; } up_write(&com->lc_sem); return rc; }
/** * Implementation of the llog_operations::lop_write * * This function writes the new record in the llog or modify the existed one. * * \param[in] env execution environment * \param[in] loghandle llog handle of the current llog * \param[in] rec llog record header. This is a real header of * the full llog record to write. This is * the beginning of buffer to write, the length * of buffer is stored in \a rec::lrh_len * \param[out] reccookie pointer to the cookie to return back if needed. * It is used for further cancel of this llog * record. * \param[in] idx index of the llog record. If \a idx == -1 then * this is append case, otherwise \a idx is * the index of record to modify * \param[in] th current transaction handle * * \retval 0 on successful write && \a reccookie == NULL * 1 on successful write && \a reccookie != NULL * \retval negative error if write failed */ static int llog_osd_write_rec(const struct lu_env *env, struct llog_handle *loghandle, struct llog_rec_hdr *rec, struct llog_cookie *reccookie, int idx, struct thandle *th) { struct llog_thread_info *lgi = llog_info(env); struct llog_log_hdr *llh; int reclen = rec->lrh_len; int index, rc; struct llog_rec_tail *lrt; struct dt_object *o; size_t left; bool header_is_updated = false; ENTRY; LASSERT(env); llh = loghandle->lgh_hdr; LASSERT(llh); o = loghandle->lgh_obj; LASSERT(o); LASSERT(th); CDEBUG(D_OTHER, "new record %x to "DFID"\n", rec->lrh_type, PFID(lu_object_fid(&o->do_lu))); /* record length should not bigger than LLOG_CHUNK_SIZE */ if (reclen > LLOG_CHUNK_SIZE) RETURN(-E2BIG); rc = dt_attr_get(env, o, &lgi->lgi_attr, NULL); if (rc) RETURN(rc); /** * The modification case. * If idx set then the record with that index must be modified. * There are three cases possible: * 1) the common case is the llog header update (idx == 0) * 2) the llog record modification during llog process. * This is indicated by the \a loghandle::lgh_cur_idx > 0. * In that case the \a loghandle::lgh_cur_offset * 3) otherwise this is assumed that llog consist of records of * fixed size, i.e. catalog. The llog header must has llh_size * field equal to record size. The record offset is calculated * just by /a idx value * * During modification we don't need extra header update because * the bitmap and record count are not changed. The record header * and tail remains the same too. */ if (idx != LLOG_NEXT_IDX) { /* llog can be empty only when first record is being written */ LASSERT(ergo(idx > 0, lgi->lgi_attr.la_size > 0)); if (!ext2_test_bit(idx, llh->llh_bitmap)) { CERROR("%s: modify unset record %u\n", o->do_lu.lo_dev->ld_obd->obd_name, idx); RETURN(-ENOENT); } if (idx != rec->lrh_index) { CERROR("%s: modify index mismatch %d %u\n", o->do_lu.lo_dev->ld_obd->obd_name, idx, rec->lrh_index); RETURN(-EFAULT); } if (idx == LLOG_HEADER_IDX) { /* llog header update */ LASSERT(reclen == sizeof(struct llog_log_hdr)); LASSERT(rec == &llh->llh_hdr); lgi->lgi_off = 0; lgi->lgi_buf.lb_len = reclen; lgi->lgi_buf.lb_buf = rec; rc = dt_record_write(env, o, &lgi->lgi_buf, &lgi->lgi_off, th); RETURN(rc); } else if (loghandle->lgh_cur_idx > 0) { /** * The lgh_cur_offset can be used only if index is * the same. */ if (idx != loghandle->lgh_cur_idx) { CERROR("%s: modify index mismatch %d %d\n", o->do_lu.lo_dev->ld_obd->obd_name, idx, loghandle->lgh_cur_idx); RETURN(-EFAULT); } lgi->lgi_off = loghandle->lgh_cur_offset; CDEBUG(D_OTHER, "modify record "DOSTID": idx:%d, " "len:%u offset %llu\n", POSTID(&loghandle->lgh_id.lgl_oi), idx, rec->lrh_len, (long long)lgi->lgi_off); } else if (llh->llh_size > 0) { if (llh->llh_size != rec->lrh_len) { CERROR("%s: wrong record size, llh_size is %u" " but record size is %u\n", o->do_lu.lo_dev->ld_obd->obd_name, llh->llh_size, rec->lrh_len); RETURN(-EINVAL); } lgi->lgi_off = sizeof(*llh) + (idx - 1) * reclen; } else { /* This can be result of lgh_cur_idx is not set during * llog processing or llh_size is not set to proper * record size for fixed records llog. Therefore it is * impossible to get record offset. */ CERROR("%s: can't get record offset, idx:%d, " "len:%u.\n", o->do_lu.lo_dev->ld_obd->obd_name, idx, rec->lrh_len); RETURN(-EFAULT); } /* update only data, header and tail remain the same */ lgi->lgi_off += sizeof(struct llog_rec_hdr); lgi->lgi_buf.lb_len = REC_DATA_LEN(rec); lgi->lgi_buf.lb_buf = REC_DATA(rec); rc = dt_record_write(env, o, &lgi->lgi_buf, &lgi->lgi_off, th); if (rc == 0 && reccookie) { reccookie->lgc_lgl = loghandle->lgh_id; reccookie->lgc_index = idx; rc = 1; } RETURN(rc); } /** * The append case. * The most common case of using llog. The new index is assigned to * the new record, new bit is set in llog bitmap and llog count is * incremented. * * Make sure that records don't cross a chunk boundary, so we can * process them page-at-a-time if needed. If it will cross a chunk * boundary, write in a fake (but referenced) entry to pad the chunk. */ LASSERT(lgi->lgi_attr.la_valid & LA_SIZE); lgi->lgi_off = lgi->lgi_attr.la_size; left = LLOG_CHUNK_SIZE - (lgi->lgi_off & (LLOG_CHUNK_SIZE - 1)); /* NOTE: padding is a record, but no bit is set */ if (left != 0 && left != reclen && left < (reclen + LLOG_MIN_REC_SIZE)) { index = loghandle->lgh_last_idx + 1; rc = llog_osd_pad(env, o, &lgi->lgi_off, left, index, th); if (rc) RETURN(rc); loghandle->lgh_last_idx++; /* for pad rec */ } /* if it's the last idx in log file, then return -ENOSPC */ if (loghandle->lgh_last_idx >= LLOG_BITMAP_SIZE(llh) - 1) RETURN(-ENOSPC); /* increment the last_idx along with llh_tail index, they should * be equal for a llog lifetime */ loghandle->lgh_last_idx++; index = loghandle->lgh_last_idx; llh->llh_tail.lrt_index = index; /** * NB: the caller should make sure only 1 process access * the lgh_last_idx, e.g. append should be exclusive. * Otherwise it might hit the assert. */ LASSERT(index < LLOG_BITMAP_SIZE(llh)); rec->lrh_index = index; lrt = rec_tail(rec); lrt->lrt_len = rec->lrh_len; lrt->lrt_index = rec->lrh_index; /* the lgh_hdr_lock protects llog header data from concurrent * update/cancel, the llh_count and llh_bitmap are protected */ spin_lock(&loghandle->lgh_hdr_lock); if (ext2_set_bit(index, llh->llh_bitmap)) { CERROR("%s: index %u already set in log bitmap\n", o->do_lu.lo_dev->ld_obd->obd_name, index); spin_unlock(&loghandle->lgh_hdr_lock); LBUG(); /* should never happen */ } llh->llh_count++; spin_unlock(&loghandle->lgh_hdr_lock); lgi->lgi_off = 0; lgi->lgi_buf.lb_len = llh->llh_hdr.lrh_len; lgi->lgi_buf.lb_buf = &llh->llh_hdr; rc = dt_record_write(env, o, &lgi->lgi_buf, &lgi->lgi_off, th); if (rc) GOTO(out, rc); header_is_updated = true; rc = dt_attr_get(env, o, &lgi->lgi_attr, NULL); if (rc) GOTO(out, rc); LASSERT(lgi->lgi_attr.la_valid & LA_SIZE); lgi->lgi_off = lgi->lgi_attr.la_size; lgi->lgi_buf.lb_len = reclen; lgi->lgi_buf.lb_buf = rec; rc = dt_record_write(env, o, &lgi->lgi_buf, &lgi->lgi_off, th); if (rc < 0) GOTO(out, rc); CDEBUG(D_OTHER, "added record "DOSTID": idx: %u, %u\n", POSTID(&loghandle->lgh_id.lgl_oi), index, rec->lrh_len); if (reccookie != NULL) { reccookie->lgc_lgl = loghandle->lgh_id; reccookie->lgc_index = index; if ((rec->lrh_type == MDS_UNLINK_REC) || (rec->lrh_type == MDS_SETATTR64_REC)) reccookie->lgc_subsys = LLOG_MDS_OST_ORIG_CTXT; else if (rec->lrh_type == OST_SZ_REC) reccookie->lgc_subsys = LLOG_SIZE_ORIG_CTXT; else reccookie->lgc_subsys = -1; rc = 1; } RETURN(rc); out: /* cleanup llog for error case */ spin_lock(&loghandle->lgh_hdr_lock); ext2_clear_bit(index, llh->llh_bitmap); llh->llh_count--; spin_unlock(&loghandle->lgh_hdr_lock); /* restore llog last_idx */ loghandle->lgh_last_idx--; llh->llh_tail.lrt_index = loghandle->lgh_last_idx; /* restore the header on disk if it was written */ if (header_is_updated) { lgi->lgi_off = 0; lgi->lgi_buf.lb_len = llh->llh_hdr.lrh_len; lgi->lgi_buf.lb_buf = &llh->llh_hdr; dt_record_write(env, o, &lgi->lgi_buf, &lgi->lgi_off, th); } RETURN(rc); }
/** * Implementation of the llog_operations::lop_next_block * * This function finds the the next llog block to return which contains * record with required index. It is main part of llog processing. * * \param[in] env execution environment * \param[in] loghandle llog handle of the current llog * \param[in,out] cur_idx index preceeding cur_offset * \param[in] next_idx target index to find * \param[in,out] cur_offset furtherst point read in the file * \param[in] buf pointer to data buffer to fill * \param[in] len required len to read, it is * LLOG_CHUNK_SIZE usually. * * \retval 0 on successful buffer read * \retval negative value on error */ static int llog_osd_next_block(const struct lu_env *env, struct llog_handle *loghandle, int *cur_idx, int next_idx, __u64 *cur_offset, void *buf, int len) { struct llog_thread_info *lgi = llog_info(env); struct dt_object *o; struct dt_device *dt; int rc; ENTRY; LASSERT(env); LASSERT(lgi); if (len == 0 || len & (LLOG_CHUNK_SIZE - 1)) RETURN(-EINVAL); CDEBUG(D_OTHER, "looking for log index %u (cur idx %u off "LPU64")\n", next_idx, *cur_idx, *cur_offset); LASSERT(loghandle); LASSERT(loghandle->lgh_ctxt); o = loghandle->lgh_obj; LASSERT(o); LASSERT(dt_object_exists(o)); dt = lu2dt_dev(o->do_lu.lo_dev); LASSERT(dt); rc = dt_attr_get(env, o, &lgi->lgi_attr, BYPASS_CAPA); if (rc) GOTO(out, rc); while (*cur_offset < lgi->lgi_attr.la_size) { struct llog_rec_hdr *rec, *last_rec; struct llog_rec_tail *tail; llog_skip_over(cur_offset, *cur_idx, next_idx); /* read up to next LLOG_CHUNK_SIZE block */ lgi->lgi_buf.lb_len = LLOG_CHUNK_SIZE - (*cur_offset & (LLOG_CHUNK_SIZE - 1)); lgi->lgi_buf.lb_buf = buf; rc = dt_read(env, o, &lgi->lgi_buf, cur_offset); if (rc < 0) { CERROR("%s: can't read llog block from log "DFID " offset "LPU64": rc = %d\n", o->do_lu.lo_dev->ld_obd->obd_name, PFID(lu_object_fid(&o->do_lu)), *cur_offset, rc); GOTO(out, rc); } if (rc < len) { /* signal the end of the valid buffer to * llog_process */ memset(buf + rc, 0, len - rc); } if (rc == 0) /* end of file, nothing to do */ GOTO(out, rc); if (rc < sizeof(*tail)) { CERROR("%s: invalid llog block at log id "DOSTID"/%u " "offset "LPU64"\n", o->do_lu.lo_dev->ld_obd->obd_name, POSTID(&loghandle->lgh_id.lgl_oi), loghandle->lgh_id.lgl_ogen, *cur_offset); GOTO(out, rc = -EINVAL); } rec = buf; if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) lustre_swab_llog_rec(rec); tail = (struct llog_rec_tail *)((char *)buf + rc - sizeof(struct llog_rec_tail)); /* get the last record in block */ last_rec = (struct llog_rec_hdr *)((char *)buf + rc - tail->lrt_len); if (LLOG_REC_HDR_NEEDS_SWABBING(last_rec)) lustre_swab_llog_rec(last_rec); LASSERT(last_rec->lrh_index == tail->lrt_index); *cur_idx = tail->lrt_index; /* this shouldn't happen */ if (tail->lrt_index == 0) { CERROR("%s: invalid llog tail at log id "DOSTID"/%u " "offset "LPU64"\n", o->do_lu.lo_dev->ld_obd->obd_name, POSTID(&loghandle->lgh_id.lgl_oi), loghandle->lgh_id.lgl_ogen, *cur_offset); GOTO(out, rc = -EINVAL); } if (tail->lrt_index < next_idx) continue; /* sanity check that the start of the new buffer is no farther * than the record that we wanted. This shouldn't happen. */ if (rec->lrh_index > next_idx) { CERROR("%s: missed desired record? %u > %u\n", o->do_lu.lo_dev->ld_obd->obd_name, rec->lrh_index, next_idx); GOTO(out, rc = -ENOENT); } /* Trim unsupported extensions for compat w/ older clients */ if (!(loghandle->lgh_hdr->llh_flags & LLOG_F_EXT_JOBID)) changelog_block_trim_ext(rec, last_rec, CLF_VERSION | CLF_RENAME); GOTO(out, rc = 0); } GOTO(out, rc = -EIO); out: return rc; }
static int out_attr_get(struct tgt_session_info *tsi) { const struct lu_env *env = tsi->tsi_env; struct tgt_thread_info *tti = tgt_th_info(env); struct obdo *obdo = &tti->tti_u.update.tti_obdo; struct lu_attr *la = &tti->tti_attr; struct dt_object *obj = tti->tti_u.update.tti_dt_object; int idx = tti->tti_u.update.tti_update_reply_index; int rc; ENTRY; if (!lu_object_exists(&obj->do_lu)) { /* Usually, this will be called when the master MDT try * to init a remote object(see osp_object_init), so if * the object does not exist on slave, we need set BANSHEE flag, * so the object can be removed from the cache immediately */ set_bit(LU_OBJECT_HEARD_BANSHEE, &obj->do_lu.lo_header->loh_flags); RETURN(-ENOENT); } dt_read_lock(env, obj, MOR_TGT_CHILD); rc = dt_attr_get(env, obj, la, NULL); if (rc) GOTO(out_unlock, rc); /* * If it is a directory, we will also check whether the * directory is empty. * la_flags = 0 : Empty. * = 1 : Not empty. */ la->la_flags = 0; if (S_ISDIR(la->la_mode)) { struct dt_it *it; const struct dt_it_ops *iops; if (!dt_try_as_dir(env, obj)) GOTO(out_unlock, rc = -ENOTDIR); iops = &obj->do_index_ops->dio_it; it = iops->init(env, obj, LUDA_64BITHASH, BYPASS_CAPA); if (!IS_ERR(it)) { int result; result = iops->get(env, it, (const void *)""); if (result > 0) { int i; for (result = 0, i = 0; result == 0 && i < 3; ++i) result = iops->next(env, it); if (result == 0) la->la_flags = 1; } else if (result == 0) /* * Huh? Index contains no zero key? */ rc = -EIO; iops->put(env, it); iops->fini(env, it); } } obdo->o_valid = 0; obdo_from_la(obdo, la, la->la_valid); obdo_cpu_to_le(obdo, obdo); lustre_set_wire_obdo(NULL, obdo, obdo); out_unlock: dt_read_unlock(env, obj); CDEBUG(D_INFO, "%s: insert attr get reply %p index %d: rc = %d\n", tgt_name(tsi->tsi_tgt), tti->tti_u.update.tti_update_reply, 0, rc); object_update_result_insert(tti->tti_u.update.tti_update_reply, obdo, sizeof(*obdo), idx, rc); RETURN(rc); }
/** * Implementation of the llog_operations::lop_prev_block * * This function finds the llog block to return which contains * record with required index but in reverse order - from end of llog * to the beginning. * It is main part of reverse llog processing. * * \param[in] env execution environment * \param[in] loghandle llog handle of the current llog * \param[in] prev_idx target index to find * \param[in] buf pointer to data buffer to fill * \param[in] len required len to read, it is LLOG_CHUNK_SIZE usually. * * \retval 0 on successful buffer read * \retval negative value on error */ static int llog_osd_prev_block(const struct lu_env *env, struct llog_handle *loghandle, int prev_idx, void *buf, int len) { struct llog_thread_info *lgi = llog_info(env); struct dt_object *o; struct dt_device *dt; loff_t cur_offset; int rc; ENTRY; if (len == 0 || len & (LLOG_CHUNK_SIZE - 1)) RETURN(-EINVAL); CDEBUG(D_OTHER, "looking for log index %u\n", prev_idx); LASSERT(loghandle); LASSERT(loghandle->lgh_ctxt); o = loghandle->lgh_obj; LASSERT(o); LASSERT(dt_object_exists(o)); dt = lu2dt_dev(o->do_lu.lo_dev); LASSERT(dt); cur_offset = LLOG_CHUNK_SIZE; llog_skip_over(&cur_offset, 0, prev_idx); rc = dt_attr_get(env, o, &lgi->lgi_attr, BYPASS_CAPA); if (rc) GOTO(out, rc); while (cur_offset < lgi->lgi_attr.la_size) { struct llog_rec_hdr *rec, *last_rec; struct llog_rec_tail *tail; lgi->lgi_buf.lb_len = len; lgi->lgi_buf.lb_buf = buf; rc = dt_read(env, o, &lgi->lgi_buf, &cur_offset); if (rc < 0) { CERROR("%s: can't read llog block from log "DFID " offset "LPU64": rc = %d\n", o->do_lu.lo_dev->ld_obd->obd_name, PFID(lu_object_fid(&o->do_lu)), cur_offset, rc); GOTO(out, rc); } if (rc == 0) /* end of file, nothing to do */ GOTO(out, rc); if (rc < sizeof(*tail)) { CERROR("%s: invalid llog block at log id "DOSTID"/%u " "offset "LPU64"\n", o->do_lu.lo_dev->ld_obd->obd_name, POSTID(&loghandle->lgh_id.lgl_oi), loghandle->lgh_id.lgl_ogen, cur_offset); GOTO(out, rc = -EINVAL); } rec = buf; if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) lustre_swab_llog_rec(rec); tail = (struct llog_rec_tail *)((char *)buf + rc - sizeof(struct llog_rec_tail)); /* get the last record in block */ last_rec = (struct llog_rec_hdr *)((char *)buf + rc - le32_to_cpu(tail->lrt_len)); if (LLOG_REC_HDR_NEEDS_SWABBING(last_rec)) lustre_swab_llog_rec(last_rec); LASSERT(last_rec->lrh_index == tail->lrt_index); /* this shouldn't happen */ if (tail->lrt_index == 0) { CERROR("%s: invalid llog tail at log id "DOSTID"/%u " "offset "LPU64"\n", o->do_lu.lo_dev->ld_obd->obd_name, POSTID(&loghandle->lgh_id.lgl_oi), loghandle->lgh_id.lgl_ogen, cur_offset); GOTO(out, rc = -EINVAL); } if (tail->lrt_index < prev_idx) continue; /* sanity check that the start of the new buffer is no farther * than the record that we wanted. This shouldn't happen. */ if (rec->lrh_index > prev_idx) { CERROR("%s: missed desired record? %u > %u\n", o->do_lu.lo_dev->ld_obd->obd_name, rec->lrh_index, prev_idx); GOTO(out, rc = -ENOENT); } GOTO(out, rc = 0); } GOTO(out, rc = -EIO); out: return rc; }
/** * \retval +ve repaired * \retval 0 no need to repair * \retval -ve error cases */ static int lfsck_namespace_double_scan_one(const struct lu_env *env, struct lfsck_component *com, struct dt_object *child, __u8 flags) { struct lfsck_thread_info *info = lfsck_env_info(env); struct lu_attr *la = &info->lti_la; struct lu_name *cname = &info->lti_name; struct lu_fid *pfid = &info->lti_fid; struct lu_fid *cfid = &info->lti_fid2; struct lfsck_instance *lfsck = com->lc_lfsck; struct lfsck_bookmark *bk = &lfsck->li_bookmark_ram; struct lfsck_namespace *ns = com->lc_file_ram; struct linkea_data ldata = { 0 }; struct thandle *handle = NULL; bool locked = false; bool update = false; int rc; ENTRY; if (com->lc_journal) { again: LASSERT(!locked); update = false; com->lc_journal = 1; handle = dt_trans_create(env, lfsck->li_next); if (IS_ERR(handle)) RETURN(rc = PTR_ERR(handle)); rc = dt_declare_xattr_set(env, child, lfsck_buf_get_const(env, NULL, DEFAULT_LINKEA_SIZE), XATTR_NAME_LINK, 0, handle); if (rc != 0) GOTO(stop, rc); rc = dt_trans_start(env, lfsck->li_next, handle); if (rc != 0) GOTO(stop, rc); dt_write_lock(env, child, MOR_TGT_CHILD); locked = true; } if (unlikely(lfsck_is_dead_obj(child))) GOTO(stop, rc = 0); rc = dt_attr_get(env, child, la, BYPASS_CAPA); if (rc == 0) rc = lfsck_links_read(env, child, &ldata); if (rc != 0) { if ((bk->lb_param & LPF_DRYRUN) && (rc == -EINVAL || rc == -ENODATA)) rc = 1; GOTO(stop, rc); } linkea_first_entry(&ldata); while (ldata.ld_lee != NULL) { struct dt_object *parent = NULL; rc = lfsck_linkea_entry_unpack(lfsck, &ldata, cname, pfid); if (rc > 0) update = true; if (!fid_is_sane(pfid)) goto shrink; parent = lfsck_object_find(env, lfsck, pfid); if (parent == NULL) goto shrink; else if (IS_ERR(parent)) GOTO(stop, rc = PTR_ERR(parent)); if (!dt_object_exists(parent)) goto shrink; /* XXX: Currently, skip remote object, the consistency for * remote object will be processed in LFSCK phase III. */ if (dt_object_remote(parent)) { lfsck_object_put(env, parent); linkea_next_entry(&ldata); continue; } if (unlikely(!dt_try_as_dir(env, parent))) goto shrink; /* To guarantee the 'name' is terminated with '0'. */ memcpy(info->lti_key, cname->ln_name, cname->ln_namelen); info->lti_key[cname->ln_namelen] = 0; cname->ln_name = info->lti_key; rc = dt_lookup(env, parent, (struct dt_rec *)cfid, (const struct dt_key *)cname->ln_name, BYPASS_CAPA); if (rc != 0 && rc != -ENOENT) { lfsck_object_put(env, parent); GOTO(stop, rc); } if (rc == 0) { if (lu_fid_eq(cfid, lfsck_dto2fid(child))) { lfsck_object_put(env, parent); linkea_next_entry(&ldata); continue; } goto shrink; } /* If there is no name entry in the parent dir and the object * link count is less than the linkea entries count, then the * linkea entry should be removed. */ if (ldata.ld_leh->leh_reccount > la->la_nlink) goto shrink; /* XXX: For the case of there is a linkea entry, but without * name entry pointing to the object and its hard links * count is not less than the object name entries count, * then seems we should add the 'missed' name entry back * to namespace, but before LFSCK phase III finished, we * do not know whether the object has some inconsistency * on other MDTs. So now, do NOT add the name entry back * to the namespace, but keep the linkEA entry. LU-2914 */ lfsck_object_put(env, parent); linkea_next_entry(&ldata); continue; shrink: if (parent != NULL) lfsck_object_put(env, parent); if (bk->lb_param & LPF_DRYRUN) RETURN(1); CDEBUG(D_LFSCK, "Remove linkEA: "DFID"[%.*s], "DFID"\n", PFID(lfsck_dto2fid(child)), cname->ln_namelen, cname->ln_name, PFID(pfid)); linkea_del_buf(&ldata, cname); update = true; } if (update) { if (!com->lc_journal) { com->lc_journal = 1; goto again; } rc = lfsck_links_write(env, child, &ldata, handle); } GOTO(stop, rc); stop: if (locked) { /* XXX: For the case linkea entries count does not match the object hard * links count, we cannot update the later one simply. Before LFSCK * phase III finished, we cannot know whether there are some remote * name entries to be repaired or not. LU-2914 */ if (rc == 0 && !lfsck_is_dead_obj(child) && ldata.ld_leh != NULL && ldata.ld_leh->leh_reccount != la->la_nlink) CWARN("%s: the object "DFID" linkEA entry count %u " "may not match its hardlink count %u\n", lfsck_lfsck2name(lfsck), PFID(cfid), ldata.ld_leh->leh_reccount, la->la_nlink); dt_write_unlock(env, child); } if (handle != NULL) dt_trans_stop(env, lfsck->li_next, handle); if (rc == 0 && update) { ns->ln_objs_nlink_repaired++; rc = 1; } return rc; }