Mercurial > gftp.yaz
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/fsplib/fsplib.c Sun Jan 16 16:12:13 2005 +0000 @@ -0,0 +1,1362 @@ +/* +This file is part of fsplib - FSP protocol stack implemented in C +language. See http://fsp.sourceforge.net for more information. + +Copyright (c) 2003-2004 by Radim HSN Kolar (hsn@netmag.cz) + +You may copy or modify this file in any manner you wish, provided +that this notice is always included, and that you hold the author +harmless for any loss or damage resulting from the installation or +use 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; + 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; + + 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; + 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; + + /* set entry namelen field */ + if (namelen > 255) + entry->namlen = 255; + else + entry->namlen = namelen; + /* set record length */ + entry->reclen = 10+namelen; + + /* pad to 4 byte boundary */ + while( dir->dirpos & 0x3 ) + { + dir->dirpos++; + entry->reclen++; + } + + /* and return it */ + *result=entry; + return 0; + } +} + +struct dirent * fsp_readdir(FSP_DIR *dirp) +{ + static struct dirent entry; + struct dirent *result; + + + if (dirp == NULL) return NULL; + if ( fsp_readdir_r(dirp,&entry,&result) ) + return NULL; + else + return result; +} + +long fsp_telldir(FSP_DIR *dirp) +{ + return dirp->dirpos; +} + +void fsp_seekdir(FSP_DIR *dirp, long loc) +{ + dirp->dirpos=loc; +} + +void fsp_rewinddir(FSP_DIR *dirp) +{ + dirp->dirpos=0; +} + +int fsp_closedir(FSP_DIR *dirp) +{ + if (dirp == NULL) + return -1; + if(dirp->dirname) free(dirp->dirname); + free(dirp->data); + free(dirp); + return 0; +} + +/* ***************** File input/output functions ********* */ +FSP_FILE * fsp_fopen(FSP_SESSION *session, const char *path,const char *modeflags) +{ + FSP_FILE *f; + + if(session == NULL || path == NULL || modeflags == NULL) + { + errno = EINVAL; + return NULL; + } + + f=calloc(1,sizeof(FSP_FILE)); + if (f == NULL) + { + return NULL; + } + + /* check and parse flags */ + switch (*modeflags++) + { + case 'r': + break; + case 'w': + f->writing=1; + break; + case 'a': + /* not supported */ + free(f); + errno = ENOTSUP; + return NULL; + default: + free(f); + errno = EINVAL; + return NULL; + } + + if (*modeflags == '+' || ( *modeflags=='b' && modeflags[1]=='+')) + { + free(f); + errno = ENOTSUP; + return NULL; + } + + /* build request packet */ + if(f->writing) + { + f->out.cmd=FSP_CC_UP_LOAD; + } + else + { + if(buildfilename(session,&f->out,path)) + { + free(f); + return NULL; + } + f->bufpos=FSP_SPACE; + f->out.cmd=FSP_CC_GET_FILE; + } + f->out.xlen=0; + + /* setup control variables */ + f->s=session; + f->name=strdup(path); + if(f->name == NULL) + { + free(f); + errno = ENOMEM; + return NULL; + } + + return f; +} + +size_t fsp_fread(void *dest,size_t size,size_t count,FSP_FILE *file) +{ + size_t total,done,havebytes; + char *ptr; + + total=count*size; + done=0; + ptr=dest; + + if(file->eof) return 0; + + while(1) + { + /* need more data? */ + if(file->bufpos>=FSP_SPACE) + { + /* fill the buffer */ + file->out.pos=file->pos; + if(fsp_transaction(file->s,&file->out,&file->in)) + { + file->err=1; + return done/size; + } + if(file->in.cmd != FSP_CC_GET_FILE) + { + errno = EIO; + file->err=1; + return done/size; + } + file->bufpos=FSP_SPACE-file->in.len; + if(file->bufpos > 0) + { + memmove(file->in.buf+file->bufpos,file->in.buf,file->in.len); + } + file->pos+=file->in.len; + } + havebytes=FSP_SPACE - file->bufpos; + if (havebytes == 0 ) + { + /* end of file! */ + file->eof=1; + errno = 0; + return done/size; + } + /* copy ready data to output buffer */ + if(havebytes <= total ) + { + /* copy all we have */ + memcpy(ptr,file->in.buf+file->bufpos,havebytes); + ptr+=havebytes; + file->bufpos=FSP_SPACE; + done+=havebytes; + total-=havebytes; + } else + { + /* copy bytes left */ + memcpy(ptr,file->in.buf+file->bufpos,total); + file->bufpos+=total; + errno = 0; + return count; + } + } +} + +size_t fsp_fwrite(const void * source, size_t size, size_t count, FSP_FILE * file) +{ + size_t total,done,freebytes; + const char *ptr; + + if(file->eof || file->err) + return 0; + + file->out.len=FSP_SPACE; + total=count*size; + done=0; + ptr=source; + + while(1) + { + /* need to write some data? */ + if(file->bufpos>=FSP_SPACE) + { + /* fill the buffer */ + file->out.pos=file->pos; + if(fsp_transaction(file->s,&file->out,&file->in)) + { + file->err=1; + return done/size; + } + if(file->in.cmd != FSP_CC_UP_LOAD) + { + errno = EIO; + file->err=1; + return done/size; + } + file->bufpos=0; + file->pos+=file->out.len; + done+=file->out.len; + } + freebytes=FSP_SPACE - file->bufpos; + /* copy input data to output buffer */ + if(freebytes <= total ) + { + /* copy all we have */ + memcpy(file->out.buf+file->bufpos,ptr,freebytes); + ptr+=freebytes; + file->bufpos=FSP_SPACE; + total-=freebytes; + } else + { + /* copy bytes left */ + memcpy(file->out.buf+file->bufpos,ptr,total); + file->bufpos+=total; + errno = 0; + return count; + } + } +} + +int fsp_fpurge(FSP_FILE *file) +{ + if(file->writing) + { + file->bufpos=0; + } + else + { + file->bufpos=FSP_SPACE; + } + errno = 0; + return 0; +} + +int fsp_fflush(FSP_FILE *file) +{ + if(file == NULL) + { + errno = ENOTSUP; + return -1; + } + if(!file->writing) + { + errno = EBADF; + return -1; + } + if(file->eof || file->bufpos==0) + { + errno = 0; + return 0; + } + + file->out.pos=file->pos; + file->out.len=file->bufpos; + if(fsp_transaction(file->s,&file->out,&file->in)) + { + file->err=1; + return -1; + } + if(file->in.cmd != FSP_CC_UP_LOAD) + { + errno = EIO; + file->err=1; + return -1; + } + file->bufpos=0; + file->pos+=file->out.len; + + errno = 0; + return 0; +} + + + +int fsp_fclose(FSP_FILE *file) +{ + int rc; + + rc=0; + errno = 0; + if(file->writing) + { + if(fsp_fflush(file)) + { + rc=-1; + } + else if(fsp_install(file->s,file->name,0)) + { + rc=-1; + } + } + free(file->name); + free(file); + return rc; +} + +int fsp_fseek(FSP_FILE *stream, long offset, int whence) +{ + long newoffset; + + switch(whence) + { + case SEEK_SET: + newoffset = offset; + break; + case SEEK_CUR: + newoffset = stream->pos + offset; + break; + case SEEK_END: + errno = ENOTSUP; + return -1; + default: + errno = EINVAL; + return -1; + } + if(stream->writing) + { + if(fsp_fflush(stream)) + { + return -1; + } + } + stream->pos=newoffset; + stream->eof=0; + fsp_fpurge(stream); + return 0; +} + +long fsp_ftell(FSP_FILE *f) +{ + return f->pos + f->bufpos; +} + +void fsp_rewind(FSP_FILE *f) +{ + if(f->writing) + fsp_fflush(f); + f->pos=0; + f->err=0; + f->eof=0; + fsp_fpurge(f); +} + +/* **************** Utility functions ****************** */ + +/* return 0 if user has enough privs for uploading the file */ +int fsp_canupload(FSP_SESSION *s,const char *fname) +{ + char *dir; + unsigned char dirpro; + int rc; + struct stat sb; + + dir=directoryfromfilename(fname); + if(dir == NULL) + { + errno = ENOMEM; + return -1; + } + + rc=fsp_getpro(s,dir,&dirpro); + free(dir); + + if(rc) + { + return -1; + } + + if(dirpro & FSP_DIR_OWNER) + return 0; + + if( ! (dirpro & FSP_DIR_ADD)) + return -1; + + if (dirpro & FSP_DIR_DEL) + return 0; + + /* we need to check file existence, because we can not overwrite files */ + + rc = fsp_stat(s,fname,&sb); + + if (rc == 0) + return -1; + else + return 0; +} + +/* install file opened for writing */ +int fsp_install(FSP_SESSION *s,const char *fname,time_t timestamp) +{ + int rc; + FSP_PKT in,out; + + /* and install a new file */ + out.cmd=FSP_CC_INSTALL; + out.xlen=0; + out.pos=0; + rc=0; + if( buildfilename(s,&out,fname) ) + rc=-1; + else + { + if (timestamp != 0) + { + /* add timestamp extra data */ + *(uint32_t *)(out.buf+out.len)=htonl(timestamp); + out.xlen=4; + out.pos=4; + } + if(fsp_transaction(s,&out,&in)) + { + rc=-1; + } else + { + if(in.cmd != FSP_CC_INSTALL) + { + rc=-1; + errno = EPERM; + } + } + } + + return rc; +} +/* Get protection byte from the directory */ +int fsp_getpro(FSP_SESSION *s,const char *directory,unsigned char *result) +{ + FSP_PKT in,out; + + if(buildfilename(s,&out,directory)) + return -1; + out.cmd=FSP_CC_GET_PRO; + out.xlen=0; + out.pos=0; + + if(fsp_transaction(s,&out,&in)) + return -1; + + if(in.cmd != FSP_CC_GET_PRO || in.pos != FSP_PRO_BYTES) + { + errno = ENOMSG; + return -1; + } + + if(result) + *result=in.buf[in.len]; + errno = 0; + return 0; +} + +int fsp_stat(FSP_SESSION *s,const char *path,struct stat *sb) +{ + FSP_PKT in,out; + unsigned char ftype; + + if(buildfilename(s,&out,path)) + return -1; + out.cmd=FSP_CC_STAT; + out.xlen=0; + out.pos=0; + + if(fsp_transaction(s,&out,&in)) + return -1; + + if(in.cmd != FSP_CC_STAT) + { + errno = ENOTSUP; + return -1; + } + /* parse results */ + ftype=in.buf[8]; + if(ftype == 0) + { + errno = ENOENT; + return -1; + } + sb->st_uid=sb->st_gid=0; + sb->st_mtime=sb->st_ctime=sb->st_atime=ntohl( *(const uint32_t *)( in.buf )); + sb->st_size=ntohl( *(const uint32_t *)(in.buf + 4 )); + sb->st_blocks=(sb->st_size+511)/512; + if (ftype==FSP_RDTYPE_DIR) + { + sb->st_mode=S_IFDIR | 0755; + sb->st_nlink=2; + } + else + { + sb->st_mode=S_IFREG | 0644; + sb->st_nlink=1; + } + + errno = 0; + return 0; +} + +int fsp_mkdir(FSP_SESSION *s,const char *directory) +{ + return simplecommand(s,directory,FSP_CC_MAKE_DIR); +} + +int fsp_rmdir(FSP_SESSION *s,const char *directory) +{ + return simplecommand(s,directory,FSP_CC_DEL_DIR); +} + +int fsp_unlink(FSP_SESSION *s,const char *directory) +{ + return simplecommand(s,directory,FSP_CC_DEL_FILE); +} + +int fsp_rename(FSP_SESSION *s,const char *from, const char *to) +{ + FSP_PKT in,out; + int l; + + if(buildfilename(s,&out,from)) + return -1; + /* append target name */ + l=strlen(to)+1; + if( l + out.len > FSP_SPACE ) + { + errno = ENAMETOOLONG; + return -1; + } + memcpy(out.buf+out.len,to,l); + out.xlen = l; + + if(s->password) + { + l=strlen(s->password)+1; + if(out.len + out.xlen + l > FSP_SPACE) + { + errno = ENAMETOOLONG; + return -1; + } + out.buf[out.len+out.xlen-1] = '\n'; + memcpy(out.buf+out.len+out.xlen,s->password,l); + out.xlen += l; + } + + out.cmd=FSP_CC_RENAME; + out.pos=out.xlen; + + if(fsp_transaction(s,&out,&in)) + return -1; + + if(in.cmd != FSP_CC_RENAME) + { + errno = EPERM; + return -1; + } + + errno = 0; + return 0; +} + +int fsp_access(FSP_SESSION *s,const char *path, int mode) +{ + struct stat sb; + int rc; + unsigned char dirpro; + char *dir; + + rc=fsp_stat(s,path,&sb); + if(rc == -1) + { + /* not found */ + /* errno is set by fsp_stat */ + return -1; + } + + /* just test file existence */ + if(mode == F_OK) + { + errno = 0; + return 0; + } + + /* deny execute access to file */ + if (mode & X_OK) + { + if(S_ISREG(sb.st_mode)) + { + errno = EACCES; + return -1; + } + } + + /* Need to get ACL of directory */ + if(S_ISDIR(sb.st_mode)) + dir=NULL; + else + dir=directoryfromfilename(path); + + rc=fsp_getpro(s,dir==NULL?path:dir,&dirpro); + /* get pro failure */ + if(rc) + { + if(dir) free(dir); + errno = EACCES; + return -1; + } + /* owner shortcut */ + if(dirpro & FSP_DIR_OWNER) + { + if(dir) free(dir); + errno = 0; + return 0; + } + /* check read access */ + if(mode & R_OK) + { + if(dir) + { + if(! (dirpro & FSP_DIR_GET)) + { + free(dir); + errno = EACCES; + return -1; + } + } else + { + if(! (dirpro & FSP_DIR_LIST)) + { + errno = EACCES; + return -1; + } + } + } + /* check write access */ + if(mode & W_OK) + { + if(dir) + { + if( !(dirpro & FSP_DIR_DEL) || !(dirpro & FSP_DIR_ADD)) + { + free(dir); + errno = EACCES; + return -1; + } + } else + { + /* when checking directory for write access we are cheating + by allowing ADD or DEL right */ + if( !(dirpro & FSP_DIR_DEL) && !(dirpro & FSP_DIR_ADD)) + { + errno = EACCES; + return -1; + } + } + } + + if(dir) free(dir); + errno = 0; + return 0; +}