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 *
thdr(Session * s,Share * sp)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
ptparam(Pkt * p)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
ptdata(Pkt * p)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
trpc(Pkt * p)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
gtparam(Pkt * p)101 gtparam(Pkt *p)
102 {
103 p->pos = p->tparam;
104 }
105
106 static void
gtdata(Pkt * p)107 gtdata(Pkt *p)
108 {
109 p->pos = p->tdata;
110 }
111
112
113 int
RAPshareenum(Session * s,Share * sp,Share ** ent)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
RAPshareinfo(Session * s,Share * sp,char * share,Shareinfo2 * si2p)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
RAPsessionenum(Session * s,Share * sp,Sessinfo ** sip)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
RAPgroupenum(Session * s,Share * sp,Namelist ** nlp)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
RAPgroupusers(Session * s,Share * sp,char * group,Namelist ** nlp)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
RAPuserenum(Session * s,Share * sp,Namelist ** nlp)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
RAPuserenum2(Session * s,Share * sp,Namelist ** nlp)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
RAPuserinfo(Session * s,Share * sp,char * user,Userinfo * uip)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
RAPServerenum2(Session * s,Share * sp,char * workgroup,int type,int * more,Serverinfo ** si)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
RAPServerenum3(Session * s,Share * sp,char * workgroup,int type,int last,Serverinfo * si)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
RAPFileenum2(Session * s,Share * sp,char * user,char * path,Fileinfo ** fip)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 }
786