source: TI05-delivery/trunk/src/bbftp-server-3.2.0/bbftpd/sendafile.c @ 1392

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/trunk/src/bbftp-server-3.2.0/bbftpd/sendafile.c@1392
Revision 1392, 21.6 KB checked in by spascoe, 13 years ago (diff)

More missing #includes exposed when compiling on easterly.

Line 
1/*
2 * bbftpd/sendafile.c
3 * Copyright (C) 1999, 2000, 2001, 2002 IN2P3, CNRS
4 * bbftp@in2p3.fr
5 * http://doc.in2p3.fr/bbftp
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */ 
21
22/****************************************************************************
23
24 
25 RETURN:
26         0  Keep the connection open (does not mean that the file has been
27           successfully transfered)
28        -1 Tell the calling program to close the connection
29
30 Child Exit code:
31        = 0    Success
32        > 0    MES_BAD has to be sent to the client
33       
34        In fact only the first child ending in error will be taken in account.
35        The absolute value of the exit code will be an errno code and the
36        strerror(errno) will be sent as an explanation message.
37        There is no 255 exit code because no error from the child may be
38        considered as fatal
39 
40 sendafile.c  v 1.6.0  2000/03/25   - Creation of the routine. This part of
41                                      code was contained in readcontrol.c
42              v 1.6.1  2000/03/28   - Portage to OSF1
43                                    - use data socket in non bloking mode
44              v 1.6.2  2000/03/31   - Correct error on timer
45              v 1.8.0  2000/04/14   - Change location of headers file for zlib
46              v 1.8.3  2000/04/18   - Implement better return code to client
47              v 1.8.7  2000/05/24   - Modify headers
48              v 1.8.8  2000/05/31   - Check if file to read is not a directory
49                                    - Take care of a zero length file
50              v 1.8.10 2000/08/11   - Portage to Linux
51                                    - Better start child (wait only if HUP was
52                                      not sent)
53              v 1.9.0  2000/08/18   - Use configure to help portage
54              v 1.9.4  2000/10/16   - Close correctly data socket in child
55                                      in order not to keep socket in TIME_WAIT
56                                      on the server
57              v 2.0.0  2000/10/18   - Set state before starting children
58              v 2.0.1  2001/04/23   - Correct indentation
59              v 2.1.0  2001/05/30   - Correct bbftpd_log level
60                                     
61 *****************************************************************************/
62
63#include <bbftpd.h>
64#if HAVE_STRING_H
65# include <string.h>
66#endif
67#include <stdlib.h>
68
69#include <errno.h>
70#include <fcntl.h>
71#include <netinet/in.h>
72#include <stdio.h>
73#include <signal.h>
74#include <bbftpd_private_log.h>
75#include <sys/stat.h>
76#include <sys/time.h>
77#include <sys/types.h>
78#include <unistd.h>
79
80#include <bbftpd.h>
81#include <common.h>
82#include <daemon.h>
83#include <status.h>
84#include <structures.h>
85
86#ifdef WITH_GZIP
87#include <zlib.h>
88#endif
89
90extern int flagsighup ;
91extern int msgsock ;
92extern char currentfilename[MAXLENFILE];
93extern int childendinerror ;
94extern int pid_child[MAXPORT] ;
95extern int state ;
96extern  int     recvcontrolto ;
97extern  int     datato ;
98extern  int     ackto ;
99
100#ifndef WORDS_BIGENDIAN
101#ifndef HAVE_NTOHLL
102my64_t    ntohll(my64_t v) ;
103#endif
104#endif
105
106int sendafile(int code) {
107
108    char    receive_buffer[MAXMESSLEN] ;
109    char    receive_buffer_sup[MAXMESSLEN] ;
110    char    send_buffer[MAXMESSLEN] ;
111    int        savederrno ;
112
113    char    readbuffer[READBUFLEN] ;
114    char    buffercomp[READBUFLEN] ;
115    struct    mess_compress *msg_compress ;
116#ifdef WITH_GZIP
117    uLong    buflen ;
118    uLong    bufcomplen ;
119#endif
120    my64_t    filelen64 ;
121    my64_t    toprint64 ;
122   
123#ifdef STANDART_FILE_CALL
124    off_t         nbperchild ;
125    off_t         nbtosend ;
126    off_t         nbread ;
127    off_t         nbsent ;
128    off_t         numberread ;
129    off_t         startpoint ;
130    off_t        realnbtosend ;
131    off_t        filelen ;
132    struct stat statbuf ;
133#else
134    off64_t     nbperchild ;
135    off64_t     nbtosend ;
136    off64_t     nbread ;
137    off64_t     nbsent ;
138    off64_t     numberread ;
139    off64_t     startpoint ;
140    off64_t        realnbtosend ;
141    off64_t        filelen ;
142    struct stat64 statbuf ;
143#endif
144
145    int        lentosend ;
146
147    char    readfilename[MAXLENFILE] ;
148   
149    int        retcode ;
150    int        fd ;
151    int        i ;
152    int        sendsock ;
153    char    logmessage[256] ;
154    int        compressiontype ;
155   
156    struct message *msg ;
157    struct mess_store *msg_store ;
158    struct mess_retr_ok *msg_retr_ok ;
159   
160    int        nfds ;
161    fd_set    selectmask ; /* Select mask */
162    struct timeval    wait_timer;
163   
164   
165    /*
166    ** Initilize the pid array
167    */
168     for ( i=0 ; i< MAXPORT ; i++) {
169        pid_child[i] = 0 ;
170    }
171    childendinerror = 0 ; /* No child so no error */
172
173    strcpy(currentfilename,"") ;
174    if ( (retcode = readmessage(msgsock,receive_buffer,STORMESSLEN,recvcontrolto) ) < 0 ) {
175        bbftpd_log(BBFTPD_ERR,"Error receiving file char") ;
176        return retcode ;
177    }
178    msg_store = (struct mess_store *) receive_buffer ;
179    strcpy(readfilename,msg_store->filename) ;
180#ifndef WORDS_BIGENDIAN
181    msg_store->nbport = ntohl(msg_store->nbport) ;
182    for (i = 0 ; i< MAXPORT ; i++ ) {
183        msg_store->port[i] = ntohl(msg_store->port[i]) ;
184    }
185#endif
186    if ( code == MSG_RETR ) {
187        compressiontype = NOCOMPRESSION ;
188        bbftpd_log(BBFTPD_DEBUG,"Retreiving file %s with %d children",readfilename,msg_store->nbport) ;
189    } else {
190        compressiontype = COMPRESSION ;
191        bbftpd_log(BBFTPD_DEBUG,"Retreiving file %s with %d children in compressed mode",readfilename,msg_store->nbport) ;
192    }
193    /*
194    ** WARNING ----------
195    **
196    ** Do not use the receive buffer in father before having forked
197    ** because all data related to the ports are in it
198    **
199    ** WARNING ----------
200    */
201    /*
202    ** First stat the file in order to know if it is a directory
203    */
204#ifdef STANDART_FILE_CALL
205    if ( stat(readfilename,&statbuf) < 0 ) {
206#else
207    if ( stat64(readfilename,&statbuf) < 0 ) {
208#endif
209        /*
210        ** We tell the client not to retry in the following case (even in waiting
211        ** WAITRETRYTIME the problem will not be solved) :
212        **        EACCES        : Search permission denied
213        **        ELOOP        : To many symbolic links on path
214        **        ENAMETOOLONG: Path argument too long
215        **        ENOENT        : The file does not exists
216        **        ENOTDIR        : A component in path is not a directory
217        */
218        savederrno = errno ;
219        sprintf(logmessage,"Error stating file %s : %s ",readfilename,strerror(savederrno)) ;
220        bbftpd_log(BBFTPD_ERR,"Error stating file %s : %s ",readfilename,strerror(savederrno)) ;
221        if ( savederrno == EACCES ||
222            savederrno == ELOOP ||
223            savederrno == ENAMETOOLONG ||
224            savederrno == ENOENT ||
225            savederrno == ENOTDIR ) {
226            reply(MSG_BAD_NO_RETRY,logmessage) ;
227            return 0 ;
228        } else {
229            reply(MSG_BAD,logmessage) ;
230            return 0 ;
231        }
232    } else {
233        /*
234        ** The file exists so check if it is a directory
235        */
236        if ( (statbuf.st_mode & S_IFDIR) == S_IFDIR) {
237            bbftpd_log(BBFTPD_ERR,"file %s is a directory",readfilename) ;
238            sprintf(logmessage,"File %s is a directory",readfilename) ;
239            reply(MSG_BAD_NO_RETRY,logmessage) ;
240            return 0 ;
241        }
242    }               
243    /*
244    ** Getting filesize in order to send it to the client
245    */
246#ifdef STANDART_FILE_CALL
247    if ( (fd = open(readfilename,O_RDONLY)) < 0 ) {
248#else
249    if ( (fd = open64(readfilename,O_RDONLY)) < 0 ) {
250#endif
251        /*
252        ** An error on openning the local file is considered
253        ** as fatal. Maybe this need to be improved depending
254        ** on errno
255        */
256        savederrno = errno ;
257        bbftpd_log(BBFTPD_ERR,"Error opening local file %s : %s",readfilename,strerror(errno)) ;
258        sprintf(logmessage,"Error opening local file %s : %s ",readfilename,strerror(errno)) ;
259        /*
260        ** We tell the client not to retry in the following case (even in waiting
261        ** WAITRETRYTIME the problem will not be solved) :
262        **        EACCES        : Search permission denied
263        **        ELOOP        : To many symbolic links on path
264        **        ENOENT        : No such file or directory
265        **        ENAMETOOLONG: Path argument too long
266        **        ENOTDIR        : A component in path is not a directory
267        */
268        if ( savederrno == EACCES ||
269                savederrno == ELOOP ||
270                savederrno == ENOENT ||
271                savederrno == ENAMETOOLONG ||
272                savederrno == ENOTDIR ) {
273            reply(MSG_BAD_NO_RETRY,logmessage) ;
274        } else {
275            reply(MSG_BAD,logmessage) ;
276        }
277        return 0 ;
278    }
279#ifdef STANDART_FILE_CALL
280    if ( (filelen = lseek(fd,0,SEEK_END) ) < 0 ) {
281#else
282    if ( (filelen = lseek64(fd,0,SEEK_END) ) < 0 ) {
283#endif
284        /*
285        ** An error on seekin the local file is considered
286        ** as fatal. lseek error  in this case is completly
287        ** abnormal
288        */
289        close(fd) ;
290        bbftpd_log(BBFTPD_ERR,"Error seeking local file %s : %s",readfilename,strerror(errno)) ;
291        sprintf(logmessage,"Error seeking local file %s : %s ",readfilename,strerror(errno)) ;
292        reply(MSG_BAD,logmessage) ;
293        return 0 ;
294    }
295    filelen64 = filelen ;
296    /*
297    ** Close the file as the only interresting thing was the length
298    */
299    close(fd) ;
300    /*
301    ** We are going to send the file length
302    */
303    msg = (struct message *) send_buffer ;
304    msg->code = MSG_RETR_OK ;
305#ifndef WORDS_BIGENDIAN
306    msg->msglen = ntohl(RETROKMESSLEN) ;
307#else
308    msg->msglen = RETROKMESSLEN ;
309#endif
310    if ( writemessage(msgsock,send_buffer,MINMESSLEN,recvcontrolto) < 0 ) {
311        /*
312        ** Something wrong in sending message
313        ** tell calling to close the connection
314        */
315        bbftpd_log(BBFTPD_ERR,"Error sending RETROK part 1") ;
316        return -1 ;
317    }
318    msg_retr_ok = (struct mess_retr_ok *) send_buffer ;
319#ifndef WORDS_BIGENDIAN
320    msg_retr_ok->filesize = ntohll(filelen64) ;
321#else
322    msg_retr_ok->filesize = filelen64 ;
323#endif
324    if ( writemessage(msgsock,send_buffer,RETROKMESSLEN,recvcontrolto) < 0 ) {
325        /*
326        ** Something wrong in sending message
327        ** tell calling to close the connection
328        */
329        bbftpd_log(BBFTPD_ERR,"Error sending RETROK part 2") ;
330        return -1 ;
331    }
332    /*
333    ** Wait for the START message
334    */
335    if ( readmessage(msgsock,receive_buffer_sup,MINMESSLEN,RETRSTARTTO) < 0 ) {
336        /*
337        ** Something wrong in receiving message
338        ** tell calling to close the connection
339        */
340        bbftpd_log(BBFTPD_ERR,"Error receiving RETRSTART") ;
341        return -1 ;
342    }
343    msg = (struct message *) receive_buffer_sup ;
344    if ( msg->code == MSG_ABR) {
345        /*
346        ** In this case the client will not close the connection
347        ** so do the same
348        */
349        bbftpd_log(BBFTPD_ERR,"Receive ABORT message") ;
350        return 0 ;
351    } else if ( msg->code == MSG_CREATE_ZERO ) {
352        bbftpd_log(BBFTPD_INFO,"Send zero length file") ;
353        return 0 ;
354    } else if ( msg->code != MSG_RETR_START ) {
355        bbftpd_log(BBFTPD_ERR,"Receive Unknown message code while waiting RETRSTART %d",msg->code) ;
356        return -1 ;
357    }
358    /*
359    ** So we receive the start message ...
360    */
361    /*
362    ** Now start all our children
363    */
364    nbperchild = filelen/msg_store->nbport ;
365    for (i = 1 ; i <= msg_store->nbport ; i++) {
366        if ( i == msg_store->nbport) {
367            startpoint = (i-1)*nbperchild;
368            nbtosend = filelen-(nbperchild*(msg_store->nbport-1)) ;
369        } else {
370            startpoint = (i-1)*nbperchild;
371            nbtosend = nbperchild ;
372        }
373        /*
374        ** Now create the socket to send
375        */
376        sendsock = 0 ;
377        while (sendsock == 0 ) {
378            sendsock = createreceivesock(msg_store->port[i-1],i,logmessage) ;
379        }
380        if ( sendsock < 0 ) {
381            /*
382            ** We set childendinerror to 1 in order to prevent the father
383            ** to send a BAD message which can desynchronize the client and the
384            ** server (We need only one error message)
385            ** Bug discovered by amlutz on 2000/03/11
386            */
387            if ( childendinerror == 0 ) {
388                childendinerror = 1 ;
389                reply(MSG_BAD,logmessage) ;
390            }
391            clean_child() ;
392            return 0 ;
393        }
394        /*
395        ** Set flagsighup to zero in order to be able in child
396        ** not to wait STARTCHILDTO if signal was sent before
397        ** entering select. (Seen on Linux with one child)
398        */
399        flagsighup = 0 ;
400        /*
401        ** At this stage we are ready to receive packets
402        ** So we are going to fork
403        */
404        if ( (retcode = fork()) == 0 ) {
405            /*
406            ** We are in child
407            */
408            /*
409            ** Pause until father send a SIGHUP in order to prevent
410            ** child to die before father has started all children
411            */
412            if ( flagsighup == 0) {
413                nfds = sysconf(_SC_OPEN_MAX) ;
414                wait_timer.tv_sec  = STARTCHILDTO ;
415                wait_timer.tv_usec = 0 ;
416                select(nfds,0,0,0,&wait_timer) ;
417            }
418            bbftpd_log(BBFTPD_DEBUG,"Child Starting") ;
419            /*
420            ** Close all unnecessary stuff
421            */
422            close(msgsock) ;
423            /*
424            ** And open the file
425            */
426#ifdef STANDART_FILE_CALL
427            if ( (fd = open(readfilename,O_RDONLY)) < 0 ) {
428#else
429            if ( (fd = open64(readfilename,O_RDONLY)) < 0 ) {
430#endif
431                /*
432                ** An error on openning the local file is considered
433                ** as fatal. Maybe this need to be improved depending
434                ** on errno
435                */
436                i = errno ;
437                bbftpd_log(BBFTPD_ERR,"Error opening local file %s : %s",readfilename,strerror(errno)) ;
438                close(sendsock) ;
439                exit(i) ;
440            }
441#ifdef STANDART_FILE_CALL
442            if ( lseek(fd,startpoint,SEEK_SET) < 0 ) {
443#else
444            if ( lseek64(fd,startpoint,SEEK_SET) < 0 ) {
445#endif
446                i = errno ;
447                close(fd) ;
448                bbftpd_log(BBFTPD_ERR,"Error seeking file : %s",strerror(errno)) ;
449                close(sendsock) ;
450                exit(i)  ;
451            }
452            /*
453            ** Start the sending loop
454            */
455            nbread = 0 ;
456            while ( nbread < nbtosend ) {
457                if ( (numberread = read ( fd, readbuffer, (sizeof(readbuffer) <= nbtosend - nbread) ? sizeof(readbuffer) : nbtosend-nbread) ) > 0 ) {
458                    nbread = nbread+numberread ;
459#ifdef WITH_GZIP
460                    if ( compressiontype == COMPRESSION ) {
461                        /*
462                        ** In case of compression we are going to use
463                        ** a temporary buffer
464                        */
465                        bufcomplen = READBUFLEN ;
466                        buflen = numberread ;
467                        msg_compress = ( struct mess_compress *) send_buffer;
468                        retcode = compress((Bytef *)buffercomp,&bufcomplen,(Bytef *)readbuffer,buflen) ;
469                        if ( retcode != 0 ) {
470                            /*
471                            ** Compress error, in this cas we are sending the
472                            ** date uncompressed
473                            */
474                            msg_compress->code = DATA_NOCOMPRESS ;
475                            lentosend = numberread ;
476#ifndef WORDS_BIGENDIAN
477                            msg_compress->datalen = ntohl(lentosend) ;
478#else
479                            msg_compress->datalen = lentosend ;
480#endif
481                            realnbtosend = numberread ;
482                        } else {
483                            msg_compress->code = DATA_COMPRESS ;
484                            lentosend = bufcomplen ;
485#ifndef WORDS_BIGENDIAN
486                            msg_compress->datalen = ntohl(lentosend) ;
487#else
488                            msg_compress->datalen = lentosend ;
489#endif
490                            realnbtosend =  bufcomplen ;
491                            memcpy(readbuffer,buffercomp,READBUFLEN) ;
492                        }
493                        /*
494                        ** Send the header
495                        */
496                        if ( writemessage(sendsock,send_buffer,COMPMESSLEN,datato) < 0 ) {
497                            i = ETIMEDOUT ;
498                            bbftpd_log(BBFTPD_ERR,"Error sending header data") ;
499                            close(sendsock) ;
500                            exit(i) ;
501                        }
502                    } else {
503                        realnbtosend = numberread ;
504                    }
505#else
506                    realnbtosend = numberread ;
507#endif
508                    /*
509                    ** Send the data
510                    */
511                    nbsent = 0 ;
512                    while ( nbsent < realnbtosend ) {
513                        lentosend = realnbtosend-nbsent ;
514                        nfds = sysconf(_SC_OPEN_MAX) ;
515                        FD_ZERO(&selectmask) ;
516                        FD_SET(sendsock,&selectmask) ;
517                        wait_timer.tv_sec  = datato  ;
518                        wait_timer.tv_usec = 0 ;
519                        if ( (retcode = select(nfds,0,&selectmask,0,&wait_timer) ) == -1 ) {
520                            /*
521                            ** Select error
522                            */
523                            i = errno ;
524                            bbftpd_log(BBFTPD_ERR,"Error select while sending : %s",strerror(errno)) ;
525                            close(fd) ;
526                            close(sendsock) ;
527                            exit(i) ;
528                        } else if ( retcode == 0 ) {
529                            bbftpd_log(BBFTPD_ERR,"Time out while sending") ;
530                            close(fd) ;
531                            i=ETIMEDOUT ;
532                            close(sendsock) ;
533                            exit(i) ;
534                        } else {
535                            retcode = send(sendsock,&readbuffer[nbsent],lentosend,0) ;
536                            if ( retcode < 0 ) {
537                                i = errno ;
538                                bbftpd_log(BBFTPD_ERR,"Error while sending %s",strerror(i)) ;
539                                close(sendsock) ;
540                                exit(i) ;
541                            } else if ( retcode == 0 ) {
542                                i = ECONNRESET ;
543                                bbftpd_log(BBFTPD_ERR,"Connexion breaks") ;
544                                close(fd) ;
545                                close(sendsock) ;
546                                exit(i) ;
547                            } else {
548                                nbsent = nbsent+retcode ;
549                            }
550                        }
551                    }
552                } else {
553                    i = errno ;
554                    bbftpd_log(BBFTPD_ERR,"Child Error reading : %s",strerror(errno)) ;
555                    close(sendsock) ;
556                    exit(i) ;
557                }
558            }
559            /*
560            ** All data has been sent so wait for the acknoledge
561            */
562            if ( readmessage(sendsock,receive_buffer,MINMESSLEN,ackto) < 0 ) {
563                bbftpd_log(BBFTPD_ERR,"Error waiting ACK") ;
564                close(sendsock) ;
565                exit(ETIMEDOUT) ;
566            }
567            msg = (struct message *) receive_buffer ;
568            if ( msg->code != MSG_ACK) {
569                bbftpd_log(BBFTPD_ERR,"Error unknown messge while waiting ACK %d",msg->code) ;
570                close(sendsock) ;
571                exit(1) ;
572            }
573            toprint64 = nbtosend ;
574            bbftpd_log(BBFTPD_DEBUG,"Child send %" LONG_LONG_FORMAT " bytes ; end correct ",toprint64) ;
575            close(sendsock) ;
576            exit(0) ;
577        } else {
578            /*
579            ** We are in father
580            */
581            if ( retcode == -1 ) {
582                /*
583                ** Fork failed ...
584                */
585                bbftpd_log(BBFTPD_ERR,"fork failed : %s",strerror(errno)) ;
586                sprintf(logmessage,"fork failed : %s ",strerror(errno)) ;
587                if ( childendinerror == 0 ) {
588                    childendinerror = 1 ;
589                    reply(MSG_BAD,logmessage) ;
590                }
591                clean_child() ;
592                return 0 ;
593            } else {
594                bbftpd_log(BBFTPD_DEBUG,"Started child pid %d",retcode) ;
595                pid_child[i-1] = retcode ;
596                close(sendsock) ;
597            }
598        }
599    }
600    /*
601    ** Set the state before starting children because if the file was
602    ** small the child has ended before state was setup to correct value
603    */
604    state = S_SENDING ;
605    /*
606    ** Start all children
607    */
608    for (i = 0 ; i<MAXPORT ; i++) {
609        if (pid_child[i] != 0) {
610            kill(pid_child[i],SIGHUP) ;
611        }
612    }
613    return 0 ;
614}
Note: See TracBrowser for help on using the repository browser.