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