static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { int err; union ubifs_key key; struct inode *inode = NULL; struct ubifs_dent_node *dent; struct ubifs_info *c = dir->i_sb->s_fs_info; dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino); if (dentry->d_name.len > UBIFS_MAX_NLEN) return ERR_PTR(-ENAMETOOLONG); dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); if (!dent) return ERR_PTR(-ENOMEM); dent_key_init(c, &key, dir->i_ino, &dentry->d_name); err = ubifs_tnc_lookup_nm(c, &key, dent, &dentry->d_name); if (err) { if (err == -ENOENT) { dbg_gen("not found"); goto done; } goto out; } if (dbg_check_name(c, dent, &dentry->d_name)) { err = -EINVAL; goto out; } inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum)); if (IS_ERR(inode)) { /* * This should not happen. Probably the file-system needs * checking. */ err = PTR_ERR(inode); ubifs_err(c, "dead directory entry '%pd', error %d", dentry, err); ubifs_ro_mode(c, err); goto out; } done: kfree(dent); /* * Note, d_splice_alias() would be required instead if we supported * NFS. */ d_add(dentry, inode); return NULL; out: kfree(dent); return ERR_PTR(err); }
/** * ubifs_sync_wbufs_by_inode - synchronize write-buffers for an inode. * @c: UBIFS file-system description object * @inode: inode to synchronize * * This function synchronizes write-buffers which contain nodes belonging to * @inode. Returns zero in case of success and a negative error code in case of * failure. */ int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode) { int i, err = 0; for (i = 0; i < c->jhead_cnt; i++) { struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; if (i == GCHD) /* * GC head is special, do not look at it. Even if the * head contains something related to this inode, it is * a _copy_ of corresponding on-flash node which sits * somewhere else. */ continue; if (!wbuf_has_ino(wbuf, inode->i_ino)) continue; mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); if (wbuf_has_ino(wbuf, inode->i_ino)) err = ubifs_wbuf_sync_nolock(wbuf); mutex_unlock(&wbuf->io_mutex); if (err) { ubifs_ro_mode(c, err); return err; } } return 0; }
/** * ubifs_jnl_write_2_inodes - write 2 inodes to the journal. * @c: UBIFS file-system description object * @inode1: first inode to write * @inode2: second inode to write * @sync: non-zero if the write-buffer has to be synchronized * * This function writes 2 inodes @inode1 and @inode2 to the journal (to the * base head - first @inode1, then @inode2). Returns zero in case of success * and a negative error code in case of failure. */ int ubifs_jnl_write_2_inodes(struct ubifs_info *c, const struct inode *inode1, const struct inode *inode2, int sync) { int err, len1, len2, aligned_len, aligned_len1, lnum, offs; struct ubifs_ino_node *ino; union ubifs_key key; dbg_jnl("ino %lu, ino %lu", inode1->i_ino, inode2->i_ino); ubifs_assert(inode1->i_nlink > 0); ubifs_assert(inode2->i_nlink > 0); len1 = UBIFS_INO_NODE_SZ + ubifs_inode(inode1)->data_len; len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode2)->data_len; aligned_len1 = ALIGN(len1, 8); aligned_len = aligned_len1 + ALIGN(len2, 8); ino = kmalloc(aligned_len, GFP_NOFS); if (!ino) return -ENOMEM; /* Make reservation before allocating sequence numbers */ err = make_reservation(c, BASEHD, aligned_len); if (err) goto out_free; pack_inode(c, ino, inode1, 0, 0); pack_inode(c, (void *)ino + aligned_len1, inode2, 1, 0); err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0); if (!sync && !err) { struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; ubifs_wbuf_add_ino_nolock(wbuf, inode1->i_ino); ubifs_wbuf_add_ino_nolock(wbuf, inode2->i_ino); } release_head(c, BASEHD); if (err) goto out_ro; ino_key_init(c, &key, inode1->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, len1); if (err) goto out_ro; ino_key_init(c, &key, inode2->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2); if (err) goto out_ro; finish_reservation(c); kfree(ino); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: kfree(ino); return err; }
/** * ubifs_jrn_write_inode - flush inode to the journal. * @c: UBIFS file-system description object * @inode: inode to flush * @last_reference: inode has been deleted * @sync: non-zero if the write-buffer has to be synchronized * * This function writes inode @inode to the journal (to the base head). Returns * zero in case of success and a negative error code in case of failure. */ int ubifs_jrn_write_inode(struct ubifs_info *c, const struct inode *inode, int last_reference, int sync) { int err, len, lnum, offs; struct ubifs_ino_node *ino; struct ubifs_inode *ui = ubifs_inode(inode); dbg_jrn("ino %lu%s", inode->i_ino, last_reference ? " (last reference)" : ""); if (last_reference) ubifs_assert(inode->i_nlink == 0); /* If the inode is deleted, do not write the attached data */ len = UBIFS_INO_NODE_SZ; if (!last_reference) len += ui->data_len; ino = kmalloc(len, GFP_NOFS); if (!ino) return -ENOMEM; pack_inode(c, ino, inode, 1, last_reference); err = make_reservation(c, BASEHD, len); if (err) goto out_free; err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync); if (!sync && !err) ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inode->i_ino); release_head(c, BASEHD); if (err) goto out_ro; if (last_reference) { err = ubifs_tnc_remove_ino(c, inode->i_ino); if (err) goto out_ro; ubifs_delete_orphan(c, inode->i_ino); err = ubifs_add_dirt(c, lnum, len); } else { union ubifs_key key; ino_key_init(c, &key, inode->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, len); } if (err) goto out_ro; finish_reservation(c); kfree(ino); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: kfree(ino); return err; }
/** * ubifs_bg_wbufs_sync - synchronize write-buffers. * @c: UBIFS file-system description object * * This function is called by background thread to synchronize write-buffers. * Returns zero in case of success and a negative error code in case of * failure. */ int ubifs_bg_wbufs_sync(struct ubifs_info *c) { int err, i; ubifs_assert(!c->ro_media && !c->ro_mount); if (!c->need_wbuf_sync) return 0; c->need_wbuf_sync = 0; if (c->ro_error) { err = -EROFS; goto out_timers; } dbg_io("synchronize"); for (i = 0; i < c->jhead_cnt; i++) { struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; cond_resched(); /* * If the mutex is locked then wbuf is being changed, so * synchronization is not necessary. */ if (mutex_is_locked(&wbuf->io_mutex)) continue; mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); if (!wbuf->need_sync) { mutex_unlock(&wbuf->io_mutex); continue; } err = ubifs_wbuf_sync_nolock(wbuf); mutex_unlock(&wbuf->io_mutex); if (err) { ubifs_err("cannot sync write-buffer, error %d", err); ubifs_ro_mode(c, err); goto out_timers; } } return 0; out_timers: /* Cancel all timers to prevent repeated errors */ for (i = 0; i < c->jhead_cnt; i++) { struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); cancel_wbuf_timer_nolock(wbuf); mutex_unlock(&wbuf->io_mutex); } return err; }
int ubifs_leb_map(struct ubifs_info *c, int lnum) { int err; ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; if (!dbg_is_tst_rcvry(c)) err = ubi_leb_map(c->ubi, lnum); else err = dbg_leb_map(c, lnum); if (err) { ubifs_err("mapping LEB %d failed, error %d", lnum, err); ubifs_ro_mode(c, err); dbg_dump_stack(); } return err; }
/** * ubifs_bg_thread - UBIFS background thread function. * @info: points to the file-system description object * * This function implements various file-system background activities: * o when a write-buffer timer expires it synchronizes the appropriate * write-buffer; * o when the journal is about to be full, it starts in-advance commit. * * Note, other stuff like background garbage collection may be added here in * future. */ int ubifs_bg_thread(void *info) { int err; struct ubifs_info *c = info; ubifs_msg("background thread \"%s\" started, PID %d", c->vi.ubi_num, c->bgt_name, current->pid); set_freezable(); while (1) { if (kthread_should_stop()) break; if (try_to_freeze()) continue; set_current_state(TASK_INTERRUPTIBLE); /* Check if there is something to do */ if (!c->need_bgt) { /* * Nothing prevents us from going sleep now and * be never woken up and block the task which * could wait in 'kthread_stop()' forever. */ if (kthread_should_stop()) break; schedule(); continue; } else __set_current_state(TASK_RUNNING); c->need_bgt = 0; err = ubifs_bg_wbufs_sync(c); if (err) ubifs_ro_mode(c, err); run_bg_commit(c); cond_resched(); } ubifs_msg("background thread \"%s\" stops", c->vi.ubi_num, c->bgt_name); return 0; }
int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len) { int err; ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; if (!dbg_is_tst_rcvry(c)) err = ubi_leb_change(c->ubi, lnum, buf, len); else err = dbg_leb_change(c, lnum, buf, len); if (err) { ubifs_err("changing %d bytes in LEB %d failed, error %d", len, lnum, err); ubifs_ro_mode(c, err); dbg_dump_stack(); } return err; }
/** * next_sqnum - get next sequence number. * @c: UBIFS file-system description object */ static unsigned long long next_sqnum(struct ubifs_info *c) { unsigned long long sqnum; spin_lock(&c->cnt_lock); sqnum = ++c->max_sqnum; spin_unlock(&c->cnt_lock); if (unlikely(sqnum >= SQNUM_WARN_WATERMARK)) { if (sqnum >= SQNUM_WATERMARK) { ubifs_err("sequence number overflow %llu, end of life", sqnum); ubifs_ro_mode(c, -EINVAL); } ubifs_warn("running out of sequence numbers, end of life soon"); } return sqnum; }
int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs, int len, int dtype) { int err; ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; if (!dbg_is_tst_rcvry(c)) err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype); else err = dbg_leb_write(c, lnum, buf, offs, len, dtype); if (err) { ubifs_err("writing %d bytes to LEB %d:%d failed, error %d", len, lnum, offs, err); ubifs_ro_mode(c, err); dbg_dump_stack(); } return err; }
/** * ubifs_sync_wbufs_by_inodes - synchronize write-buffers which have data. * belonging to specified inodes. * @c: UBIFS file-system description object * @inodes: array of inodes * @count: number of elements in @inodes * * This function synchronizes write-buffers which contain nodes belonging to * any inode specified in @inodes array. Returns zero in case of success and a * negative error code in case of failure. */ int ubifs_sync_wbufs_by_inodes(struct ubifs_info *c, struct inode * const *inodes, int count) { int i, j, err = 0; ubifs_assert(count); for (i = 0; i < c->jhead_cnt; i++) { struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; if (i == GCHD) /* * GC head is special, do not look at it. Even if the * head contains something related to this inode, it is * a _copy_ of corresponding on-flash node which sits * somewhere else. */ continue; for (j = 0; j < count && !err; j++) if (wbuf_has_ino(wbuf, inodes[j]->i_ino)) { mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); if (wbuf_has_ino(wbuf, inodes[j]->i_ino)) err = ubifs_wbuf_sync_nolock(wbuf); mutex_unlock(&wbuf->io_mutex); break; } if (err) { ubifs_ro_mode(c, err); break; } } return err; }
/** * do_commit - commit the journal. * @c: UBIFS file-system description object * * This function implements UBIFS commit. It has to be called with commit lock * locked. Returns zero in case of success and a negative error code in case of * failure. */ static int do_commit(struct ubifs_info *c) { int err, new_ltail_lnum, old_ltail_lnum, i; struct ubifs_zbranch zroot; struct ubifs_lp_stats lst; dbg_cmt("start"); if (c->ro_media) { err = -EROFS; goto out_up; } /* Sync all write buffers (necessary for recovery) */ for (i = 0; i < c->jhead_cnt; i++) { err = ubifs_wbuf_sync(&c->jheads[i].wbuf); if (err) goto out_up; } c->cmt_no += 1; err = ubifs_gc_start_commit(c); if (err) goto out_up; err = dbg_check_lprops(c); if (err) goto out_up; err = ubifs_log_start_commit(c, &new_ltail_lnum); if (err) goto out_up; err = ubifs_tnc_start_commit(c, &zroot); if (err) goto out_up; err = ubifs_lpt_start_commit(c); if (err) goto out_up; err = ubifs_orphan_start_commit(c); if (err) goto out_up; ubifs_get_lp_stats(c, &lst); up_write(&c->commit_sem); err = ubifs_tnc_end_commit(c); if (err) goto out; err = ubifs_lpt_end_commit(c); if (err) goto out; err = ubifs_orphan_end_commit(c); if (err) goto out; old_ltail_lnum = c->ltail_lnum; err = ubifs_log_end_commit(c, new_ltail_lnum); if (err) goto out; err = dbg_check_old_index(c, &zroot); if (err) goto out; mutex_lock(&c->mst_mutex); c->mst_node->cmt_no = cpu_to_le64(c->cmt_no); c->mst_node->log_lnum = cpu_to_le32(new_ltail_lnum); c->mst_node->root_lnum = cpu_to_le32(zroot.lnum); c->mst_node->root_offs = cpu_to_le32(zroot.offs); c->mst_node->root_len = cpu_to_le32(zroot.len); c->mst_node->ihead_lnum = cpu_to_le32(c->ihead_lnum); c->mst_node->ihead_offs = cpu_to_le32(c->ihead_offs); c->mst_node->index_size = cpu_to_le64(c->old_idx_sz); c->mst_node->lpt_lnum = cpu_to_le32(c->lpt_lnum); c->mst_node->lpt_offs = cpu_to_le32(c->lpt_offs); c->mst_node->nhead_lnum = cpu_to_le32(c->nhead_lnum); c->mst_node->nhead_offs = cpu_to_le32(c->nhead_offs); c->mst_node->ltab_lnum = cpu_to_le32(c->ltab_lnum); c->mst_node->ltab_offs = cpu_to_le32(c->ltab_offs); c->mst_node->lsave_lnum = cpu_to_le32(c->lsave_lnum); c->mst_node->lsave_offs = cpu_to_le32(c->lsave_offs); c->mst_node->lscan_lnum = cpu_to_le32(c->lscan_lnum); c->mst_node->empty_lebs = cpu_to_le32(lst.empty_lebs); c->mst_node->idx_lebs = cpu_to_le32(lst.idx_lebs); c->mst_node->total_free = cpu_to_le64(lst.total_free); c->mst_node->total_dirty = cpu_to_le64(lst.total_dirty); c->mst_node->total_used = cpu_to_le64(lst.total_used); c->mst_node->total_dead = cpu_to_le64(lst.total_dead); c->mst_node->total_dark = cpu_to_le64(lst.total_dark); if (c->no_orphs) c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); else c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_NO_ORPHS); err = ubifs_write_master(c); mutex_unlock(&c->mst_mutex); if (err) goto out; err = ubifs_log_post_commit(c, old_ltail_lnum); if (err) goto out; err = ubifs_gc_end_commit(c); if (err) goto out; err = ubifs_lpt_post_commit(c); if (err) goto out; spin_lock(&c->cs_lock); c->cmt_state = COMMIT_RESTING; wake_up(&c->cmt_wq); dbg_cmt("commit end"); spin_unlock(&c->cs_lock); return 0; out_up: up_write(&c->commit_sem); out: ubifs_err("commit failed, error %d", err); spin_lock(&c->cs_lock); c->cmt_state = COMMIT_BROKEN; wake_up(&c->cmt_wq); spin_unlock(&c->cs_lock); ubifs_ro_mode(c, err); return err; }
/** * ubifs_jrn_truncate - update the journal for a truncation. * @c: UBIFS file-system description object * @inum: inode number of inode being truncated * @old_size: old size * @new_size: new size * * When the size of a file decreases due to truncation, a truncation node is * written, the journal tree is updated, and the last data block is re-written * if it has been affected. * * This function returns %0 in the case of success, and a negative error code in * case of failure. */ int ubifs_jrn_truncate(struct ubifs_info *c, ino_t inum, loff_t old_size, loff_t new_size) { union ubifs_key key, to_key; struct ubifs_trun_node *trun; struct ubifs_data_node *dn; int err, dlen, len, lnum, offs, bit, sz; unsigned int blk; dbg_jrn("ino %lu, size %lld -> %lld", inum, old_size, new_size); sz = UBIFS_TRUN_NODE_SZ + UBIFS_MAX_DATA_NODE_SZ * WORST_COMPR_FACTOR; trun = kmalloc(sz, GFP_NOFS); if (!trun) return -ENOMEM; trun->ch.node_type = UBIFS_TRUN_NODE; trun_key_init_flash(c, &trun->key, inum); trun->old_size = cpu_to_le64(old_size); trun->new_size = cpu_to_le64(new_size); ubifs_prepare_node(c, trun, UBIFS_TRUN_NODE_SZ, 0); dlen = new_size & (UBIFS_BLOCK_SIZE - 1); if (dlen) { /* Get last data block so it can be truncated */ dn = (void *)trun + ALIGN(UBIFS_TRUN_NODE_SZ, 8); blk = new_size / UBIFS_BLOCK_SIZE; data_key_init(c, &key, inum, blk); dbg_jrn_key(c, &key, "key"); err = ubifs_tnc_lookup(c, &key, dn); if (err == -ENOENT) dlen = 0; /* Not found (so it is a hole) */ else if (err) goto out_free; else { if (le32_to_cpu(dn->size) <= dlen) dlen = 0; /* Nothing to do */ else { int compr_type = le16_to_cpu(dn->compr_type); if (compr_type != UBIFS_COMPR_NONE) { err = recomp_data_node(dn, &dlen); if (err) goto out_free; } else { dn->size = cpu_to_le32(dlen); dlen += UBIFS_DATA_NODE_SZ; } zero_data_node_unused(dn); ubifs_prepare_node(c, dn, dlen, 0); } } } if (dlen) len = ALIGN(UBIFS_TRUN_NODE_SZ, 8) + dlen; else len = UBIFS_TRUN_NODE_SZ; err = make_reservation(c, BASEHD, len); if (err) goto out_free; err = write_head(c, BASEHD, trun, len, &lnum, &offs, 0); if (!err) ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum); release_head(c, BASEHD); if (err) goto out_ro; if (dlen) { offs += ALIGN(UBIFS_TRUN_NODE_SZ, 8); err = ubifs_tnc_add(c, &key, lnum, offs, dlen); if (err) goto out_ro; } err = ubifs_add_dirt(c, lnum, UBIFS_TRUN_NODE_SZ); if (err) goto out_ro; bit = new_size & (UBIFS_BLOCK_SIZE - 1); blk = new_size / UBIFS_BLOCK_SIZE + (bit ? 1 : 0); data_key_init(c, &key, inum, blk); bit = old_size & (UBIFS_BLOCK_SIZE - 1); blk = old_size / UBIFS_BLOCK_SIZE - (bit ? 0: 1); data_key_init(c, &to_key, inum, blk); err = ubifs_tnc_remove_range(c, &key, &to_key); if (err) goto out_ro; finish_reservation(c); kfree(trun); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: kfree(trun); return err; }
/** * ubifs_jrn_rename - rename a directory entry. * @c: UBIFS file-system description object * @old_dir: parent inode of directory entry to rename * @old_dentry: directory entry to rename * @new_dir: parent inode of directory entry to rename * @new_dentry: new directory entry (or directory entry to replace) * @sync: non-zero if the write-buffer has to be synchronized * * Returns zero in case of success and a negative error code in case of failure. */ int ubifs_jrn_rename(struct ubifs_info *c, const struct inode *old_dir, const struct dentry *old_dentry, const struct inode *new_dir, const struct dentry *new_dentry, int sync) { const struct inode *old_inode = old_dentry->d_inode; const struct inode *new_inode = new_dentry->d_inode; int err, dlen1, dlen2, ilen, lnum, offs, len; int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ; int last_reference = !!(new_inode && new_inode->i_nlink == 0); struct ubifs_dent_node *dent, *dent2; void *p; union ubifs_key key; dbg_jrn("dent '%.*s' in dir ino %lu to dent '%.*s' in dir ino %lu", old_dentry->d_name.len, old_dentry->d_name.name, old_dir->i_ino, new_dentry->d_name.len, new_dentry->d_name.name, new_dir->i_ino); ubifs_assert(ubifs_inode(old_dir)->data_len == 0); ubifs_assert(ubifs_inode(new_dir)->data_len == 0); dlen1 = UBIFS_DENT_NODE_SZ + new_dentry->d_name.len + 1; dlen2 = UBIFS_DENT_NODE_SZ + old_dentry->d_name.len + 1; if (new_inode) { ilen = UBIFS_INO_NODE_SZ; if (!last_reference) ilen += ubifs_inode(new_inode)->data_len; } else ilen = 0; aligned_dlen1 = ALIGN(dlen1, 8); aligned_dlen2 = ALIGN(dlen2, 8); len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8); if (old_dir != new_dir) len += plen; dent = kmalloc(len, GFP_NOFS); if (!dent) return -ENOMEM; /* Make new dent */ dent->ch.node_type = UBIFS_DENT_NODE; dent_key_init_flash(c, &dent->key, new_dir->i_ino, &new_dentry->d_name); dent->inum = cpu_to_le64(old_inode->i_ino); dent->type = get_dent_type(old_inode->i_mode); dent->nlen = cpu_to_le16(new_dentry->d_name.len); memcpy(dent->name, new_dentry->d_name.name, new_dentry->d_name.len); dent->name[new_dentry->d_name.len] = '\0'; zero_dent_node_unused(dent); ubifs_prep_grp_node(c, dent, dlen1, 0); dent2 = (void *)dent + aligned_dlen1; /* Make deletion dent */ dent2->ch.node_type = UBIFS_DENT_NODE; dent_key_init_flash(c, &dent2->key, old_dir->i_ino, &old_dentry->d_name); dent2->inum = cpu_to_le64(0); dent2->type = DT_UNKNOWN; dent2->nlen = cpu_to_le16(old_dentry->d_name.len); memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len); dent2->name[old_dentry->d_name.len] = '\0'; zero_dent_node_unused(dent2); ubifs_prep_grp_node(c, dent2, dlen2, 0); p = (void *)dent2 + aligned_dlen2; if (new_inode) { pack_inode(c, p, new_inode, 0, last_reference); p += ALIGN(ilen, 8); } if (old_dir == new_dir) pack_inode(c, p, old_dir, 1, 0); else { pack_inode(c, p, old_dir, 0, 0); p += ALIGN(plen, 8); pack_inode(c, p, new_dir, 1, 0); } err = make_reservation(c, BASEHD, len); if (err) goto out_free; if (last_reference) { err = ubifs_add_orphan(c, new_inode->i_ino); if (err) { release_head(c, BASEHD); goto out_finish; } } err = write_head(c, BASEHD, dent, len, &lnum, &offs, sync); if (!sync && !err) { struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; ubifs_wbuf_add_ino_nolock(wbuf, new_dir->i_ino); ubifs_wbuf_add_ino_nolock(wbuf, old_dir->i_ino); } release_head(c, BASEHD); if (err) goto out_ro; if (new_inode) ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, new_inode->i_ino); dent_key_init(c, &key, new_dir->i_ino, &new_dentry->d_name); err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, &new_dentry->d_name); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, dlen2); if (err) goto out_ro; dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); if (err) goto out_ro; offs += aligned_dlen1 + aligned_dlen2; if (new_inode) { ino_key_init(c, &key, new_inode->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, ilen); if (err) goto out_ro; offs += ALIGN(ilen, 8); } ino_key_init(c, &key, old_dir->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, plen); if (err) goto out_ro; if (old_dir != new_dir) { offs += ALIGN(plen, 8); ino_key_init(c, &key, new_dir->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, plen); if (err) goto out_ro; } finish_reservation(c); kfree(dent); return 0; out_ro: ubifs_ro_mode(c, err); if (last_reference) ubifs_delete_orphan(c, new_inode->i_ino); out_finish: finish_reservation(c); out_free: kfree(dent); return err; }
static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { int err; union ubifs_key key; struct inode *inode = NULL; struct ubifs_dent_node *dent; struct ubifs_info *c = dir->i_sb->s_fs_info; struct fscrypt_name nm; dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino); if (ubifs_crypt_is_encrypted(dir)) { err = fscrypt_get_encryption_info(dir); /* * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is * created while the directory was encrypted and we * have access to the key. */ if (fscrypt_has_encryption_key(dir)) fscrypt_set_encrypted_dentry(dentry); fscrypt_set_d_op(dentry); if (err && err != -ENOKEY) return ERR_PTR(err); } err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm); if (err) return ERR_PTR(err); if (fname_len(&nm) > UBIFS_MAX_NLEN) { err = -ENAMETOOLONG; goto out_fname; } dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); if (!dent) { err = -ENOMEM; goto out_fname; } if (nm.hash) { ubifs_assert(fname_len(&nm) == 0); ubifs_assert(fname_name(&nm) == NULL); dent_key_init_hash(c, &key, dir->i_ino, nm.hash); err = ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash); } else { dent_key_init(c, &key, dir->i_ino, &nm); err = ubifs_tnc_lookup_nm(c, &key, dent, &nm); } if (err) { if (err == -ENOENT) { dbg_gen("not found"); goto done; } goto out_dent; } if (dbg_check_name(c, dent, &nm)) { err = -EINVAL; goto out_dent; } inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum)); if (IS_ERR(inode)) { /* * This should not happen. Probably the file-system needs * checking. */ err = PTR_ERR(inode); ubifs_err(c, "dead directory entry '%pd', error %d", dentry, err); ubifs_ro_mode(c, err); goto out_dent; } if (ubifs_crypt_is_encrypted(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && !fscrypt_has_permitted_context(dir, inode)) { ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu", dir->i_ino, inode->i_ino); err = -EPERM; goto out_inode; } done: kfree(dent); fscrypt_free_filename(&nm); /* * Note, d_splice_alias() would be required instead if we supported * NFS. */ d_add(dentry, inode); return NULL; out_inode: iput(inode); out_dent: kfree(dent); out_fname: fscrypt_free_filename(&nm); return ERR_PTR(err); }
/** * ubifs_jrn_write_data - write a data node to the journal. * @c: UBIFS file-system description object * @inode: inode the data node belongs to * @key: node key * @buf: buffer to write * @len: data length (must not exceed %UBIFS_BLOCK_SIZE) * * This function writes a data node to the journal. Returns %0 if the data node * was successfully written, and a negative error code in case of failure. */ int ubifs_jrn_write_data(struct ubifs_info *c, const struct inode *inode, const union ubifs_key *key, const void *buf, int len) { int err, lnum, offs, compr_type, out_len; int dlen = UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR; const struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_data_node *data; dbg_jrn_key(c, key, "ino %lu, blk %u, len %d, key ", key_ino(c, key), key_block(c, key), len); ubifs_assert(len <= UBIFS_BLOCK_SIZE); data = kmalloc(dlen, GFP_NOFS); if (!data) return -ENOMEM; data->ch.node_type = UBIFS_DATA_NODE; key_write(c, key, &data->key); data->size = cpu_to_le32(len); zero_data_node_unused(data); if (!(ui->flags && UBIFS_COMPR_FL)) /* Compression is disabled for this inode */ compr_type = UBIFS_COMPR_NONE; else compr_type = ui->compr_type; out_len = dlen - UBIFS_DATA_NODE_SZ; ubifs_compress(buf, len, &data->data, &out_len, &compr_type); ubifs_assert(out_len <= UBIFS_BLOCK_SIZE); dlen = UBIFS_DATA_NODE_SZ + out_len; data->compr_type = cpu_to_le16(compr_type); err = make_reservation(c, DATAHD, dlen); if (err) goto out_free; err = write_node(c, DATAHD, data, dlen, &lnum, &offs); if (!err) ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_ino(c, key)); release_head(c, DATAHD); if (err) goto out_ro; err = ubifs_tnc_add(c, key, lnum, offs, dlen); if (err) goto out_ro; finish_reservation(c); kfree(data); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: kfree(data); return err; }
/** * ubifs_jrn_update - update inode. * @c: UBIFS file-system description object * @dir: parent inode or host inode in case of extended attributes * @nm: directory entry name * @inode: inode * @deletion: indicates a directory entry deletion i.e unlink or rmdir * @sync: non-zero if the write-buffer has to be synchronized * @xent: non-zero if the directory entry is an extended attribute entry * * This function updates an inode by writing a directory entry (or extended * attribute entry), the inode itself, and the parent directory inode (or the * host inode) to the journal. * * The function writes the host inode @dir last, which is important in case of * extended attributes. Indeed, then we guarantee that if the host inode gets * synchronized, and the write-buffer it sits in gets flushed, the extended * attribute inode gets flushed too. And this is exactly what the user expects - * synchronizing the host inode synchronizes its extended attributes. * Similarly, this guarantees that if @dir is synchronized, its directory entry * corresponding to @nm gets synchronized too. * * This function returns %0 on success and a negative error code on failure. */ int ubifs_jrn_update(struct ubifs_info *c, const struct inode *dir, const struct qstr *nm, const struct inode *inode, int deletion, int sync, int xent) { int err, dlen, ilen, len, lnum, ino_offs, dent_offs; int aligned_dlen, aligned_ilen; int last_reference = !!(deletion && inode->i_nlink == 0); struct ubifs_dent_node *dent; struct ubifs_ino_node *ino; union ubifs_key dent_key, ino_key; dbg_jrn("ino %lu, dent '%.*s', data len %d in dir ino %lu", inode->i_ino, nm->len, nm->name, ubifs_inode(inode)->data_len, dir->i_ino); ubifs_assert(ubifs_inode(dir)->data_len == 0); dlen = UBIFS_DENT_NODE_SZ + nm->len + 1; ilen = UBIFS_INO_NODE_SZ; /* * If the last reference to the inode is being deleted, then there is no * need to attach and write inode data, it is being deleted anyway. */ if (!last_reference) ilen += ubifs_inode(inode)->data_len; aligned_dlen = ALIGN(dlen, 8); aligned_ilen = ALIGN(ilen, 8); len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ; dent = kmalloc(len, GFP_NOFS); if (!dent) return -ENOMEM; if (!xent) { dent->ch.node_type = UBIFS_DENT_NODE; dent_key_init(c, &dent_key, dir->i_ino, nm); } else { dent->ch.node_type = UBIFS_XENT_NODE; xent_key_init(c, &dent_key, dir->i_ino, nm); } key_write(c, &dent_key, dent->key); dent->inum = deletion ? 0 : cpu_to_le64(inode->i_ino); dent->type = get_dent_type(inode->i_mode); dent->nlen = cpu_to_le16(nm->len); memcpy(dent->name, nm->name, nm->len); dent->name[nm->len] = '\0'; zero_dent_node_unused(dent); ubifs_prep_grp_node(c, dent, dlen, 0); ino = (void *)dent + aligned_dlen; pack_inode(c, ino, inode, 0, last_reference); ino = (void *)ino + aligned_ilen; pack_inode(c, ino, dir, 1, 0); err = make_reservation(c, BASEHD, len); if (err) goto out_free; if (last_reference) { err = ubifs_add_orphan(c, inode->i_ino); if (err) { release_head(c, BASEHD); goto out_finish; } } err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, sync); if (!sync && !err) { struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino); ubifs_wbuf_add_ino_nolock(wbuf, dir->i_ino); } release_head(c, BASEHD); kfree(dent); if (err) goto out_ro; if (deletion) { err = ubifs_tnc_remove_nm(c, &dent_key, nm); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, dlen); } else err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen, nm); if (err) goto out_ro; /* * Note, we do not remove the inode from TNC even if the last reference * to it has just been deleted, because the inode may still be opened. * Instead, the inode has been added to orphan lists and the orphan * subsystem will take further care about it. */ ino_key_init(c, &ino_key, inode->i_ino); ino_offs = dent_offs + aligned_dlen; err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen); if (err) goto out_ro; ino_key_init(c, &ino_key, dir->i_ino); ino_offs += aligned_ilen; err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, UBIFS_INO_NODE_SZ); if (err) goto out_ro; finish_reservation(c); return 0; out_finish: finish_reservation(c); out_free: kfree(dent); return err; out_ro: ubifs_ro_mode(c, err); if (last_reference) ubifs_delete_orphan(c, inode->i_ino); finish_reservation(c); return err; }
int ubifs_jrn_delete_xattr(struct ubifs_info *c, const struct inode *host, const struct inode *inode, const struct qstr *nm, int sync) { int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen; struct ubifs_dent_node *xent; struct ubifs_ino_node *ino; union ubifs_key xent_key, key1, key2; dbg_jrn("host %lu, xattr ino %lu, name '%s', data len %d", host->i_ino, inode->i_ino, nm->name, ubifs_inode(inode)->data_len); ubifs_assert(inode->i_nlink == 0); /* * Since we are deleting the inode, we do not bother to attach any data * to it and assume its length is %UBIFS_INO_NODE_SZ. */ xlen = UBIFS_DENT_NODE_SZ + nm->len + 1; aligned_xlen = ALIGN(xlen, 8); hlen = ubifs_inode(host)->data_len + UBIFS_INO_NODE_SZ; len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8); xent = kmalloc(len, GFP_NOFS); if (!xent) return -ENOMEM; xent->ch.node_type = UBIFS_XENT_NODE; xent_key_init(c, &xent_key, host->i_ino, nm); key_write(c, &xent_key, xent->key); xent->inum = 0; xent->type = get_dent_type(inode->i_mode); xent->nlen = cpu_to_le16(nm->len); memcpy(xent->name, nm->name, nm->len); xent->name[nm->len] = '\0'; zero_dent_node_unused(xent); ubifs_prep_grp_node(c, xent, xlen, 0); ino = (void *)xent + aligned_xlen; pack_inode(c, ino, inode, 0, 1); ino = (void *)ino + UBIFS_INO_NODE_SZ; pack_inode(c, ino, host, 1, 0); err = make_reservation(c, BASEHD, len); if (err) { kfree(xent); return err; } err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync); if (!sync && !err) ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino); release_head(c, BASEHD); kfree(xent); if (err) goto out_ro; /* Remove the extended attribute entry from TNC */ err = ubifs_tnc_remove_nm(c, &xent_key, nm); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, xlen); if (err) goto out_ro; /* * Remove all nodes belonging to the extended attribute inode from TNC. * Well, there actually must be only one node - the inode itself. */ lowest_ino_key(c, &key1, inode->i_ino); highest_ino_key(c, &key2, inode->i_ino); err = ubifs_tnc_remove_range(c, &key1, &key2); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, UBIFS_INO_NODE_SZ); if (err) goto out_ro; /* And update TNC with the new host inode position */ ino_key_init(c, &key1, host->i_ino); err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen); if (err) goto out_ro; finish_reservation(c); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); return err; }
/** * ubifs_add_bud_to_log - add a new bud to the log. * @c: UBIFS file-system description object * @jhead: journal head the bud belongs to * @lnum: LEB number of the bud * @offs: starting offset of the bud * * This function writes reference node for the new bud LEB @lnum it to the log, * and adds it to the buds tress. It also makes sure that log size does not * exceed the 'c->max_bud_bytes' limit. Returns zero in case of success, * %-EAGAIN if commit is required, and a negative error codes in case of * failure. */ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs) { int err; struct ubifs_bud *bud; struct ubifs_ref_node *ref; bud = kmalloc(sizeof(struct ubifs_bud), GFP_NOFS); if (!bud) return -ENOMEM; ref = kzalloc(c->ref_node_alsz, GFP_NOFS); if (!ref) { kfree(bud); return -ENOMEM; } mutex_lock(&c->log_mutex); ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) { err = -EROFS; goto out_unlock; } /* Make sure we have enough space in the log */ if (empty_log_bytes(c) - c->ref_node_alsz < c->min_log_bytes) { dbg_log("not enough log space - %lld, required %d", empty_log_bytes(c), c->min_log_bytes); ubifs_commit_required(c); err = -EAGAIN; goto out_unlock; } /* * Make sure the amount of space in buds will not exceed the * 'c->max_bud_bytes' limit, because we want to guarantee mount time * limits. * * It is not necessary to hold @c->buds_lock when reading @c->bud_bytes * because we are holding @c->log_mutex. All @c->bud_bytes take place * when both @c->log_mutex and @c->bud_bytes are locked. */ if (c->bud_bytes + c->leb_size - offs > c->max_bud_bytes) { dbg_log("bud bytes %lld (%lld max), require commit", c->bud_bytes, c->max_bud_bytes); ubifs_commit_required(c); err = -EAGAIN; goto out_unlock; } /* * If the journal is full enough - start background commit. Note, it is * OK to read 'c->cmt_state' without spinlock because integer reads * are atomic in the kernel. */ if (c->bud_bytes >= c->bg_bud_bytes && c->cmt_state == COMMIT_RESTING) { dbg_log("bud bytes %lld (%lld max), initiate BG commit", c->bud_bytes, c->max_bud_bytes); ubifs_request_bg_commit(c); } bud->lnum = lnum; bud->start = offs; bud->jhead = jhead; ref->ch.node_type = UBIFS_REF_NODE; ref->lnum = cpu_to_le32(bud->lnum); ref->offs = cpu_to_le32(bud->start); ref->jhead = cpu_to_le32(jhead); if (c->lhead_offs > c->leb_size - c->ref_node_alsz) { c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum); c->lhead_offs = 0; } if (c->lhead_offs == 0) { /* Must ensure next log LEB has been unmapped */ err = ubifs_leb_unmap(c, c->lhead_lnum); if (err) goto out_unlock; } if (bud->start == 0) { /* * Before writing the LEB reference which refers an empty LEB * to the log, we have to make sure it is mapped, because * otherwise we'd risk to refer an LEB with garbage in case of * an unclean reboot, because the target LEB might have been * unmapped, but not yet physically erased. */ err = ubi_leb_map(c->ubi, bud->lnum, UBI_SHORTTERM); if (err) goto out_unlock; } dbg_log("write ref LEB %d:%d", c->lhead_lnum, c->lhead_offs); err = ubifs_write_node(c, ref, UBIFS_REF_NODE_SZ, c->lhead_lnum, c->lhead_offs, UBI_SHORTTERM); if (err) goto out_unlock; c->lhead_offs += c->ref_node_alsz; ubifs_add_bud(c, bud); mutex_unlock(&c->log_mutex); kfree(ref); return 0; out_unlock: if (err != -EAGAIN) ubifs_ro_mode(c, err); mutex_unlock(&c->log_mutex); kfree(ref); kfree(bud); return err; }