Add a DELTREE and READDIR operation
[sahlberg/dbench.git] / libnfs.c
1 /* 
2    nfs library for dbench
3
4    Copyright (C) 2008 by Ronnie Sahlberg (ronniesahlberg@gmail.com)
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "mount.h"
21 #include "nfs.h"
22 #include "libnfs.h"
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26
27 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
28
29 typedef struct _data_t {
30         const char *dptr;
31         int dsize;
32 } data_t;
33
34 typedef struct _tree_t {
35         data_t key;
36         data_t fh;
37         ssize_t size;
38         struct _tree_t *parent;
39         struct _tree_t *left;
40         struct _tree_t *right;
41 } tree_t;
42
43
44 struct nfsio {
45         int s;
46         CLIENT *clnt;
47         tree_t *fhandles;
48 };
49
50 static void free_node(tree_t *t)
51 {
52         free(discard_const(t->key.dptr));
53         free(discard_const(t->fh.dptr));
54         free(t);
55 }
56
57 static tree_t *find_fhandle(tree_t *tree, const char *key)
58 {
59         int i;
60
61         if (tree == NULL) {
62                 return NULL;
63         }
64
65         i = strcmp(key, tree->key.dptr);
66         if (i == 0) {
67                 return tree;
68         }
69         if (i < 0) {
70                 return find_fhandle(tree->left, key);
71         }
72
73         return find_fhandle(tree->right, key);
74 }
75
76 static data_t *recursive_lookup_fhandle(struct nfsio *nfsio, const char *name)
77 {
78         tree_t *t;
79         char *strp;
80         char *tmpname;
81         nfsstat3 ret;
82         
83         while (name[0] == '.') name++;
84
85         if (name[0] == 0) {
86                 return NULL;
87         }
88
89         tmpname = strdup(name);
90         strp = rindex(tmpname, '/');
91         if (strp == NULL) {
92                 free(tmpname);
93                 return NULL;
94         }
95         *strp = 0;
96
97         recursive_lookup_fhandle(nfsio, tmpname);
98         free(tmpname);
99
100         t = find_fhandle(nfsio->fhandles, name);
101         if (t != NULL) {
102                 return &t->fh;
103         }
104
105         ret = nfsio_lookup(nfsio, name, NULL);
106         if (ret != 0) {
107                 return NULL;
108         }
109
110         t = find_fhandle(nfsio->fhandles, name);
111         if (t != NULL) {
112                 return &t->fh;
113         }
114
115         return ;
116 }
117
118 static data_t *lookup_fhandle(struct nfsio *nfsio, const char *name, ssize_t *size)
119 {
120         tree_t *t;
121
122         while (name[0] == '.') name++;
123
124         if (name[0] == 0) {
125                 name = "/";
126         }
127
128         t = find_fhandle(nfsio->fhandles, name);
129         if (t == NULL) {
130                 return recursive_lookup_fhandle(nfsio, name);
131         }
132
133         if (size) {
134                 *size = t->size;
135         }
136
137         return &t->fh;
138 }
139
140 static void delete_fhandle(struct nfsio *nfsio, const char *name)
141 {
142         tree_t *t;
143
144         while (name[0] == '.') name++;
145
146         t = find_fhandle(nfsio->fhandles, name);
147         if (t == NULL) {
148                 return;
149         }
150
151         /* we have a left child */
152         if (t->left) {
153                 tree_t *tmp_tree;
154
155                 for(tmp_tree=t->left;tmp_tree->right;tmp_tree=tmp_tree->right)
156                         ;
157                 tmp_tree->right = t->right;
158                 if (t->right) {
159                         t->right->parent = tmp_tree;
160                 }
161
162                 if (t->parent == NULL) {
163                         nfsio->fhandles = tmp_tree;
164                         tmp_tree->parent = NULL;
165                         free_node(t);
166                         return;
167                 }
168
169                 if (t->parent->left == t) {
170                         t->parent->left = t->left;
171                         if (t->left) {
172                                 t->left->parent = t->parent;
173                         }
174                         free_node(t);
175                         return;
176                 }
177
178                 t->parent->right = t->left;
179                 if (t->left) {
180                         t->left->parent = t->parent;
181                 }
182                 free_node(t);
183                 return;
184         }
185
186         /* we only have a right child */
187         if (t->right) {
188                 tree_t *tmp_tree;
189
190                 for(tmp_tree=t->right;tmp_tree->left;tmp_tree=tmp_tree->left)
191                         ;
192                 tmp_tree->left = t->left;
193                 if (t->left) {
194                         t->left->parent = tmp_tree;
195                 }
196
197                 if (t->parent == NULL) {
198                         nfsio->fhandles = tmp_tree;
199                         tmp_tree->parent = NULL;
200                         free_node(t);
201                         return;
202                 }
203
204                 if (t->parent->left == t) {
205                         t->parent->left = t->right;
206                         if (t->right) {
207                                 t->right->parent = t->parent;
208                         }
209                         free_node(t);
210                         return;
211                 }
212
213                 t->parent->right = t->right;
214                 if (t->right) {
215                         t->right->parent = t->parent;
216                 }
217                 free_node(t);
218                 return;
219         }
220
221         /* we are a leaf node */
222         if (t->parent == NULL) {
223                 nfsio->fhandles = NULL;
224         } else {
225                 if (t->parent->left == t) {
226                         t->parent->left = NULL;
227                 } else {
228                         t->parent->right = NULL;
229                 }
230         }
231         free_node(t);
232         return;
233 }
234
235 static void insert_fhandle(struct nfsio *nfsio, const char *name, const char *fhandle, int length, ssize_t size)
236 {
237         tree_t *tmp_t;
238         tree_t *t;
239         int i;
240
241         while (name[0] == '.') name++;
242
243         t = malloc(sizeof(tree_t));
244         if (t == NULL) {
245                 fprintf(stderr, "MALLOC failed to allocate tree_t in insert_fhandle\n");
246                 exit(10);
247         }
248
249         t->key.dptr = strdup(name);
250         if (t->key.dptr == NULL) {
251                 fprintf(stderr, "STRDUP failed to allocate key in insert_fhandle\n");
252                 exit(10);
253         }
254         t->key.dsize = strlen(name);
255
256
257         t->fh.dptr = malloc(length);
258         if (t->key.dptr == NULL) {
259                 fprintf(stderr, "MALLOC failed to allocate fhandle in insert_fhandle\n");
260                 exit(10);
261         }
262         memcpy(discard_const(t->fh.dptr), fhandle, length);
263         t->fh.dsize = length;   
264         
265         t->size   = size;
266
267         t->left   = NULL;
268         t->right  = NULL;
269         t->parent = NULL;
270
271         if (nfsio->fhandles == NULL) {
272                 nfsio->fhandles = t;
273                 return;
274         }
275
276         tmp_t = nfsio->fhandles;
277 again:
278         i = strcmp(t->key.dptr, tmp_t->key.dptr);
279         if (i == 0) {
280                 free(discard_const(tmp_t->fh.dptr));
281                 tmp_t->fh.dsize = t->fh.dsize;
282                 tmp_t->fh.dptr  = t->fh.dptr;
283                 free(discard_const(t->key.dptr));
284                 free(t);
285                 return;
286         }
287         if (i < 0) {
288                 if (tmp_t->left == NULL) {
289                         tmp_t->left = t;
290                         t->parent = tmp_t;
291                         return;
292                 }
293                 tmp_t = tmp_t->left;
294                 goto again;
295         }
296         if (tmp_t->right == NULL) {
297                 tmp_t->right = t;
298                 t->parent = tmp_t;
299                 return;
300         }
301         tmp_t = tmp_t->right;
302         goto again;
303 }
304
305
306 struct nfs_errors {
307         const char *err;
308         int idx;
309 };
310
311 static const struct nfs_errors nfs_errors[] = {
312         {"NFS3_OK", 0},
313         {"NFS3ERR_PERM", 1},
314         {"NFS3ERR_NOENT", 2},
315         {"NFS3ERR_IO", 5},
316         {"NFS3ERR_NXIO", 6},
317         {"NFS3ERR_ACCES", 13},
318         {"NFS3ERR_EXIST", 17},
319         {"NFS3ERR_XDEV", 18},
320         {"NFS3ERR_NODEV", 19},
321         {"NFS3ERR_NOTDIR", 20},
322         {"NFS3ERR_ISDIR", 21},
323         {"NFS3ERR_INVAL", 22},
324         {"NFS3ERR_FBIG", 27},
325         {"NFS3ERR_NOSPC", 28},
326         {"NFS3ERR_ROFS", 30},
327         {"NFS3ERR_MLINK", 31},
328         {"NFS3ERR_NAMETOOLONG", 63},
329         {"NFS3ERR_NOTEMPTY", 66},
330         {"NFS3ERR_DQUOT", 69},
331         {"NFS3ERR_STALE", 70},
332         {"NFS3ERR_REMOTE", 71},
333         {"NFS3ERR_BADHANDLE", 10001},
334         {"NFS3ERR_NOT_SYNC", 10002},
335         {"NFS3ERR_BAD_COOKIE", 10003},
336         {"NFS3ERR_NOTSUPP", 10004},
337         {"NFS3ERR_TOOSMALL", 10005},
338         {"NFS3ERR_SERVERFAULT", 10006},
339         {"NFS3ERR_BADTYPE", 10007},
340         {"NFS3ERR_JUKEBOX", 10008},
341 };
342
343
344
345 const char *nfs_error(int error)
346 {
347         unsigned int i;
348
349         for(i=0;i<sizeof(nfs_errors)/sizeof(struct nfs_errors);i++) {
350                 if (error == nfs_errors[i].idx) {
351                         return nfs_errors[i].err;
352                 }
353         }
354         return "Unknown NFS error";
355 }
356
357
358
359
360 void nfsio_disconnect(struct nfsio *nfsio)
361 {
362         if (nfsio->clnt != NULL) {
363                 clnt_destroy(nfsio->clnt);
364                 nfsio->clnt = NULL;
365         }
366         if (nfsio->s != -1) {
367                 close(nfsio->s);
368         }
369         // qqq free the tree*/
370
371         free(nfsio);
372 }
373
374
375
376
377 struct nfsio *nfsio_connect(const char *server, const char *export, const char *protocol)
378 {
379         dirpath mountdir=discard_const(export);
380         struct nfsio *nfsio;
381         mountres3 *mountres;
382         fhandle3 *fh;
383         struct sockaddr_in sin;
384         int ret;
385
386         nfsio = malloc(sizeof(struct nfsio));
387         if (nfsio == NULL) {
388                 fprintf(stderr, "Failed to malloc nfsio\n");
389                 return NULL;
390         }
391         bzero(nfsio, sizeof(struct nfsio));
392
393         nfsio->s = -1;
394
395
396         /*
397          * set up the MOUNT client. If we are running as root, we get
398          * a port <1024 by default. If we are not root, we can not
399          * bind to these ports, so the server must be in "insecure"
400          * mode.
401          */
402         memset(&sin, 0, sizeof(sin));
403         sin.sin_port = 0;
404         sin.sin_family = PF_INET;
405         if (inet_aton(server, &sin.sin_addr) == 0) {
406                 fprintf(stderr, "Invalid address '%s'\n", server);
407                 nfsio_disconnect(nfsio);
408                 return NULL;
409         }
410
411         if (!strcmp(protocol, "tcp")) {
412                 nfsio->s = RPC_ANYSOCK;
413                 nfsio->clnt = clnttcp_create(&sin, MOUNT_PROGRAM, MOUNT_V3, &nfsio->s, 32768, 32768);
414         } else {
415                 struct timeval wait;
416
417                 wait.tv_sec  = 5;
418                 wait.tv_usec = 0;
419                 nfsio->s = RPC_ANYSOCK;
420                 nfsio->clnt = clntudp_create(&sin, MOUNT_PROGRAM, MOUNT_V3, wait, &nfsio->s);
421         }
422
423         if (nfsio->clnt == NULL) {
424                 printf("ERROR: failed to connect to MOUNT daemon on %s\n", server);
425                 nfsio_disconnect(nfsio);
426                 return NULL;
427         }
428         nfsio->clnt->cl_auth = authunix_create_default();
429
430         mountres=mountproc3_mnt_3(&mountdir, nfsio->clnt);
431         if (mountres == NULL) {
432                 printf("ERROR: failed to call the MNT procedure\n");
433                 nfsio_disconnect(nfsio);
434                 return NULL;
435         }
436         if (mountres->fhs_status != MNT3_OK) {
437                 printf("ERROR: Server returned error %d when trying to MNT\n",mountres->fhs_status);
438                 nfsio_disconnect(nfsio);
439                 return NULL;
440         }
441
442         fh = &mountres->mountres3_u.mountinfo.fhandle;
443         insert_fhandle(nfsio, "/",
444                               fh->fhandle3_val,
445                               fh->fhandle3_len,
446                               0);
447
448
449         /* we dont need the mount client any more */
450         clnt_destroy(nfsio->clnt);
451         nfsio->clnt = NULL;
452         close(nfsio->s);
453         nfsio->s = -1;
454
455
456         /*
457          * set up the NFS client. If we are running as root, we get
458          * a port <1024 by default. If we are not root, we can not
459          * bind to these ports, so the server must be in "insecure"
460          * mode.
461          */
462         memset(&sin, 0, sizeof(sin));
463         sin.sin_port = 0;
464         sin.sin_family = PF_INET;
465         if (inet_aton(server, &sin.sin_addr) == 0) {
466                 fprintf(stderr, "Invalid address '%s'\n", server);
467                 nfsio_disconnect(nfsio);
468                 return NULL;
469         }
470
471         if (!strcmp(protocol, "tcp")) {
472                 nfsio->s = RPC_ANYSOCK;
473                 nfsio->clnt = clnttcp_create(&sin, NFS_PROGRAM, NFS_V3, &nfsio->s, 327680, 327680);
474         } else {
475                 struct timeval wait;
476
477                 wait.tv_sec  = 5;
478                 wait.tv_usec = 0;
479                 nfsio->s = RPC_ANYSOCK;
480                 nfsio->clnt = clntudp_create(&sin, NFS_PROGRAM, NFS_V3, wait, &nfsio->s);
481         }
482
483         if (nfsio->clnt == NULL) {
484                 fprintf(stderr, "Failed to initialize nfs client structure\n");
485                 nfsio_disconnect(nfsio);
486                 return NULL;
487         }
488         nfsio->clnt->cl_auth = authunix_create_default();
489
490         return nfsio;
491 }
492
493
494 nfsstat3 nfsio_getattr(struct nfsio *nfsio, const char *name, fattr3 *attributes)
495 {
496         struct GETATTR3args GETATTR3args;
497         struct GETATTR3res *GETATTR3res;
498         data_t *fh;
499
500         fh = lookup_fhandle(nfsio, name, NULL);
501         if (fh == NULL) {
502                 fprintf(stderr, "failed to fetch handle in nfsio_getattr\n");
503                 return NFS3ERR_SERVERFAULT;
504         }
505
506         GETATTR3args.object.data.data_len = fh->dsize;
507         GETATTR3args.object.data.data_val = discard_const(fh->dptr);
508
509         GETATTR3res = nfsproc3_getattr_3(&GETATTR3args, nfsio->clnt);
510
511         if (GETATTR3res == NULL) {
512                 fprintf(stderr, "nfsproc3_getattr_3 failed in getattr\n");
513                 return NFS3ERR_SERVERFAULT;
514         }
515
516         if (GETATTR3res->status != NFS3_OK) {
517                 fprintf(stderr, "nfsproc3_getattr_3 failed in getattr. status:%d\n", GETATTR3res->status);
518                 return GETATTR3res->status;
519         }
520
521         if (attributes) {
522                 memcpy(attributes, &GETATTR3res->GETATTR3res_u.resok.obj_attributes, sizeof(fattr3));
523         }
524
525         return NFS3_OK;
526 }
527
528 nfsstat3 nfsio_lookup(struct nfsio *nfsio, const char *name, fattr3 *attributes)
529 {
530
531         struct LOOKUP3args LOOKUP3args;
532         struct LOOKUP3res *LOOKUP3res;
533         char *tmp_name = NULL;
534         int ret = NFS3_OK;
535         data_t *fh;
536         char *ptr;
537
538         tmp_name = strdup(name);
539         if (tmp_name == NULL) {
540                 fprintf(stderr, "failed to strdup name in nfsio_lookup\n");
541                 ret = NFS3ERR_SERVERFAULT;
542                 goto finished;
543         }
544
545         ptr = rindex(tmp_name, '/');
546         if (ptr == NULL) {      
547                 fprintf(stderr, "name did not contain '/' in nfsio_lookup\n");
548                 ret = NFS3ERR_SERVERFAULT;
549                 goto finished;
550         }
551
552         *ptr = 0;
553         ptr++;
554
555         fh = lookup_fhandle(nfsio, tmp_name, NULL);
556         if (fh == NULL) {
557                 fprintf(stderr, "failed to fetch parent handle for '%s' in nfsio_lookup\n", tmp_name);
558                 ret = NFS3ERR_SERVERFAULT;
559                 goto finished;
560         }
561
562         LOOKUP3args.what.dir.data.data_len = fh->dsize;
563         LOOKUP3args.what.dir.data.data_val = discard_const(fh->dptr);
564         LOOKUP3args.what.name = ptr;
565
566         LOOKUP3res = nfsproc3_lookup_3(&LOOKUP3args, nfsio->clnt);
567
568         if (LOOKUP3res == NULL) {
569                 fprintf(stderr, "nfsproc3_lookup_3 failed in lookup\n");
570                 ret = NFS3ERR_SERVERFAULT;
571                 goto finished;
572         }
573
574         if (LOOKUP3res->status != NFS3_OK) {
575                 ret = LOOKUP3res->status;
576                 goto finished;
577         }
578
579         insert_fhandle(nfsio, name, 
580                         LOOKUP3res->LOOKUP3res_u.resok.object.data.data_val,
581                         LOOKUP3res->LOOKUP3res_u.resok.object.data.data_len,
582                         LOOKUP3res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size);
583
584         free(LOOKUP3res->LOOKUP3res_u.resok.object.data.data_val);
585
586         if (attributes) {
587                 memcpy(attributes, &LOOKUP3res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes, sizeof(fattr3));
588         }
589
590 finished:
591         if (tmp_name) {
592                 free(tmp_name);
593         }
594         return ret;
595 }
596
597
598 nfsstat3 nfsio_access(struct nfsio *nfsio, const char *name, uint32 desired, uint32 *access)
599 {
600
601         struct ACCESS3args ACCESS3args;
602         struct ACCESS3res *ACCESS3res;
603         data_t *fh;
604
605         fh = lookup_fhandle(nfsio, name, NULL);
606         if (fh == NULL) {
607                 fprintf(stderr, "failed to fetch handle in nfsio_access\n");
608                 return NFS3ERR_SERVERFAULT;
609         }
610
611         ACCESS3args.object.data.data_val = discard_const(fh->dptr);
612         ACCESS3args.object.data.data_len = fh->dsize;
613         ACCESS3args.access = desired;
614
615         ACCESS3res = nfsproc3_access_3(&ACCESS3args, nfsio->clnt);
616
617         if (ACCESS3res == NULL) {
618                 fprintf(stderr, "nfsproc3_access_3 failed in access\n");
619                 return NFS3ERR_SERVERFAULT;
620         }
621
622         if (ACCESS3res->status != NFS3_OK) {
623                 fprintf(stderr, "nfsproc3_access_3 failed. status:%d\n", 
624 ACCESS3res->status);
625                 return ACCESS3res->status;
626         }
627
628         if (access) {
629                 *access = ACCESS3res->ACCESS3res_u.resok.access;
630         }
631
632         return NFS3_OK;
633 }
634
635
636
637 nfsstat3 nfsio_create(struct nfsio *nfsio, const char *name)
638 {
639
640         struct CREATE3args CREATE3args;
641         struct CREATE3res *CREATE3res;
642         char *tmp_name = NULL;
643         data_t *fh;
644         char *ptr;
645         int ret = NFS3_OK;
646
647         tmp_name = strdup(name);
648         if (tmp_name == NULL) {
649                 fprintf(stderr, "failed to strdup name in nfsio_create\n");
650                 ret = NFS3ERR_SERVERFAULT;
651                 goto finished;
652         }
653
654         ptr = rindex(tmp_name, '/');
655         if (ptr == NULL) {      
656                 fprintf(stderr, "name did not contain '/' in nfsio_create\n");
657                 ret = NFS3ERR_SERVERFAULT;
658                 goto finished;
659         }
660
661         *ptr = 0;
662         ptr++;
663
664         fh = lookup_fhandle(nfsio, tmp_name, NULL);
665         if (fh == NULL) {
666                 fprintf(stderr, "failed to fetch parent handle in nfsio_create\n");
667                 ret = NFS3ERR_SERVERFAULT;
668                 goto finished;
669         }
670
671         CREATE3args.where.dir.data.data_len  = fh->dsize;
672         CREATE3args.where.dir.data.data_val  = discard_const(fh->dptr);
673         CREATE3args.where.name               = ptr;
674
675         CREATE3args.how.mode = UNCHECKED;
676         CREATE3args.how.createhow3_u.obj_attributes.mode.set_it  = TRUE;
677         CREATE3args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode    = 0777;
678         CREATE3args.how.createhow3_u.obj_attributes.uid.set_it   = TRUE;
679         CREATE3args.how.createhow3_u.obj_attributes.uid.set_uid3_u.uid      = 0;
680         CREATE3args.how.createhow3_u.obj_attributes.gid.set_it   = TRUE;
681         CREATE3args.how.createhow3_u.obj_attributes.gid.set_gid3_u.gid      = 0;
682         CREATE3args.how.createhow3_u.obj_attributes.size.set_it  = FALSE;
683         CREATE3args.how.createhow3_u.obj_attributes.atime.set_it = FALSE;
684         CREATE3args.how.createhow3_u.obj_attributes.mtime.set_it = FALSE;
685
686         CREATE3res = nfsproc3_create_3(&CREATE3args, nfsio->clnt);
687
688         if (CREATE3res == NULL) {
689                 fprintf(stderr, "nfsproc3_create_3 failed in nfsio_create\n");
690                 ret = NFS3ERR_SERVERFAULT;
691                 goto finished;
692         }
693
694         if (CREATE3res->status != NFS3_OK) {
695                 fprintf(stderr, "nfsproc3_create_3 failed in nfsio_create. status:%d\n", CREATE3res->status);
696                 ret = CREATE3res->status;
697                 goto finished;
698         }
699
700
701         insert_fhandle(nfsio, name, 
702                 CREATE3res->CREATE3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
703                 CREATE3res->CREATE3res_u.resok.obj.post_op_fh3_u.handle.data.data_len,
704                 0 /*qqq*/
705         );
706
707
708 finished:
709         if (tmp_name) {
710                 free(tmp_name);
711         }
712         return ret;
713 }
714
715 nfsstat3 nfsio_remove(struct nfsio *nfsio, const char *name)
716 {
717
718         struct REMOVE3args REMOVE3args;
719         struct REMOVE3res *REMOVE3res;
720         int ret = NFS3_OK;
721         char *tmp_name = NULL;
722         data_t *fh;
723         char *ptr;
724
725         tmp_name = strdup(name);
726         if (tmp_name == NULL) {
727                 fprintf(stderr, "failed to strdup name in nfsio_remove\n");
728                 ret = NFS3ERR_SERVERFAULT;
729                 goto finished;
730         }
731
732         ptr = rindex(tmp_name, '/');
733         if (ptr == NULL) {      
734                 fprintf(stderr, "name did not contain '/' in nfsio_remove\n");
735                 ret = NFS3ERR_SERVERFAULT;
736                 goto finished;
737         }
738
739         *ptr = 0;
740         ptr++;
741
742         fh = lookup_fhandle(nfsio, tmp_name, NULL);
743         if (fh == NULL) {
744                 fprintf(stderr, "failed to fetch parent handle in nfsio_remove\n");
745                 ret = NFS3ERR_SERVERFAULT;
746                 goto finished;
747         }
748
749
750         REMOVE3args.object.dir.data.data_len  = fh->dsize;
751         REMOVE3args.object.dir.data.data_val  = discard_const(fh->dptr);
752         REMOVE3args.object.name               = ptr;
753
754         REMOVE3res = nfsproc3_remove_3(&REMOVE3args, nfsio->clnt);
755
756         if (REMOVE3res == NULL) {
757                 fprintf(stderr, "nfsproc3_remove_3 failed in nfsio_remove\n");
758                 ret = NFS3ERR_SERVERFAULT;
759                 goto finished;
760         }
761
762         if (REMOVE3res->status != NFS3_OK) {
763                 fprintf(stderr, "nfsproc3_remove_3 failed in nfsio_remove. status:%d\n", REMOVE3res->status);
764                 ret = REMOVE3res->status;
765                 goto finished;
766         }
767
768
769         delete_fhandle(nfsio, name);
770
771
772 finished:
773         if (tmp_name) {
774                 free(tmp_name);
775         }
776         return ret;
777 }
778
779
780 nfsstat3 nfsio_write(struct nfsio *nfsio, const char *name, char *buf, uint32 offset, int len, int stable)
781 {
782         struct WRITE3args WRITE3args;
783         struct WRITE3res *WRITE3res;
784         int ret = NFS3_OK;
785         data_t *fh;
786
787         fh = lookup_fhandle(nfsio, name, NULL);
788         if (fh == NULL) {
789                 fprintf(stderr, "failed to fetch handle in nfsio_write\n");
790                 ret = NFS3ERR_SERVERFAULT;
791                 goto finished;
792         }
793
794         WRITE3args.file.data.data_len = fh->dsize;
795         WRITE3args.file.data.data_val = discard_const(fh->dptr);
796         WRITE3args.offset             = offset;
797         WRITE3args.count              = len;
798         WRITE3args.stable             = stable;
799         WRITE3args.data.data_len      = len;
800         WRITE3args.data.data_val      = buf;
801
802
803         WRITE3res = nfsproc3_write_3(&WRITE3args, nfsio->clnt);
804
805         if (WRITE3res == NULL) {
806                 fprintf(stderr, "nfsproc3_write_3 failed in nfsio_write\n");
807                 ret = NFS3ERR_SERVERFAULT;
808                 goto finished;
809         }
810
811         if (WRITE3res->status != NFS3_OK) {
812                 fprintf(stderr, "nfsproc3_write_3 failed in getattr. status:%d\n", WRITE3res->status);
813                 ret = WRITE3res->status;
814         }
815
816 finished:
817         return ret;
818 }
819
820 nfsstat3 nfsio_read(struct nfsio *nfsio, const char *name, char *buf, uint32 offset, int len, int *count, int *eof)
821 {
822         struct READ3args READ3args;
823         struct READ3res *READ3res;
824         int ret = NFS3_OK;
825         data_t *fh;
826         ssize_t size;
827
828         fh = lookup_fhandle(nfsio, name, &size);
829         if (fh == NULL) {
830                 fprintf(stderr, "failed to fetch handle in nfsio_read\n");
831                 ret = NFS3ERR_SERVERFAULT;
832                 goto finished;
833         }
834
835         if (offset >= size) {
836                 offset = offset % size;
837         }
838         if (offset+len >= size) {
839                 offset = 0;
840         }
841
842         READ3args.file.data.data_len = fh->dsize;
843         READ3args.file.data.data_val = discard_const(fh->dptr);
844         READ3args.offset             = offset;
845         READ3args.count              = len;
846
847         READ3res = nfsproc3_read_3(&READ3args, nfsio->clnt);
848
849         if (READ3res == NULL) {
850                 fprintf(stderr, "nfsproc3_read_3 failed in nfsio_read\n");
851                 ret = NFS3ERR_SERVERFAULT;
852                 goto finished;
853         }
854
855         if (READ3res->status != NFS3_OK) {
856                 fprintf(stderr, "nfsproc3_read_3 failed in nfsio_read. status:%d\n", READ3res->status);
857                 ret = READ3res->status;
858                 goto finished;
859         }
860
861         if (count) {
862                 *count = READ3res->READ3res_u.resok.count;
863         }
864         if (eof) {
865                 *eof = READ3res->READ3res_u.resok.eof;
866         }
867         if (buf) {
868                 memcpy(buf, &READ3res->READ3res_u.resok.data.data_val,
869                         READ3res->READ3res_u.resok.count);
870         }
871         free(READ3res->READ3res_u.resok.data.data_val);
872         READ3res->READ3res_u.resok.data.data_val = NULL;
873
874 finished:
875         return ret;
876 }
877
878
879 nfsstat3 nfsio_commit(struct nfsio *nfsio, const char *name)
880 {
881         struct COMMIT3args COMMIT3args;
882         struct COMMIT3res *COMMIT3res;  
883         int ret = NFS3_OK;
884         data_t *fh;
885
886         fh = lookup_fhandle(nfsio, name, NULL);
887         if (fh == NULL) {
888                 fprintf(stderr, "failed to fetch handle in nfsio_commit\n");
889                 ret = NFS3ERR_SERVERFAULT;
890                 goto finished;
891         }
892
893         COMMIT3args.file.data.data_len = fh->dsize;
894         COMMIT3args.file.data.data_val = discard_const(fh->dptr);
895         COMMIT3args.offset             = 0;
896         COMMIT3args.count              = 0;
897
898
899         COMMIT3res = nfsproc3_commit_3(&COMMIT3args, nfsio->clnt);
900
901         if (COMMIT3res == NULL) {
902                 fprintf(stderr, "nfsproc3_commit_3 failed in nfsio_commit\n");
903                 ret = NFS3ERR_SERVERFAULT;
904                 goto finished;
905         }
906
907         if (COMMIT3res->status != NFS3_OK) {
908                 fprintf(stderr, "nfsproc3_commit_3 failed in nfsio_commit. status:%d\n", COMMIT3res->status);
909                 ret = COMMIT3res->status;
910                 goto finished;
911         }
912
913 finished:
914         return ret;
915 }
916
917 nfsstat3 nfsio_fsinfo(struct nfsio *nfsio)
918 {
919         struct FSINFO3args FSINFO3args;
920         struct FSINFO3res *FSINFO3res;
921         data_t *fh;
922
923         fh = lookup_fhandle(nfsio, "/", NULL);
924         if (fh == NULL) {
925                 fprintf(stderr, "failed to fetch handle in nfsio_fsinfo\n");
926                 return NFS3ERR_SERVERFAULT;
927         }
928
929         FSINFO3args.fsroot.data.data_len = fh->dsize;
930         FSINFO3args.fsroot.data.data_val = discard_const(fh->dptr);
931
932         FSINFO3res = nfsproc3_fsinfo_3(&FSINFO3args, nfsio->clnt);
933
934         if (FSINFO3res == NULL) {
935                 fprintf(stderr, "nfsproc3_fsinfo_3 failed in nfsio_fsinfo\n");
936                 return NFS3ERR_SERVERFAULT;
937         }
938
939         if (FSINFO3res->status != NFS3_OK) {
940                 fprintf(stderr, "nfsproc3_fsinfo_3 failed in nfsio_fsinfo. status:%d\n", FSINFO3res->status);
941                 return FSINFO3res->status;
942         }
943
944         return NFS3_OK;
945 }
946
947
948 nfsstat3 nfsio_fsstat(struct nfsio *nfsio)
949 {
950         struct FSSTAT3args FSSTAT3args;
951         struct FSSTAT3res *FSSTAT3res;
952         data_t *fh;
953
954         fh = lookup_fhandle(nfsio, "/", NULL);
955         if (fh == NULL) {
956                 fprintf(stderr, "failed to fetch handle in nfsio_fsstat\n");
957                 return NFS3ERR_SERVERFAULT;
958         }
959
960         FSSTAT3args.fsroot.data.data_len = fh->dsize;
961         FSSTAT3args.fsroot.data.data_val = discard_const(fh->dptr);
962
963         FSSTAT3res = nfsproc3_fsstat_3(&FSSTAT3args, nfsio->clnt);
964
965         if (FSSTAT3res == NULL) {
966                 fprintf(stderr, "nfsproc3_fsstat_3 failed in nfsio_fsstat\n");
967                 return NFS3ERR_SERVERFAULT;
968         }
969
970         if (FSSTAT3res->status != NFS3_OK) {
971                 fprintf(stderr, "nfsproc3_fsstat_3 failed in nfsio_fsstat. status:%d\n", FSSTAT3res->status);
972                 return FSSTAT3res->status;
973         }
974
975         return NFS3_OK;
976 }
977
978 nfsstat3 nfsio_pathconf(struct nfsio *nfsio, char *name)
979 {
980         struct PATHCONF3args PATHCONF3args;
981         struct PATHCONF3res *PATHCONF3res;
982         data_t *fh;
983
984         fh = lookup_fhandle(nfsio, name, NULL);
985         if (fh == NULL) {
986                 fprintf(stderr, "failed to fetch handle in nfsio_pathconf\n");
987                 return NFS3ERR_SERVERFAULT;
988         }
989
990         PATHCONF3args.object.data.data_len = fh->dsize;
991         PATHCONF3args.object.data.data_val = discard_const(fh->dptr);
992
993         PATHCONF3res = nfsproc3_pathconf_3(&PATHCONF3args, nfsio->clnt);
994
995         if (PATHCONF3res == NULL) {
996                 fprintf(stderr, "nfsproc3_pathconf_3 failed in nfsio_pathconf\n");
997                 return NFS3ERR_SERVERFAULT;
998         }
999
1000         if (PATHCONF3res->status != NFS3_OK) {
1001                 fprintf(stderr, "nfsproc3_pathconf_3 failed in nfsio_pathconf. status:%d\n", PATHCONF3res->status);
1002                 return PATHCONF3res->status;
1003         }
1004
1005         return NFS3_OK;
1006 }
1007
1008
1009 nfsstat3 nfsio_symlink(struct nfsio *nfsio, const char *old, const char *new)
1010 {
1011
1012         struct SYMLINK3args SYMLINK3args;
1013         struct SYMLINK3res *SYMLINK3res;
1014         int ret = NFS3_OK;
1015         char *tmp_name = NULL;
1016         data_t *fh;
1017         char *ptr;
1018
1019         tmp_name = strdup(old);
1020         if (tmp_name == NULL) {
1021                 fprintf(stderr, "failed to strdup name in nfsio_symlink\n");
1022                 ret = NFS3ERR_SERVERFAULT;
1023                 goto finished;
1024         }
1025
1026         ptr = rindex(tmp_name, '/');
1027         if (ptr == NULL) {      
1028                 fprintf(stderr, "name did not contain '/' in nfsio_symlink\n");
1029                 ret = NFS3ERR_SERVERFAULT;
1030                 goto finished;
1031         }
1032
1033         *ptr = 0;
1034         ptr++;
1035
1036         fh = lookup_fhandle(nfsio, tmp_name, NULL);
1037         if (fh == NULL) {
1038                 fprintf(stderr, "failed to fetch parent handle in nfsio_symlink\n");
1039                 ret = NFS3ERR_SERVERFAULT;
1040                 goto finished;
1041         }
1042
1043
1044         SYMLINK3args.where.dir.data.data_len  = fh->dsize;
1045         SYMLINK3args.where.dir.data.data_val  = discard_const(fh->dptr);
1046         SYMLINK3args.where.name               = ptr;
1047
1048         SYMLINK3args.symlink.symlink_attributes.mode.set_it = TRUE;
1049         SYMLINK3args.symlink.symlink_attributes.mode.set_mode3_u.mode = 0777;
1050         SYMLINK3args.symlink.symlink_attributes.uid.set_it = TRUE;
1051         SYMLINK3args.symlink.symlink_attributes.uid.set_uid3_u.uid= 0;
1052         SYMLINK3args.symlink.symlink_attributes.gid.set_it = TRUE;
1053         SYMLINK3args.symlink.symlink_attributes.gid.set_gid3_u.gid = 0;
1054         SYMLINK3args.symlink.symlink_attributes.size.set_it = FALSE;
1055         SYMLINK3args.symlink.symlink_attributes.atime.set_it = FALSE;
1056         SYMLINK3args.symlink.symlink_attributes.mtime.set_it = FALSE;
1057         SYMLINK3args.symlink.symlink_data     = discard_const(new);
1058
1059
1060         SYMLINK3res = nfsproc3_symlink_3(&SYMLINK3args, nfsio->clnt);
1061
1062         if (SYMLINK3res == NULL) {
1063                 fprintf(stderr, "nfsproc3_symlink_3 failed in nfsio_symlink\n");
1064                 ret = NFS3ERR_SERVERFAULT;
1065                 goto finished;
1066         }
1067
1068         if (SYMLINK3res->status != NFS3_OK) {
1069                 fprintf(stderr, "nfsproc3_symlink_3 failed in nfsio_symlink. status:%d\n", SYMLINK3res->status);
1070                 ret = SYMLINK3res->status;
1071                 goto finished;
1072         }
1073
1074
1075         insert_fhandle(nfsio, old, 
1076                 SYMLINK3res->SYMLINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
1077                 SYMLINK3res->SYMLINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_len,
1078                 0 /*qqq*/
1079         );
1080
1081
1082 finished:
1083         if (tmp_name) {
1084                 free(tmp_name);
1085         }
1086         return ret;
1087 }
1088
1089
1090 nfsstat3 nfsio_link(struct nfsio *nfsio, const char *old, const char *new)
1091 {
1092
1093         struct LINK3args LINK3args;
1094         struct LINK3res *LINK3res;
1095         int ret = NFS3_OK;
1096         char *tmp_name = NULL;
1097         data_t *fh, *new_fh;
1098         char *ptr;
1099
1100         tmp_name = strdup(old);
1101         if (tmp_name == NULL) {
1102                 fprintf(stderr, "failed to strdup name in nfsio_link\n");
1103                 ret = NFS3ERR_SERVERFAULT;
1104                 goto finished;
1105         }
1106
1107         ptr = rindex(tmp_name, '/');
1108         if (ptr == NULL) {      
1109                 fprintf(stderr, "name did not contain '/' in nfsio_link\n");
1110                 ret = NFS3ERR_SERVERFAULT;
1111                 goto finished;
1112         }
1113
1114         *ptr = 0;
1115         ptr++;
1116
1117         fh = lookup_fhandle(nfsio, tmp_name, NULL);
1118         if (fh == NULL) {
1119                 fprintf(stderr, "failed to fetch parent handle in nfsio_link\n");
1120                 ret = NFS3ERR_SERVERFAULT;
1121                 goto finished;
1122         }
1123
1124
1125         new_fh = lookup_fhandle(nfsio, new, NULL);
1126         if (new_fh == NULL) {
1127                 fprintf(stderr, "failed to fetch handle in nfsio_link\n");
1128                 ret = NFS3ERR_SERVERFAULT;
1129                 goto finished;
1130         }
1131
1132
1133         LINK3args.file.data.data_len  = new_fh->dsize;
1134         LINK3args.file.data.data_val  = discard_const(new_fh->dptr);
1135
1136
1137         LINK3args.link.dir.data.data_len  = fh->dsize;
1138         LINK3args.link.dir.data.data_val  = discard_const(fh->dptr);
1139         LINK3args.link.name               = ptr;
1140
1141         LINK3res = nfsproc3_link_3(&LINK3args, nfsio->clnt);
1142
1143         if (LINK3res == NULL) {
1144                 fprintf(stderr, "nfsproc3_link_3 failed in nfsio_link\n");
1145                 ret = NFS3ERR_SERVERFAULT;
1146                 goto finished;
1147         }
1148
1149         if (LINK3res->status != NFS3_OK) {
1150                 fprintf(stderr, "nfsproc3_link_3 failed in nfsio_link. status:%d\n", LINK3res->status);
1151                 ret = LINK3res->status;
1152                 goto finished;
1153         }
1154
1155
1156 //      insert_fhandle(nfsio, old, 
1157 //              LINK3res->LINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
1158 //              LINK3res->LINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_len);
1159
1160
1161 finished:
1162         if (tmp_name) {
1163                 free(tmp_name);
1164         }
1165         return ret;
1166 }
1167
1168
1169
1170 nfsstat3 nfsio_readlink(struct nfsio *nfsio, char *name, char **link_name)
1171 {
1172         struct READLINK3args READLINK3args;
1173         struct READLINK3res *READLINK3res;
1174         data_t *fh;
1175
1176         fh = lookup_fhandle(nfsio, name, NULL);
1177         if (fh == NULL) {
1178                 fprintf(stderr, "failed to fetch handle in nfsio_readlink\n");
1179                 return NFS3ERR_SERVERFAULT;
1180         }
1181
1182
1183         READLINK3args.symlink.data.data_len  = fh->dsize;
1184         READLINK3args.symlink.data.data_val  = discard_const(fh->dptr);
1185
1186         READLINK3res = nfsproc3_readlink_3(&READLINK3args, nfsio->clnt);
1187
1188         if (READLINK3res == NULL) {
1189                 fprintf(stderr, "nfsproc3_readlink_3 failed in nfsio_readlink\n");
1190                 return NFS3ERR_SERVERFAULT;
1191         }
1192
1193         if (READLINK3res->status != NFS3_OK) {
1194                 fprintf(stderr, "nfsproc3_readlink_3 failed in nfsio_readlink. status:%d\n", READLINK3res->status);
1195                 return READLINK3res->status;
1196         }
1197
1198         if (link_name) {
1199                 *link_name = strdup(READLINK3res->READLINK3res_u.resok.data);
1200         }
1201
1202         return NFS3_OK;
1203 }
1204
1205
1206 nfsstat3 nfsio_rmdir(struct nfsio *nfsio, const char *name)
1207 {
1208
1209         struct RMDIR3args RMDIR3args;
1210         struct RMDIR3res *RMDIR3res;
1211         int ret = NFS3_OK;
1212         char *tmp_name = NULL;
1213         data_t *fh;
1214         char *ptr;
1215
1216         tmp_name = strdup(name);
1217         if (tmp_name == NULL) {
1218                 fprintf(stderr, "failed to strdup name in nfsio_rmdir\n");
1219                 return NFS3ERR_SERVERFAULT;
1220         }
1221
1222         ptr = rindex(tmp_name, '/');
1223         if (ptr == NULL) {      
1224                 fprintf(stderr, "name did not contain '/' in nfsio_rmdir\n");
1225                 ret = NFS3ERR_SERVERFAULT;
1226                 goto finished;
1227         }
1228
1229         *ptr = 0;
1230         ptr++;
1231
1232         fh = lookup_fhandle(nfsio, tmp_name, NULL);
1233         if (fh == NULL) {
1234                 fprintf(stderr, "failed to fetch parent handle in nfsio_rmdir\n");
1235                 ret = NFS3ERR_SERVERFAULT;
1236                 goto finished;
1237         }
1238
1239
1240         RMDIR3args.object.dir.data.data_len  = fh->dsize;
1241         RMDIR3args.object.dir.data.data_val  = discard_const(fh->dptr);
1242         RMDIR3args.object.name               = ptr;
1243
1244         RMDIR3res = nfsproc3_rmdir_3(&RMDIR3args, nfsio->clnt);
1245
1246         if (RMDIR3res == NULL) {
1247                 fprintf(stderr, "nfsproc3_rmdir_3 failed in nfsio_rmdir\n");
1248                 ret = NFS3ERR_SERVERFAULT;
1249                 goto finished;
1250         }
1251
1252         if (RMDIR3res->status != NFS3_OK) {
1253                 fprintf(stderr, "nfsproc3_rmdir_3(%s) failed in nfsio_rmdir. status:%s(%d)\n", name, nfs_error(RMDIR3res->status), RMDIR3res->status);
1254                 ret = RMDIR3res->status;
1255                 goto finished;
1256         }
1257
1258
1259         delete_fhandle(nfsio, name);
1260
1261
1262 finished:
1263         if (tmp_name) {
1264                 free(tmp_name);
1265         }
1266         return ret;
1267 }
1268
1269
1270
1271 nfsstat3 nfsio_mkdir(struct nfsio *nfsio, const char *name)
1272 {
1273
1274         struct MKDIR3args MKDIR3args;
1275         struct MKDIR3res *MKDIR3res;
1276         int ret = NFS3_OK;
1277         char *tmp_name = NULL;
1278         data_t *fh;
1279         char *ptr;
1280
1281         tmp_name = strdup(name);
1282         if (tmp_name == NULL) {
1283                 fprintf(stderr, "failed to strdup name in nfsio_mkdir\n");
1284                 return NFS3ERR_SERVERFAULT;
1285         }
1286
1287         ptr = rindex(tmp_name, '/');
1288         if (ptr == NULL) {      
1289                 fprintf(stderr, "name did not contain '/' in nfsio_mkdir\n");
1290                 ret = NFS3ERR_SERVERFAULT;
1291                 goto finished;
1292         }
1293
1294         *ptr = 0;
1295         ptr++;
1296
1297         fh = lookup_fhandle(nfsio, tmp_name, NULL);
1298         if (fh == NULL) {
1299                 fprintf(stderr, "failed to fetch parent handle in nfsio_mkdir\n");
1300                 ret = NFS3ERR_SERVERFAULT;
1301                 goto finished;
1302         }
1303
1304         MKDIR3args.where.dir.data.data_len  = fh->dsize;
1305         MKDIR3args.where.dir.data.data_val  = discard_const(fh->dptr);
1306         MKDIR3args.where.name               = ptr;
1307
1308         MKDIR3args.attributes.mode.set_it  = TRUE;
1309         MKDIR3args.attributes.mode.set_mode3_u.mode    = 0777;
1310         MKDIR3args.attributes.uid.set_it   = TRUE;
1311         MKDIR3args.attributes.uid.set_uid3_u.uid      = 0;
1312         MKDIR3args.attributes.gid.set_it   = TRUE;
1313         MKDIR3args.attributes.gid.set_gid3_u.gid      = 0;
1314         MKDIR3args.attributes.size.set_it  = FALSE;
1315         MKDIR3args.attributes.atime.set_it = FALSE;
1316         MKDIR3args.attributes.mtime.set_it = FALSE;
1317
1318         MKDIR3res = nfsproc3_mkdir_3(&MKDIR3args, nfsio->clnt);
1319
1320         if (MKDIR3res == NULL) {
1321                 fprintf(stderr, "nfsproc3_mkdir_3 failed in nfsio_mkdir\n");
1322                 ret = NFS3ERR_SERVERFAULT;
1323                 goto finished;
1324         }
1325
1326         if (MKDIR3res->status != NFS3_OK) {
1327                 fprintf(stderr, "nfsproc3_mkdir_3(%s) failed in nfsio_mkdir. status:%s(%d)\n", name, nfs_error(MKDIR3res->status), MKDIR3res->status);
1328                 ret = MKDIR3res->status;
1329                 goto finished;
1330         }
1331
1332         insert_fhandle(nfsio, name, 
1333                 MKDIR3res->MKDIR3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
1334                 MKDIR3res->MKDIR3res_u.resok.obj.post_op_fh3_u.handle.data.data_len,
1335                 0 /*qqq*/
1336         );
1337
1338 finished:
1339         if (tmp_name) {
1340                 free(tmp_name);
1341         }
1342         return ret;
1343 }
1344
1345
1346 nfsstat3 nfsio_readdirplus(struct nfsio *nfsio, const char *name, nfs3_dirent_cb cb, void *private_data)
1347 {
1348         struct READDIRPLUS3args READDIRPLUS3args;
1349         struct READDIRPLUS3res *READDIRPLUS3res;
1350         int ret = NFS3_OK;
1351         data_t *fh;
1352         entryplus3 *e, *last_e = NULL;
1353         char *dir = NULL;
1354
1355         dir = strdup(name);
1356         while(strlen(dir)){
1357                 if(dir[strlen(dir)-1] != '/'){
1358                         break;
1359                 }
1360                 dir[strlen(dir)-1] = 0;
1361         }
1362  
1363         fh = lookup_fhandle(nfsio, name, NULL);
1364         if (fh == NULL) {
1365                 fprintf(stderr, "failed to fetch handle for '%s' in nfsio_readdirplus\n", name);
1366                 ret = NFS3ERR_SERVERFAULT;
1367                 goto finished;
1368         }
1369
1370         READDIRPLUS3args.dir.data.data_len = fh->dsize;
1371         READDIRPLUS3args.dir.data.data_val = discard_const(fh->dptr);
1372         READDIRPLUS3args.cookie            = 0;
1373         bzero(&READDIRPLUS3args.cookieverf, NFS3_COOKIEVERFSIZE);
1374         READDIRPLUS3args.dircount          = 6000;
1375         READDIRPLUS3args.maxcount          = 8192;
1376
1377 again:
1378         READDIRPLUS3res = nfsproc3_readdirplus_3(&READDIRPLUS3args, nfsio->clnt);
1379
1380         if (READDIRPLUS3res == NULL) {
1381                 fprintf(stderr, "nfsproc3_readdirplus_3 failed in readdirplus\n");
1382                 ret = NFS3ERR_SERVERFAULT;
1383                 goto finished;
1384         }
1385
1386         if (READDIRPLUS3res->status != NFS3_OK) {
1387                 fprintf(stderr, "nfsproc3_readdirplus_3 failed in readdirplus. status:%d\n", READDIRPLUS3res->status);
1388                 ret = READDIRPLUS3res->status;
1389                 goto finished;
1390         }
1391
1392         for(e = READDIRPLUS3res->READDIRPLUS3res_u.resok.reply.entries;e;e=e->nextentry){
1393                 char *new_name;
1394
1395                 if(!strcmp(e->name, ".")){
1396                         continue;
1397                 }
1398                 if(!strcmp(e->name, "..")){
1399                         continue;
1400                 }
1401                 if(e->name_handle.handle_follows == 0){
1402                         continue;
1403                 }
1404
1405                 last_e = e;
1406
1407                 asprintf(&new_name, "%s/%s", dir, e->name);
1408                 insert_fhandle(nfsio, new_name, 
1409                         e->name_handle.post_op_fh3_u.handle.data.data_val,
1410                         e->name_handle.post_op_fh3_u.handle.data.data_len,
1411                         0 /*qqq*/
1412                 );
1413                 free(new_name);
1414
1415                 if (cb) {
1416                         cb(e, private_data);
1417                 }
1418         }       
1419
1420         if (READDIRPLUS3res->READDIRPLUS3res_u.resok.reply.eof == 0) {
1421                 if (READDIRPLUS3args.cookie == 0) {
1422                         memcpy(&READDIRPLUS3args.cookieverf, 
1423                         &READDIRPLUS3res->READDIRPLUS3res_u.resok.cookieverf,
1424                         NFS3_COOKIEVERFSIZE);           
1425                 }
1426
1427                 READDIRPLUS3args.cookie = last_e->cookie;
1428
1429                 goto again;
1430         }
1431
1432
1433 finished:
1434         if (dir) {
1435                 free(dir);
1436         }
1437         return ret;
1438 }
1439
1440
1441 nfsstat3 nfsio_rename(struct nfsio *nfsio, const char *old, const char *new)
1442 {
1443
1444         struct RENAME3args RENAME3args;
1445         struct RENAME3res *RENAME3res;
1446         int ret = NFS3_OK;
1447         char *tmp_old_name = NULL;
1448         char *tmp_new_name = NULL;
1449         data_t *old_fh, *new_fh;
1450         char *old_ptr, *new_ptr;
1451
1452         tmp_old_name = strdup(old);
1453         if (tmp_old_name == NULL) {
1454                 fprintf(stderr, "failed to strdup name in nfsio_rename\n");
1455                 ret = NFS3ERR_SERVERFAULT;
1456                 goto finished;
1457         }
1458
1459         old_ptr = rindex(tmp_old_name, '/');
1460         if (old_ptr == NULL) {  
1461                 fprintf(stderr, "name did not contain '/' in nfsio_rename\n");
1462                 ret = NFS3ERR_SERVERFAULT;
1463                 goto finished;
1464         }
1465
1466         *old_ptr = 0;
1467         old_ptr++;
1468
1469         old_fh = lookup_fhandle(nfsio, tmp_old_name, NULL);
1470         if (old_fh == NULL) {
1471                 fprintf(stderr, "failed to fetch parent handle in nfsio_rename\n");
1472                 ret = NFS3ERR_SERVERFAULT;
1473                 goto finished;
1474         }
1475
1476         tmp_new_name = strdup(new);
1477         if (tmp_new_name == NULL) {
1478                 fprintf(stderr, "failed to strdup name in nfsio_rename\n");
1479                 ret = NFS3ERR_SERVERFAULT;
1480                 goto finished;
1481         }
1482
1483         new_ptr = rindex(tmp_new_name, '/');
1484         if (new_ptr == NULL) {  
1485                 fprintf(stderr, "name did not contain '/' in nfsio_rename\n");
1486                 ret = NFS3ERR_SERVERFAULT;
1487                 goto finished;
1488         }
1489
1490         *new_ptr = 0;
1491         new_ptr++;
1492
1493         new_fh = lookup_fhandle(nfsio, tmp_new_name, NULL);
1494         if (new_fh == NULL) {
1495                 fprintf(stderr, "failed to fetch parent handle in nfsio_rename\n");
1496                 ret = NFS3ERR_SERVERFAULT;
1497                 goto finished;
1498         }
1499
1500         RENAME3args.from.dir.data.data_len  = old_fh->dsize;
1501         RENAME3args.from.dir.data.data_val  = discard_const(old_fh->dptr);
1502         RENAME3args.from.name               = old_ptr;
1503
1504         RENAME3args.to.dir.data.data_len  = new_fh->dsize;
1505         RENAME3args.to.dir.data.data_val  = discard_const(new_fh->dptr);
1506         RENAME3args.to.name               = new_ptr;
1507
1508
1509         RENAME3res = nfsproc3_rename_3(&RENAME3args, nfsio->clnt);
1510
1511         if (RENAME3res == NULL) {
1512                 fprintf(stderr, "nfsproc3_rename_3 failed in nfsio_rename\n");
1513                 ret = NFS3ERR_SERVERFAULT;
1514                 goto finished;
1515         }
1516
1517         if (RENAME3res->status != NFS3_OK) {
1518                 fprintf(stderr, "nfsproc3_rename_3 failed in nfsio_rename. status:%d\n", RENAME3res->status);
1519                 ret = RENAME3res->status;
1520                 goto finished;
1521         }
1522
1523
1524         old_fh = lookup_fhandle(nfsio, old, NULL);
1525         if (old_fh == NULL) {
1526                 fprintf(stderr, "failed to fetch parent handle in nfsio_rename\n");
1527                 ret = NFS3ERR_SERVERFAULT;
1528                 goto finished;
1529         }
1530
1531
1532         insert_fhandle(nfsio, new, old_fh->dptr, old_fh->dsize, 0 /*qqq*/);
1533         delete_fhandle(nfsio, old);
1534
1535
1536 finished:
1537         if (tmp_old_name) {
1538                 free(tmp_old_name);
1539         }
1540         if (tmp_new_name) {
1541                 free(tmp_new_name);
1542         }
1543         return ret;
1544 }
1545