aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2005-07-24 18:53:01 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-09-23 12:38:57 -0400
commit278c995c8a153bb2a9bc427e931cfb9c8034c9d7 (patch)
treefe1b62d1686c78659133d751393ab2d5acf38665
parent470056c288334eb0b37be26c9ff8aee37ed1cc7a (diff)
[PATCH] RPC,NFS: new rpc_pipefs patch
Currently rpc_mkdir/rpc_rmdir and rpc_mkpipe/mk_unlink have an API that's a little unfortunate. They take a path relative to the rpc_pipefs root and thus need to perform a full lookup. If you look at debugfs or usbfs they always store the dentry for directories they created and thus can pass in a dentry + single pathname component pair into their equivalents of the above functions. And in fact rpc_pipefs actually stores a dentry for all but one component so this change not only simplifies the core rpc_pipe code but also the callers. Unfortuntately this code path is only used by the NFS4 idmapper and AUTH_GSSAPI for which I don't have a test enviroment. Could someone give it a spin? It's the last bit needed before we can rework the lookup_hash API Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/idmap.c10
-rw-r--r--include/linux/sunrpc/clnt.h2
-rw-r--r--include/linux/sunrpc/rpc_pipe_fs.h9
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c9
-rw-r--r--net/sunrpc/clnt.c53
-rw-r--r--net/sunrpc/rpc_pipe.c268
6 files changed, 142 insertions, 209 deletions
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c
index ffb8df91dc34..1d0a5bf0d264 100644
--- a/fs/nfs/idmap.c
+++ b/fs/nfs/idmap.c
@@ -66,7 +66,6 @@ struct idmap_hashtable {
66}; 66};
67 67
68struct idmap { 68struct idmap {
69 char idmap_path[48];
70 struct dentry *idmap_dentry; 69 struct dentry *idmap_dentry;
71 wait_queue_head_t idmap_wq; 70 wait_queue_head_t idmap_wq;
72 struct idmap_msg idmap_im; 71 struct idmap_msg idmap_im;
@@ -102,11 +101,8 @@ nfs_idmap_new(struct nfs4_client *clp)
102 101
103 memset(idmap, 0, sizeof(*idmap)); 102 memset(idmap, 0, sizeof(*idmap));
104 103
105 snprintf(idmap->idmap_path, sizeof(idmap->idmap_path), 104 idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_dentry,
106 "%s/idmap", clp->cl_rpcclient->cl_pathname); 105 "idmap", idmap, &idmap_upcall_ops, 0);
107
108 idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path,
109 idmap, &idmap_upcall_ops, 0);
110 if (IS_ERR(idmap->idmap_dentry)) { 106 if (IS_ERR(idmap->idmap_dentry)) {
111 kfree(idmap); 107 kfree(idmap);
112 return; 108 return;
@@ -128,7 +124,7 @@ nfs_idmap_delete(struct nfs4_client *clp)
128 124
129 if (!idmap) 125 if (!idmap)
130 return; 126 return;
131 rpc_unlink(idmap->idmap_path); 127 rpc_unlink(idmap->idmap_dentry);
132 clp->cl_idmap = NULL; 128 clp->cl_idmap = NULL;
133 kfree(idmap); 129 kfree(idmap);
134} 130}
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index ab151bbb66df..b5b51c196690 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -59,7 +59,7 @@ struct rpc_clnt {
59 59
60 int cl_nodelen; /* nodename length */ 60 int cl_nodelen; /* nodename length */
61 char cl_nodename[UNX_MAXNODENAME]; 61 char cl_nodename[UNX_MAXNODENAME];
62 char cl_pathname[30];/* Path in rpc_pipe_fs */ 62 struct dentry * __cl_parent_dentry;
63 struct dentry * cl_dentry; /* inode */ 63 struct dentry * cl_dentry; /* inode */
64 struct rpc_clnt * cl_parent; /* Points to parent of clones */ 64 struct rpc_clnt * cl_parent; /* Points to parent of clones */
65 struct rpc_rtt cl_rtt_default; 65 struct rpc_rtt cl_rtt_default;
diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index 63929349571f..63878d05c9a9 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -41,10 +41,11 @@ RPC_I(struct inode *inode)
41 41
42extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); 42extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *);
43 43
44extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *); 44extern struct dentry *rpc_mkdir(struct dentry *, char *, struct rpc_clnt *);
45extern int rpc_rmdir(char *); 45extern void rpc_rmdir(struct dentry *);
46extern struct dentry *rpc_mkpipe(char *, void *, struct rpc_pipe_ops *, int flags); 46extern struct dentry *rpc_mkpipe(struct dentry *, char *, void *,
47extern int rpc_unlink(char *); 47 struct rpc_pipe_ops *, int flags);
48extern void rpc_unlink(struct dentry *);
48 49
49#endif 50#endif
50#endif 51#endif
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index d2b08f16c257..bd2555139fa9 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -87,7 +87,6 @@ struct gss_auth {
87 struct list_head upcalls; 87 struct list_head upcalls;
88 struct rpc_clnt *client; 88 struct rpc_clnt *client;
89 struct dentry *dentry; 89 struct dentry *dentry;
90 char path[48];
91 spinlock_t lock; 90 spinlock_t lock;
92}; 91};
93 92
@@ -690,10 +689,8 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
690 if (err) 689 if (err)
691 goto err_put_mech; 690 goto err_put_mech;
692 691
693 snprintf(gss_auth->path, sizeof(gss_auth->path), "%s/%s", 692 gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name,
694 clnt->cl_pathname, 693 clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
695 gss_auth->mech->gm_name);
696 gss_auth->dentry = rpc_mkpipe(gss_auth->path, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
697 if (IS_ERR(gss_auth->dentry)) { 694 if (IS_ERR(gss_auth->dentry)) {
698 err = PTR_ERR(gss_auth->dentry); 695 err = PTR_ERR(gss_auth->dentry);
699 goto err_put_mech; 696 goto err_put_mech;
@@ -718,7 +715,7 @@ gss_destroy(struct rpc_auth *auth)
718 auth, auth->au_flavor); 715 auth, auth->au_flavor);
719 716
720 gss_auth = container_of(auth, struct gss_auth, rpc_auth); 717 gss_auth = container_of(auth, struct gss_auth, rpc_auth);
721 rpc_unlink(gss_auth->path); 718 rpc_unlink(gss_auth->dentry);
722 gss_mech_put(gss_auth->mech); 719 gss_mech_put(gss_auth->mech);
723 720
724 rpcauth_free_credcache(auth); 721 rpcauth_free_credcache(auth);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 5a8f01d726e9..63bf591310e0 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -67,26 +67,42 @@ static u32 * call_verify(struct rpc_task *task);
67static int 67static int
68rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) 68rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
69{ 69{
70 static uint32_t clntid; 70 static unsigned int clntid;
71 char name[128];
71 int error; 72 int error;
72 73
73 if (dir_name == NULL) 74 if (dir_name == NULL)
74 return 0; 75 return 0;
75 for (;;) { 76
76 snprintf(clnt->cl_pathname, sizeof(clnt->cl_pathname), 77 retry_parent:
77 "%s/clnt%x", dir_name, 78 clnt->__cl_parent_dentry = rpc_mkdir(NULL, dir_name, NULL);
78 (unsigned int)clntid++); 79 if (IS_ERR(clnt->__cl_parent_dentry)) {
79 clnt->cl_pathname[sizeof(clnt->cl_pathname) - 1] = '\0'; 80 error = PTR_ERR(clnt->__cl_parent_dentry);
80 clnt->cl_dentry = rpc_mkdir(clnt->cl_pathname, clnt); 81 if (error == -EEXIST)
81 if (!IS_ERR(clnt->cl_dentry)) 82 goto retry_parent; /* XXX(hch): WTF? */
82 return 0; 83
84 printk(KERN_INFO "RPC: Couldn't create pipefs entry %s, error %d\n",
85 dir_name, error);
86 return error;
87 }
88
89
90 retry_child:
91 snprintf(name, sizeof(name), "clnt%x", clntid++);
92 name[sizeof(name) - 1] = '\0';
93
94 clnt->cl_dentry = rpc_mkdir(clnt->__cl_parent_dentry, name, clnt);
95 if (IS_ERR(clnt->cl_dentry)) {
83 error = PTR_ERR(clnt->cl_dentry); 96 error = PTR_ERR(clnt->cl_dentry);
84 if (error != -EEXIST) { 97 if (error == -EEXIST)
85 printk(KERN_INFO "RPC: Couldn't create pipefs entry %s, error %d\n", 98 goto retry_child;
86 clnt->cl_pathname, error); 99 printk(KERN_INFO "RPC: Couldn't create pipefs entry %s, error %d\n",
87 return error; 100 name, error);
88 } 101 rpc_rmdir(clnt->__cl_parent_dentry);
102 return error;
89 } 103 }
104
105 return 0;
90} 106}
91 107
92/* 108/*
@@ -174,7 +190,8 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname,
174 return clnt; 190 return clnt;
175 191
176out_no_auth: 192out_no_auth:
177 rpc_rmdir(clnt->cl_pathname); 193 rpc_rmdir(clnt->cl_dentry);
194 rpc_rmdir(clnt->__cl_parent_dentry);
178out_no_path: 195out_no_path:
179 if (clnt->cl_server != clnt->cl_inline_name) 196 if (clnt->cl_server != clnt->cl_inline_name)
180 kfree(clnt->cl_server); 197 kfree(clnt->cl_server);
@@ -302,8 +319,10 @@ rpc_destroy_client(struct rpc_clnt *clnt)
302 rpc_destroy_client(clnt->cl_parent); 319 rpc_destroy_client(clnt->cl_parent);
303 goto out_free; 320 goto out_free;
304 } 321 }
305 if (clnt->cl_pathname[0]) 322 if (clnt->cl_dentry)
306 rpc_rmdir(clnt->cl_pathname); 323 rpc_rmdir(clnt->cl_dentry);
324 if (clnt->__cl_parent_dentry)
325 rpc_rmdir(clnt->__cl_parent_dentry);
307 if (clnt->cl_xprt) { 326 if (clnt->cl_xprt) {
308 xprt_destroy(clnt->cl_xprt); 327 xprt_destroy(clnt->cl_xprt);
309 clnt->cl_xprt = NULL; 328 clnt->cl_xprt = NULL;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index ded6c63f11ec..b382809726d8 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -414,38 +414,6 @@ rpc_put_mount(void)
414 simple_release_fs(&rpc_mount, &rpc_mount_count); 414 simple_release_fs(&rpc_mount, &rpc_mount_count);
415} 415}
416 416
417static int
418rpc_lookup_parent(char *path, struct nameidata *nd)
419{
420 if (path[0] == '\0')
421 return -ENOENT;
422 if (rpc_get_mount()) {
423 printk(KERN_WARNING "%s: %s failed to mount "
424 "pseudofilesystem \n", __FILE__, __FUNCTION__);
425 return -ENODEV;
426 }
427 nd->mnt = mntget(rpc_mount);
428 nd->dentry = dget(rpc_mount->mnt_root);
429 nd->last_type = LAST_ROOT;
430 nd->flags = LOOKUP_PARENT;
431 nd->depth = 0;
432
433 if (path_walk(path, nd)) {
434 printk(KERN_WARNING "%s: %s failed to find path %s\n",
435 __FILE__, __FUNCTION__, path);
436 rpc_put_mount();
437 return -ENOENT;
438 }
439 return 0;
440}
441
442static void
443rpc_release_path(struct nameidata *nd)
444{
445 path_release(nd);
446 rpc_put_mount();
447}
448
449static struct inode * 417static struct inode *
450rpc_get_inode(struct super_block *sb, int mode) 418rpc_get_inode(struct super_block *sb, int mode)
451{ 419{
@@ -550,197 +518,149 @@ out_bad:
550 return -ENOMEM; 518 return -ENOMEM;
551} 519}
552 520
553static int 521struct dentry *
554__rpc_mkdir(struct inode *dir, struct dentry *dentry) 522rpc_mkdir(struct dentry *parent, char *name, struct rpc_clnt *rpc_client)
555{ 523{
524 struct inode *dir;
525 struct dentry *dentry;
556 struct inode *inode; 526 struct inode *inode;
557
558 inode = rpc_get_inode(dir->i_sb, S_IFDIR | S_IRUSR | S_IXUSR);
559 if (!inode)
560 goto out_err;
561 inode->i_ino = iunique(dir->i_sb, 100);
562 d_instantiate(dentry, inode);
563 dir->i_nlink++;
564 inode_dir_notify(dir, DN_CREATE);
565 rpc_get_mount();
566 return 0;
567out_err:
568 printk(KERN_WARNING "%s: %s failed to allocate inode for dentry %s\n",
569 __FILE__, __FUNCTION__, dentry->d_name.name);
570 return -ENOMEM;
571}
572
573static int
574__rpc_rmdir(struct inode *dir, struct dentry *dentry)
575{
576 int error; 527 int error;
577 528
578 shrink_dcache_parent(dentry); 529 if (!parent)
579 if (dentry->d_inode) { 530 parent = rpc_mount->mnt_root;
580 rpc_close_pipes(dentry->d_inode);
581 rpc_inode_setowner(dentry->d_inode, NULL);
582 }
583 if ((error = simple_rmdir(dir, dentry)) != 0)
584 return error;
585 if (!error) {
586 inode_dir_notify(dir, DN_DELETE);
587 d_drop(dentry);
588 rpc_put_mount();
589 }
590 return 0;
591}
592 531
593static struct dentry * 532 dir = parent->d_inode;
594rpc_lookup_negative(char *path, struct nameidata *nd) 533
595{ 534 error = rpc_get_mount();
596 struct dentry *dentry; 535 if (error)
597 struct inode *dir;
598 int error;
599
600 if ((error = rpc_lookup_parent(path, nd)) != 0)
601 return ERR_PTR(error); 536 return ERR_PTR(error);
602 dir = nd->dentry->d_inode; 537
603 down(&dir->i_sem); 538 down(&dir->i_sem);
604 dentry = lookup_hash(&nd->last, nd->dentry); 539 dentry = lookup_one_len(name, parent, strlen(name));
605 if (IS_ERR(dentry)) 540 if (IS_ERR(dentry))
606 goto out_err; 541 goto out_unlock;
607 if (dentry->d_inode) { 542 if (dentry->d_inode) {
608 dput(dentry);
609 dentry = ERR_PTR(-EEXIST); 543 dentry = ERR_PTR(-EEXIST);
610 goto out_err; 544 goto out_dput;
611 } 545 }
612 return dentry;
613out_err:
614 up(&dir->i_sem);
615 rpc_release_path(nd);
616 return dentry;
617}
618 546
547 inode = rpc_get_inode(dir->i_sb, S_IFDIR | S_IRUSR | S_IXUSR);
548 if (!inode)
549 goto out_dput;
550 inode->i_ino = iunique(dir->i_sb, 100);
551 dir->i_nlink++;
552 RPC_I(dentry->d_inode)->private = rpc_client;
619 553
620struct dentry * 554 d_instantiate(dentry, inode);
621rpc_mkdir(char *path, struct rpc_clnt *rpc_client) 555 dget(dentry);
622{ 556 up(&dir->i_sem);
623 struct nameidata nd; 557
624 struct dentry *dentry; 558 inode_dir_notify(dir, DN_CREATE);
625 struct inode *dir;
626 int error;
627 559
628 dentry = rpc_lookup_negative(path, &nd);
629 if (IS_ERR(dentry))
630 return dentry;
631 dir = nd.dentry->d_inode;
632 if ((error = __rpc_mkdir(dir, dentry)) != 0)
633 goto err_dput;
634 RPC_I(dentry->d_inode)->private = rpc_client;
635 error = rpc_populate(dentry, authfiles, 560 error = rpc_populate(dentry, authfiles,
636 RPCAUTH_info, RPCAUTH_EOF); 561 RPCAUTH_info, RPCAUTH_EOF);
637 if (error) 562 if (error)
638 goto err_depopulate; 563 goto out_depopulate;
639out: 564
640 up(&dir->i_sem);
641 rpc_release_path(&nd);
642 return dentry; 565 return dentry;
643err_depopulate: 566
644 rpc_depopulate(dentry); 567 out_depopulate:
645 __rpc_rmdir(dir, dentry); 568 rpc_rmdir(dentry);
646err_dput: 569 out_dput:
647 dput(dentry); 570 dput(dentry);
648 printk(KERN_WARNING "%s: %s() failed to create directory %s (errno = %d)\n", 571 out_unlock:
649 __FILE__, __FUNCTION__, path, error); 572 up(&dir->i_sem);
650 dentry = ERR_PTR(error); 573 rpc_put_mount();
651 goto out; 574 return dentry;
652} 575}
653 576
654int 577void
655rpc_rmdir(char *path) 578rpc_rmdir(struct dentry *dentry)
656{ 579{
657 struct nameidata nd; 580 struct dentry *parent = dentry->d_parent;
658 struct dentry *dentry;
659 struct inode *dir;
660 int error;
661 581
662 if ((error = rpc_lookup_parent(path, &nd)) != 0)
663 return error;
664 dir = nd.dentry->d_inode;
665 down(&dir->i_sem);
666 dentry = lookup_hash(&nd.last, nd.dentry);
667 if (IS_ERR(dentry)) {
668 error = PTR_ERR(dentry);
669 goto out_release;
670 }
671 rpc_depopulate(dentry); 582 rpc_depopulate(dentry);
672 error = __rpc_rmdir(dir, dentry); 583
673 dput(dentry); 584 down(&parent->d_inode->i_sem);
674out_release: 585 if (dentry->d_inode) {
675 up(&dir->i_sem); 586 rpc_close_pipes(dentry->d_inode);
676 rpc_release_path(&nd); 587 rpc_inode_setowner(dentry->d_inode, NULL);
677 return error; 588 simple_rmdir(parent->d_inode, dentry);
589 }
590 up(&parent->d_inode->i_sem);
591
592 inode_dir_notify(parent->d_inode, DN_DELETE);
593 rpc_put_mount();
678} 594}
679 595
680struct dentry * 596struct dentry *
681rpc_mkpipe(char *path, void *private, struct rpc_pipe_ops *ops, int flags) 597rpc_mkpipe(struct dentry *parent, char *name, void *private,
598 struct rpc_pipe_ops *ops, int flags)
682{ 599{
683 struct nameidata nd; 600 struct inode *dir = parent->d_inode;
684 struct dentry *dentry; 601 struct dentry *dentry;
685 struct inode *dir, *inode; 602 struct inode *inode;
686 struct rpc_inode *rpci; 603 struct rpc_inode *rpci;
604 int error;
605
606 error = rpc_get_mount();
607 if (error)
608 return ERR_PTR(error);
687 609
688 dentry = rpc_lookup_negative(path, &nd); 610 down(&parent->d_inode->i_sem);
611 dentry = lookup_one_len(name, parent, strlen(name));
689 if (IS_ERR(dentry)) 612 if (IS_ERR(dentry))
690 return dentry; 613 goto out_unlock;
691 dir = nd.dentry->d_inode; 614 if (dentry->d_inode) {
692 inode = rpc_get_inode(dir->i_sb, S_IFSOCK | S_IRUSR | S_IWUSR); 615 dentry = ERR_PTR(-EEXIST);
693 if (!inode) 616 goto out_dput;
694 goto err_dput; 617 }
618
619 inode = rpc_get_inode(parent->d_inode->i_sb,
620 S_IFSOCK | S_IRUSR | S_IWUSR);
621 if (!inode) {
622 dentry = ERR_PTR(-ENOMEM);
623 goto out_dput;
624 }
625
695 inode->i_ino = iunique(dir->i_sb, 100); 626 inode->i_ino = iunique(dir->i_sb, 100);
696 inode->i_fop = &rpc_pipe_fops; 627 inode->i_fop = &rpc_pipe_fops;
697 d_instantiate(dentry, inode); 628
698 rpci = RPC_I(inode); 629 rpci = RPC_I(inode);
699 rpci->private = private; 630 rpci->private = private;
700 rpci->flags = flags; 631 rpci->flags = flags;
701 rpci->ops = ops; 632 rpci->ops = ops;
633
634 d_instantiate(dentry, inode);
635 dget(dentry);
636 up(&parent->d_inode->i_sem);
637
702 inode_dir_notify(dir, DN_CREATE); 638 inode_dir_notify(dir, DN_CREATE);
703out:
704 up(&dir->i_sem);
705 rpc_release_path(&nd);
706 return dentry; 639 return dentry;
707err_dput: 640
641 out_dput:
708 dput(dentry); 642 dput(dentry);
709 dentry = ERR_PTR(-ENOMEM); 643 out_unlock:
710 printk(KERN_WARNING "%s: %s() failed to create pipe %s (errno = %d)\n", 644 up(&parent->d_inode->i_sem);
711 __FILE__, __FUNCTION__, path, -ENOMEM); 645 rpc_put_mount();
712 goto out; 646 return dentry;
713} 647}
714 648
715int 649void
716rpc_unlink(char *path) 650rpc_unlink(struct dentry *dentry)
717{ 651{
718 struct nameidata nd; 652 struct dentry *parent = dentry->d_parent;
719 struct dentry *dentry;
720 struct inode *dir;
721 int error;
722 653
723 if ((error = rpc_lookup_parent(path, &nd)) != 0) 654 down(&parent->d_inode->i_sem);
724 return error;
725 dir = nd.dentry->d_inode;
726 down(&dir->i_sem);
727 dentry = lookup_hash(&nd.last, nd.dentry);
728 if (IS_ERR(dentry)) {
729 error = PTR_ERR(dentry);
730 goto out_release;
731 }
732 d_drop(dentry);
733 if (dentry->d_inode) { 655 if (dentry->d_inode) {
734 rpc_close_pipes(dentry->d_inode); 656 rpc_close_pipes(dentry->d_inode);
735 rpc_inode_setowner(dentry->d_inode, NULL); 657 rpc_inode_setowner(dentry->d_inode, NULL);
736 error = simple_unlink(dir, dentry); 658 simple_unlink(parent->d_inode, dentry);
737 } 659 }
738 dput(dentry); 660 up(&parent->d_inode->i_sem);
739 inode_dir_notify(dir, DN_DELETE); 661
740out_release: 662 inode_dir_notify(parent->d_inode, DN_DELETE);
741 up(&dir->i_sem); 663 rpc_put_mount();
742 rpc_release_path(&nd);
743 return error;
744} 664}
745 665
746/* 666/*