Mercurial > gftp.yaz
comparison lib/fsplib/fsplib.c @ 662:be9663e0be00
2005-1-16 Brian Masney <masneyb@gftp.org>
* lib/fsplib/COPYING lib/fsplib/Makefile.am lib/fsplib/fsplib.c
lib/fsplib/fsplib.h lib/fsplib/lock.c lib/fsplib/lock.h - added FSPLIB
This library is written by Radim Kolar <hsn@netmag.cz> and was included
with his permission
author | masneyb |
---|---|
date | Sun, 16 Jan 2005 16:12:13 +0000 |
parents | |
children | 6b6fbcf76d00 |
comparison
equal
deleted
inserted
replaced
661:2e718fba351e | 662:be9663e0be00 |
---|---|
1 /* | |
2 This file is part of fsplib - FSP protocol stack implemented in C | |
3 language. See http://fsp.sourceforge.net for more information. | |
4 | |
5 Copyright (c) 2003-2004 by Radim HSN Kolar (hsn@netmag.cz) | |
6 | |
7 You may copy or modify this file in any manner you wish, provided | |
8 that this notice is always included, and that you hold the author | |
9 harmless for any loss or damage resulting from the installation or | |
10 use of this software. | |
11 | |
12 This is a free software. Be creative. | |
13 Let me know of any bugs and suggestions. | |
14 */ | |
15 #include <sys/types.h> | |
16 #include <sys/socket.h> | |
17 #include <sys/time.h> | |
18 #include <netinet/in.h> | |
19 #include <netdb.h> | |
20 #include <stdlib.h> | |
21 #include <stdio.h> | |
22 #include <errno.h> | |
23 #include <unistd.h> | |
24 #include <string.h> | |
25 #include <sys/stat.h> | |
26 #include <dirent.h> | |
27 #include "fsplib.h" | |
28 #include "lock.h" | |
29 | |
30 /* ************ Internal functions **************** */ | |
31 | |
32 /* builds filename in packet output buffer, appends password if needed */ | |
33 static int buildfilename(const FSP_SESSION *s,FSP_PKT *out,const char *dirname) | |
34 { | |
35 int len; | |
36 | |
37 len=strlen(dirname); | |
38 if(len >= FSP_SPACE - 1) | |
39 { | |
40 errno = ENAMETOOLONG; | |
41 return -1; | |
42 } | |
43 /* copy name + \0 */ | |
44 memcpy(out->buf,dirname,len+1); | |
45 out->len=len; | |
46 if(s->password) | |
47 { | |
48 out->buf[len]='\n'; | |
49 out->len++; | |
50 | |
51 len=strlen(s->password); | |
52 if(out->len+ len >= FSP_SPACE -1 ) | |
53 { | |
54 errno = ENAMETOOLONG; | |
55 return -1; | |
56 } | |
57 memcpy(out->buf+out->len,s->password,len+1); | |
58 out->len+=len; | |
59 } | |
60 /* add terminating \0 */ | |
61 out->len++; | |
62 return 0; | |
63 } | |
64 | |
65 /* simple FSP command */ | |
66 static int simplecommand(FSP_SESSION *s,const char *directory,unsigned char command) | |
67 { | |
68 FSP_PKT in,out; | |
69 | |
70 if(buildfilename(s,&out,directory)) | |
71 return -1; | |
72 out.cmd=command; | |
73 out.xlen=0; | |
74 out.pos=0; | |
75 | |
76 if(fsp_transaction(s,&out,&in)) | |
77 return -1; | |
78 | |
79 if(in.cmd == FSP_CC_ERR) | |
80 { | |
81 errno = EPERM; | |
82 return -1; | |
83 } | |
84 | |
85 if(in.cmd != command) | |
86 { | |
87 errno = ENOMSG; | |
88 return -1; | |
89 } | |
90 | |
91 errno = 0; | |
92 return 0; | |
93 } | |
94 /* Get directory part of filename. You must free() the result */ | |
95 static char * directoryfromfilename(const char *filename) | |
96 { | |
97 char *result; | |
98 char *tmp; | |
99 int pos; | |
100 | |
101 result=strrchr(filename,'/'); | |
102 if (result == NULL) | |
103 return strdup(""); | |
104 pos=result-filename; | |
105 tmp=malloc(pos+1); | |
106 if(!tmp) | |
107 return NULL; | |
108 memcpy(tmp,filename,pos); | |
109 tmp[pos]='\0'; | |
110 return tmp; | |
111 } | |
112 | |
113 /* ************ Packet encoding / decoding *************** */ | |
114 | |
115 /* write binary representation of FSP packet p into *space. */ | |
116 /* returns number of bytes used or zero on error */ | |
117 /* Space must be long enough to hold created packet. */ | |
118 /* Maximum created packet size is FSP_MAXPACKET */ | |
119 | |
120 size_t fsp_pkt_write(const FSP_PKT *p,void *space) | |
121 { | |
122 size_t used; | |
123 unsigned char *ptr; | |
124 int checksum; | |
125 size_t i; | |
126 | |
127 if(p->xlen + p->len > FSP_SPACE ) | |
128 { | |
129 /* not enough space */ | |
130 errno = EMSGSIZE; | |
131 return 0; | |
132 } | |
133 ptr=space; | |
134 /* pack header */ | |
135 ptr[FSP_OFFSET_CMD]=p->cmd; | |
136 ptr[FSP_OFFSET_SUM]=0; | |
137 *(uint16_t *)(ptr+FSP_OFFSET_KEY)=htons(p->key); | |
138 *(uint16_t *)(ptr+FSP_OFFSET_SEQ)=htons(p->seq); | |
139 *(uint16_t *)(ptr+FSP_OFFSET_LEN)=htons(p->len); | |
140 *(uint32_t *)(ptr+FSP_OFFSET_POS)=htonl(p->pos); | |
141 used=FSP_HSIZE; | |
142 /* copy data block */ | |
143 memcpy(ptr+FSP_HSIZE,p->buf,p->len); | |
144 used+=p->len; | |
145 /* copy extra data block */ | |
146 memcpy(ptr+used,p->buf+p->len,p->xlen); | |
147 used+=p->xlen; | |
148 /* compute checksum */ | |
149 checksum = 0; | |
150 for(i=0;i<used;i++) | |
151 { | |
152 checksum += ptr[i]; | |
153 } | |
154 checksum +=used; | |
155 ptr[FSP_OFFSET_SUM] = checksum + (checksum >> 8); | |
156 return used; | |
157 } | |
158 | |
159 /* read binary representation of FSP packet received from network into p */ | |
160 /* return zero on success */ | |
161 int fsp_pkt_read(FSP_PKT *p,const void *space,size_t recv_len) | |
162 { | |
163 int mysum; | |
164 size_t i; | |
165 const unsigned char *ptr; | |
166 | |
167 if(recv_len<FSP_HSIZE) | |
168 { | |
169 /* too short */ | |
170 errno = ERANGE; | |
171 return -1; | |
172 } | |
173 if(recv_len>FSP_MAXPACKET) | |
174 { | |
175 /* too long */ | |
176 errno = EMSGSIZE; | |
177 return -1; | |
178 } | |
179 | |
180 ptr=space; | |
181 /* check sum */ | |
182 mysum=-ptr[FSP_OFFSET_SUM]; | |
183 for(i=0;i<recv_len;i++) | |
184 { | |
185 mysum+=ptr[i]; | |
186 } | |
187 | |
188 mysum = (mysum + (mysum >> 8)) & 0xff; | |
189 | |
190 if(mysum != ptr[FSP_OFFSET_SUM]) | |
191 { | |
192 /* checksum failed */ | |
193 | |
194 #ifdef MAINTAINER_MODE | |
195 printf("mysum: %x, got %x\n",mysum,ptr[FSP_OFFSET_SUM]); | |
196 #endif | |
197 errno = EIO; | |
198 return -1; | |
199 } | |
200 | |
201 /* unpack header */ | |
202 p->cmd=ptr[FSP_OFFSET_CMD]; | |
203 p->sum=mysum; | |
204 p->key=ntohs( *(const uint16_t *)(ptr+FSP_OFFSET_KEY) ); | |
205 p->seq=ntohs( *(const uint16_t *)(ptr+FSP_OFFSET_SEQ) ); | |
206 p->len=ntohs( *(const uint16_t *)(ptr+FSP_OFFSET_LEN) ); | |
207 p->pos=ntohl( *(const uint32_t *)(ptr+FSP_OFFSET_POS) ); | |
208 if(p->len > recv_len) | |
209 { | |
210 /* bad length field, should not never happen */ | |
211 errno = EMSGSIZE; | |
212 return -1; | |
213 } | |
214 p->xlen=recv_len - p->len - FSP_HSIZE; | |
215 /* now copy data */ | |
216 memcpy(p->buf,ptr+FSP_HSIZE,recv_len - FSP_HSIZE); | |
217 return 0; | |
218 } | |
219 | |
220 /* ****************** packet sending functions ************** */ | |
221 | |
222 /* make one send + receive transaction with server */ | |
223 /* outgoing packet is in p, incomming in rpkt */ | |
224 int fsp_transaction(FSP_SESSION *s,FSP_PKT *p,FSP_PKT *rpkt) | |
225 { | |
226 char buf[FSP_MAXPACKET]; | |
227 size_t l; | |
228 ssize_t r; | |
229 fd_set mask; | |
230 struct timeval start[8],stop; | |
231 int i; | |
232 unsigned int retry,dupes; | |
233 int w_delay; /* how long to wait on next packet */ | |
234 int f_delay; /* how long to wait after first send */ | |
235 int l_delay; /* last delay */ | |
236 unsigned int t_delay; /* time from first send */ | |
237 | |
238 | |
239 if(p == rpkt) | |
240 { | |
241 errno = EINVAL; | |
242 return -2; | |
243 } | |
244 FD_ZERO(&mask); | |
245 /* get the next key */ | |
246 p->key = client_get_key((FSP_LOCK *)s->lock); | |
247 | |
248 dupes = retry = 0; | |
249 s->seq = (s-> seq + 0x08) & 0xfff8; | |
250 t_delay = 0; | |
251 /* compute initial delay here */ | |
252 /* we are using hardcoded value for now */ | |
253 f_delay = 1340; | |
254 for(;;retry++) | |
255 { | |
256 if(t_delay >= s->timeout) | |
257 { | |
258 client_set_key((FSP_LOCK *)s->lock,p->key); | |
259 errno = ETIMEDOUT; | |
260 return -1; | |
261 } | |
262 /* make a packet */ | |
263 p->seq = (s->seq) | (retry & 0x7); | |
264 l=fsp_pkt_write(p,buf); | |
265 | |
266 /* We should compute next delay wait time here */ | |
267 gettimeofday(&start[retry & 0x7],NULL); | |
268 if(retry == 0 ) | |
269 w_delay=f_delay; | |
270 else | |
271 { | |
272 w_delay=l_delay*3/2; | |
273 } | |
274 | |
275 l_delay=w_delay; | |
276 | |
277 /* send packet */ | |
278 if( send(s->fd,buf,l,0) < 0 ) | |
279 { | |
280 #ifdef MAINTAINER_MODE | |
281 printf("Send failed.\n"); | |
282 #endif | |
283 if(errno == EBADF || errno == ENOTSOCK) | |
284 { | |
285 client_set_key((FSP_LOCK *)s->lock,p->key); | |
286 errno = EBADF; | |
287 return -1; | |
288 } | |
289 /* io terror */ | |
290 sleep(1); | |
291 /* avoid wasting retry slot */ | |
292 retry--; | |
293 t_delay += 1000; | |
294 continue; | |
295 } | |
296 | |
297 /* keep delay value within sane limits */ | |
298 if (w_delay > (int) s->maxdelay) | |
299 w_delay=s->maxdelay; | |
300 else | |
301 if(w_delay < 1000 ) | |
302 w_delay = 1000; | |
303 | |
304 t_delay += w_delay; | |
305 /* receive loop */ | |
306 while(1) | |
307 { | |
308 if(w_delay <= 0 ) break; | |
309 /* convert w_delay to timeval */ | |
310 stop.tv_sec=w_delay/1000; | |
311 stop.tv_usec=(w_delay % 1000)*1000; | |
312 FD_SET(s->fd,&mask); | |
313 i=select(s->fd+1,&mask,NULL,NULL,&stop); | |
314 if(i==0) | |
315 break; /* timed out */ | |
316 if(i<0) | |
317 { | |
318 if(errno==EINTR) | |
319 { | |
320 /* lower w_delay */ | |
321 gettimeofday(&stop,NULL); | |
322 w_delay-=1000*(stop.tv_sec - start[retry & 0x7].tv_sec); | |
323 w_delay-= (stop.tv_usec - start[retry & 0x7].tv_usec)/1000; | |
324 continue; | |
325 } | |
326 /* hard select error */ | |
327 client_set_key((FSP_LOCK *)s->lock,p->key); | |
328 return -1; | |
329 } | |
330 r=recv(s->fd,buf,FSP_MAXPACKET,0); | |
331 if(r < 0 ) | |
332 { | |
333 /* serious recv error */ | |
334 client_set_key((FSP_LOCK *)s->lock,p->key); | |
335 return -1; | |
336 } | |
337 | |
338 gettimeofday(&stop,NULL); | |
339 w_delay-=1000*(stop.tv_sec - start[retry & 0x7].tv_sec); | |
340 w_delay-= (stop.tv_usec - start[retry & 0x7].tv_usec)/1000; | |
341 | |
342 /* process received packet */ | |
343 if ( fsp_pkt_read(rpkt,buf,r) < 0) | |
344 { | |
345 /* unpack failed */ | |
346 continue; | |
347 } | |
348 | |
349 /* check sequence number */ | |
350 if( (rpkt->seq & 0xfff8) != s->seq ) | |
351 { | |
352 #ifdef MAINTAINER_MODE | |
353 printf("dupe\n"); | |
354 #endif | |
355 /* duplicate */ | |
356 dupes++; | |
357 continue; | |
358 } | |
359 | |
360 /* now we have a correct packet */ | |
361 | |
362 /* compute rtt delay */ | |
363 w_delay=1000*(stop.tv_sec - start[retry & 0x7].tv_sec); | |
364 w_delay+=(stop.tv_usec - start[retry & 0x7].tv_usec)/1000; | |
365 /* update last stats */ | |
366 s->last_rtt=w_delay; | |
367 s->last_delay=f_delay; | |
368 s->last_dupes=dupes; | |
369 s->last_resends=retry; | |
370 /* update cumul. stats */ | |
371 s->dupes+=dupes; | |
372 s->resends+=retry; | |
373 s->trips++; | |
374 s->rtts+=w_delay; | |
375 | |
376 /* grab a next key */ | |
377 client_set_key((FSP_LOCK *)s->lock,rpkt->key); | |
378 errno = 0; | |
379 return 0; | |
380 } | |
381 } | |
382 } | |
383 | |
384 /* ******************* Session management functions ************ */ | |
385 | |
386 /* initializes a session */ | |
387 FSP_SESSION * fsp_open_session(const char *host,unsigned short port,const char *password) | |
388 { | |
389 FSP_SESSION *s; | |
390 int fd; | |
391 struct addrinfo hints,*res; | |
392 char port_s[6]; | |
393 struct sockaddr_in *addrin; | |
394 FSP_LOCK *lock; | |
395 | |
396 memset (&hints, 0, sizeof (hints)); | |
397 /* fspd do not supports inet6 */ | |
398 hints.ai_family = PF_INET; | |
399 hints.ai_socktype = SOCK_DGRAM; | |
400 | |
401 if (port == 0) | |
402 strcpy(port_s,"fsp"); | |
403 else | |
404 sprintf(port_s,"%hu",port); | |
405 | |
406 if ( getaddrinfo(host,port_s,&hints,&res) ) | |
407 { | |
408 return NULL; /* host not found */ | |
409 } | |
410 | |
411 /* create socket */ | |
412 fd=socket(res->ai_family,res->ai_socktype,res->ai_protocol); | |
413 if ( fd < 0) | |
414 return NULL; | |
415 | |
416 /* connect socket */ | |
417 if( connect(fd, res->ai_addr, res->ai_addrlen)) | |
418 { | |
419 close(fd); | |
420 return NULL; | |
421 } | |
422 | |
423 /* allocate memory */ | |
424 s=calloc(1,sizeof(FSP_SESSION)); | |
425 if ( !s ) | |
426 { | |
427 close(fd); | |
428 errno = ENOMEM; | |
429 return NULL; | |
430 } | |
431 | |
432 lock=malloc(sizeof(FSP_LOCK)); | |
433 | |
434 if ( !lock ) | |
435 { | |
436 close(fd); | |
437 free(s); | |
438 errno = ENOMEM; | |
439 return NULL; | |
440 } | |
441 | |
442 s->lock=lock; | |
443 | |
444 /* init locking subsystem */ | |
445 addrin = (struct sockaddr_in *)res->ai_addr; | |
446 if ( client_init_key( (FSP_LOCK *)s->lock,addrin->sin_addr.s_addr,ntohs(addrin->sin_port))) | |
447 { | |
448 free(s); | |
449 close(fd); | |
450 free(lock); | |
451 return NULL; | |
452 } | |
453 | |
454 s->fd=fd; | |
455 s->timeout=300000; /* 5 minutes */ | |
456 s->maxdelay=60000; /* 1 minute */ | |
457 s->seq=random(); | |
458 if ( password ) | |
459 s->password = strdup(password); | |
460 return s; | |
461 } | |
462 | |
463 /* closes a session */ | |
464 void fsp_close_session(FSP_SESSION *s) | |
465 { | |
466 FSP_PKT bye,in; | |
467 | |
468 if( s == NULL) | |
469 return; | |
470 if ( s->fd == -1) | |
471 return; | |
472 /* Send bye packet */ | |
473 bye.cmd=FSP_CC_BYE; | |
474 bye.len=bye.xlen=0; | |
475 bye.pos=0; | |
476 s->timeout=7000; | |
477 fsp_transaction(s,&bye,&in); | |
478 | |
479 close(s->fd); | |
480 if (s->password) free(s->password); | |
481 client_destroy_key((FSP_LOCK *)s->lock); | |
482 free(s->lock); | |
483 memset(s,0,sizeof(FSP_SESSION)); | |
484 s->fd=-1; | |
485 free(s); | |
486 } | |
487 | |
488 /* *************** Directory listing functions *************** */ | |
489 | |
490 /* get a directory listing from a server */ | |
491 FSP_DIR * fsp_opendir(FSP_SESSION *s,const char *dirname) | |
492 { | |
493 FSP_PKT in,out; | |
494 int pos; | |
495 unsigned short blocksize; | |
496 FSP_DIR *dir; | |
497 unsigned char *tmp; | |
498 | |
499 if (s == NULL) return NULL; | |
500 if (dirname == NULL) return NULL; | |
501 | |
502 if(buildfilename(s,&out,dirname)) | |
503 { | |
504 return NULL; | |
505 } | |
506 pos=0; | |
507 blocksize=0; | |
508 dir=NULL; | |
509 out.cmd = FSP_CC_GET_DIR; | |
510 out.xlen=0; | |
511 | |
512 /* load directory listing from the server */ | |
513 while(1) | |
514 { | |
515 out.pos=pos; | |
516 if ( fsp_transaction(s,&out,&in) ) | |
517 { | |
518 pos = -1; | |
519 break; | |
520 } | |
521 if ( in.cmd != FSP_CC_GET_DIR ) | |
522 { | |
523 /* bad reply from the server */ | |
524 pos = -1; | |
525 break; | |
526 } | |
527 /* End of directory? */ | |
528 if ( in.len == 0) | |
529 break; | |
530 /* set blocksize */ | |
531 if (blocksize == 0 ) | |
532 blocksize = in.len; | |
533 /* alloc directory */ | |
534 if (dir == NULL) | |
535 { | |
536 dir = calloc(1,sizeof(FSP_DIR)); | |
537 if (dir == NULL) | |
538 { | |
539 pos = -1; | |
540 break; | |
541 } | |
542 } | |
543 /* append data */ | |
544 tmp=realloc(dir->data,pos+in.len); | |
545 if(tmp == NULL) | |
546 { | |
547 pos = -1; | |
548 break; | |
549 } | |
550 dir->data=tmp; | |
551 memcpy(dir->data + pos, in.buf,in.len); | |
552 pos += in.len; | |
553 if (in.len < blocksize) | |
554 /* last block is smaller */ | |
555 break; | |
556 } | |
557 if (pos == -1) | |
558 { | |
559 /* failure */ | |
560 if (dir) | |
561 { | |
562 if(dir->data) | |
563 free(dir->data); | |
564 free(dir); | |
565 } | |
566 return NULL; | |
567 } | |
568 | |
569 dir->inuse=1; | |
570 dir->blocksize=blocksize; | |
571 dir->dirname=strdup(dirname); | |
572 dir->datasize=pos; | |
573 | |
574 return dir; | |
575 } | |
576 | |
577 int fsp_readdir_r(FSP_DIR *dir,struct dirent *entry, struct dirent **result) | |
578 { | |
579 FSP_RDENTRY fentry,*fresult; | |
580 int rc; | |
581 | |
582 if (dir == NULL || entry == NULL || *result == NULL) | |
583 return -EINVAL; | |
584 if (dir->dirpos<0 || dir->dirpos % 4) | |
585 return -ESPIPE; | |
586 | |
587 rc=fsp_readdir_native(dir,&fentry,&fresult); | |
588 | |
589 if (rc != 0) | |
590 return rc; | |
591 | |
592 /* convert FSP dirent to OS dirent */ | |
593 | |
594 if (fentry.type == FSP_RDTYPE_DIR ) | |
595 entry->d_type=DT_DIR; | |
596 else | |
597 entry->d_type=DT_REG; | |
598 entry->d_fileno = 10; | |
599 entry->d_reclen = fentry.reclen; | |
600 strncpy(entry->d_name,fentry.name,MAXNAMLEN); | |
601 | |
602 if (fentry.namlen > MAXNAMLEN) | |
603 { | |
604 entry->d_name[MAXNAMLEN + 1 ] = '\0'; | |
605 #ifdef HAVE_NAMLEN | |
606 entry->d_namlen = MAXNAMLEN; | |
607 } else | |
608 { | |
609 entry->d_namlen = fentry.namlen; | |
610 #endif | |
611 } | |
612 | |
613 if (fresult == &fentry ) | |
614 { | |
615 *result = entry; | |
616 } | |
617 else | |
618 *result = NULL; | |
619 | |
620 return 0; | |
621 } | |
622 | |
623 /* native FSP directory reader */ | |
624 int fsp_readdir_native(FSP_DIR *dir,FSP_RDENTRY *entry, FSP_RDENTRY **result) | |
625 { | |
626 unsigned char ftype; | |
627 int namelen; | |
628 | |
629 if (dir == NULL || entry == NULL || *result == NULL) | |
630 return -EINVAL; | |
631 if (dir->dirpos<0 || dir->dirpos % 4) | |
632 return -ESPIPE; | |
633 | |
634 while(1) | |
635 { | |
636 if ( dir->dirpos >= (int)dir->datasize ) | |
637 { | |
638 /* end of the directory */ | |
639 *result = NULL; | |
640 return 0; | |
641 } | |
642 if (dir->blocksize - (dir->dirpos % dir->blocksize) < 9) | |
643 ftype= FSP_RDTYPE_SKIP; | |
644 else | |
645 /* get the file type */ | |
646 ftype=dir->data[dir->dirpos+8]; | |
647 | |
648 if (ftype == FSP_RDTYPE_END ) | |
649 { | |
650 dir->dirpos=dir->datasize; | |
651 continue; | |
652 } | |
653 if (ftype == FSP_RDTYPE_SKIP ) | |
654 { | |
655 /* skip to next directory block */ | |
656 dir->dirpos = ( dir->dirpos / dir->blocksize + 1 ) * dir->blocksize; | |
657 #ifdef MAINTAINER_MODE | |
658 printf("new block dirpos: %d\n",dir->dirpos); | |
659 #endif | |
660 continue; | |
661 } | |
662 /* extract binary data */ | |
663 entry->lastmod=ntohl( *(const uint32_t *)( dir->data+ dir->dirpos )); | |
664 entry->size=ntohl( *(const uint32_t *)(dir->data+ dir->dirpos +4 )); | |
665 entry->type=ftype; | |
666 | |
667 /* skip file date and file size */ | |
668 dir->dirpos += 9; | |
669 /* read file name */ | |
670 entry->name[255 + 1] = '\0'; | |
671 strncpy(entry->name,(char *)( dir->data + dir->dirpos ),MAXNAMLEN); | |
672 namelen = strlen( (char *) dir->data+dir->dirpos); | |
673 /* skip over file name */ | |
674 dir->dirpos += namelen +1; | |
675 | |
676 /* set entry namelen field */ | |
677 if (namelen > 255) | |
678 entry->namlen = 255; | |
679 else | |
680 entry->namlen = namelen; | |
681 /* set record length */ | |
682 entry->reclen = 10+namelen; | |
683 | |
684 /* pad to 4 byte boundary */ | |
685 while( dir->dirpos & 0x3 ) | |
686 { | |
687 dir->dirpos++; | |
688 entry->reclen++; | |
689 } | |
690 | |
691 /* and return it */ | |
692 *result=entry; | |
693 return 0; | |
694 } | |
695 } | |
696 | |
697 struct dirent * fsp_readdir(FSP_DIR *dirp) | |
698 { | |
699 static struct dirent entry; | |
700 struct dirent *result; | |
701 | |
702 | |
703 if (dirp == NULL) return NULL; | |
704 if ( fsp_readdir_r(dirp,&entry,&result) ) | |
705 return NULL; | |
706 else | |
707 return result; | |
708 } | |
709 | |
710 long fsp_telldir(FSP_DIR *dirp) | |
711 { | |
712 return dirp->dirpos; | |
713 } | |
714 | |
715 void fsp_seekdir(FSP_DIR *dirp, long loc) | |
716 { | |
717 dirp->dirpos=loc; | |
718 } | |
719 | |
720 void fsp_rewinddir(FSP_DIR *dirp) | |
721 { | |
722 dirp->dirpos=0; | |
723 } | |
724 | |
725 int fsp_closedir(FSP_DIR *dirp) | |
726 { | |
727 if (dirp == NULL) | |
728 return -1; | |
729 if(dirp->dirname) free(dirp->dirname); | |
730 free(dirp->data); | |
731 free(dirp); | |
732 return 0; | |
733 } | |
734 | |
735 /* ***************** File input/output functions ********* */ | |
736 FSP_FILE * fsp_fopen(FSP_SESSION *session, const char *path,const char *modeflags) | |
737 { | |
738 FSP_FILE *f; | |
739 | |
740 if(session == NULL || path == NULL || modeflags == NULL) | |
741 { | |
742 errno = EINVAL; | |
743 return NULL; | |
744 } | |
745 | |
746 f=calloc(1,sizeof(FSP_FILE)); | |
747 if (f == NULL) | |
748 { | |
749 return NULL; | |
750 } | |
751 | |
752 /* check and parse flags */ | |
753 switch (*modeflags++) | |
754 { | |
755 case 'r': | |
756 break; | |
757 case 'w': | |
758 f->writing=1; | |
759 break; | |
760 case 'a': | |
761 /* not supported */ | |
762 free(f); | |
763 errno = ENOTSUP; | |
764 return NULL; | |
765 default: | |
766 free(f); | |
767 errno = EINVAL; | |
768 return NULL; | |
769 } | |
770 | |
771 if (*modeflags == '+' || ( *modeflags=='b' && modeflags[1]=='+')) | |
772 { | |
773 free(f); | |
774 errno = ENOTSUP; | |
775 return NULL; | |
776 } | |
777 | |
778 /* build request packet */ | |
779 if(f->writing) | |
780 { | |
781 f->out.cmd=FSP_CC_UP_LOAD; | |
782 } | |
783 else | |
784 { | |
785 if(buildfilename(session,&f->out,path)) | |
786 { | |
787 free(f); | |
788 return NULL; | |
789 } | |
790 f->bufpos=FSP_SPACE; | |
791 f->out.cmd=FSP_CC_GET_FILE; | |
792 } | |
793 f->out.xlen=0; | |
794 | |
795 /* setup control variables */ | |
796 f->s=session; | |
797 f->name=strdup(path); | |
798 if(f->name == NULL) | |
799 { | |
800 free(f); | |
801 errno = ENOMEM; | |
802 return NULL; | |
803 } | |
804 | |
805 return f; | |
806 } | |
807 | |
808 size_t fsp_fread(void *dest,size_t size,size_t count,FSP_FILE *file) | |
809 { | |
810 size_t total,done,havebytes; | |
811 char *ptr; | |
812 | |
813 total=count*size; | |
814 done=0; | |
815 ptr=dest; | |
816 | |
817 if(file->eof) return 0; | |
818 | |
819 while(1) | |
820 { | |
821 /* need more data? */ | |
822 if(file->bufpos>=FSP_SPACE) | |
823 { | |
824 /* fill the buffer */ | |
825 file->out.pos=file->pos; | |
826 if(fsp_transaction(file->s,&file->out,&file->in)) | |
827 { | |
828 file->err=1; | |
829 return done/size; | |
830 } | |
831 if(file->in.cmd != FSP_CC_GET_FILE) | |
832 { | |
833 errno = EIO; | |
834 file->err=1; | |
835 return done/size; | |
836 } | |
837 file->bufpos=FSP_SPACE-file->in.len; | |
838 if(file->bufpos > 0) | |
839 { | |
840 memmove(file->in.buf+file->bufpos,file->in.buf,file->in.len); | |
841 } | |
842 file->pos+=file->in.len; | |
843 } | |
844 havebytes=FSP_SPACE - file->bufpos; | |
845 if (havebytes == 0 ) | |
846 { | |
847 /* end of file! */ | |
848 file->eof=1; | |
849 errno = 0; | |
850 return done/size; | |
851 } | |
852 /* copy ready data to output buffer */ | |
853 if(havebytes <= total ) | |
854 { | |
855 /* copy all we have */ | |
856 memcpy(ptr,file->in.buf+file->bufpos,havebytes); | |
857 ptr+=havebytes; | |
858 file->bufpos=FSP_SPACE; | |
859 done+=havebytes; | |
860 total-=havebytes; | |
861 } else | |
862 { | |
863 /* copy bytes left */ | |
864 memcpy(ptr,file->in.buf+file->bufpos,total); | |
865 file->bufpos+=total; | |
866 errno = 0; | |
867 return count; | |
868 } | |
869 } | |
870 } | |
871 | |
872 size_t fsp_fwrite(const void * source, size_t size, size_t count, FSP_FILE * file) | |
873 { | |
874 size_t total,done,freebytes; | |
875 const char *ptr; | |
876 | |
877 if(file->eof || file->err) | |
878 return 0; | |
879 | |
880 file->out.len=FSP_SPACE; | |
881 total=count*size; | |
882 done=0; | |
883 ptr=source; | |
884 | |
885 while(1) | |
886 { | |
887 /* need to write some data? */ | |
888 if(file->bufpos>=FSP_SPACE) | |
889 { | |
890 /* fill the buffer */ | |
891 file->out.pos=file->pos; | |
892 if(fsp_transaction(file->s,&file->out,&file->in)) | |
893 { | |
894 file->err=1; | |
895 return done/size; | |
896 } | |
897 if(file->in.cmd != FSP_CC_UP_LOAD) | |
898 { | |
899 errno = EIO; | |
900 file->err=1; | |
901 return done/size; | |
902 } | |
903 file->bufpos=0; | |
904 file->pos+=file->out.len; | |
905 done+=file->out.len; | |
906 } | |
907 freebytes=FSP_SPACE - file->bufpos; | |
908 /* copy input data to output buffer */ | |
909 if(freebytes <= total ) | |
910 { | |
911 /* copy all we have */ | |
912 memcpy(file->out.buf+file->bufpos,ptr,freebytes); | |
913 ptr+=freebytes; | |
914 file->bufpos=FSP_SPACE; | |
915 total-=freebytes; | |
916 } else | |
917 { | |
918 /* copy bytes left */ | |
919 memcpy(file->out.buf+file->bufpos,ptr,total); | |
920 file->bufpos+=total; | |
921 errno = 0; | |
922 return count; | |
923 } | |
924 } | |
925 } | |
926 | |
927 int fsp_fpurge(FSP_FILE *file) | |
928 { | |
929 if(file->writing) | |
930 { | |
931 file->bufpos=0; | |
932 } | |
933 else | |
934 { | |
935 file->bufpos=FSP_SPACE; | |
936 } | |
937 errno = 0; | |
938 return 0; | |
939 } | |
940 | |
941 int fsp_fflush(FSP_FILE *file) | |
942 { | |
943 if(file == NULL) | |
944 { | |
945 errno = ENOTSUP; | |
946 return -1; | |
947 } | |
948 if(!file->writing) | |
949 { | |
950 errno = EBADF; | |
951 return -1; | |
952 } | |
953 if(file->eof || file->bufpos==0) | |
954 { | |
955 errno = 0; | |
956 return 0; | |
957 } | |
958 | |
959 file->out.pos=file->pos; | |
960 file->out.len=file->bufpos; | |
961 if(fsp_transaction(file->s,&file->out,&file->in)) | |
962 { | |
963 file->err=1; | |
964 return -1; | |
965 } | |
966 if(file->in.cmd != FSP_CC_UP_LOAD) | |
967 { | |
968 errno = EIO; | |
969 file->err=1; | |
970 return -1; | |
971 } | |
972 file->bufpos=0; | |
973 file->pos+=file->out.len; | |
974 | |
975 errno = 0; | |
976 return 0; | |
977 } | |
978 | |
979 | |
980 | |
981 int fsp_fclose(FSP_FILE *file) | |
982 { | |
983 int rc; | |
984 | |
985 rc=0; | |
986 errno = 0; | |
987 if(file->writing) | |
988 { | |
989 if(fsp_fflush(file)) | |
990 { | |
991 rc=-1; | |
992 } | |
993 else if(fsp_install(file->s,file->name,0)) | |
994 { | |
995 rc=-1; | |
996 } | |
997 } | |
998 free(file->name); | |
999 free(file); | |
1000 return rc; | |
1001 } | |
1002 | |
1003 int fsp_fseek(FSP_FILE *stream, long offset, int whence) | |
1004 { | |
1005 long newoffset; | |
1006 | |
1007 switch(whence) | |
1008 { | |
1009 case SEEK_SET: | |
1010 newoffset = offset; | |
1011 break; | |
1012 case SEEK_CUR: | |
1013 newoffset = stream->pos + offset; | |
1014 break; | |
1015 case SEEK_END: | |
1016 errno = ENOTSUP; | |
1017 return -1; | |
1018 default: | |
1019 errno = EINVAL; | |
1020 return -1; | |
1021 } | |
1022 if(stream->writing) | |
1023 { | |
1024 if(fsp_fflush(stream)) | |
1025 { | |
1026 return -1; | |
1027 } | |
1028 } | |
1029 stream->pos=newoffset; | |
1030 stream->eof=0; | |
1031 fsp_fpurge(stream); | |
1032 return 0; | |
1033 } | |
1034 | |
1035 long fsp_ftell(FSP_FILE *f) | |
1036 { | |
1037 return f->pos + f->bufpos; | |
1038 } | |
1039 | |
1040 void fsp_rewind(FSP_FILE *f) | |
1041 { | |
1042 if(f->writing) | |
1043 fsp_fflush(f); | |
1044 f->pos=0; | |
1045 f->err=0; | |
1046 f->eof=0; | |
1047 fsp_fpurge(f); | |
1048 } | |
1049 | |
1050 /* **************** Utility functions ****************** */ | |
1051 | |
1052 /* return 0 if user has enough privs for uploading the file */ | |
1053 int fsp_canupload(FSP_SESSION *s,const char *fname) | |
1054 { | |
1055 char *dir; | |
1056 unsigned char dirpro; | |
1057 int rc; | |
1058 struct stat sb; | |
1059 | |
1060 dir=directoryfromfilename(fname); | |
1061 if(dir == NULL) | |
1062 { | |
1063 errno = ENOMEM; | |
1064 return -1; | |
1065 } | |
1066 | |
1067 rc=fsp_getpro(s,dir,&dirpro); | |
1068 free(dir); | |
1069 | |
1070 if(rc) | |
1071 { | |
1072 return -1; | |
1073 } | |
1074 | |
1075 if(dirpro & FSP_DIR_OWNER) | |
1076 return 0; | |
1077 | |
1078 if( ! (dirpro & FSP_DIR_ADD)) | |
1079 return -1; | |
1080 | |
1081 if (dirpro & FSP_DIR_DEL) | |
1082 return 0; | |
1083 | |
1084 /* we need to check file existence, because we can not overwrite files */ | |
1085 | |
1086 rc = fsp_stat(s,fname,&sb); | |
1087 | |
1088 if (rc == 0) | |
1089 return -1; | |
1090 else | |
1091 return 0; | |
1092 } | |
1093 | |
1094 /* install file opened for writing */ | |
1095 int fsp_install(FSP_SESSION *s,const char *fname,time_t timestamp) | |
1096 { | |
1097 int rc; | |
1098 FSP_PKT in,out; | |
1099 | |
1100 /* and install a new file */ | |
1101 out.cmd=FSP_CC_INSTALL; | |
1102 out.xlen=0; | |
1103 out.pos=0; | |
1104 rc=0; | |
1105 if( buildfilename(s,&out,fname) ) | |
1106 rc=-1; | |
1107 else | |
1108 { | |
1109 if (timestamp != 0) | |
1110 { | |
1111 /* add timestamp extra data */ | |
1112 *(uint32_t *)(out.buf+out.len)=htonl(timestamp); | |
1113 out.xlen=4; | |
1114 out.pos=4; | |
1115 } | |
1116 if(fsp_transaction(s,&out,&in)) | |
1117 { | |
1118 rc=-1; | |
1119 } else | |
1120 { | |
1121 if(in.cmd != FSP_CC_INSTALL) | |
1122 { | |
1123 rc=-1; | |
1124 errno = EPERM; | |
1125 } | |
1126 } | |
1127 } | |
1128 | |
1129 return rc; | |
1130 } | |
1131 /* Get protection byte from the directory */ | |
1132 int fsp_getpro(FSP_SESSION *s,const char *directory,unsigned char *result) | |
1133 { | |
1134 FSP_PKT in,out; | |
1135 | |
1136 if(buildfilename(s,&out,directory)) | |
1137 return -1; | |
1138 out.cmd=FSP_CC_GET_PRO; | |
1139 out.xlen=0; | |
1140 out.pos=0; | |
1141 | |
1142 if(fsp_transaction(s,&out,&in)) | |
1143 return -1; | |
1144 | |
1145 if(in.cmd != FSP_CC_GET_PRO || in.pos != FSP_PRO_BYTES) | |
1146 { | |
1147 errno = ENOMSG; | |
1148 return -1; | |
1149 } | |
1150 | |
1151 if(result) | |
1152 *result=in.buf[in.len]; | |
1153 errno = 0; | |
1154 return 0; | |
1155 } | |
1156 | |
1157 int fsp_stat(FSP_SESSION *s,const char *path,struct stat *sb) | |
1158 { | |
1159 FSP_PKT in,out; | |
1160 unsigned char ftype; | |
1161 | |
1162 if(buildfilename(s,&out,path)) | |
1163 return -1; | |
1164 out.cmd=FSP_CC_STAT; | |
1165 out.xlen=0; | |
1166 out.pos=0; | |
1167 | |
1168 if(fsp_transaction(s,&out,&in)) | |
1169 return -1; | |
1170 | |
1171 if(in.cmd != FSP_CC_STAT) | |
1172 { | |
1173 errno = ENOTSUP; | |
1174 return -1; | |
1175 } | |
1176 /* parse results */ | |
1177 ftype=in.buf[8]; | |
1178 if(ftype == 0) | |
1179 { | |
1180 errno = ENOENT; | |
1181 return -1; | |
1182 } | |
1183 sb->st_uid=sb->st_gid=0; | |
1184 sb->st_mtime=sb->st_ctime=sb->st_atime=ntohl( *(const uint32_t *)( in.buf )); | |
1185 sb->st_size=ntohl( *(const uint32_t *)(in.buf + 4 )); | |
1186 sb->st_blocks=(sb->st_size+511)/512; | |
1187 if (ftype==FSP_RDTYPE_DIR) | |
1188 { | |
1189 sb->st_mode=S_IFDIR | 0755; | |
1190 sb->st_nlink=2; | |
1191 } | |
1192 else | |
1193 { | |
1194 sb->st_mode=S_IFREG | 0644; | |
1195 sb->st_nlink=1; | |
1196 } | |
1197 | |
1198 errno = 0; | |
1199 return 0; | |
1200 } | |
1201 | |
1202 int fsp_mkdir(FSP_SESSION *s,const char *directory) | |
1203 { | |
1204 return simplecommand(s,directory,FSP_CC_MAKE_DIR); | |
1205 } | |
1206 | |
1207 int fsp_rmdir(FSP_SESSION *s,const char *directory) | |
1208 { | |
1209 return simplecommand(s,directory,FSP_CC_DEL_DIR); | |
1210 } | |
1211 | |
1212 int fsp_unlink(FSP_SESSION *s,const char *directory) | |
1213 { | |
1214 return simplecommand(s,directory,FSP_CC_DEL_FILE); | |
1215 } | |
1216 | |
1217 int fsp_rename(FSP_SESSION *s,const char *from, const char *to) | |
1218 { | |
1219 FSP_PKT in,out; | |
1220 int l; | |
1221 | |
1222 if(buildfilename(s,&out,from)) | |
1223 return -1; | |
1224 /* append target name */ | |
1225 l=strlen(to)+1; | |
1226 if( l + out.len > FSP_SPACE ) | |
1227 { | |
1228 errno = ENAMETOOLONG; | |
1229 return -1; | |
1230 } | |
1231 memcpy(out.buf+out.len,to,l); | |
1232 out.xlen = l; | |
1233 | |
1234 if(s->password) | |
1235 { | |
1236 l=strlen(s->password)+1; | |
1237 if(out.len + out.xlen + l > FSP_SPACE) | |
1238 { | |
1239 errno = ENAMETOOLONG; | |
1240 return -1; | |
1241 } | |
1242 out.buf[out.len+out.xlen-1] = '\n'; | |
1243 memcpy(out.buf+out.len+out.xlen,s->password,l); | |
1244 out.xlen += l; | |
1245 } | |
1246 | |
1247 out.cmd=FSP_CC_RENAME; | |
1248 out.pos=out.xlen; | |
1249 | |
1250 if(fsp_transaction(s,&out,&in)) | |
1251 return -1; | |
1252 | |
1253 if(in.cmd != FSP_CC_RENAME) | |
1254 { | |
1255 errno = EPERM; | |
1256 return -1; | |
1257 } | |
1258 | |
1259 errno = 0; | |
1260 return 0; | |
1261 } | |
1262 | |
1263 int fsp_access(FSP_SESSION *s,const char *path, int mode) | |
1264 { | |
1265 struct stat sb; | |
1266 int rc; | |
1267 unsigned char dirpro; | |
1268 char *dir; | |
1269 | |
1270 rc=fsp_stat(s,path,&sb); | |
1271 if(rc == -1) | |
1272 { | |
1273 /* not found */ | |
1274 /* errno is set by fsp_stat */ | |
1275 return -1; | |
1276 } | |
1277 | |
1278 /* just test file existence */ | |
1279 if(mode == F_OK) | |
1280 { | |
1281 errno = 0; | |
1282 return 0; | |
1283 } | |
1284 | |
1285 /* deny execute access to file */ | |
1286 if (mode & X_OK) | |
1287 { | |
1288 if(S_ISREG(sb.st_mode)) | |
1289 { | |
1290 errno = EACCES; | |
1291 return -1; | |
1292 } | |
1293 } | |
1294 | |
1295 /* Need to get ACL of directory */ | |
1296 if(S_ISDIR(sb.st_mode)) | |
1297 dir=NULL; | |
1298 else | |
1299 dir=directoryfromfilename(path); | |
1300 | |
1301 rc=fsp_getpro(s,dir==NULL?path:dir,&dirpro); | |
1302 /* get pro failure */ | |
1303 if(rc) | |
1304 { | |
1305 if(dir) free(dir); | |
1306 errno = EACCES; | |
1307 return -1; | |
1308 } | |
1309 /* owner shortcut */ | |
1310 if(dirpro & FSP_DIR_OWNER) | |
1311 { | |
1312 if(dir) free(dir); | |
1313 errno = 0; | |
1314 return 0; | |
1315 } | |
1316 /* check read access */ | |
1317 if(mode & R_OK) | |
1318 { | |
1319 if(dir) | |
1320 { | |
1321 if(! (dirpro & FSP_DIR_GET)) | |
1322 { | |
1323 free(dir); | |
1324 errno = EACCES; | |
1325 return -1; | |
1326 } | |
1327 } else | |
1328 { | |
1329 if(! (dirpro & FSP_DIR_LIST)) | |
1330 { | |
1331 errno = EACCES; | |
1332 return -1; | |
1333 } | |
1334 } | |
1335 } | |
1336 /* check write access */ | |
1337 if(mode & W_OK) | |
1338 { | |
1339 if(dir) | |
1340 { | |
1341 if( !(dirpro & FSP_DIR_DEL) || !(dirpro & FSP_DIR_ADD)) | |
1342 { | |
1343 free(dir); | |
1344 errno = EACCES; | |
1345 return -1; | |
1346 } | |
1347 } else | |
1348 { | |
1349 /* when checking directory for write access we are cheating | |
1350 by allowing ADD or DEL right */ | |
1351 if( !(dirpro & FSP_DIR_DEL) && !(dirpro & FSP_DIR_ADD)) | |
1352 { | |
1353 errno = EACCES; | |
1354 return -1; | |
1355 } | |
1356 } | |
1357 } | |
1358 | |
1359 if(dir) free(dir); | |
1360 errno = 0; | |
1361 return 0; | |
1362 } |