📄 fsplib.c
字号:
/*This file is part of fsplib - FSP protocol stack implemented in Clanguage. See http://fsp.sourceforge.net for more information.Copyright (c) 2003-2005 by Radim HSN Kolar (hsn@netmag.cz)You may copy or modify this file in any manner you wish, providedthat this notice is always included, and that you hold the authorharmless for any loss or damage resulting from the installation oruse of this software. This is a free software. Be creative. Let me know of any bugs and suggestions.*/ #include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <netinet/in.h>#include <netdb.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <unistd.h>#include <string.h>#include <sys/stat.h>#include <dirent.h>#include "fsplib.h"#include "lock.h"/* ************ Internal functions **************** */ /* builds filename in packet output buffer, appends password if needed */static int buildfilename(const FSP_SESSION *s,FSP_PKT *out,const char *dirname){ int len; len=strlen(dirname); if(len >= FSP_SPACE - 1) { errno = ENAMETOOLONG; return -1; } /* copy name + \0 */ memcpy(out->buf,dirname,len+1); out->len=len; if(s->password) { out->buf[len]='\n'; out->len++; len=strlen(s->password); if(out->len+ len >= FSP_SPACE -1 ) { errno = ENAMETOOLONG; return -1; } memcpy(out->buf+out->len,s->password,len+1); out->len+=len; } /* add terminating \0 */ out->len++; return 0;}/* simple FSP command */static int simplecommand(FSP_SESSION *s,const char *directory,unsigned char command){ FSP_PKT in,out; if(buildfilename(s,&out,directory)) return -1; out.cmd=command; out.xlen=0; out.pos=0; if(fsp_transaction(s,&out,&in)) return -1; if(in.cmd == FSP_CC_ERR) { errno = EPERM; return -1; } if(in.cmd != command) { errno = ENOMSG; return -1; } errno = 0; return 0;}/* Get directory part of filename. You must free() the result */static char * directoryfromfilename(const char *filename){ char *result; char *tmp; int pos; result=strrchr(filename,'/'); if (result == NULL) return strdup(""); pos=result-filename; tmp=malloc(pos+1); if(!tmp) return NULL; memcpy(tmp,filename,pos); tmp[pos]='\0'; return tmp; }/* ************ Packet encoding / decoding *************** *//* write binary representation of FSP packet p into *space. *//* returns number of bytes used or zero on error *//* Space must be long enough to hold created packet. *//* Maximum created packet size is FSP_MAXPACKET */size_t fsp_pkt_write(const FSP_PKT *p,void *space){ size_t used; unsigned char *ptr; int checksum; size_t i; if(p->xlen + p->len > FSP_SPACE ) { /* not enough space */ errno = EMSGSIZE; return 0; } ptr=space; /* pack header */ ptr[FSP_OFFSET_CMD]=p->cmd; ptr[FSP_OFFSET_SUM]=0; *(uint16_t *)(ptr+FSP_OFFSET_KEY)=htons(p->key); *(uint16_t *)(ptr+FSP_OFFSET_SEQ)=htons(p->seq); *(uint16_t *)(ptr+FSP_OFFSET_LEN)=htons(p->len); *(uint32_t *)(ptr+FSP_OFFSET_POS)=htonl(p->pos); used=FSP_HSIZE; /* copy data block */ memcpy(ptr+FSP_HSIZE,p->buf,p->len); used+=p->len; /* copy extra data block */ memcpy(ptr+used,p->buf+p->len,p->xlen); used+=p->xlen; /* compute checksum */ checksum = 0; for(i=0;i<used;i++) { checksum += ptr[i]; } checksum +=used; ptr[FSP_OFFSET_SUM] = checksum + (checksum >> 8); return used;}/* read binary representation of FSP packet received from network into p *//* return zero on success */int fsp_pkt_read(FSP_PKT *p,const void *space,size_t recv_len){ int mysum; size_t i; const unsigned char *ptr; if(recv_len<FSP_HSIZE) { /* too short */ errno = ERANGE; return -1; } if(recv_len>FSP_MAXPACKET) { /* too long */ errno = EMSGSIZE; return -1; } ptr=space; /* check sum */ mysum=-ptr[FSP_OFFSET_SUM]; for(i=0;i<recv_len;i++) { mysum+=ptr[i]; } mysum = (mysum + (mysum >> 8)) & 0xff; if(mysum != ptr[FSP_OFFSET_SUM]) { /* checksum failed */#ifdef MAINTAINER_MODE printf("mysum: %x, got %x\n",mysum,ptr[FSP_OFFSET_SUM]);#endif errno = EIO; return -1; } /* unpack header */ p->cmd=ptr[FSP_OFFSET_CMD]; p->sum=mysum; p->key=ntohs( *(const uint16_t *)(ptr+FSP_OFFSET_KEY) ); p->seq=ntohs( *(const uint16_t *)(ptr+FSP_OFFSET_SEQ) ); p->len=ntohs( *(const uint16_t *)(ptr+FSP_OFFSET_LEN) ); p->pos=ntohl( *(const uint32_t *)(ptr+FSP_OFFSET_POS) ); if(p->len > recv_len) { /* bad length field, should not never happen */ errno = EMSGSIZE; return -1; } p->xlen=recv_len - p->len - FSP_HSIZE; /* now copy data */ memcpy(p->buf,ptr+FSP_HSIZE,recv_len - FSP_HSIZE); return 0;}/* ****************** packet sending functions ************** *//* make one send + receive transaction with server *//* outgoing packet is in p, incomming in rpkt */int fsp_transaction(FSP_SESSION *s,FSP_PKT *p,FSP_PKT *rpkt){ char buf[FSP_MAXPACKET]; size_t l; ssize_t r; fd_set mask; struct timeval start[8],stop; int i; unsigned int retry,dupes; int w_delay; /* how long to wait on next packet */ int f_delay; /* how long to wait after first send */ int l_delay; /* last delay */ unsigned int t_delay; /* time from first send */ if(p == rpkt) { errno = EINVAL; return -2; } FD_ZERO(&mask); /* get the next key */ p->key = client_get_key((FSP_LOCK *)s->lock); dupes = retry = 0; s->seq = (s-> seq + 0x08) & 0xfff8; t_delay = 0; /* compute initial delay here */ /* we are using hardcoded value for now */ f_delay = 1340; l_delay = 0; for(;;retry++) { if(t_delay >= s->timeout) { client_set_key((FSP_LOCK *)s->lock,p->key); errno = ETIMEDOUT; return -1; } /* make a packet */ p->seq = (s->seq) | (retry & 0x7); l=fsp_pkt_write(p,buf); /* We should compute next delay wait time here */ gettimeofday(&start[retry & 0x7],NULL); if(retry == 0 ) w_delay=f_delay; else { w_delay=l_delay*3/2; } l_delay=w_delay; /* send packet */ if( send(s->fd,buf,l,0) < 0 ) {#ifdef MAINTAINER_MODE printf("Send failed.\n");#endif if(errno == EBADF || errno == ENOTSOCK) { client_set_key((FSP_LOCK *)s->lock,p->key); errno = EBADF; return -1; } /* io terror */ sleep(1); /* avoid wasting retry slot */ retry--; t_delay += 1000; continue; } /* keep delay value within sane limits */ if (w_delay > (int) s->maxdelay) w_delay=s->maxdelay; else if(w_delay < 1000 ) w_delay = 1000; t_delay += w_delay; /* receive loop */ while(1) { if(w_delay <= 0 ) break; /* convert w_delay to timeval */ stop.tv_sec=w_delay/1000; stop.tv_usec=(w_delay % 1000)*1000; FD_SET(s->fd,&mask); i=select(s->fd+1,&mask,NULL,NULL,&stop); if(i==0) break; /* timed out */ if(i<0) { if(errno==EINTR) { /* lower w_delay */ gettimeofday(&stop,NULL); w_delay-=1000*(stop.tv_sec - start[retry & 0x7].tv_sec); w_delay-= (stop.tv_usec - start[retry & 0x7].tv_usec)/1000; continue; } /* hard select error */ client_set_key((FSP_LOCK *)s->lock,p->key); return -1; } r=recv(s->fd,buf,FSP_MAXPACKET,0); if(r < 0 ) { /* serious recv error */ client_set_key((FSP_LOCK *)s->lock,p->key); return -1; } gettimeofday(&stop,NULL); w_delay-=1000*(stop.tv_sec - start[retry & 0x7].tv_sec); w_delay-= (stop.tv_usec - start[retry & 0x7].tv_usec)/1000; /* process received packet */ if ( fsp_pkt_read(rpkt,buf,r) < 0) { /* unpack failed */ continue; } /* check sequence number */ if( (rpkt->seq & 0xfff8) != s->seq ) {#ifdef MAINTAINER_MODE printf("dupe\n");#endif /* duplicate */ dupes++; continue; } /* now we have a correct packet */ /* compute rtt delay */ w_delay=1000*(stop.tv_sec - start[retry & 0x7].tv_sec); w_delay+=(stop.tv_usec - start[retry & 0x7].tv_usec)/1000; /* update last stats */ s->last_rtt=w_delay; s->last_delay=f_delay; s->last_dupes=dupes; s->last_resends=retry; /* update cumul. stats */ s->dupes+=dupes; s->resends+=retry; s->trips++; s->rtts+=w_delay; /* grab a next key */ client_set_key((FSP_LOCK *)s->lock,rpkt->key); errno = 0; return 0; } }}/* ******************* Session management functions ************ *//* initializes a session */FSP_SESSION * fsp_open_session(const char *host,unsigned short port,const char *password){ FSP_SESSION *s; int fd; struct addrinfo hints,*res; char port_s[6]; struct sockaddr_in *addrin; FSP_LOCK *lock; memset (&hints, 0, sizeof (hints)); /* fspd do not supports inet6 */ hints.ai_family = PF_INET; hints.ai_socktype = SOCK_DGRAM; if (port == 0) strcpy(port_s,"fsp"); else sprintf(port_s,"%hu",port); if ( getaddrinfo(host,port_s,&hints,&res) ) { return NULL; /* host not found */ } /* create socket */ fd=socket(res->ai_family,res->ai_socktype,res->ai_protocol); if ( fd < 0) return NULL; /* connect socket */ if( connect(fd, res->ai_addr, res->ai_addrlen)) { close(fd); return NULL; } /* allocate memory */ s=calloc(1,sizeof(FSP_SESSION)); if ( !s ) { close(fd); errno = ENOMEM; return NULL; } lock=malloc(sizeof(FSP_LOCK)); if ( !lock ) { close(fd); free(s); errno = ENOMEM; return NULL; } s->lock=lock; /* init locking subsystem */ addrin = (struct sockaddr_in *)res->ai_addr; if ( client_init_key( (FSP_LOCK *)s->lock,addrin->sin_addr.s_addr,ntohs(addrin->sin_port))) { free(s); close(fd); free(lock); return NULL; } s->fd=fd; s->timeout=300000; /* 5 minutes */ s->maxdelay=60000; /* 1 minute */ s->seq=random(); if ( password ) s->password = strdup(password); return s;}/* closes a session */void fsp_close_session(FSP_SESSION *s){ FSP_PKT bye,in; if( s == NULL) return; if ( s->fd == -1) return; /* Send bye packet */ bye.cmd=FSP_CC_BYE; bye.len=bye.xlen=0; bye.pos=0; s->timeout=7000; fsp_transaction(s,&bye,&in); close(s->fd); if (s->password) free(s->password); client_destroy_key((FSP_LOCK *)s->lock); free(s->lock); memset(s,0,sizeof(FSP_SESSION)); s->fd=-1; free(s);}/* *************** Directory listing functions *************** *//* get a directory listing from a server */FSP_DIR * fsp_opendir(FSP_SESSION *s,const char *dirname){ FSP_PKT in,out; int pos; unsigned short blocksize; FSP_DIR *dir; unsigned char *tmp; if (s == NULL) return NULL; if (dirname == NULL) return NULL; if(buildfilename(s,&out,dirname)) { return NULL; } pos=0; blocksize=0; dir=NULL; out.cmd = FSP_CC_GET_DIR; out.xlen=0; /* load directory listing from the server */ while(1) { out.pos=pos; if ( fsp_transaction(s,&out,&in) ) { pos = -1; break; } if ( in.cmd != FSP_CC_GET_DIR ) { /* bad reply from the server */ pos = -1; break; } /* End of directory? */ if ( in.len == 0) break; /* set blocksize */ if (blocksize == 0 ) blocksize = in.len; /* alloc directory */ if (dir == NULL) { dir = calloc(1,sizeof(FSP_DIR)); if (dir == NULL) { pos = -1; break; } } /* append data */ tmp=realloc(dir->data,pos+in.len); if(tmp == NULL) { pos = -1; break; } dir->data=tmp; memcpy(dir->data + pos, in.buf,in.len); pos += in.len; if (in.len < blocksize) /* last block is smaller */ break; } if (pos == -1) { /* failure */ if (dir) { if(dir->data) free(dir->data); free(dir); } return NULL; } dir->inuse=1; dir->blocksize=blocksize; dir->dirname=strdup(dirname); dir->datasize=pos; return dir;}int fsp_readdir_r(FSP_DIR *dir,struct dirent *entry, struct dirent **result){ FSP_RDENTRY fentry,*fresult; int rc; char *c; if (dir == NULL || entry == NULL || *result == NULL) return -EINVAL; if (dir->dirpos<0 || dir->dirpos % 4) return -ESPIPE; rc=fsp_readdir_native(dir,&fentry,&fresult); if (rc != 0) return rc; /* convert FSP dirent to OS dirent */ if (fentry.type == FSP_RDTYPE_DIR ) entry->d_type=DT_DIR; else entry->d_type=DT_REG; /* remove symlink destination */ c=strchr(fentry.name,'\n'); if (c) { *c='\0'; rc=fentry.namlen-strlen(fentry.name); fentry.reclen-=rc; fentry.namlen-=rc; } entry->d_fileno = 10; entry->d_reclen = fentry.reclen; strncpy(entry->d_name,fentry.name,MAXNAMLEN); if (fentry.namlen > MAXNAMLEN) { entry->d_name[MAXNAMLEN + 1 ] = '\0';#ifdef HAVE_NAMLEN entry->d_namlen = MAXNAMLEN; } else { entry->d_namlen = fentry.namlen;#endif } if (fresult == &fentry ) { *result = entry; } else *result = NULL; return 0; }/* native FSP directory reader */int fsp_readdir_native(FSP_DIR *dir,FSP_RDENTRY *entry, FSP_RDENTRY **result){ unsigned char ftype; int namelen; if (dir == NULL || entry == NULL || *result == NULL) return -EINVAL; if (dir->dirpos<0 || dir->dirpos % 4) return -ESPIPE; while(1) { if ( dir->dirpos >= (int)dir->datasize ) { /* end of the directory */ *result = NULL; return 0; } if (dir->blocksize - (dir->dirpos % dir->blocksize) < 9) ftype= FSP_RDTYPE_SKIP; else /* get the file type */ ftype=dir->data[dir->dirpos+8]; if (ftype == FSP_RDTYPE_END ) { dir->dirpos=dir->datasize; continue; } if (ftype == FSP_RDTYPE_SKIP ) { /* skip to next directory block */ dir->dirpos = ( dir->dirpos / dir->blocksize + 1 ) * dir->blocksize;#ifdef MAINTAINER_MODE printf("new block dirpos: %d\n",dir->dirpos);#endif continue; } /* extract binary data */ entry->lastmod=ntohl( *(const uint32_t *)( dir->data+ dir->dirpos )); entry->size=ntohl( *(const uint32_t *)(dir->data+ dir->dirpos +4 )); entry->type=ftype; /* skip file date and file size */ dir->dirpos += 9; /* read file name */ entry->name[255 + 1] = '\0'; strncpy(entry->name,(char *)( dir->data + dir->dirpos ),MAXNAMLEN); namelen = strlen( (char *) dir->data+dir->dirpos); /* skip over file name */ dir->dirpos += namelen +1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -