source: TI05-delivery/trunk/src/bbftp-server-3.2.0/bbftpd/storeafile.c @ 1328

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

I've created a bbftpd log facility to replace syslog.
At present it allows you to log messages to a file in addition
to syslog. This should help testing on other machines.

Line 
1/*
2 * bbftpd/storeafile.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         255    MES_BAD_NO_RETRY has to be sent to the client.
32        = 0    Success
33        > 0    MES_BAD has to be sent to the client
34       
35        In fact only the first child ending in error will be taken in account.
36        The absolute value of the exit code will be an errno code and the
37        strerror(errno) will be sent as an explanation message.
38 
39 storeafile.c v 1.6.0  2000/03/25   - Creation of the routine. This part of
40                                      code was contained in readcontrol.c
41              v 1.6.1  2000/03/28   - Portage to OSF1
42                                    - use data socket in non bloking mode
43              v 1.8.0  2000/04/14   - Change location of headers file for zlib
44              v 1.8.3  2000/04/18   - Implement better return code to client
45              v 1.8.7  2000/05/24   - Modify headers
46              v 1.8.8  2000/05/31   - Check if file to store is not already a directory
47                                    - Take care of zero length file
48              v 1.8.10 2000/08/11   - Portage to Linux
49                                    - Better start child (wait only if HUP was
50                                      not sent)
51              v 1.9.0  2000/08/18   - Use configure to help portage
52              v 1.9.4  2000/10/16   - Close correctly data socket in child
53                                      in order not to keep socket in TIME_WAIT
54                                      on the server
55              v 2.0.0  2000/10/18   - Set state before starting children
56              v 2.0.1  2001/04/23   - Correct indentation
57              v 2.0.2  2001/05/04   - Correct return code treatment
58              v 2.1.0  2001/05/30   - Correct bbftpd_log level
59                                     
60 *****************************************************************************/
61#include <errno.h>
62#include <fcntl.h>
63#include <limits.h>
64#include <netinet/in.h>
65#include <signal.h>
66#include <stdio.h>
67#include <bbftpd_log.h>
68#include <sys/stat.h>
69#include <sys/time.h>
70#include <sys/types.h>
71#include <unistd.h>
72
73#include <bbftpd.h>
74#include <common.h>
75#include <daemon.h>
76#include <status.h>
77#include <structures.h>
78
79#ifdef WITH_GZIP
80#include <zlib.h>
81#endif
82
83extern int flagsighup ;
84extern int msgsock ;
85extern char currentfilename[MAXLENFILE];
86extern int childendinerror ;
87extern int pid_child[MAXPORT] ;
88extern int state ;
89extern int unlinkfile ;
90extern  int     recvcontrolto ;
91extern  int     sendcontrolto ;
92extern  int     datato ;
93
94#ifndef WORDS_BIGENDIAN
95#ifndef HAVE_NTOHLL
96my64_t    ntohll(my64_t v) ;
97#endif
98#endif
99
100int storeafile(int code) {
101
102    int        retcode ;
103    int        i ;
104    char    receive_buffer[MAXMESSLEN] ;
105    struct message *msg ;
106    struct mess_store *msg_store ;
107    struct mess_compress *msg_compress ;
108    int        compressionon ;
109    int        savederrno ;
110
111    char    data_buffer[READBUFLEN] ;
112    char    comp_buffer[READBUFLEN] ;
113   
114    char    logmessage[256] ;
115       
116    int     fd ;
117
118    int    nfds ; /* Max number of file descriptor */
119    fd_set    selectmask ; /* Select mask */
120    struct timeval    wait_timer;
121
122    my64_t    toprint64 ;
123   
124#ifdef STANDART_FILE_CALL
125    off_t         nbperchild ;
126    off_t         nbtoget;
127    off_t         nbget ;
128    off_t         startpoint ;
129    off_t        toseek ;
130    struct stat statbuf ;
131#else
132    off64_t     nbperchild ;
133    off64_t     nbtoget;
134    off64_t     nbget ;
135    off64_t        startpoint ;
136    off64_t        toseek ;
137    struct stat64 statbuf ;
138#endif
139    int lentowrite;
140    int lenwrited;
141    int datatoreceive;
142    int dataonone;
143    int    recsock ;
144
145#ifdef WITH_GZIP   
146    uLong    buflen ;
147    uLong    bufcomplen ;
148#endif
149
150    /*
151    ** Initilize the pid array
152    */
153     for ( i=0 ; i< MAXPORT ; i++) {
154        pid_child[i] = 0 ;
155    }
156    childendinerror = 0 ; /* No child so no error */
157    /*
158    ** Read the characteristics of the file
159    */
160    if ( (retcode = readmessage(msgsock,receive_buffer,STORMESSLEN,recvcontrolto)) < 0 ) {
161        /*
162        ** Error ...
163        */
164        return -1 ;
165    }
166    msg_store = (struct mess_store *)receive_buffer ;
167    strcpy(currentfilename,msg_store->filename) ;
168#ifndef WORDS_BIGENDIAN
169    msg_store->nbport = ntohl(msg_store->nbport) ;
170    for (i = 0 ; i< MAXPORT ; i++ ) {
171        msg_store->port[i] = ntohl(msg_store->port[i]) ;
172    }
173    msg_store->filesize = ntohll(msg_store->filesize) ;
174#endif
175    if ( code == MSG_STORE ) {
176        bbftpd_log(BBFTPD_DEBUG,"Storing file %s of size %" LONG_LONG_FORMAT " with %d children",currentfilename,msg_store->filesize,msg_store->nbport) ;
177    } else {
178        bbftpd_log(BBFTPD_DEBUG,"Storing file %s of size %" LONG_LONG_FORMAT " with %d children in compressed mode",currentfilename,msg_store->filesize,msg_store->nbport) ;
179    }
180    /*
181    ** First stat the file in order to know if it is a directory
182    */
183#ifdef STANDART_FILE_CALL
184    if ( stat(currentfilename,&statbuf) < 0 ) {
185#else
186    if ( stat64(currentfilename,&statbuf) < 0 ) {
187#endif
188        /*
189        ** It may be normal to get an error if the file
190        ** does not exist but some error code must lead
191        ** to the interruption of the transfer:
192        **        EACCES        : Search permission denied
193        **        ELOOP        : To many symbolic links on path
194        **        ENAMETOOLONG: Path argument too long
195        **        ENOTDIR        : A component in path is not a directory
196        */
197        savederrno = errno ;
198        if ( savederrno == EACCES ||
199            savederrno == ELOOP ||
200            savederrno == ENAMETOOLONG ||
201            savederrno == ENOTDIR ) {
202            sprintf(logmessage,"Error stating file %s : %s ",currentfilename,strerror(savederrno)) ;
203            bbftpd_log(BBFTPD_ERR,"Error stating file %s : %s ",currentfilename,strerror(savederrno)) ;
204            reply(MSG_BAD_NO_RETRY,logmessage) ;
205            return 0 ;
206        } else if (savederrno == ENOENT) {
207            /*
208            ** That is normal the file does not exist
209            */
210        } else {
211            sprintf(logmessage,"Error stating file %s : %s ",currentfilename,strerror(savederrno)) ;
212            bbftpd_log(BBFTPD_ERR,"Error stating file %s : %s ",currentfilename,strerror(savederrno)) ;
213            reply(MSG_BAD,logmessage) ;
214            return 0 ;
215        }
216    } else {
217        /*
218        ** The file exists so check if it is a directory
219        */
220        if ( (statbuf.st_mode & S_IFDIR) == S_IFDIR) {
221            bbftpd_log(BBFTPD_ERR,"file %s is a directory",currentfilename) ;
222            sprintf(logmessage,"File %s is a directory",currentfilename) ;
223            reply(MSG_BAD_NO_RETRY,logmessage) ;
224            return 0 ;
225        }
226    }               
227    /*
228    ** We create the file
229    */
230#ifdef STANDART_FILE_CALL
231    if ((fd = open(currentfilename,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)) < 0 ) {
232#else
233    if ((fd = open64(currentfilename,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)) < 0 ) {
234#endif
235        /*
236        ** Depending on errno we are going to tell the client to
237        ** retry or not
238        */
239        savederrno = errno ;
240        sprintf(logmessage,"Error creation file %s : %s ",currentfilename,strerror(errno)) ;
241        bbftpd_log(BBFTPD_ERR,"Error creation file %s : %s ",currentfilename,strerror(errno)) ;
242        /*
243        ** We tell the client not to retry in the following case (even in waiting
244        ** WAITRETRYTIME the problem will not be solved) :
245        **        EACCES        : Search permission denied
246        **        EDQUOT        : No more quota
247        **        ENOSPC        : No more space
248        **        ELOOP        : To many symbolic links on path
249        **        ENAMETOOLONG: Path argument too long
250        **        ENOTDIR        : A component in path is not a directory
251        */
252        if ( savederrno == EACCES ||
253                savederrno == EDQUOT ||
254                savederrno == ENOSPC ||
255                savederrno == ELOOP ||
256                savederrno == ENAMETOOLONG ||
257                savederrno == ENOTDIR ) {
258            reply(MSG_BAD_NO_RETRY,logmessage) ;
259        } else {
260            reply(MSG_BAD,logmessage) ;
261        }
262        return 0 ;
263    }
264    /*
265    ** If it is a zero length file close it and reply OK
266    */
267    if ( msg_store->filesize == 0 ) {
268        close(fd) ;
269        reply(MSG_OK,"OK") ;
270        return 0 ;
271    }
272    unlinkfile = 1 ;
273    /*
274    ** Lseek to set it to the correct size
275    */
276    toseek = msg_store->filesize-1 ;
277#ifdef STANDART_FILE_CALL
278    if ( lseek(fd,toseek,SEEK_SET) < 0 ) {
279#else
280    if ( lseek64(fd,toseek,SEEK_SET) < 0 ) {
281#endif
282        sprintf(logmessage,"Error seeking file %s : %s ",currentfilename,strerror(errno)) ;
283        close(fd) ;
284        unlink(currentfilename) ;
285        bbftpd_log(BBFTPD_ERR,"Error seeking file %s : %s ",currentfilename,strerror(errno)) ;
286        reply(MSG_BAD,logmessage) ;
287        return 0 ;
288    }
289    /*
290    ** Write one byte
291    */
292    if ( write(fd,"\0",1) != 1) {
293        savederrno = errno ;
294        sprintf(logmessage,"Error writing file %s : %s ",currentfilename,strerror(errno)) ;
295        close(fd) ;
296        unlink(currentfilename) ;
297        bbftpd_log(BBFTPD_ERR,"Error writing file %s : %s ",currentfilename,strerror(errno)) ;
298        /*
299        ** We tell the client not to retry in the following case (even in waiting
300        ** WAITRETRYTIME the problem will not be solved) :
301        **        EDQUOT        : No more quota
302        **        ENOSPC        : No space on device
303        */
304        if ( savederrno == EDQUOT ||
305                savederrno == ENOSPC ) {
306            reply(MSG_BAD_NO_RETRY,logmessage) ;
307        } else {
308            reply(MSG_BAD,logmessage) ;
309        }
310        return 0 ;
311    }
312    /*
313    ** And close the file
314    */
315    if ( close(fd) < 0 ) {
316        savederrno = errno ;
317        unlink(currentfilename) ;
318        bbftpd_log(BBFTPD_ERR,"Error closing file %s : %s ",currentfilename,strerror(savederrno)) ;
319        sprintf(logmessage,"Error closing file %s : %s ",currentfilename,strerror(savederrno)) ;
320        if ( savederrno == ENOSPC ) {
321            reply(MSG_BAD_NO_RETRY,logmessage) ;
322        } else {
323            reply(MSG_BAD,logmessage) ;
324        }
325        return 0 ;
326    }
327    /*
328    ** We calculate the starting point for each child
329    */
330    nbperchild = msg_store->filesize/msg_store->nbport ;
331    for (i = 1 ; i <= msg_store->nbport ; i++) {
332        if ( i == msg_store->nbport) {
333            startpoint = (i-1)*nbperchild;
334            nbtoget = msg_store->filesize-(nbperchild*(msg_store->nbport-1)) ;
335        } else {
336            startpoint = (i-1)*nbperchild;
337            nbtoget = nbperchild ;
338        }
339               
340        /*
341        ** Now create the socket to receive
342        */
343        recsock = 0 ;
344        while (recsock == 0 ) {
345            recsock = createreceivesock(msg_store->port[i-1],i,logmessage) ;
346        }
347        if ( recsock < 0 ) {
348            unlink(currentfilename) ;
349            /*
350            ** We set childendinerror to 1 in order to prevent the father
351            ** to send a BAD message which can desynchronize the client and the
352            ** server (We need only one error message)
353            ** Bug discovered by amlutz on 2000/03/11
354            */
355            if ( childendinerror == 0 ) {
356                childendinerror = 1 ;
357                reply(MSG_BAD,logmessage) ;
358            }
359            clean_child() ;
360            return 0 ;
361        }
362        /*
363        ** Set flagsighup to zero in order to be able in child
364        ** not to wait STARTCHILDTO if signal was sent before
365        ** entering select. (Seen on Linux with one child)
366        */
367        flagsighup = 0 ;
368        /*
369        ** At this stage we are ready to receive packets
370        ** So we are going to fork
371        */
372        if ( (retcode = fork()) == 0 ) {
373            /*
374            ** We are in child
375            */
376            /*
377            ** Pause until father send a SIGHUP in order to prevent
378            ** child to die before father has started all children
379            */
380            if ( flagsighup == 0) {
381                wait_timer.tv_sec  = STARTCHILDTO ;
382                wait_timer.tv_usec = 0 ;
383                nfds = sysconf(_SC_OPEN_MAX) ;
384                select(nfds,0,0,0,&wait_timer) ;
385            }
386            bbftpd_log(BBFTPD_DEBUG,"Child Starting") ;
387            /*
388            ** Close all unnecessary stuff
389            */
390            close(msgsock) ;
391            /*
392            ** Check if file exist
393            */
394#ifdef STANDART_FILE_CALL
395            if ( stat(currentfilename,&statbuf) < 0 ) {
396#else
397            if ( stat64(currentfilename,&statbuf) < 0 ) {
398#endif
399                /*
400                ** If the file does not exist that means that another
401                ** child has detroyed it
402                */ 
403                i = errno ;
404                bbftpd_log(BBFTPD_ERR,"Error stating file %s : %s ",currentfilename,strerror(errno)) ;
405                close(recsock) ;
406                exit(i) ;
407            }
408            /*
409            ** Open and seek to position
410            */
411#ifdef STANDART_FILE_CALL
412            if ((fd = open(currentfilename,O_RDWR,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)) < 0 ) {
413#else
414            if ((fd = open64(currentfilename,O_RDWR,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)) < 0 ) {
415#endif
416                i = errno ;
417                unlink(currentfilename) ;
418                bbftpd_log(BBFTPD_ERR,"Error opening file %s : %s ",currentfilename,strerror(errno)) ;
419                /*
420                ** At this point a non recoverable error is
421                **        EDQUOT        : No more quota
422                **        ENOSPC        : No more space
423                */
424                if ( i == EDQUOT ||
425                        i == ENOSPC ) {
426                    close(recsock) ;
427                    exit(255) ;
428                } else {
429                    close(recsock) ;
430                    exit(i) ;
431                }
432            }
433#ifdef STANDART_FILE_CALL
434            if ( lseek(fd,startpoint,SEEK_SET) < 0 ) {
435#else
436            if ( lseek64(fd,startpoint,SEEK_SET) < 0 ) {
437#endif
438                i = errno ;
439                close(fd) ;
440                unlink(currentfilename) ;
441                bbftpd_log(BBFTPD_ERR,"error seeking file : %s",strerror(errno)) ;
442                close(recsock) ;
443                exit(i)  ;
444            }
445            /*
446            ** start the reading loop
447            */
448            nbget = 0 ;
449            while ( nbget < nbtoget) {
450#ifdef WITH_GZIP
451                if ( code == MSG_STORE_C ) {
452                    /*
453                    ** Receive the header first
454                    */
455                    if (readmessage(recsock,receive_buffer,COMPMESSLEN,datato) < 0 ) {
456                        bbftpd_log(BBFTPD_ERR,"Error reading compression header") ;
457                        close(fd) ;
458                        unlink(currentfilename) ;
459                        i = ETIMEDOUT ;
460                        exit(i) ;
461                    }
462                    msg_compress = ( struct mess_compress *) receive_buffer ;
463#ifndef WORDS_BIGENDIAN
464                    msg_compress->datalen = ntohl(msg_compress->datalen) ;
465#endif
466                    if ( msg_compress->code == DATA_COMPRESS) {
467                        compressionon = 1 ;
468                    } else {
469                        compressionon = 0 ;
470                    }
471                    datatoreceive = msg_compress->datalen ;
472                } else {
473                    /*
474                    ** No compression just adjust the length to receive
475                    */
476                    if ( READBUFLEN <= nbtoget-nbget ) {
477                        datatoreceive = READBUFLEN  ;
478                    } else {
479                        datatoreceive = nbtoget-nbget ;
480                    }
481                }
482#else
483                if ( READBUFLEN <= nbtoget-nbget ) {
484                    datatoreceive = READBUFLEN  ;
485                } else {
486                    datatoreceive = nbtoget-nbget ;
487                }
488#endif
489                /*
490                ** Start the data collection
491                */
492                dataonone = 0 ;
493                while ( dataonone < datatoreceive ) {
494                    nfds = sysconf(_SC_OPEN_MAX) ;
495                    FD_ZERO(&selectmask) ;
496                    FD_SET(recsock,&selectmask) ;
497                    wait_timer.tv_sec  = datato ;
498                    wait_timer.tv_usec = 0 ;
499                    if ( (retcode = select(nfds,&selectmask,0,0,&wait_timer) ) == -1 ) {
500                        /*
501                        ** Select error
502                        */
503                        i = errno ;
504                        bbftpd_log(BBFTPD_ERR,"Error select while receiving : %s",strerror(errno)) ;
505                        close(fd) ;
506                        unlink(currentfilename) ;
507                        close(recsock) ;
508                        exit(i) ;
509                    } else if ( retcode == 0 ) {
510                        bbftpd_log(BBFTPD_ERR,"Time out while receiving") ;
511                        close(fd) ;
512                        unlink(currentfilename) ;
513                        i=ETIMEDOUT ;
514                        close(recsock) ;
515                        exit(i) ;
516                    } else {
517                        retcode = recv(recsock,&data_buffer[dataonone],datatoreceive-dataonone,0) ;
518                        if ( retcode < 0 ) {
519                            i = errno ;
520                            bbftpd_log(BBFTPD_ERR,"Error while receiving : %s",strerror(errno)) ;
521                            close(fd) ;
522                            unlink(currentfilename) ;
523                            close(recsock) ;
524                            exit(i) ;
525                        } else if ( retcode == 0 ) {
526                            i = ECONNRESET ;
527                            bbftpd_log(BBFTPD_ERR,"Connexion breaks") ;
528                            close(fd) ;
529                            unlink(currentfilename) ;
530                            close(recsock) ;
531                            exit(i) ;
532                        } else {
533                            dataonone = dataonone + retcode ;
534                        }
535                    }
536                }
537                /*
538                ** We have received all data needed
539                */
540#ifdef WITH_GZIP
541                if ( code == MSG_STORE_C ) {
542                    if ( compressionon == 1 ) {
543                        bufcomplen = READBUFLEN ;
544                        buflen = dataonone ;
545                        retcode = uncompress((Bytef *) comp_buffer, &bufcomplen, (Bytef *) data_buffer, buflen) ;
546                        if ( retcode != 0 ) {
547                            i = EILSEQ ;
548                            bbftpd_log(BBFTPD_ERR,"Error while decompressing %d ",retcode) ;
549                            close(fd) ;
550                            unlink(currentfilename) ;
551                            close(recsock) ;
552                            exit(i) ;
553                        }
554                        memcpy(data_buffer,comp_buffer,READBUFLEN) ;
555                        lentowrite = bufcomplen ;
556                    } else {
557                        lentowrite = dataonone ;
558                    }
559                } else {
560                    lentowrite = dataonone ;
561                }
562#else
563                lentowrite = dataonone ;
564#endif
565                /*
566                ** Write it to the file
567                */
568                lenwrited = 0 ;
569                while ( lenwrited < lentowrite ) {
570                    if ( (retcode = write(fd,&data_buffer[lenwrited],lentowrite-lenwrited)) < 0 ) {
571                        i = errno ;
572                        bbftpd_log(BBFTPD_ERR,"error writing file : %s",strerror(errno)) ;
573                        close(fd) ;
574                        unlink(currentfilename) ;
575                        if ( i == EDQUOT ||
576                                i == ENOSPC ) {
577                            close(recsock) ;
578                            exit(255) ;
579                        } else {
580                            close(recsock) ;
581                            exit(i) ;
582                        }
583                    } 
584                    lenwrited = lenwrited + retcode ;
585                }
586                nbget = nbget+lenwrited ;
587            }
588            /*
589            ** All data have been received so send the ACK message
590            */
591            msg = (struct message *) data_buffer ;
592            msg->code = MSG_ACK ;
593            msg->msglen = 0 ;
594            if ( writemessage(recsock,data_buffer,MINMESSLEN,sendcontrolto) < 0 ) {
595                close(fd) ;
596                unlink(currentfilename) ;
597                bbftpd_log(BBFTPD_ERR,"Error sending ACK ") ;
598                close(recsock) ;
599                exit(ETIMEDOUT) ;
600            }
601            toprint64 = nbget ;
602            bbftpd_log(BBFTPD_DEBUG,"Child received %" LONG_LONG_FORMAT " bytes ; end correct ",toprint64) ;
603            close(recsock) ;
604            exit(0) ;
605        } else {
606            /*
607            ** We are in father
608            */
609            if ( retcode == -1 ) {
610                /*
611                ** Fork failed ...
612                */
613                bbftpd_log(BBFTPD_ERR,"fork failed : %s",strerror(errno)) ;
614                unlink(currentfilename) ;
615                sprintf(logmessage,"fork failed : %s ",strerror(errno)) ;
616                if ( childendinerror == 0 ) {
617                    childendinerror = 1 ;
618                    reply(MSG_BAD,logmessage) ;
619                }
620                clean_child() ;
621                return 0 ;
622            } else {
623                /*
624                ** Set the parameter telling the sig child routine to unlink
625                ** in case of error
626                */
627                bbftpd_log(BBFTPD_DEBUG,"Started child pid %d",retcode) ;
628                pid_child[i-1] = retcode ;
629                close(recsock) ;
630            }
631        }
632    }
633    /*
634    ** Set the state before starting children because if the file was
635    ** small the child has ended before state was setup to correct value
636    */
637    state = S_RECEIVING ;
638    /*
639    ** Start all children
640    */
641    for (i = 0 ; i<MAXPORT ; i++) {
642        if (pid_child[i] != 0) {
643            kill(pid_child[i],SIGHUP) ;
644        }
645    }
646    return 0 ;
647}
Note: See TracBrowser for help on using the repository browser.