1 /*********************************************************************\
2 * Copyright (c) 2003-5 by Radim Kolar (hsn@cybermail.net) *
3 * Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu) *
4 * *
5 * You may copy or modify this file in any manner you wish, provided *
6 * that this notice is always included, and that you hold the author *
7 * harmless for any loss or damage resulting from the installation or *
8 * use of this software. *
9 \*********************************************************************/
10
11 #include "tweak.h"
12 #include "server_def.h"
13 #include "s_extern.h"
14 #include <stdlib.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #ifdef HAVE_UTIME_H
19 #include <utime.h>
20 #endif
21 #include "my-string.h"
22 #include "fifocache.h"
23
24 /****************************************************************************
25 * This is a modified server_file that uses cache for directory listings and
26 * directory status information. *
27 *****************************************************************************/
28
29 static struct FifoCache *dirlistcache;
30 static struct FifoCache *dirstatcache;
31 /* open file handles cache */
32 static struct FifoCache *fpcache;
33
search_fpcache(unsigned long inet_num,unsigned short port_num,const char * fname)34 static FPCACHE *search_fpcache (unsigned long inet_num,
35 unsigned short port_num,
36 const char * fname)
37 {
38 unsigned int i;
39 FPCACHE *entry;
40 const char **key;
41
42 for (i=0;i<fp_cache_limit;i++)
43 {
44 /* search for file in cache */
45 entry=(FPCACHE *)(fpcache->e_head+i*sizeof(FPCACHE));
46 if (port_num==entry->port_num && inet_num==entry->inet_num)
47 {
48 key=f_cache_get_key(fpcache,entry);
49 if(!strcmp(fname,*key))
50 {
51 /* file found in cache, return cache-pointer */
52 fpcache->hit++;
53 return entry;
54 }
55 }
56 }
57 fpcache->miss++;
58 return((FPCACHE *)0);
59 }
60
fpcache_free_entry(void * entry)61 static void fpcache_free_entry (void *entry)
62 {
63 FPCACHE *f=entry;
64
65 if(f->fp)
66 fclose(f->fp);
67 }
68
fpcache_entry_profiler(const void * entry)69 static unsigned int fpcache_entry_profiler (const void *entry)
70 {
71 const FPCACHE *f=entry;
72
73 if(f->fp)
74 #ifdef __DragonFly__
75 return sizeof(struct __FILE_public);
76 #else
77 return sizeof(FILE);
78 #endif
79 else
80 return 0;
81 }
82
83
dirlistcache_free_entry(void * entry)84 static void dirlistcache_free_entry (void * entry)
85 {
86 DIRLISTING *d=entry;
87
88 if(d->listing)
89 free(d->listing);
90 }
91
dirlistcache_entry_profiler(const void * entry)92 static unsigned int dirlistcache_entry_profiler (const void *entry)
93 {
94 const DIRLISTING *d=entry;
95
96 return d->listing_size;
97 }
98
dirstatcache_free_entry(void * entry)99 static void dirstatcache_free_entry (void * entry)
100 {
101 DIRINFO *d=entry;
102
103 if(d->realname)
104 free(d->realname);
105 if(d->owner_password)
106 free(d->owner_password);
107 if(d->public_password)
108 free(d->public_password);
109 if(d->owner)
110 free_ip_table(d->owner);
111 if(d->readme)
112 free(d->readme);
113 }
114
dirstatcache_entry_profiler(const void * entry)115 static unsigned int dirstatcache_entry_profiler (const void *entry)
116 {
117 /* TODO profiling for owner ip_table */
118 const DIRINFO *d=entry;
119 unsigned int res=0;
120 if(d->realname)
121 {
122 res=strlen(d->realname);
123 res++;
124 }
125 if(d->owner_password)
126 {
127 res+=strlen(d->owner_password);
128 res++;
129 }
130 if(d->public_password)
131 {
132 res+=strlen(d->public_password);
133 res++;
134 }
135 if(d->readme)
136 {
137 res+=strlen(d->readme);
138 res++;
139 }
140
141 return res;
142 }
143
string_free(void * entry)144 static void string_free (void * entry)
145 {
146 char **s=entry;
147 if(*s!=NULL)
148 free(*s);
149 }
150
string_profiler(const void * entry)151 static unsigned int string_profiler (const void *entry)
152 {
153 char * const *s=entry;
154
155 if(*s!=NULL)
156 return(strlen(*s));
157 else
158 return 0;
159 }
160
string_compare(const void * e1,const void * e2)161 static int string_compare (const void *e1,const void *e2)
162 {
163
164 char *const *s1=e1;
165 char *const *s2=e2;
166
167 /* strcmp do not likes NULLs */
168 if(*s1 && *s2)
169 {
170 return strcmp(*s1,*s2);
171 }else
172 return 1;
173 }
174
175 /* should init all types of caches in future */
init_caches(void)176 int init_caches (void)
177 {
178 dirlistcache=f_cache_new(dir_cache_limit,sizeof(DIRLISTING),dirlistcache_free_entry,sizeof(char *),string_free,string_compare);
179 dirstatcache=f_cache_new(stat_cache_limit,sizeof(DIRINFO),dirstatcache_free_entry,sizeof(char *),string_free,string_compare);
180 fpcache=f_cache_new(fp_cache_limit,sizeof(FPCACHE),fpcache_free_entry,sizeof(char *),string_free,string_compare);
181
182 if(dirlistcache && dirstatcache && fpcache)
183 {
184 f_cache_set_memory_profilers(dirlistcache,string_profiler,dirlistcache_entry_profiler);
185 f_cache_set_memory_profilers(dirstatcache,string_profiler,dirstatcache_entry_profiler);
186 f_cache_set_memory_profilers(fpcache,string_profiler,fpcache_entry_profiler);
187 return 0;
188 }
189 else
190 {
191 f_cache_destroy(dirlistcache);
192 f_cache_destroy(dirstatcache);
193 f_cache_destroy(fpcache);
194 return -1;
195 }
196 }
197
stat_caches(FILE * fp)198 void stat_caches (FILE *fp)
199 {
200 if(fp==NULL) fp=stderr;
201 fprintf(fp,"DIRLISTCACHE ");
202 f_cache_stats(dirlistcache,fp);
203 fprintf(fp,"DIRSTATCACHE ");
204 f_cache_stats(dirstatcache,fp);
205 fprintf(fp,"FPCACHE ");
206 f_cache_stats(fpcache,fp);
207 }
208
209 /* should init all types of caches in future */
shutdown_caches(void)210 void shutdown_caches (void)
211 {
212 #ifdef LAMERPACK
213 fclose(stderr);
214 #endif
215 fprintf(stderr,"DIRLISTCACHE ");
216 f_cache_stats(dirlistcache,stderr);
217 f_cache_clear(dirlistcache);
218 f_cache_destroy(dirlistcache);
219 fprintf(stderr,"DIRSTATCACHE ");
220 f_cache_stats(dirstatcache,stderr);
221 f_cache_clear(dirstatcache);
222 f_cache_destroy(dirstatcache);
223 fprintf(stderr,"FPCACHE ");
224 f_cache_stats(fpcache,stderr);
225 f_cache_clear(fpcache);
226 f_cache_destroy(fpcache);
227 }
228
229
230
231 /*****************************************************************************
232 * Validate path - check that path does not fall outside the bounds of the
233 * FSP root directory, weed out references to / and ../.. type links.
234 * Input: fullp - pointer to full filename
235 * lenfullp - length of full filename (including \0)
236 * *di - where to return DIRINFO information about directory
237 * want_directory - want to operate on directory, not a file
238 *****************************************************************************/
validate_path(char * fullp,unsigned lenfullp,PPATH * pp,DIRINFO ** di,int want_directory)239 const char *validate_path (char * fullp, unsigned lenfullp, PPATH * pp,DIRINFO **di, int want_directory)
240 {
241 char work [NBSIZE];
242 const char *err;
243 char *s;
244 struct stat sd;
245 DIRINFO newdir;
246
247 if(dbug)
248 {
249 fprintf(stderr,"Validating path = %s len=%u\n",lenfullp>0? fullp:NULL,lenfullp);
250 }
251 /* split fullpath into parts */
252 if( (err=parse_path(fullp,lenfullp,pp)) ) return err;
253 if(want_directory)
254 {
255 /* everything in path is a directory */
256 pp->d_ptr=pp->fullp;
257 pp->d_len=pp->d_len+pp->f_len+1;
258 }
259 /* extract dir from path to work */
260 memcpy(work,pp->d_ptr,pp->d_len);
261 work[pp->d_len]='\0';
262 if(dbug)
263 {
264 fprintf(stderr,"looking for directory %s in statcache.\n",work);
265 }
266 err=work;
267 *di=f_cache_find(dirstatcache,&err);
268 if(*di)
269 {
270 if(dbug) fprintf(stderr,"hit!\n");
271 /* need to recheck a cached directory? */
272 if((*di)->lastcheck+stat_cache_timeout>cur_time)
273 {
274 if(dbug) fprintf(stderr,"...and fresh\n");
275 return NULL; /* no, we have fresh cache entry */
276 }
277 else
278 if(dbug)
279 fprintf(stderr,"...but stale\n");
280 }
281 /* try to stat directory */
282 if(FSP_STAT(work,&sd))
283 return("No such directory");
284 if(!S_ISDIR(sd.st_mode))
285 return("Not a directory");
286 /* recheck directory */
287 if(*di)
288 {
289 if(use_directory_mtime && sd.st_mtime==(*di)->mtime)
290 {
291 /* rechecked ok */
292 (*di)->lastcheck=cur_time;
293 return NULL;
294 } else
295 {
296 /* throw out old directory */
297 f_cache_delete_entry(dirstatcache,*di);
298 *di=NULL;
299 }
300 }
301
302 /* build a new directory info */
303 if (chdir(work))
304 return("Can't change directory");
305 /* save fixed input directory to err */
306 s=strdup(work);
307 if(!getcwd(work,NBSIZE))
308 {
309 chdir(home_dir);
310 free(s);
311 return("Can't get current directory");
312 }
313 /* check namespace */
314 if(homedir_restricted)
315 if (strncmp(work,home_dir,strlen(home_dir))) {
316 chdir(home_dir);
317 free(s);
318 return("Not following link out of restricted area");
319 }
320 /* init. dir structure */
321 newdir.realname=strdup(work);
322 if(!newdir.realname)
323 {
324 free(s);
325 chdir(home_dir);
326 return("Memory exhausted");
327 }
328 if(use_directory_mtime)
329 newdir.mtime=sd.st_mtime;
330 else
331 newdir.mtime=cur_time;
332 newdir.lastcheck=cur_time;
333 /* load access perms */
334 load_access_rights(&newdir);
335 chdir(home_dir);
336 /* put new record into cache */
337 if(dbug) fprintf(stderr,"putting into statcache: %s\n",s);
338 *di=f_cache_put(dirstatcache,&s,&newdir);
339 return NULLP;
340 }
341
342 /* copy file : from -> to */
copy_file(const char * n1,const char * n2)343 static const char *copy_file (const char * n1, const char * n2)
344 {
345 FILE *ft,*fp;
346 size_t bytes;
347 char buf[1024];
348
349 if(!(ft = fopen(n1,"rb"))) {
350 return("Can't open temporary file");
351 }
352
353 if(!(fp = fopen(n2,"wb"))) {
354 fclose(ft);
355 return("Can't open file for output");
356
357 }
358 /* copy temporary file to actual fput file */
359 while( (bytes = fread(buf,1,sizeof(buf),ft)))
360 {
361 if ( bytes != fwrite(buf,1,bytes,fp)) {
362 break;
363 }
364 }
365
366 if ( ferror(ft) || ferror(fp) )
367 {
368 fclose(ft);
369 fclose(fp);
370 unlink(n2);
371 return ("Write error");
372 }
373
374 fclose(ft);
375 fclose(fp);
376 return NULL;
377 }
378
379 /* appends new packet to directory listing, return 0 on success */
append_dir_listing(DIRLISTING * dl,const char * buf,unsigned int size)380 static int append_dir_listing (DIRLISTING * dl,const char * buf,unsigned int size)
381 {
382 int8_t *newbuf;
383
384 /* append this buffer */
385 newbuf=realloc(dl->listing,dl->listing_size+size);
386 if(newbuf==NULL)
387 {
388 if(dl->listing) free(dl->listing);
389 dl->listing=NULL;
390 return -1;
391 }
392 memcpy(newbuf+dl->listing_size,buf,size);
393 dl->listing_size+=size;
394 dl->listing=newbuf;
395
396 return 0;
397 }
398
399 /* builds directory listing into DIRLISTING structure, in case of any
400 * error. nulls dl->listing
401 */
build_dir_listing(DIRLISTING * dl,const char * directory)402 static void build_dir_listing (DIRLISTING * dl,const char *directory)
403 {
404 int skip; /* how many bytes skip to next 4byte boundary */
405 unsigned int rem; /* remaining free space in UDP data space */
406 DIR *dir_f; /* opened directory for listing */
407 struct dirent *dp; /* record in directory */
408 struct stat sb; /* stat data of record */
409 register char *s; /* pointer to filename */
410 size_t nlen; /* filename length including zero terminator */
411 char buffer[UBUF_SPACE]; /* buffer for building dirblock packet */
412 char name[NBSIZE]; /* buffer for stat name */
413 int namelen; /* directory name length */
414 unsigned int bufpos; /* current write pos. in buffer */
415 unsigned int dirblocksize;
416
417 /* init pointers */
418 dl->listing=NULL;
419 dl->listing_size=0;
420 bufpos=0;
421
422 if(!(dir_f = opendir(directory))) {
423 fprintf(stderr,"Can't open dir during listing initialization\n");
424 return;
425 }
426 /* do not build longer directory blocks than 1024 bytes */
427 if(packetsize > UBUF_SPACE)
428 dirblocksize=UBUF_SPACE;
429 else
430 dirblocksize = packetsize;
431 memset(buffer,0,dirblocksize); /* clear memory on the stack */
432 strcpy(name,directory);
433 namelen=strlen(directory);
434 name[namelen++]='/'; /* add directory separator to name */
435
436 for(rem = dirblocksize; (dp = readdir(dir_f)); ) {
437 if (dp->d_ino == 0) continue;
438 s = dp->d_name;
439
440 /* hide dot files, but allow . or .. */
441 if((s[0]=='.') && ((s[1]!=0) && (s[1] != '.' || s[2] != 0))) continue;
442
443 strcpy(name+namelen,s);
444 if(FSP_STAT(name,&sb)) continue;
445 if(!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) continue;
446 if(sb.st_size>FOURGIGS) sb.st_size=FOURGIGS;
447
448 nlen = strlen(s)+1;
449
450 /* do we have space in buffer for entire entry? */
451 if(rem < RDHSIZE + nlen) {
452 /* fill rest of buffer with '*' */
453 memset(buffer+bufpos,RDTYPE_SKIP,rem);
454 /* append this buffer */
455 if(append_dir_listing(dl,buffer,dirblocksize))
456 {
457 closedir(dir_f);
458 return;
459 }
460 rem = dirblocksize;
461 bufpos = 0;
462 }
463
464 BB_WRITE4(buffer+bufpos,sb.st_mtime);
465 bufpos+=4;
466 BB_WRITE4(buffer+bufpos,sb.st_size );
467 bufpos+=4;
468 buffer[bufpos++]=S_ISDIR(sb.st_mode) ? RDTYPE_DIR : RDTYPE_FILE;
469 memcpy(buffer+bufpos,s,nlen);
470 bufpos+=nlen;
471 rem -= (nlen + RDHSIZE);
472
473 /* pad to 4byte boundary */
474 if((skip = (nlen + RDHSIZE) & 0x3)) {
475 skip=4-skip;
476 bufpos+=skip;
477 rem -=skip;
478 }
479 }
480 closedir(dir_f);
481
482 /* do we have space for final END entry? */
483 if(rem <RDHSIZE )
484 {
485 /* no, make a new packet */
486 memset(buffer+bufpos,RDTYPE_SKIP,rem);
487 /* append this buffer */
488 if(append_dir_listing(dl,buffer,dirblocksize))
489 return;
490
491 bufpos = 0;
492 }
493
494 /* add final entry */
495 bufpos+=8;
496 buffer[bufpos++]=RDTYPE_END;
497 /* append last buffer */
498 append_dir_listing(dl,buffer,bufpos);
499 dl->mtime=cur_time;
500 }
501
server_get_dir(DIRLISTING ** dl,const DIRINFO * di)502 const char *server_get_dir (DIRLISTING ** dl, const DIRINFO * di)
503 {
504 struct stat sf;
505 char list_p[NBSIZE];
506
507 /* get directory from memory cache */
508 if(dbug) fprintf(stderr,"finding %s in dirlistcache\n",di->realname);
509 *dl=f_cache_find(dirlistcache,&(di->realname));
510 if(*dl && (*dl)->mtime < di->mtime)
511 {
512 /* expired */
513 f_cache_delete_entry(dirlistcache,*dl);
514 *dl=NULL;
515 if(dbug) fprintf(stderr,"... directory was updated, list needs rebuild.\n");
516 }
517 if(*dl==NULL)
518 {
519 /* need to build a directory listing */
520 DIRLISTING dlnew;
521 char *key;
522 unsigned int ok;
523
524 if(dbug) fprintf(stderr," miss.\n");
525 ok=0;
526 if(use_prebuild_dirlists)
527 {
528 sprintf(list_p,"%s/"FSP_DIRLISTING,di->realname);
529 /* we have up-to-date directory listing somewhere? */
530 if(!FSP_STAT(list_p,&sf))
531 if(sf.st_mtime>=di->mtime) {
532 /* try to load it */
533 FILE *f;
534
535 dlnew.listing_size=sf.st_size;
536 dlnew.listing=malloc(dlnew.listing_size);
537 if(dlnew.listing)
538 {
539 if( (f=fopen(list_p,"rb")) )
540 {
541 if(dlnew.listing_size==fread(dlnew.listing,1,dlnew.listing_size,f))
542 ok=1;
543 fclose(f);
544 }
545 if(!ok)
546 {
547 free(dlnew.listing);
548 dlnew.listing=NULL;
549 }
550 }
551 if(ok)
552 {
553 if(dbug) fprintf(stderr,"cached content sucessfully used.\n");
554 dlnew.mtime=sf.st_mtime;
555 }
556 }
557 }
558 /* build list if we do not have it */
559 if(!ok)
560 build_dir_listing (&dlnew,di->realname);
561 if(!dlnew.listing)
562 return("Server can't list directory");
563 /* put new list into cache */
564 key=strdup(di->realname);
565 *dl=f_cache_put(dirlistcache,&key,&dlnew);
566 if(use_prebuild_dirlists && !ok)
567 {
568 /* try to write a cache directory content */
569 FILE *f;
570 ok=0;
571 f=fopen(list_p,"wb");
572 if(f)
573 {
574 ok=fwrite(dlnew.listing,1,dlnew.listing_size,f);
575 if(ok==dlnew.listing_size)
576 ok=1;
577 fclose(f);
578 }
579 if(!ok)
580 unlink(list_p);
581 else
582 (*dl)->mtime=cur_time;
583 }
584 }
585 else
586 {
587 if(dbug) fprintf(stderr," hit!\n");
588 }
589 return(NULLP);
590 }
591
592 /**********************************************************************/
593 /* assume path and ACL is validated */
server_del_file(PPATH * pp,DIRINFO * di)594 const char *server_del_file (PPATH * pp, DIRINFO * di)
595 {
596 struct stat sb;
597
598 if(FSP_STAT(pp->fullp,&sb)) return("unlink: file not accessible");
599 if(!(S_ISREG(sb.st_mode))) return("unlink: not an ordinary file");
600
601 if(unlink(pp->fullp) == -1) return("unlink: cannot unlink");
602 di->mtime=cur_time;
603 di->lastcheck=cur_time;
604
605 return(NULLP);
606 }
607
608 /**********************************************************************/
609
server_del_dir(PPATH * pp,DIRINFO * di)610 const char *server_del_dir (PPATH * pp, DIRINFO *di)
611 {
612 struct stat sb;
613 DIRINFO null;
614
615 if(FSP_STAT(pp->fullp,&sb)) return("rmdir: directory not accessible");
616 if(!(S_ISDIR(sb.st_mode))) return("rmdir: not an ordinary directory");
617
618 memset(&null,0,sizeof(DIRINFO));
619 null.protection = DIR_LIST | DIR_GET;
620
621 chdir(pp->fullp);
622 save_access_rights(&null);
623 chdir(home_dir);
624 if(rmdir(pp->fullp) != 0) {
625 chdir(pp->fullp);
626 save_access_rights(di);
627 chdir(home_dir);
628 return("rmdir: cannot unlink");
629 }
630 else
631 {
632 di->lastcheck=0;
633 }
634
635 return(NULLP);
636 }
637
638 /**********************************************************************/
639
server_make_dir(PPATH * pp,unsigned long inet_num,DIRINFO ** di)640 const char *server_make_dir (PPATH * pp, unsigned long inet_num,DIRINFO **di)
641 {
642 DIRINFO newdir;
643 char temp_p[NBSIZE];
644 char *name;
645
646 /* make directory and place ownerfile in it */
647 if(mkdir(pp->fullp,0777) != 0) return("Can't create directory");
648 memset(&newdir,0,sizeof(DIRINFO));
649 newdir.protection=(*di)->protection;
650 if((*di)->owner_password)
651 newdir.owner_password=strdup((*di)->owner_password);
652 if((*di)->public_password)
653 newdir.public_password=strdup((*di)->public_password);
654 newdir.lastcheck=cur_time;
655 newdir.mtime=cur_time;
656 /* make owner record */
657 sprintf(temp_p,"%d.%d.%d.%d O Creator\n",
658 ((unsigned char *)(&inet_num))[0],
659 ((unsigned char *)(&inet_num))[1],
660 ((unsigned char *)(&inet_num))[2],
661 ((unsigned char *)(&inet_num))[3]);
662 add_ipline(temp_p,&newdir.owner);
663 chdir(pp->fullp);
664 save_access_rights(&newdir);
665 getcwd(temp_p,NBSIZE);
666 chdir(home_dir);
667 newdir.realname=strdup(temp_p);
668 name=strdup(pp->fullp);
669 *di=f_cache_put(dirstatcache,&name,&newdir);
670 return(NULLP);
671 }
672
673 /**********************************************************************/
674
server_get_file(PPATH * pp,FILE ** fp,unsigned long inet_num,unsigned short port_num,DIRINFO * di)675 const char *server_get_file (PPATH * pp,
676 FILE ** fp,
677 unsigned long inet_num,
678 unsigned short port_num,
679 DIRINFO * di
680 )
681 {
682 struct stat sb;
683 char realfn[NBSIZE];
684 FPCACHE *cache_f;
685
686 sprintf(realfn,"%s/%s",di->realname,pp->f_ptr);
687 cache_f=search_fpcache(inet_num,port_num,realfn);
688
689 if(!cache_f) {
690 FPCACHE newfile;
691 char *key;
692 /* file not found in cache? */
693
694 if (FSP_STAT(realfn,&sb)) return("No such file");
695 if(!(S_ISREG(sb.st_mode)))
696 {
697 if(S_ISDIR(sb.st_mode))
698 return ("Is a directory");
699 else
700 return("Not a regular file");
701 }
702 /* open new file */
703 if(!(*fp = fopen(pp->fullp,"rb"))) return("Can't open file");
704
705 /* add it to the file-cache */
706 newfile.inet_num=inet_num;
707 newfile.port_num=port_num;
708 newfile.fp=*fp;
709 key=strdup(realfn);
710 f_cache_put(fpcache,&key,&newfile);
711 }
712 /* get filepoint from cache */
713 else *fp = cache_f->fp;
714
715 return(NULLP);
716 }
717
718 /**********************************************************************/
719 /* returns number of readme bytes
720 */
server_get_pro(DIRINFO * di,char * result,const char * acc)721 int server_get_pro (DIRINFO * di, char * result, const char * acc)
722 {
723 int pos=0;
724
725 if(di->readme)
726 {
727 strcpy(result,di->readme);
728 pos=strlen(result); /* add readme */
729 pos++; /* add zero terminator char */
730 }
731 /* append xtra data space area */
732 result[pos] = di->protection^DIR_GET;
733 if(acc[0]=='O')
734 result[pos] |= DIR_OWNER;
735
736 return pos;
737 }
738
739 /**********************************************************************/
740
server_set_pro(DIRINFO * di,const char * key)741 const char *server_set_pro (DIRINFO *di, const char * key)
742 {
743 unsigned char act;
744
745 switch(key[1]) {
746 case 'c':
747 act=DIR_ADD;
748 break;
749 case 'd':
750 act=DIR_DEL;
751 break;
752 case 'g':
753 act=DIR_GET;
754 break;
755 case 'm':
756 act=DIR_MKDIR;
757 break;
758 case 'l':
759 act=DIR_LIST;
760 break;
761 case 'r':
762 act=DIR_RENAME;
763 break;
764 default:
765 return("Invalid syntax. <+|-> <c|d|g|m|l|r>");
766 }
767
768 switch(key[0]) {
769 case '+':
770 di->protection|=act;
771 break;
772 case '-':
773 di->protection&=~act;
774 break;
775 default:
776 return("Invalid syntax. <+|-> <c|d|g|m|l|r>");
777 }
778
779 di->mtime=cur_time;
780 di->lastcheck=cur_time;
781
782 chdir(di->realname);
783 save_access_rights (di);
784 chdir(home_dir);
785
786 return(NULLP);
787 }
788
789 /**********************************************************************
790 * These two are used for file uploading.
791 **********************************************************************/
792
server_up_load(char * data,unsigned int len,unsigned long pos,unsigned long inet_num,unsigned short port_num)793 const char *server_up_load (char * data, unsigned int len, unsigned long pos,
794 unsigned long inet_num, unsigned short port_num)
795 {
796 FILE *fp;
797 char tname[NBSIZE];
798 FPCACHE *cache_f;
799 char *tmp;
800 struct stat sf;
801
802 sprintf(tname, "%s/.T%08lX%04X", tmp_dir,inet_num, port_num);
803
804 tmp=tname;
805 cache_f=f_cache_find(fpcache,&tmp);
806 if(! cache_f ) {
807 /* file not found in cache */
808 FPCACHE newfile;
809 if (pos) {
810 fp = fopen(tname, "r+b");
811 } else {
812 unlink(tname);
813 fp = fopen(tname,"wb");
814 }
815
816 if(!fp) return("Cannot open temporary file");
817
818 /* check for symlinks or other junk */
819 if(lstat(tname,&sf) || !S_ISREG(sf.st_mode))
820 {
821 fclose(fp);
822 remove(tname);
823 return("Temporary file is NOT a regular file");
824 }
825 /* test if we do not create hole in file which is caused that
826 client continues upload across server crash, which causes
827 some data loss due to libc stdio write caching */
828 /* server do not cleans temporary directory on startup - so
829 uploads across restart should work */
830 if(pos > sf.st_size)
831 {
832 fclose(fp);
833 unlink(tname);
834 return("You are creating hole in file. Restart upload.");
835 }
836 if(pos + len < sf.st_size)
837 {
838 fclose(fp);
839 unlink(tname);
840 if ( pos == 0 )
841 return server_up_load(data, len, pos, inet_num, port_num);
842 return("Non continuous upload detected. Restart upload please.");
843 }
844 /* seek to starting position */
845 if(fseeko(fp, pos, SEEK_SET))
846 {
847 fclose(fp);
848 unlink(tname);
849 return("Seeking in file failed");
850 }
851 /* protect temporary file */
852 chmod(tname,S_IRUSR|S_IWUSR);
853 /* add it to the file-cache */
854 newfile.inet_num=inet_num;
855 newfile.port_num=port_num;
856 newfile.fp=fp;
857 tmp=strdup(tname);
858 f_cache_put(fpcache,&tmp,&newfile);
859 } else {
860 /* get file pointer from cache */
861 fp=cache_f->fp;
862 }
863
864 /* check for uploading on non-tail of file */
865 sf.st_size= ftello(fp);
866 if(pos > sf.st_size)
867 {
868 f_cache_delete_entry(fpcache,cache_f);
869 unlink(tname);
870 return("You are creating hole in file. Restart upload.");
871 }
872 if(pos + len < sf.st_size)
873 {
874 f_cache_delete_entry(fpcache,cache_f);
875 unlink(tname);
876 if ( pos == 0 )
877 return server_up_load(data, len, pos, inet_num, port_num);
878 return("Non continuous upload detected. Restart upload please.");
879 }
880 if ( pos != sf.st_size )
881 {
882 if(fseeko(fp, pos, SEEK_SET)) {
883 f_cache_delete_entry(fpcache,cache_f);
884 return("Seeking in file failed");
885 }
886 }
887 if(len!=fwrite(data, 1, len, fp))
888 {
889 f_cache_delete_entry(fpcache,cache_f);
890 return("Writing to file failed");
891 }
892 return(NULLP);
893 }
894
server_install(PPATH * pp,unsigned long inet_num,unsigned short port_num,const char * acc,DIRINFO * di,unsigned int l2,const char * s2)895 const char *server_install (PPATH * pp, unsigned long inet_num,
896 unsigned short port_num, const char * acc, DIRINFO *di, unsigned int l2, const char *s2)
897 {
898 char tname[NBSIZE];
899 const char *tmp;
900 FPCACHE *cache_f;
901 #ifdef HAVE_UTIME_H
902 struct utimbuf ut;
903 #endif
904
905 sprintf(tname, "%s/.T%08lX%04X", tmp_dir,inet_num, port_num);
906 /* if file still in cache, then close it & remove it from cache */
907 tmp=tname;
908 cache_f=f_cache_find(fpcache,&tmp);
909 f_cache_delete_entry(fpcache,cache_f);
910
911 if (dbug)
912 fprintf(stderr,"server_install: tname: %s, pp->fullp: %s\n",tname, pp->fullp);
913 /* zero length filename */
914 if( strcmp(pp->fullp,".") == 0 )
915 {
916 if (dbug)
917 fprintf(stderr,"server_install: zero length name. aborting upload.\n");
918 unlink(tname);
919 return (NULLP);
920 }
921
922 if(fexist(pp->fullp) &&
923 ( (di->protection & DIR_DEL)==0 && acc[0] != 'O')
924 )
925 {
926 unlink(tname);
927 if(dbug)
928 fprintf(stderr,"File %s already exists, but user is not directory owner and public can't delete files.\n",pp->fullp);
929 return("no permission for replacing that file. Not an owner.");
930 }
931
932 di->lastcheck=cur_time;
933 di->mtime=cur_time;
934
935 /* delete target file, if any */
936 unlink(pp->fullp);
937
938 umask(upload_umask);
939 /* so just copy the temp file */
940 tmp=copy_file(tname,pp->fullp);
941 unlink(tname);
942 umask(system_umask);
943 #ifdef HAVE_UTIME_H
944 if(l2>=4)
945 {
946 ut.modtime=BB_READ4(s2);
947 ut.actime=cur_time;
948 utime(pp->fullp,&ut);
949 }
950 #endif
951
952 return(tmp);
953 }
954
955 /**********************************************************************/
956 /* assume path is validated */
957 /* start GRAB OPERATION! */
server_secure_file(PPATH * pp,unsigned long inet_num,unsigned short port_num,DIRINFO * di)958 const char *server_secure_file (PPATH * pp, unsigned long inet_num,
959 unsigned short port_num,DIRINFO *di)
960 {
961 struct stat sb;
962 char temp_p[NBSIZE];
963 const char *tmp;
964
965 if(FSP_STAT(pp->fullp,&sb)) return("grab: file not accessible");
966 if(!(S_ISREG(sb.st_mode))) return("grab: not an ordinary file");
967
968 sprintf(temp_p,"%s/.G%08lX%04X", tmp_dir, inet_num,port_num);
969
970 unlink(temp_p);
971 /* link emulated as a filecopy */
972 tmp=copy_file(pp->fullp,temp_p);
973 if(tmp) return tmp;
974
975 if(unlink(pp->fullp) == -1) {
976 unlink(temp_p);
977 return("grab: cannot unlink original file");
978 }
979
980 di->lastcheck=cur_time;
981 di->mtime=cur_time;
982
983 return(NULLP);
984 }
985
server_grab_file(FILE ** fp,unsigned long inet_num,unsigned short port_num)986 const char *server_grab_file (FILE ** fp,
987 unsigned long inet_num,
988 unsigned short port_num)
989 {
990 struct stat sb;
991 char temp_p[NBSIZE];
992 FPCACHE *cache_f;
993 char *key;
994
995 sprintf(temp_p,"%s/.G%08lX%04X",tmp_dir,inet_num,port_num);
996 key=temp_p;
997 cache_f=f_cache_find(fpcache,&key);
998 if(!cache_f)
999 {
1000 FPCACHE newfile;
1001
1002 if(FSP_STAT(temp_p,&sb)) return("grab: can't find file");
1003 if(!(S_ISREG(sb.st_mode))) return("grab: Not a file");
1004 if(!(*fp = fopen(temp_p,"rb"))) return("grab: can't open file");
1005 newfile.inet_num=inet_num;
1006 newfile.port_num=port_num;
1007 newfile.fp=*fp;
1008 key=strdup(temp_p);
1009 f_cache_put(fpcache,&key,&newfile);
1010 } else
1011 *fp=cache_f->fp;
1012
1013 return(NULLP);
1014 }
1015
server_grab_done(unsigned long inet_num,unsigned short port_num)1016 const char *server_grab_done (unsigned long inet_num,
1017 unsigned short port_num)
1018 {
1019 struct stat sb;
1020 char temp_p[NBSIZE];
1021 FPCACHE *cache_f;
1022 char *key;
1023
1024 sprintf(temp_p,"%s/.G%08lX%04X",tmp_dir,inet_num,port_num);
1025 if(FSP_STAT(temp_p,&sb)) return("grabdone: can't find temporary file");
1026 key=temp_p;
1027 cache_f=f_cache_find(fpcache,&key);
1028 f_cache_delete_entry(fpcache,cache_f);
1029 if(unlink(temp_p) == -1) return("grabdone: can't delete temporary file");
1030 return(NULLP);
1031 }
1032
1033 /* return STAT info about filesystem object */
server_stat(UBUF * ubuf)1034 const char *server_stat (UBUF * ubuf )
1035 {
1036 struct stat sb;
1037 int rc;
1038 PPATH pp;
1039 unsigned short len;
1040 DIRINFO *junk;
1041
1042 rc=0;
1043 len=BB_READ2(ubuf->bb_len);
1044 if(len<2) {
1045 strcpy(ubuf->buf,"");
1046 len=1;
1047 }
1048 if(parse_path(ubuf->buf,len,&pp))
1049 rc=1;
1050 sb.st_mtime=0;
1051 sb.st_mtime=0;
1052 if(!rc)
1053 {
1054 rc=FSP_STAT(pp.fullp,&sb);
1055 if(!rc)
1056 {
1057 /* SECURITY CHECK: validate directory */
1058 if S_ISDIR(sb.st_mode)
1059 {
1060 if(validate_path(ubuf->buf,len,&pp,&junk,1))
1061 rc=1;
1062 } else
1063 if(S_ISREG(sb.st_mode))
1064 {
1065 if(validate_path(ubuf->buf,len,&pp,&junk,0))
1066 rc=1;
1067 }
1068 }
1069 }
1070
1071 BB_WRITE4(ubuf->buf,sb.st_mtime);
1072 BB_WRITE4(ubuf->buf+4,sb.st_size );
1073
1074 if(rc)
1075 rc=0;
1076 else
1077 if S_ISDIR(sb.st_mode) rc=RDTYPE_DIR;
1078 else
1079 if S_ISREG(sb.st_mode) rc=RDTYPE_FILE;
1080 else
1081 rc=0; /* not a file or directory */
1082
1083 (ubuf->buf)[8]=rc;
1084 return(NULLP);
1085 }
1086
1087 /* rename FILE/directory object */
server_rename(PPATH * src,PPATH * dest,DIRINFO * sdir,DIRINFO * tdir)1088 const char *server_rename (PPATH *src,PPATH *dest,DIRINFO *sdir, DIRINFO *tdir)
1089 {
1090 struct stat sb;
1091 int issrcdir, istargetdir;
1092
1093 /* explore type of source object */
1094 if(FSP_STAT(src->fullp,&sb)) return("can't find source file or directory");
1095 if(S_ISDIR(sb.st_mode))
1096 issrcdir=1;
1097 else
1098 if(S_ISREG(sb.st_mode))
1099 issrcdir=0;
1100 else
1101 return ("Refusing to operate on special files");
1102
1103 /* --- explore Target --- */
1104 if(FSP_STAT(dest->fullp,&sb))
1105 istargetdir=-1; /* non - existent! */
1106 else
1107 if(S_ISDIR(sb.st_mode))
1108 istargetdir=1;
1109 else
1110 if(S_ISREG(sb.st_mode))
1111 istargetdir=0;
1112 else
1113 return ("Refusing to operate on special files");
1114
1115 /* --=== now check ACL and do it ===-- */
1116
1117 /* Cross - directory rename? */
1118 if (sdir == tdir)
1119 {
1120 /* no, do simple rename */
1121 /* not needed checked at upper level
1122 pe=require_access_rights( sdir,DIR_RENAME,inet,src.passwd);
1123 if(pe[0]!='N' && pe[0]!='O')
1124 return ("Permission denied");
1125 if(istargetdir==0)
1126 pe=require_access_rights( sdir,DIR_DEL,inet,src.passwd);
1127 if(pe[0]!='N' && pe[0]!='O')
1128 return ("No permission for overwriting files");
1129 */
1130 /* now go to the action */
1131 if (rename(src->fullp,dest->fullp))
1132 return ("Rename failed");
1133 /* update dir listing */
1134 sdir->lastcheck=cur_time;
1135 sdir->mtime=cur_time;
1136 return NULLP;
1137 }
1138 return ("Cross-directory renames are not implemented yet.");
1139 /* return(NULLP); */
1140 }
1141 /*********************************************************************
1142 test and resolve home directory
1143 *********************************************************************/
1144
init_home_dir(void)1145 void init_home_dir (void)
1146 {
1147 void *newhd;
1148
1149 /* test and goto home dir */
1150 if(chdir(home_dir) == -1) {
1151 perror(home_dir);
1152 exit(6);
1153 }
1154
1155 /* resolve home dir */
1156 newhd=realloc(home_dir,UBUF_SPACE);
1157 if(!newhd)
1158 {
1159 perror("realloc1 homedir");
1160 exit(5);
1161 }
1162
1163 if(!getcwd(newhd,UBUF_SPACE))
1164 {
1165 perror("getcwd for homedir");
1166 exit(6);
1167 }
1168
1169 home_dir=realloc(newhd,strlen(newhd)+1);
1170 if(!home_dir)
1171 {
1172 perror("realloc2 homedir");
1173 exit(5);
1174 }
1175
1176 if(tmp_dir)
1177 {
1178 mkdir(tmp_dir,0700);
1179 chmod(tmp_dir,0700);
1180 if(chdir(tmp_dir)==-1)
1181 {
1182 free(tmp_dir);
1183 tmp_dir=NULL;
1184 read_only=1;
1185 }
1186 chdir(home_dir);
1187 }
1188
1189 #ifndef LAMERPACK
1190 if(dbug) {
1191 #endif
1192 fprintf(stderr,"home on %s\n",home_dir);
1193 if(tmp_dir)
1194 fprintf(stderr,"tmpdir on %s\n",tmp_dir);
1195 #ifndef LAMERPACK
1196 }
1197 #endif
1198 }
1199