+static int join_file(struct inode *head_inode, struct file *head_filp,
+ struct file *tail_filp)
+{
+ struct dentry *tail_dentry = tail_filp->f_dentry;
+ struct lookup_intent oit = {.it_op = IT_OPEN,
+ .it_flags = head_filp->f_flags|O_JOIN_FILE};
+ struct ldlm_enqueue_info einfo = { LDLM_IBITS, LCK_CW,
+ ll_md_blocking_ast, ldlm_completion_ast, NULL, NULL };
+
+ struct lustre_handle lockh;
+ struct md_op_data *op_data;
+ int rc;
+ loff_t data;
+ ENTRY;
+
+ tail_dentry = tail_filp->f_dentry;
+
+ data = i_size_read(head_inode);
+ op_data = ll_prep_md_op_data(NULL, head_inode,
+ tail_dentry->d_parent->d_inode,
+ tail_dentry->d_name.name,
+ tail_dentry->d_name.len, 0,
+ LUSTRE_OPC_ANY, &data);
+ if (IS_ERR(op_data))
+ RETURN(PTR_ERR(op_data));
+
+ rc = md_enqueue(ll_i2mdexp(head_inode), &einfo, &oit,
+ op_data, &lockh, NULL, 0, 0);
+
+ ll_finish_md_op_data(op_data);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ rc = oit.d.lustre.it_status;
+
+ if (rc < 0 || it_open_error(DISP_OPEN_OPEN, &oit)) {
+ rc = rc ? rc : it_open_error(DISP_OPEN_OPEN, &oit);
+ ptlrpc_req_finished((struct ptlrpc_request *)
+ oit.d.lustre.it_data);
+ GOTO(out, rc);
+ }
+
+ if (oit.d.lustre.it_lock_mode) { /* If we got lock - release it right
+ * away */
+ ldlm_lock_decref(&lockh, oit.d.lustre.it_lock_mode);
+ oit.d.lustre.it_lock_mode = 0;
+ }
+ ll_release_openhandle(head_filp->f_dentry, &oit);
+out:
+ ll_intent_release(&oit);
+ RETURN(rc);
+}
+
+static int ll_file_join(struct inode *head, struct file *filp,
+ char *filename_tail)
+{
+ struct inode *tail = NULL, *first = NULL, *second = NULL;
+ struct dentry *tail_dentry;
+ struct file *tail_filp, *first_filp, *second_filp;
+ struct ll_lock_tree first_tree, second_tree;
+ struct ll_lock_tree_node *first_node, *second_node;
+ struct ll_inode_info *hlli = ll_i2info(head), *tlli;
+ int rc = 0, cleanup_phase = 0;
+ ENTRY;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:head=%lu/%u(%p) tail %s\n",
+ head->i_ino, head->i_generation, head, filename_tail);
+
+ tail_filp = filp_open(filename_tail, O_WRONLY, 0644);
+ if (IS_ERR(tail_filp)) {
+ CERROR("Can not open tail file %s", filename_tail);
+ rc = PTR_ERR(tail_filp);
+ GOTO(cleanup, rc);
+ }
+ tail = igrab(tail_filp->f_dentry->d_inode);
+
+ tlli = ll_i2info(tail);
+ tail_dentry = tail_filp->f_dentry;
+ LASSERT(tail_dentry);
+ cleanup_phase = 1;
+
+ /*reorder the inode for lock sequence*/
+ first = head->i_ino > tail->i_ino ? head : tail;
+ second = head->i_ino > tail->i_ino ? tail : head;
+ first_filp = head->i_ino > tail->i_ino ? filp : tail_filp;
+ second_filp = head->i_ino > tail->i_ino ? tail_filp : filp;
+
+ CDEBUG(D_INFO, "reorder object from %lu:%lu to %lu:%lu \n",
+ head->i_ino, tail->i_ino, first->i_ino, second->i_ino);
+ first_node = ll_node_from_inode(first, 0, OBD_OBJECT_EOF, LCK_EX);
+ if (IS_ERR(first_node)){
+ rc = PTR_ERR(first_node);
+ GOTO(cleanup, rc);
+ }
+ first_tree.lt_fd = first_filp->private_data;
+ rc = ll_tree_lock(&first_tree, first_node, NULL, 0, 0);
+ if (rc != 0)
+ GOTO(cleanup, rc);
+ cleanup_phase = 2;
+
+ second_node = ll_node_from_inode(second, 0, OBD_OBJECT_EOF, LCK_EX);
+ if (IS_ERR(second_node)){
+ rc = PTR_ERR(second_node);
+ GOTO(cleanup, rc);
+ }
+ second_tree.lt_fd = second_filp->private_data;
+ rc = ll_tree_lock(&second_tree, second_node, NULL, 0, 0);
+ if (rc != 0)
+ GOTO(cleanup, rc);
+ cleanup_phase = 3;
+
+ rc = join_sanity_check(head, tail);
+ if (rc)
+ GOTO(cleanup, rc);
+
+ rc = join_file(head, filp, tail_filp);
+ if (rc)
+ GOTO(cleanup, rc);
+cleanup:
+ switch (cleanup_phase) {
+ case 3:
+ ll_tree_unlock(&second_tree);
+ obd_cancel_unused(ll_i2dtexp(second),
+ ll_i2info(second)->lli_smd, 0, NULL);
+ case 2:
+ ll_tree_unlock(&first_tree);
+ obd_cancel_unused(ll_i2dtexp(first),
+ ll_i2info(first)->lli_smd, 0, NULL);
+ case 1:
+ filp_close(tail_filp, 0);
+ if (tail)
+ iput(tail);
+ if (head && rc == 0) {
+ obd_free_memmd(ll_i2sbi(head)->ll_dt_exp,
+ &hlli->lli_smd);
+ hlli->lli_smd = NULL;
+ }
+ case 0:
+ break;
+ default:
+ CERROR("invalid cleanup_phase %d\n", cleanup_phase);
+ LBUG();
+ }
+ RETURN(rc);
+}
+
+int ll_release_openhandle(struct dentry *dentry, struct lookup_intent *it)
+{
+ struct inode *inode = dentry->d_inode;
+ struct obd_client_handle *och;
+ int rc;
+ ENTRY;
+
+ LASSERT(inode);
+
+ /* Root ? Do nothing. */
+ if (dentry->d_inode->i_sb->s_root == dentry)
+ RETURN(0);
+
+ /* No open handle to close? Move away */
+ if (!it_disposition(it, DISP_OPEN_OPEN))
+ RETURN(0);
+
+ LASSERT(it_open_error(DISP_OPEN_OPEN, it) == 0);
+
+ OBD_ALLOC(och, sizeof(*och));
+ if (!och)
+ GOTO(out, rc = -ENOMEM);
+
+ ll_och_fill(ll_i2sbi(inode)->ll_md_exp,
+ ll_i2info(inode), it, och);
+
+ rc = ll_close_inode_openhandle(ll_i2sbi(inode)->ll_md_exp,
+ inode, och);
+ out:
+ /* this one is in place of ll_file_open */
+ ptlrpc_req_finished(it->d.lustre.it_data);
+ it_clear_disposition(it, DISP_ENQ_OPEN_REF);
+ RETURN(rc);
+}
+
+int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ int flags;
+ ENTRY;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),cmd=%x\n", inode->i_ino,
+ inode->i_generation, inode, cmd);
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_IOCTL, 1);
+
+ /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */
+ if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */
+ RETURN(-ENOTTY);