/** * 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_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; }
/** * 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; }
static int ubifs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct ubifs_info *c = dir->i_sb->s_fs_info; struct inode *inode = d_inode(old_dentry); struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_inode *dir_ui = ubifs_inode(dir); int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2, .dirtied_ino_d = ALIGN(ui->data_len, 8) }; struct fscrypt_name nm; /* * Budget request settings: new direntry, changing the target inode, * changing the parent inode. */ dbg_gen("dent '%pd' to ino %lu (nlink %d) in dir ino %lu", dentry, inode->i_ino, inode->i_nlink, dir->i_ino); ubifs_assert(inode_is_locked(dir)); ubifs_assert(inode_is_locked(inode)); if (ubifs_crypt_is_encrypted(dir) && !fscrypt_has_permitted_context(dir, inode)) return -EPERM; err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); if (err) return err; err = dbg_check_synced_i_size(c, inode); if (err) goto out_fname; err = ubifs_budget_space(c, &req); if (err) goto out_fname; lock_2_inodes(dir, inode); /* Handle O_TMPFILE corner case, it is allowed to link a O_TMPFILE. */ if (inode->i_nlink == 0) ubifs_delete_orphan(c, inode->i_ino); inc_nlink(inode); ihold(inode); inode->i_ctime = current_time(inode); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0); if (err) goto out_cancel; unlock_2_inodes(dir, inode); ubifs_release_budget(c, &req); d_instantiate(dentry, inode); fscrypt_free_filename(&nm); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; drop_nlink(inode); if (inode->i_nlink == 0) ubifs_add_orphan(c, inode->i_ino); unlock_2_inodes(dir, inode); ubifs_release_budget(c, &req); iput(inode); out_fname: fscrypt_free_filename(&nm); return err; } static int ubifs_unlink(struct inode *dir, struct dentry *dentry) { struct ubifs_info *c = dir->i_sb->s_fs_info; struct inode *inode = d_inode(dentry); struct ubifs_inode *dir_ui = ubifs_inode(dir); int err, sz_change, budgeted = 1; struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 }; unsigned int saved_nlink = inode->i_nlink; struct fscrypt_name nm; /* * Budget request settings: deletion direntry, deletion inode (+1 for * @dirtied_ino), changing the parent directory inode. If budgeting * fails, go ahead anyway because we have extra space reserved for * deletions. */ dbg_gen("dent '%pd' from ino %lu (nlink %d) in dir ino %lu", dentry, inode->i_ino, inode->i_nlink, dir->i_ino); if (ubifs_crypt_is_encrypted(dir)) { err = fscrypt_get_encryption_info(dir); if (err && err != -ENOKEY) return err; } err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm); if (err) return err; sz_change = CALC_DENT_SIZE(fname_len(&nm)); ubifs_assert(inode_is_locked(dir)); ubifs_assert(inode_is_locked(inode)); err = dbg_check_synced_i_size(c, inode); if (err) goto out_fname; err = ubifs_budget_space(c, &req); if (err) { if (err != -ENOSPC) goto out_fname; budgeted = 0; } lock_2_inodes(dir, inode); inode->i_ctime = current_time(dir); drop_nlink(inode); dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0); if (err) goto out_cancel; unlock_2_inodes(dir, inode); if (budgeted) ubifs_release_budget(c, &req); else { /* We've deleted something - clean the "no space" flags */ c->bi.nospace = c->bi.nospace_rp = 0; smp_wmb(); } fscrypt_free_filename(&nm); return 0; out_cancel: dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; set_nlink(inode, saved_nlink); unlock_2_inodes(dir, inode); if (budgeted) ubifs_release_budget(c, &req); out_fname: fscrypt_free_filename(&nm); return err; } /** * check_dir_empty - check if a directory is empty or not. * @dir: VFS inode object of the directory to check * * This function checks if directory @dir is empty. Returns zero if the * directory is empty, %-ENOTEMPTY if it is not, and other negative error codes * in case of of errors. */ int ubifs_check_dir_empty(struct inode *dir) { struct ubifs_info *c = dir->i_sb->s_fs_info; struct fscrypt_name nm = { 0 }; struct ubifs_dent_node *dent; union ubifs_key key; int err; lowest_dent_key(c, &key, dir->i_ino); dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); if (err == -ENOENT) err = 0; } else { kfree(dent); err = -ENOTEMPTY; } return err; } static int ubifs_rmdir(struct inode *dir, struct dentry *dentry) { struct ubifs_info *c = dir->i_sb->s_fs_info; struct inode *inode = d_inode(dentry); int err, sz_change, budgeted = 1; struct ubifs_inode *dir_ui = ubifs_inode(dir); struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 }; struct fscrypt_name nm; /* * Budget request settings: deletion direntry, deletion inode and * changing the parent inode. If budgeting fails, go ahead anyway * because we have extra space reserved for deletions. */ dbg_gen("directory '%pd', ino %lu in dir ino %lu", dentry, inode->i_ino, dir->i_ino); ubifs_assert(inode_is_locked(dir)); ubifs_assert(inode_is_locked(inode)); err = ubifs_check_dir_empty(d_inode(dentry)); if (err) return err; if (ubifs_crypt_is_encrypted(dir)) { err = fscrypt_get_encryption_info(dir); if (err && err != -ENOKEY) return err; } err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm); if (err) return err; sz_change = CALC_DENT_SIZE(fname_len(&nm)); err = ubifs_budget_space(c, &req); if (err) { if (err != -ENOSPC) goto out_fname; budgeted = 0; } lock_2_inodes(dir, inode); inode->i_ctime = current_time(dir); clear_nlink(inode); drop_nlink(dir); dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0); if (err) goto out_cancel; unlock_2_inodes(dir, inode); if (budgeted) ubifs_release_budget(c, &req); else { /* We've deleted something - clean the "no space" flags */ c->bi.nospace = c->bi.nospace_rp = 0; smp_wmb(); } fscrypt_free_filename(&nm); return 0; out_cancel: dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; inc_nlink(dir); set_nlink(inode, 2); unlock_2_inodes(dir, inode); if (budgeted) ubifs_release_budget(c, &req); out_fname: fscrypt_free_filename(&nm); return err; } static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; struct ubifs_inode *dir_ui = ubifs_inode(dir); struct ubifs_info *c = dir->i_sb->s_fs_info; int err, sz_change; struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1 }; struct fscrypt_name nm; /* * Budget request settings: new inode, new direntry and changing parent * directory inode. */ dbg_gen("dent '%pd', mode %#hx in dir ino %lu", dentry, mode, dir->i_ino); err = ubifs_budget_space(c, &req); if (err) return err; err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); if (err) goto out_budg; sz_change = CALC_DENT_SIZE(fname_len(&nm)); inode = ubifs_new_inode(c, dir, S_IFDIR | mode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_fname; } err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&dir_ui->ui_mutex); insert_inode_hash(inode); inc_nlink(inode); inc_nlink(dir); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0); if (err) { ubifs_err(c, "cannot create directory, error %d", err); goto out_cancel; } mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); d_instantiate(dentry, inode); fscrypt_free_filename(&nm); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; drop_nlink(dir); mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); iput(inode); out_fname: fscrypt_free_filename(&nm); out_budg: ubifs_release_budget(c, &req); return err; } static int ubifs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct inode *inode; struct ubifs_inode *ui; struct ubifs_inode *dir_ui = ubifs_inode(dir); struct ubifs_info *c = dir->i_sb->s_fs_info; union ubifs_dev_desc *dev = NULL; int sz_change; int err, devlen = 0; struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .dirtied_ino = 1 }; struct fscrypt_name nm; /* * Budget request settings: new inode, new direntry and changing parent * directory inode. */ dbg_gen("dent '%pd' in dir ino %lu", dentry, dir->i_ino); if (S_ISBLK(mode) || S_ISCHR(mode)) { dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); if (!dev) return -ENOMEM; devlen = ubifs_encode_dev(dev, rdev); } req.new_ino_d = ALIGN(devlen, 8); err = ubifs_budget_space(c, &req); if (err) { kfree(dev); return err; } err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); if (err) { kfree(dev); goto out_budg; } sz_change = CALC_DENT_SIZE(fname_len(&nm)); inode = ubifs_new_inode(c, dir, mode); if (IS_ERR(inode)) { kfree(dev); err = PTR_ERR(inode); goto out_fname; } init_special_inode(inode, inode->i_mode, rdev); inode->i_size = ubifs_inode(inode)->ui_size = devlen; ui = ubifs_inode(inode); ui->data = dev; ui->data_len = devlen; err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); insert_inode_hash(inode); d_instantiate(dentry, inode); fscrypt_free_filename(&nm); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); iput(inode); out_fname: fscrypt_free_filename(&nm); out_budg: ubifs_release_budget(c, &req); return err; } static int ubifs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct inode *inode; struct ubifs_inode *ui; struct ubifs_inode *dir_ui = ubifs_inode(dir); struct ubifs_info *c = dir->i_sb->s_fs_info; int err, len = strlen(symname); int sz_change = CALC_DENT_SIZE(len); struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1); struct fscrypt_symlink_data *sd = NULL; struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .new_ino_d = ALIGN(len, 8), .dirtied_ino = 1 }; struct fscrypt_name nm; if (ubifs_crypt_is_encrypted(dir)) { err = fscrypt_get_encryption_info(dir); if (err) goto out_budg; if (!fscrypt_has_encryption_key(dir)) { err = -EPERM; goto out_budg; } disk_link.len = (fscrypt_fname_encrypted_size(dir, len) + sizeof(struct fscrypt_symlink_data)); } /* * Budget request settings: new inode, new direntry and changing parent * directory inode. */ dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry, symname, dir->i_ino); if (disk_link.len > UBIFS_MAX_INO_DATA) return -ENAMETOOLONG; err = ubifs_budget_space(c, &req); if (err) return err; err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); if (err) goto out_budg; inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_fname; } ui = ubifs_inode(inode); ui->data = kmalloc(disk_link.len, GFP_NOFS); if (!ui->data) { err = -ENOMEM; goto out_inode; } if (ubifs_crypt_is_encrypted(dir)) { struct qstr istr = QSTR_INIT(symname, len); struct fscrypt_str ostr; sd = kzalloc(disk_link.len, GFP_NOFS); if (!sd) { err = -ENOMEM; goto out_inode; } ostr.name = sd->encrypted_path; ostr.len = disk_link.len; err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr); if (err) { kfree(sd); goto out_inode; } sd->len = cpu_to_le16(ostr.len); disk_link.name = (char *)sd; } else { inode->i_link = ui->data; } memcpy(ui->data, disk_link.name, disk_link.len); ((char *)ui->data)[disk_link.len - 1] = '\0'; /* * The terminating zero byte is not written to the flash media and it * is put just to make later in-memory string processing simpler. Thus, * data length is @len, not @len + %1. */ ui->data_len = disk_link.len - 1; inode->i_size = ubifs_inode(inode)->ui_size = disk_link.len - 1; err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); insert_inode_hash(inode); d_instantiate(dentry, inode); fscrypt_free_filename(&nm); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); iput(inode); out_fname: fscrypt_free_filename(&nm); out_budg: ubifs_release_budget(c, &req); return err; } /** * lock_4_inodes - a wrapper for locking three UBIFS inodes. * @inode1: first inode * @inode2: second inode * @inode3: third inode * @inode4: fouth inode * * This function is used for 'ubifs_rename()' and @inode1 may be the same as * @inode2 whereas @inode3 and @inode4 may be %NULL. * * We do not implement any tricks to guarantee strict lock ordering, because * VFS has already done it for us on the @i_mutex. So this is just a simple * wrapper function. */ static void lock_4_inodes(struct inode *inode1, struct inode *inode2, struct inode *inode3, struct inode *inode4) { mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); if (inode2 != inode1) mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); if (inode3) mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3); if (inode4) mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4); } /** * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename. * @inode1: first inode * @inode2: second inode * @inode3: third inode * @inode4: fouth inode */ static void unlock_4_inodes(struct inode *inode1, struct inode *inode2, struct inode *inode3, struct inode *inode4) { if (inode4) mutex_unlock(&ubifs_inode(inode4)->ui_mutex); if (inode3) mutex_unlock(&ubifs_inode(inode3)->ui_mutex); if (inode1 != inode2) mutex_unlock(&ubifs_inode(inode2)->ui_mutex); mutex_unlock(&ubifs_inode(inode1)->ui_mutex); } static int do_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { struct ubifs_info *c = old_dir->i_sb->s_fs_info; struct inode *old_inode = d_inode(old_dentry); struct inode *new_inode = d_inode(new_dentry); struct inode *whiteout = NULL; struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode); struct ubifs_inode *whiteout_ui = NULL; int err, release, sync = 0, move = (new_dir != old_dir); int is_dir = S_ISDIR(old_inode->i_mode); int unlink = !!new_inode, new_sz, old_sz; struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1, .dirtied_ino = 3 }; struct ubifs_budget_req ino_req = { .dirtied_ino = 1, .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) }; struct timespec time; unsigned int uninitialized_var(saved_nlink); struct fscrypt_name old_nm, new_nm; /* * Budget request settings: deletion direntry, new direntry, removing * the old inode, and changing old and new parent directory inodes. * * However, this operation also marks the target inode as dirty and * does not write it, so we allocate budget for the target inode * separately. */ dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x", old_dentry, old_inode->i_ino, old_dir->i_ino, new_dentry, new_dir->i_ino, flags); if (unlink) ubifs_assert(inode_is_locked(new_inode)); if (old_dir != new_dir) { if (ubifs_crypt_is_encrypted(new_dir) && !fscrypt_has_permitted_context(new_dir, old_inode)) return -EPERM; } if (unlink && is_dir) { err = ubifs_check_dir_empty(new_inode); if (err) return err; } err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_nm); if (err) return err; err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_nm); if (err) { fscrypt_free_filename(&old_nm); return err; } new_sz = CALC_DENT_SIZE(fname_len(&new_nm)); old_sz = CALC_DENT_SIZE(fname_len(&old_nm)); err = ubifs_budget_space(c, &req); if (err) { fscrypt_free_filename(&old_nm); fscrypt_free_filename(&new_nm); return err; } err = ubifs_budget_space(c, &ino_req); if (err) { fscrypt_free_filename(&old_nm); fscrypt_free_filename(&new_nm); ubifs_release_budget(c, &req); return err; } if (flags & RENAME_WHITEOUT) { union ubifs_dev_desc *dev = NULL; dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); if (!dev) { err = -ENOMEM; goto out_release; } err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout); if (err) { kfree(dev); goto out_release; } whiteout->i_state |= I_LINKABLE; whiteout_ui = ubifs_inode(whiteout); whiteout_ui->data = dev; whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0)); ubifs_assert(!whiteout_ui->dirty); } lock_4_inodes(old_dir, new_dir, new_inode, whiteout); /* * Like most other Unix systems, set the @i_ctime for inodes on a * rename. */ time = current_time(old_dir); old_inode->i_ctime = time; /* We must adjust parent link count when renaming directories */ if (is_dir) { if (move) { /* * @old_dir loses a link because we are moving * @old_inode to a different directory. */ drop_nlink(old_dir); /* * @new_dir only gains a link if we are not also * overwriting an existing directory. */ if (!unlink) inc_nlink(new_dir); } else { /* * @old_inode is not moving to a different directory, * but @old_dir still loses a link if we are * overwriting an existing directory. */ if (unlink) drop_nlink(old_dir); } } old_dir->i_size -= old_sz; ubifs_inode(old_dir)->ui_size = old_dir->i_size; old_dir->i_mtime = old_dir->i_ctime = time; new_dir->i_mtime = new_dir->i_ctime = time; /* * And finally, if we unlinked a direntry which happened to have the * same name as the moved direntry, we have to decrement @i_nlink of * the unlinked inode and change its ctime. */ if (unlink) { /* * Directories cannot have hard-links, so if this is a * directory, just clear @i_nlink. */ saved_nlink = new_inode->i_nlink; if (is_dir) clear_nlink(new_inode); else drop_nlink(new_inode); new_inode->i_ctime = time; } else { new_dir->i_size += new_sz; ubifs_inode(new_dir)->ui_size = new_dir->i_size; } /* * Do not ask 'ubifs_jnl_rename()' to flush write-buffer if @old_inode * is dirty, because this will be done later on at the end of * 'ubifs_rename()'. */ if (IS_SYNC(old_inode)) { sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir); if (unlink && IS_SYNC(new_inode)) sync = 1; } if (whiteout) { struct ubifs_budget_req wht_req = { .dirtied_ino = 1, .dirtied_ino_d = \ ALIGN(ubifs_inode(whiteout)->data_len, 8) }; err = ubifs_budget_space(c, &wht_req); if (err) { kfree(whiteout_ui->data); whiteout_ui->data_len = 0; iput(whiteout); goto out_release; } inc_nlink(whiteout); mark_inode_dirty(whiteout); whiteout->i_state &= ~I_LINKABLE; iput(whiteout); } err = ubifs_jnl_rename(c, old_dir, old_inode, &old_nm, new_dir, new_inode, &new_nm, whiteout, sync); if (err) goto out_cancel; unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); ubifs_release_budget(c, &req); mutex_lock(&old_inode_ui->ui_mutex); release = old_inode_ui->dirty; mark_inode_dirty_sync(old_inode); mutex_unlock(&old_inode_ui->ui_mutex); if (release) ubifs_release_budget(c, &ino_req); if (IS_SYNC(old_inode)) err = old_inode->i_sb->s_op->write_inode(old_inode, NULL); fscrypt_free_filename(&old_nm); fscrypt_free_filename(&new_nm); return err; out_cancel: if (unlink) { set_nlink(new_inode, saved_nlink); } else { new_dir->i_size -= new_sz; ubifs_inode(new_dir)->ui_size = new_dir->i_size; } old_dir->i_size += old_sz; ubifs_inode(old_dir)->ui_size = old_dir->i_size; if (is_dir) { if (move) { inc_nlink(old_dir); if (!unlink) drop_nlink(new_dir); } else { if (unlink) inc_nlink(old_dir); } } if (whiteout) { drop_nlink(whiteout); iput(whiteout); } unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); out_release: ubifs_release_budget(c, &ino_req); ubifs_release_budget(c, &req); fscrypt_free_filename(&old_nm); fscrypt_free_filename(&new_nm); return err; } static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct ubifs_info *c = old_dir->i_sb->s_fs_info; struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1, .dirtied_ino = 2 }; int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir); struct inode *fst_inode = d_inode(old_dentry); struct inode *snd_inode = d_inode(new_dentry); struct timespec time; int err; struct fscrypt_name fst_nm, snd_nm; ubifs_assert(fst_inode && snd_inode); if ((ubifs_crypt_is_encrypted(old_dir) || ubifs_crypt_is_encrypted(new_dir)) && (old_dir != new_dir) && (!fscrypt_has_permitted_context(new_dir, fst_inode) || !fscrypt_has_permitted_context(old_dir, snd_inode))) return -EPERM; err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &fst_nm); if (err) return err; err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &snd_nm); if (err) { fscrypt_free_filename(&fst_nm); return err; } lock_4_inodes(old_dir, new_dir, NULL, NULL); time = current_time(old_dir); fst_inode->i_ctime = time; snd_inode->i_ctime = time; old_dir->i_mtime = old_dir->i_ctime = time; new_dir->i_mtime = new_dir->i_ctime = time; if (old_dir != new_dir) { if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { inc_nlink(new_dir); drop_nlink(old_dir); } else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { drop_nlink(new_dir); inc_nlink(old_dir); } } err = ubifs_jnl_xrename(c, old_dir, fst_inode, &fst_nm, new_dir, snd_inode, &snd_nm, sync); unlock_4_inodes(old_dir, new_dir, NULL, NULL); ubifs_release_budget(c, &req); fscrypt_free_filename(&fst_nm); fscrypt_free_filename(&snd_nm); return err; } static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE)) return -EINVAL; ubifs_assert(inode_is_locked(old_dir)); ubifs_assert(inode_is_locked(new_dir)); if (flags & RENAME_EXCHANGE) return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry); return do_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } int ubifs_getattr(const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { loff_t size; struct inode *inode = d_inode(path->dentry); struct ubifs_inode *ui = ubifs_inode(inode); mutex_lock(&ui->ui_mutex); if (ui->flags & UBIFS_APPEND_FL) stat->attributes |= STATX_ATTR_APPEND; if (ui->flags & UBIFS_COMPR_FL) stat->attributes |= STATX_ATTR_COMPRESSED; if (ui->flags & UBIFS_CRYPT_FL) stat->attributes |= STATX_ATTR_ENCRYPTED; if (ui->flags & UBIFS_IMMUTABLE_FL) stat->attributes |= STATX_ATTR_IMMUTABLE; stat->attributes_mask |= (STATX_ATTR_APPEND | STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED | STATX_ATTR_IMMUTABLE); generic_fillattr(inode, stat); stat->blksize = UBIFS_BLOCK_SIZE; stat->size = ui->ui_size; /* * Unfortunately, the 'stat()' system call was designed for block * device based file systems, and it is not appropriate for UBIFS, * because UBIFS does not have notion of "block". For example, it is * difficult to tell how many block a directory takes - it actually * takes less than 300 bytes, but we have to round it to block size, * which introduces large mistake. This makes utilities like 'du' to * report completely senseless numbers. This is the reason why UBIFS * goes the same way as JFFS2 - it reports zero blocks for everything * but regular files, which makes more sense than reporting completely * wrong sizes. */ if (S_ISREG(inode->i_mode)) { size = ui->xattr_size; size += stat->size; size = ALIGN(size, UBIFS_BLOCK_SIZE); /* * Note, user-space expects 512-byte blocks count irrespectively * of what was reported in @stat->size. */ stat->blocks = size >> 9; } else stat->blocks = 0; mutex_unlock(&ui->ui_mutex); return 0; } static int ubifs_dir_open(struct inode *dir, struct file *file) { if (ubifs_crypt_is_encrypted(dir)) return fscrypt_get_encryption_info(dir) ? -EACCES : 0; return 0; } const struct inode_operations ubifs_dir_inode_operations = { .lookup = ubifs_lookup, .create = ubifs_create, .link = ubifs_link, .symlink = ubifs_symlink, .unlink = ubifs_unlink, .mkdir = ubifs_mkdir, .rmdir = ubifs_rmdir, .mknod = ubifs_mknod, .rename = ubifs_rename, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .listxattr = ubifs_listxattr, #ifdef CONFIG_UBIFS_ATIME_SUPPORT .update_time = ubifs_update_time, #endif .tmpfile = ubifs_tmpfile, }; const struct file_operations ubifs_dir_operations = { .llseek = generic_file_llseek, .release = ubifs_dir_release, .read = generic_read_dir, .iterate_shared = ubifs_readdir, .fsync = ubifs_fsync, .unlocked_ioctl = ubifs_ioctl, .open = ubifs_dir_open, #ifdef CONFIG_COMPAT .compat_ioctl = ubifs_compat_ioctl, #endif };