]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cifs/trans.c
stats: show amount of reclaimable pages (add -r flag)
[plan9front.git] / sys / src / cmd / cifs / trans.c
1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <9p.h>
6 #include "cifs.h"
7 #include "remsmb.h"
8 #include "apinums.h"
9
10 static Pkt *
11 thdr(Session *s, Share *sp)
12 {
13         Pkt *p;
14
15         p = cifshdr(s, sp, SMB_COM_TRANSACTION);
16         p->tbase = pl16(p, 0);  /* 0  Total parameter bytes to be sent, filled later */
17         pl16(p, 0);             /* 2  Total data bytes to be sent, filled later */
18         pl16(p, 64);                    /* 4  Max parameter to return */
19         pl16(p, s->mtu - T2HDRLEN - 128);       /* 6  Max data to return */
20         pl16(p, 1);                     /* 8  Max setup count to return */
21         pl16(p, 0);                     /* 10 Flags */
22         pl32(p, 1000);                  /* 12 Timeout (ms) */
23         pl16(p, 0);                     /* 16 Reserved */
24         pl16(p, 0);                     /* 18 Parameter count, filled later */
25         pl16(p, 0);                     /* 20 Parameter offset, filled later */
26         pl16(p, 0);                     /* 22 Data count, filled later */
27         pl16(p, 0);                     /* 24 Data offset, filled later */
28         pl16(p, 0);                     /* 26 Setup count (in words) */
29         pbytes(p);                      /* end of cifs words section */
30         return p;
31 }
32
33 static void
34 ptparam(Pkt *p)
35 {
36         uchar *pos;
37
38         if(((p->pos - p->tbase) % 2) != 0)
39                 p8(p, 0);                       /* pad to word boundry */
40         pos = p->pos;
41         p->pos = p->tbase + 20;
42         pl16(p, pos - p->buf - NBHDRLEN);       /* param offset */
43         p->tparam = p->pos = pos;
44 }
45
46 static void
47 ptdata(Pkt *p)
48 {
49         uchar *pos = p->pos;
50
51         assert(p->tparam != 0);
52         if(((p->pos - p->tbase) % 2) != 0)
53                 p8(p, 0);               /* pad to word boundry */
54
55         p->pos = p->tbase + 0;
56         pl16(p, pos - p->tparam);       /* total param count */
57
58         p->pos = p->tbase + 18;
59         pl16(p, pos - p->tparam);       /* param count */
60
61         p->pos = p->tbase + 24;
62         pl16(p, pos - p->buf - NBHDRLEN); /* data offset */
63
64         p->tdata = p->pos = pos;
65 }
66
67 static int
68 trpc(Pkt *p)
69 {
70         int got;
71         uchar *pos = p->pos;
72
73         assert(p->tbase != 0);
74         assert(p->tdata != 0);
75
76         p->pos = p->tbase + 2;
77         pl16(p, pos - p->tdata);        /* total data count */
78
79         p->pos = p->tbase + 22;
80         pl16(p, pos - p->tdata);        /* data count */
81
82         p->pos = pos;
83         if((got = cifsrpc(p)) == -1)
84                 return -1;
85
86         gl16(p);                        /* Total parameter count */
87         gl16(p);                        /* Total data count */
88         gl16(p);                        /* Reserved */
89         gl16(p);                        /* Parameter count in this buffer */
90         p->tparam = p->buf + NBHDRLEN + gl16(p); /* Parameter offset */
91         gl16(p);                        /* Parameter displacement */
92         gl16(p);                        /* Data count (this buffer); */
93         p->tdata = p->buf + NBHDRLEN + gl16(p); /* Data offset */
94         gl16(p);                        /* Data displacement */
95         g8(p);                          /* Setup count */
96         g8(p);                          /* Reserved */
97         return got;
98 }
99
100 static void
101 gtparam(Pkt *p)
102 {
103         p->pos = p->tparam;
104 }
105
106 int
107 RAPshareenum(Session *s, Share *sp, Share **ent)
108 {
109         int ngot = 0, err, navail, nret;
110         char tmp[1024];
111         Pkt *p;
112         Share *q;
113
114         p = thdr(s, sp);
115         pstr(p, "\\PIPE\\LANMAN");
116         ptparam(p);
117
118         pl16(p, API_WShareEnum);
119         pascii(p, REMSmb_NetShareEnum_P);       /* request descriptor */
120         pascii(p, REMSmb_share_info_0);         /* reply descriptor */
121         pl16(p, 0);                             /* detail level */
122         pl16(p, s->mtu - 1024);                 /* receive buffer length */
123         ptdata(p);
124
125         if(trpc(p) == -1){
126                 free(p);
127                 return -1;
128         }
129
130         gtparam(p);
131         err = gl16(p);                          /* error code */
132         gl16(p);                                /* rx buffer offset */
133         nret = gl16(p);                         /* number of entries returned */
134         navail = gl16(p);                       /* number of entries available */
135
136         if(err && err != RAP_ERR_MOREINFO){
137                 werrstr("%s", raperrstr(err));
138                 free(p);
139                 return -1;
140         }
141
142         if(ngot == 0){
143                 *ent = emalloc9p(sizeof(Share) * navail);
144                 memset(*ent, 0, sizeof(Share) * navail);
145         }
146
147         q = *ent + ngot;
148         for (; ngot < navail && nret--; ngot++){
149                 gmem(p, tmp, 13);               /* name */
150                 tmp[13] = 0;
151                 q->name = estrdup9p(tmp);
152                 q++;
153         }
154
155         if(ngot < navail)
156                 fprint(2, "%s: %d/%d - share list incomplete\n", argv0, ngot, navail);
157
158         free(p);
159         return ngot;
160 }
161
162
163 int
164 RAPshareinfo(Session *s, Share *sp, char *share, Shareinfo2 *si2p)
165 {
166         int conv, err;
167         char tmp[1024];
168         Pkt *p;
169
170         p = thdr(s, sp);
171         pstr(p, "\\PIPE\\LANMAN");
172
173         ptparam(p);
174         pl16(p, API_WShareGetInfo);
175         pascii(p, REMSmb_NetShareGetInfo_P);    /* request descriptor */
176         pascii(p, REMSmb_share_info_2);         /* reply descriptor */
177         pascii(p, share);
178         pl16(p, 1);                             /* detail level */
179         pl16(p, s->mtu - 1024);                 /* receive buffer length */
180
181         ptdata(p);
182
183         if(trpc(p) == -1){
184                 free(p);
185                 return -1;
186         }
187
188         gtparam(p);
189         err = gl16(p);                          /* error code */
190         conv = gl16(p);                         /* rx buffer offset */
191         gl16(p);                                /* number of entries returned */
192         gl16(p);                                /* number of entries available */
193
194         if(err){
195                 werrstr("%s", raperrstr(err));
196                 free(p);
197                 return -1;
198         }
199
200         memset(si2p, 0, sizeof(Shareinfo2));
201
202         gmem(p, tmp, 13);
203         tmp[13] = 0;
204         g8(p);                                  /* padding */
205         si2p->name = estrdup9p(tmp);
206         si2p->type = gl16(p);
207         gconv(p, conv, tmp, sizeof tmp);
208         si2p->comment = estrdup9p(tmp);
209         gl16(p);                                /* comment offset high (unused) */
210         si2p->perms = gl16(p);
211         si2p->maxusrs = gl16(p);
212         si2p->activeusrs = gl16(p);
213         gconv(p, conv, tmp, sizeof tmp);
214         si2p->path = estrdup9p(tmp);
215         gl16(p);                                /* path offset high (unused) */
216         gmem(p, tmp, 9);
217         tmp[9] = 0;
218         si2p->passwd = estrdup9p(tmp);
219
220         free(p);
221         return 0;
222 }
223
224 /*
225  * Tried to split sessionenum into two passes, one getting the names
226  * of the connected workstations and the other collecting the detailed info,
227  * however API_WSessionGetInfo doesn't seem to work agains win2k3 for infolevel
228  * ten and infolevel one and two are priviledged calls.  This means this code
229  * will work for small numbers of sessions agains win2k3 and fail for samba 3.0
230  * as it supports info levels zero and two only.
231  */
232 int
233 RAPsessionenum(Session *s, Share *sp, Sessinfo **sip)
234 {
235         int ngot = 0, conv, err, navail, nret;
236         char tmp[1024];
237         Pkt *p;
238         Sessinfo *q;
239
240         p = thdr(s, sp);
241         pstr(p, "\\PIPE\\LANMAN");
242         ptparam(p);
243
244         pl16(p, API_WSessionEnum);
245         pascii(p, REMSmb_NetSessionEnum_P);     /* request descriptor */
246         pascii(p, REMSmb_session_info_10);      /* reply descriptor */
247         pl16(p, 10);                            /* detail level */
248         pl16(p, s->mtu - 1024);                 /* receive buffer length */
249         ptdata(p);
250
251         if(trpc(p) == -1){
252                 free(p);
253                 return -1;
254         }
255
256         gtparam(p);
257         err = gl16(p);                          /* error code */
258         conv = gl16(p);                         /* rx buffer offset */
259         nret = gl16(p);                         /* number of entries returned */
260         navail = gl16(p);                       /* number of entries available */
261
262         if(err && err != RAP_ERR_MOREINFO){
263                 werrstr("%s", raperrstr(err));
264                 free(p);
265                 return -1;
266         }
267
268         if(ngot == 0){
269                 *sip = emalloc9p(sizeof(Sessinfo) * navail);
270                 memset(*sip, 0, sizeof(Sessinfo) * navail);
271         }
272
273         q = *sip + ngot;
274         while(nret-- != 0){
275                 gconv(p, conv, tmp, sizeof tmp);
276                 q->wrkstn = estrdup9p(tmp);
277                 gconv(p, conv, tmp, sizeof tmp);
278                 q->user = estrdup9p(tmp);
279                 q->sesstime = gl32(p);
280                 q->idletime = gl32(p);
281                 ngot++;
282                 q++;
283         }
284         if(ngot < navail)
285                 fprint(2, "warning: %d/%d - session list incomplete\n", ngot, navail);
286         free(p);
287         return ngot;
288 }
289
290
291 int
292 RAPgroupenum(Session *s, Share *sp, Namelist **nlp)
293 {
294         int ngot, err, navail, nret;
295         char tmp[1024];
296         Pkt *p;
297         Namelist *q;
298
299         ngot = 0;
300         p = thdr(s, sp);
301         pstr(p, "\\PIPE\\LANMAN");
302         ptparam(p);
303
304         pl16(p, API_WGroupEnum);
305         pascii(p, REMSmb_NetGroupEnum_P);       /* request descriptor */
306         pascii(p, REMSmb_group_info_0);         /* reply descriptor */
307         pl16(p, 0);                             /* detail level */
308         pl16(p, s->mtu - 1024);                 /* receive buffer length */
309         ptdata(p);
310
311         if(trpc(p) == -1){
312                 free(p);
313                 return -1;
314         }
315
316         gtparam(p);
317         err = gl16(p);                          /* error code */
318         gl16(p);                                /* rx buffer offset */
319         nret = gl16(p);                         /* number of entries returned */
320         navail = gl16(p);                       /* number of entries available */
321
322         if(err && err != RAP_ERR_MOREINFO){
323                 werrstr("%s", raperrstr(err));
324                 free(p);
325                 return -1;
326         }
327
328         *nlp = emalloc9p(sizeof(Namelist) * navail);
329         memset(*nlp, 0, sizeof(Namelist) * navail);
330
331         q = *nlp + ngot;
332         while(ngot < navail && nret--){
333                 gmem(p, tmp, 21);
334                 tmp[21] = 0;
335                 q->name = estrdup9p(tmp);
336                 ngot++;
337                 q++;
338                 if(p->pos >= p->eop)            /* Windows seems to lie somtimes */
339                         break;
340         }
341         free(p);
342         return ngot;
343 }
344
345
346 int
347 RAPgroupusers(Session *s, Share *sp, char *group, Namelist **nlp)
348 {
349         int ngot, err, navail, nret;
350         char tmp[1024];
351         Pkt *p;
352         Namelist *q;
353
354         ngot = 0;
355         p = thdr(s, sp);
356         pstr(p, "\\PIPE\\LANMAN");
357         ptparam(p);
358
359         pl16(p, API_WGroupGetUsers);
360         pascii(p, REMSmb_NetGroupGetUsers_P);   /* request descriptor */
361         pascii(p, REMSmb_user_info_0);          /* reply descriptor */
362         pascii(p, group);                       /* group name for list */
363         pl16(p, 0);                             /* detail level */
364         pl16(p, s->mtu - 1024);                 /* receive buffer length */
365         ptdata(p);
366
367         if(trpc(p) == -1){
368                 free(p);
369                 return -1;
370         }
371
372         gtparam(p);
373         err = gl16(p);                          /* error code */
374         gl16(p);                                /* rx buffer offset */
375         nret = gl16(p);                         /* number of entries returned */
376         navail = gl16(p);                       /* number of entries available */
377
378         if(err && err != RAP_ERR_MOREINFO){
379                 werrstr("%s", raperrstr(err));
380                 free(p);
381                 return -1;
382         }
383
384         *nlp = emalloc9p(sizeof(Namelist) * navail);
385         memset(*nlp, 0, sizeof(Namelist) * navail);
386
387         q = *nlp + ngot;
388         while(ngot < navail && nret--){
389                 gmem(p, tmp, 21);
390                 tmp[21] = 0;
391                 q->name = estrdup9p(tmp);
392                 ngot++;
393                 q++;
394                 if(p->pos >= p->eop)            /* Windows seems to lie somtimes */
395                         break;
396         }
397         free(p);
398         return ngot;
399 }
400
401 int
402 RAPuserenum(Session *s, Share *sp, Namelist **nlp)
403 {
404         int ngot, err, navail, nret;
405         char tmp[1024];
406         Pkt *p;
407         Namelist *q;
408
409         ngot = 0;
410         p = thdr(s, sp);
411         pstr(p, "\\PIPE\\LANMAN");
412         ptparam(p);
413
414         pl16(p, API_WUserEnum);
415         pascii(p, REMSmb_NetUserEnum_P);        /* request descriptor */
416         pascii(p, REMSmb_user_info_0);          /* reply descriptor */
417         pl16(p, 0);                             /* detail level */
418         pl16(p, s->mtu - 1024);                 /* receive buffer length */
419         ptdata(p);
420
421         if(trpc(p) == -1){
422                 free(p);
423                 return -1;
424         }
425
426         gtparam(p);
427         err = gl16(p);                          /* error code */
428         gl16(p);                                /* rx buffer offset */
429         nret = gl16(p);                         /* number of entries returned */
430         navail = gl16(p);                       /* number of entries available */
431
432         if(err && err != RAP_ERR_MOREINFO){
433                 werrstr("%s", raperrstr(err));
434                 free(p);
435                 return -1;
436         }
437
438         *nlp = emalloc9p(sizeof(Namelist) * navail);
439         memset(*nlp, 0, sizeof(Namelist) * navail);
440
441         q = *nlp + ngot;
442         while(ngot < navail && nret--){
443                 gmem(p, tmp, 21);
444                 tmp[21] = 0;
445                 q->name = estrdup9p(tmp);
446                 ngot++;
447                 q++;
448                 if(p->pos >= p->eop)            /* Windows seems to lie somtimes */
449                         break;
450         }
451         free(p);
452         return ngot;
453 }
454
455 int
456 RAPuserenum2(Session *s, Share *sp, Namelist **nlp)
457 {
458         int ngot, resume, err, navail, nret;
459         char tmp[1024];
460         Pkt *p;
461         Namelist *q;
462
463         ngot = 0;
464         resume = 0;
465 more:
466         p = thdr(s, sp);
467         pstr(p, "\\PIPE\\LANMAN");
468         ptparam(p);
469
470         pl16(p, API_WUserEnum2);
471         pascii(p, REMSmb_NetUserEnum2_P);       /* request descriptor */
472         pascii(p, REMSmb_user_info_0);          /* reply descriptor */
473         pl16(p, 0);                             /* detail level */
474         pl16(p, s->mtu - 1024);                 /* receive buffer length */
475         pl32(p, resume);                        /* resume key to allow multiple fetches */
476         ptdata(p);
477
478         if(trpc(p) == -1){
479                 free(p);
480                 return -1;
481         }
482
483         gtparam(p);
484         err = gl16(p);                          /* error code */
485         gl16(p);                                /* rx buffer offset */
486         resume = gl32(p);                       /* resume key returned */
487         nret = gl16(p);                         /* number of entries returned */
488         navail = gl16(p);                       /* number of entries available */
489
490         if(err && err != RAP_ERR_MOREINFO){
491                 werrstr("%s", raperrstr(err));
492                 free(p);
493                 return -1;
494         }
495
496         if(ngot == 0){
497                 *nlp = emalloc9p(sizeof(Namelist) * navail);
498                 memset(*nlp, 0, sizeof(Namelist) * navail);
499         }
500         q = *nlp + ngot;
501         while(ngot < navail && nret--){
502                 gmem(p, tmp, 21);
503                 tmp[21] = 0;
504                 q->name = estrdup9p(tmp);
505                 ngot++;
506                 q++;
507                 if(p->pos >= p->eop)            /* Windows seems to lie somtimes */
508                         break;
509         }
510         free(p);
511         if(ngot < navail)
512                 goto more;
513         return ngot;
514 }
515
516 int
517 RAPuserinfo(Session *s, Share *sp, char *user, Userinfo *uip)
518 {
519         int conv, err;
520         char tmp[1024];
521         Pkt *p;
522
523         p = thdr(s, sp);
524         pstr(p, "\\PIPE\\LANMAN");
525         ptparam(p);
526
527         pl16(p, API_WUserGetInfo);
528         pascii(p, REMSmb_NetUserGetInfo_P);     /* request descriptor */
529         pascii(p, REMSmb_user_info_10);         /* reply descriptor */
530         pascii(p, user);                        /* username */
531         pl16(p, 10);                            /* detail level */
532         pl16(p, s->mtu - 1024);                 /* receive buffer length */
533         ptdata(p);
534
535         if(trpc(p) == -1){
536                 free(p);
537                 return -1;
538         }
539
540         gtparam(p);
541         err = gl16(p);                          /* error code */
542         conv = gl16(p);                         /* rx buffer offset */
543         gl16(p);                                /* number of entries returned */
544         gl16(p);                                /* number of entries available */
545
546         if(err && err != RAP_ERR_MOREINFO){
547                 werrstr("%s", raperrstr(err));
548                 free(p);
549                 return -1;
550         }
551
552         gmem(p, tmp, 21);
553         tmp[21] = 0;
554         uip->user = estrdup9p(tmp);
555         g8(p);                          /* padding */
556         gconv(p, conv, tmp, sizeof tmp);
557         uip->comment = estrdup9p(tmp);
558         gconv(p, conv, tmp, sizeof tmp);
559         uip->user_comment = estrdup9p(tmp);
560         gconv(p, conv, tmp, sizeof tmp);
561         uip->fullname = estrdup9p(tmp);
562
563         free(p);
564         return 0;
565 }
566
567 /*
568  * This works agains win2k3 but fails
569  * against XP with the undocumented error 71/0x47
570  */
571 int
572 RAPServerenum2(Session *s, Share *sp, char *workgroup, int type, int *more,
573         Serverinfo **si)
574 {
575         int ngot = 0, conv, err, nret, navail;
576         char tmp[1024];
577         Pkt *p;
578         Serverinfo *q;
579
580         p = thdr(s, sp);
581         pstr(p, "\\PIPE\\LANMAN");
582
583         ptparam(p);
584         pl16(p, API_NetServerEnum2);
585         pascii(p, REMSmb_NetServerEnum2_P);     /* request descriptor */
586         pascii(p, REMSmb_server_info_1);        /* reply descriptor */
587         pl16(p, 1);                             /* detail level */
588         pl16(p, s->mtu - 1024);                 /* receive buffer length */
589         pl32(p, type);
590         pascii(p, workgroup);
591
592         ptdata(p);
593
594         if(trpc(p) == -1){
595                 free(p);
596                 return -1;
597         }
598
599         gtparam(p);
600         err = gl16(p);                          /* error code */
601         conv = gl16(p);                         /* rx buffer offset */
602         nret = gl16(p);                         /* number of entries returned */
603         navail = gl16(p);                       /* number of entries available */
604
605         if(err && err != RAP_ERR_MOREINFO){
606                 werrstr("%s", raperrstr(err));
607                 free(p);
608                 return -1;
609         }
610
611         *si = emalloc9p(sizeof(Serverinfo) * navail);
612         memset(*si, 0, sizeof(Serverinfo) * navail);
613
614         q = *si;
615         for (; nret-- != 0 && ngot < navail; ngot++){
616                 gmem(p, tmp, 16);
617                 tmp[16] = 0;
618                 q->name = estrdup9p(tmp);
619                 q->major = g8(p);
620                 q->minor = g8(p);
621                 q->type = gl32(p);
622                 gconv(p, conv, tmp, sizeof tmp);
623                 q->comment = estrdup9p(tmp);
624                 q++;
625         }
626         free(p);
627         *more = err == RAP_ERR_MOREINFO;
628         return ngot;
629 }
630
631 int
632 RAPServerenum3(Session *s, Share *sp, char *workgroup, int type, int last,
633         Serverinfo *si)
634 {
635         int conv, err, ngot, nret, navail;
636         char *first, tmp[1024];
637         Pkt *p;
638         Serverinfo *q;
639
640         ngot = last +1;
641         first = si[last].name;
642 more:
643         p = thdr(s, sp);
644         pstr(p, "\\PIPE\\LANMAN");
645
646         ptparam(p);
647         pl16(p, API_NetServerEnum3);
648         pascii(p, REMSmb_NetServerEnum3_P);     /* request descriptor */
649         pascii(p, REMSmb_server_info_1);        /* reply descriptor */
650         pl16(p, 1);                             /* detail level */
651         pl16(p, s->mtu - 1024);                 /* receive buffer length */
652         pl32(p, type);
653         pascii(p, workgroup);
654         pascii(p, first);
655
656         ptdata(p);
657
658         if(trpc(p) == -1){
659                 free(p);
660                 return -1;
661         }
662
663         gtparam(p);
664         err = gl16(p);                          /* error code */
665         conv = gl16(p);                         /* rx buffer offset */
666         nret = gl16(p);                         /* number of entries returned */
667         navail = gl16(p);                       /* number of entries available */
668
669         if(err && err != RAP_ERR_MOREINFO){
670                 werrstr("%s", raperrstr(err));
671                 free(p);
672                 return -1;
673         }
674
675         if(nret < 2){                           /* paranoia */
676                 free(p);
677                 return ngot;
678         }
679
680         q = si+ngot;
681         while(nret-- != 0 && ngot < navail){
682                 gmem(p, tmp, 16);
683                 tmp[16] = 0;
684                 q->name = estrdup9p(tmp);
685                 q->major = g8(p);
686                 q->minor = g8(p);
687                 q->type = gl32(p);
688                 gconv(p, conv, tmp, sizeof tmp);
689                 tmp[sizeof tmp - 1] = 0;
690                 q->comment = estrdup9p(tmp);
691                 if(strcmp(first, tmp) == 0){ /* 1st one thru _may_ be a repeat */
692                         free(q->name);
693                         free(q->comment);
694                         continue;
695                 }
696                 ngot++;
697                 q++;
698         }
699         free(p);
700         if(ngot < navail)
701                 goto more;
702         return ngot;
703 }
704
705 /* Only the Administrator has permission to do this */
706 int
707 RAPFileenum2(Session *s, Share *sp, char *user, char *path, Fileinfo **fip)
708 {
709         int conv, err, ngot, resume, nret, navail;
710         char tmp[1024];
711         Pkt *p;
712         Fileinfo *q;
713
714         ngot = 0;
715         resume = 0;
716 more:
717         p = thdr(s, sp);
718         pstr(p, "\\PIPE\\LANMAN");
719
720         ptparam(p);
721         pl16(p, API_WFileEnum2);
722         pascii(p, REMSmb_NetFileEnum2_P);       /* request descriptor */
723         pascii(p, REMSmb_file_info_1);          /* reply descriptor */
724         pascii(p, path);
725         pascii(p, user);
726         pl16(p, 1);                             /* detail level */
727         pl16(p, s->mtu - 1024);                 /* receive buffer length */
728         pl32(p, resume);                        /* resume key */
729 /* FIXME: maybe the padding and resume key are the wrong way around? */
730         pl32(p, 0);                             /* padding ? */
731
732         ptdata(p);
733
734         if(trpc(p) == -1){
735                 free(p);
736                 return -1;
737         }
738
739         gtparam(p);
740         err = gl16(p);                          /* error code */
741         conv = gl16(p);                         /* rx buffer offset */
742         resume = gl32(p);                       /* resume key returned */
743         nret = gl16(p);                         /* number of entries returned */
744         navail = gl16(p);                       /* number of entries available */
745
746         if(err && err != RAP_ERR_MOREINFO){
747                 werrstr("%s", raperrstr(err));
748                 free(p);
749                 return -1;
750         }
751
752         if(nret < 2){                           /* paranoia */
753                 free(p);
754                 return ngot;
755         }
756
757         if(ngot == 0){
758                 *fip = emalloc9p(sizeof(Fileinfo) * navail);
759                 memset(*fip, 0, sizeof(Fileinfo) * navail);
760         }
761         q = *fip + ngot;
762         for(; nret-- && ngot < navail; ngot++){
763                 q->ident = gl16(p);
764                 q->perms = gl16(p);
765                 q->locks = gl16(p);
766                 gconv(p, conv, tmp, sizeof tmp);
767                 tmp[sizeof tmp - 1] = 0;
768                 q->path = estrdup9p(tmp);
769                 gconv(p, conv, tmp, sizeof tmp);
770                 tmp[sizeof tmp - 1] = 0;
771                 q->user = estrdup9p(tmp);
772                 q++;
773         }
774         free(p);
775         if(ngot < navail)
776                 goto more;
777         return ngot;
778 }