Blame src/pgetJob.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include "pgetJob.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
Packit 8f70b4
ResType pget_vars[] = {
Packit 8f70b4
   {"pget:save-status",	"10s",   ResMgr::TimeIntervalValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"pget:default-n",   "5",	 ResMgr::UNumberValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"pget:min-chunk-size", "1M", ResMgr::UNumberValidate,ResMgr::NoClosure},
Packit 8f70b4
   {0}
Packit 8f70b4
};
Packit 8f70b4
ResDecls pget_vars_register(pget_vars);
Packit 8f70b4
Packit 8f70b4
#undef MIN
Packit 8f70b4
#define MIN(a,b) ((a)<(b)?(a):(b))
Packit 8f70b4
Packit 8f70b4
#define super CopyJob
Packit 8f70b4
Packit 8f70b4
int pgetJob::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(status_timer.Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      SaveStatus();
Packit 8f70b4
      status_timer.Reset();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(c->Done())
Packit 8f70b4
   {
Packit 8f70b4
      if(status_file)
Packit 8f70b4
      {
Packit 8f70b4
	 remove(status_file);
Packit 8f70b4
	 status_file.set(0);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(no_parallel || max_chunks<2)
Packit 8f70b4
   {
Packit 8f70b4
      c->Resume();
Packit 8f70b4
      return super::Do();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(chunks_done && chunks && c->GetPos()>=limit0)
Packit 8f70b4
   {
Packit 8f70b4
      c->SetRangeLimit(limit0);    // make it stop.
Packit 8f70b4
      c->Resume();
Packit 8f70b4
      c->Do();
Packit 8f70b4
      free_chunks();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(chunks==0 || c->GetPos()
Packit 8f70b4
   {
Packit 8f70b4
      c->Resume();
Packit 8f70b4
      m|=super::Do();
Packit 8f70b4
   }
Packit 8f70b4
   else if(chunks.count()>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(chunks[0]->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 Log::global->Format(0,"pget: chunk[%d] error: %s\n",0,chunks[0]->ErrorText());
Packit 8f70b4
	 no_parallel=true;
Packit 8f70b4
	 c->Resume();
Packit 8f70b4
      }
Packit 8f70b4
      else if(!chunks[0]->Done() && chunks[0]->GetBytesCount()
Packit 8f70b4
      {
Packit 8f70b4
	 c->Resume();
Packit 8f70b4
	 if(chunks.count()==1)
Packit 8f70b4
	 {
Packit 8f70b4
	    free_chunks();
Packit 8f70b4
	    no_parallel=true;
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    limit0=chunks[0]->c->GetRangeLimit();
Packit 8f70b4
	    chunks.remove(0);
Packit 8f70b4
	 }
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 c->Suspend();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   off_t offset=c->GetPos();
Packit 8f70b4
   off_t size=c->GetSize();
Packit 8f70b4
Packit 8f70b4
   if(chunks==0 && !chunks_done)
Packit 8f70b4
   {
Packit 8f70b4
      if(size==NO_SIZE_YET)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(size==NO_SIZE || (c->put && c->put->GetLocal()==0))
Packit 8f70b4
      {
Packit 8f70b4
	 Log::global->Write(0,_("pget: falling back to plain get"));
Packit 8f70b4
	 Log::global->Write(0," (");
Packit 8f70b4
	 if(c->put && c->put->GetLocal()==0)
Packit 8f70b4
	 {
Packit 8f70b4
	    Log::global->Write(0,_("the target file is remote"));
Packit 8f70b4
	    if(size==NO_SIZE)
Packit 8f70b4
	       Log::global->Write(0,", ");
Packit 8f70b4
	 }
Packit 8f70b4
	 if(size==NO_SIZE)
Packit 8f70b4
	    Log::global->Write(0,_("the source file size is unknown"));
Packit 8f70b4
	 Log::global->Write(0,")\n");
Packit 8f70b4
Packit 8f70b4
	 no_parallel=true;
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      // Make sure the destination file is open before starting chunks,
Packit 8f70b4
      // it disables temp-name creation in the chunk's Init.
Packit 8f70b4
      if(c->put->GetLocal()->getfd()==-1)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      c->put->NeedSeek(); // seek before writing
Packit 8f70b4
Packit 8f70b4
      if(pget_cont)
Packit 8f70b4
	 LoadStatus();
Packit 8f70b4
      else if(status_file)
Packit 8f70b4
	 remove(status_file);
Packit 8f70b4
      if(!chunks)
Packit 8f70b4
	 InitChunks(offset,size);
Packit 8f70b4
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
      if(!chunks)
Packit 8f70b4
      {
Packit 8f70b4
	 no_parallel=true;
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      if(!pget_cont)
Packit 8f70b4
      {
Packit 8f70b4
	 SaveStatus();
Packit 8f70b4
	 status_timer.Reset();
Packit 8f70b4
	 if(ResMgr::QueryBool("file:use-fallocate",0)) {
Packit 8f70b4
	    // allocate space after creating *.lftp-pget-status file,
Packit 8f70b4
	    // so that the incomplete status is more obvious.
Packit 8f70b4
	    const Ref<FDStream>& local=c->put->GetLocal();
Packit 8f70b4
	    if(lftp_fallocate(local->getfd(),size)==-1 && errno!=ENOSYS && errno!=EOPNOTSUPP) {
Packit 8f70b4
	       eprintf(_("pget: warning: space allocation for %s (%lld bytes) failed: %s\n"),
Packit 8f70b4
		  local->name.get(),(long long)size,strerror(errno));
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   /* cycle through the chunks */
Packit 8f70b4
   chunks_done=true;
Packit 8f70b4
   total_xferred=MIN(offset,limit0);
Packit 8f70b4
   off_t got_already=c->GetSize()-limit0;
Packit 8f70b4
   total_xfer_rate=c->GetRate();
Packit 8f70b4
Packit 8f70b4
   off_t rem=limit0-c->GetPos();
Packit 8f70b4
   if(rem<=0)
Packit 8f70b4
      total_eta=0;
Packit 8f70b4
   else
Packit 8f70b4
      total_eta=c->GetETA(rem);
Packit 8f70b4
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      if(chunks[i]->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 Log::global->Format(0,"pget: chunk[%d] error: %s\n",i,chunks[i]->ErrorText());
Packit 8f70b4
	 no_parallel=true;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      if(!chunks[i]->Done())
Packit 8f70b4
      {
Packit 8f70b4
	 if(chunks[i]->GetPos()>=chunks[i]->start)
Packit 8f70b4
	    total_xferred+=MIN(chunks[i]->GetPos(),chunks[i]->limit)
Packit 8f70b4
			   -chunks[i]->start;
Packit 8f70b4
	 if(total_eta>=0)
Packit 8f70b4
	 {
Packit 8f70b4
	    long eta=chunks[i]->GetETA();
Packit 8f70b4
	    if(eta<0)
Packit 8f70b4
	       total_eta=-1;
Packit 8f70b4
	    else if(eta>total_eta)
Packit 8f70b4
	       total_eta=eta;	// total eta is the maximum.
Packit 8f70b4
	 }
Packit 8f70b4
	 total_xfer_rate+=chunks[i]->GetRate();
Packit 8f70b4
	 chunks_done=false;
Packit 8f70b4
      }
Packit 8f70b4
      else  // done
Packit 8f70b4
      {
Packit 8f70b4
	 total_xferred+=chunks[i]->limit-chunks[i]->start;
Packit 8f70b4
      }
Packit 8f70b4
      got_already-=chunks[i]->limit-chunks[i]->start;
Packit 8f70b4
   }
Packit 8f70b4
   total_xferred+=got_already;
Packit 8f70b4
Packit 8f70b4
   if(no_parallel)
Packit 8f70b4
   {
Packit 8f70b4
      free_chunks();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// xgettext:c-format
Packit 8f70b4
static const char pget_status_format[]=N_("`%s', got %lld of %lld (%d%%) %s%s");
Packit 8f70b4
#define PGET_STATUS _(pget_status_format),name, \
Packit 8f70b4
   (long long)total_xferred,(long long)size, \
Packit 8f70b4
   percent(total_xferred,size),Speedometer::GetStrS(total_xfer_rate), \
Packit 8f70b4
   c->GetETAStrSFromTime(total_eta)
Packit 8f70b4
Packit 8f70b4
void pgetJob::ShowRunStatus(const SMTaskRef<StatusLine>& s)
Packit 8f70b4
{
Packit 8f70b4
   if(Done() || no_parallel || max_chunks<2 || !chunks)
Packit 8f70b4
   {
Packit 8f70b4
      super::ShowRunStatus(s);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *name=SqueezeName(s->GetWidthDelayed()-58);
Packit 8f70b4
   off_t size=GetSize();
Packit 8f70b4
   StringSet status;
Packit 8f70b4
   status.AppendFormat(PGET_STATUS);
Packit 8f70b4
Packit 8f70b4
   int w=s->GetWidthDelayed();
Packit 8f70b4
   char *bar=string_alloca(w--);
Packit 8f70b4
   memset(bar,'+',w);
Packit 8f70b4
   bar[w]=0;
Packit 8f70b4
Packit 8f70b4
   int i;
Packit 8f70b4
   int p=c->GetPos()*w/size;
Packit 8f70b4
   for(i=start0*w/size; i
Packit 8f70b4
      bar[i]='o';
Packit 8f70b4
   p=limit0*w/size;
Packit 8f70b4
   for( ; i
Packit 8f70b4
      bar[i]='.';
Packit 8f70b4
Packit 8f70b4
   for(int chunk=0; chunk
Packit 8f70b4
   {
Packit 8f70b4
      p=(chunks[chunk]->Done()?chunks[chunk]->limit:chunks[chunk]->GetPos())*w/size;
Packit 8f70b4
      for(i=chunks[chunk]->start*w/size; i
Packit 8f70b4
	 bar[i]='o';
Packit 8f70b4
      p=chunks[chunk]->limit*w/size;
Packit 8f70b4
      for( ; i
Packit 8f70b4
	 bar[i]='.';
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   status.Append(bar);
Packit 8f70b4
Packit 8f70b4
   s->Show(status);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// list subjobs (chunk xfers) only when verbose
Packit 8f70b4
xstring& pgetJob::FormatJobs(xstring& s,int verbose,int indent)
Packit 8f70b4
{
Packit 8f70b4
   indent--;
Packit 8f70b4
   if(!chunks)
Packit 8f70b4
      return Job::FormatJobs(s,verbose,indent);
Packit 8f70b4
   if(verbose>1)
Packit 8f70b4
   {
Packit 8f70b4
      if(c->GetPos()
Packit 8f70b4
      {
Packit 8f70b4
	 s.appendf("%*s\\chunk %lld-%lld\n",indent,"",(long long)start0,(long long)limit0);
Packit 8f70b4
	 c->SetRangeLimit(limit0); // to see right ETA.
Packit 8f70b4
	 CopyJob::FormatStatus(s,verbose,"\t");
Packit 8f70b4
	 c->SetRangeLimit(FILE_END);
Packit 8f70b4
      }
Packit 8f70b4
      Job::FormatJobs(s,verbose,indent);
Packit 8f70b4
   }
Packit 8f70b4
   return s;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& pgetJob::FormatStatus(xstring& s,int verbose,const char *prefix)
Packit 8f70b4
{
Packit 8f70b4
   if(Done() || no_parallel || max_chunks<2 || !chunks)
Packit 8f70b4
      return super::FormatStatus(s,verbose,prefix);
Packit 8f70b4
Packit 8f70b4
   s.append(prefix);
Packit 8f70b4
   const char *name=GetDispName();
Packit 8f70b4
   off_t size=GetSize();
Packit 8f70b4
   s.appendf(PGET_STATUS);
Packit 8f70b4
   return s.append('\n');
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void pgetJob::free_chunks()
Packit 8f70b4
{
Packit 8f70b4
   if(chunks)
Packit 8f70b4
   {
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 chunks_bytes+=chunks[i]->GetBytesCount();
Packit 8f70b4
      chunks.unset();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
pgetJob::pgetJob(FileCopy *c1,const char *n,int m)
Packit 8f70b4
   : CopyJob(c1,n,"pget")
Packit 8f70b4
{
Packit 8f70b4
   chunks_bytes=0;
Packit 8f70b4
   start0=limit0=0;
Packit 8f70b4
   total_xferred=0;
Packit 8f70b4
   total_xfer_rate=0;
Packit 8f70b4
   no_parallel=false;
Packit 8f70b4
   chunks_done=false;
Packit 8f70b4
   pget_cont=c->SetContinue(false);
Packit 8f70b4
   max_chunks=m?m:ResMgr::Query("pget:default-n",0);
Packit 8f70b4
   total_eta=-1;
Packit 8f70b4
   status_timer.SetResource("pget:save-status",0);
Packit 8f70b4
   const Ref<FDStream>& local=c->put->GetLocal();
Packit 8f70b4
   if(local && local->full_name)
Packit 8f70b4
   {
Packit 8f70b4
      status_file.vset(local->full_name.get(),".lftp-pget-status",NULL);
Packit 8f70b4
      if(pget_cont)
Packit 8f70b4
	 LoadStatus0();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void pgetJob::PrepareToDie()
Packit 8f70b4
{
Packit 8f70b4
   free_chunks();
Packit 8f70b4
   super::PrepareToDie();
Packit 8f70b4
}
Packit 8f70b4
pgetJob::~pgetJob()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
pgetJob::ChunkXfer *pgetJob::NewChunk(const char *remote,off_t start,off_t limit)
Packit 8f70b4
{
Packit 8f70b4
   const Ref<FDStream>& local=c->put->GetLocal();
Packit 8f70b4
   FileCopyPeerFDStream
Packit 8f70b4
		*dst_peer=new FileCopyPeerFDStream(local,FileCopyPeer::PUT);
Packit 8f70b4
   dst_peer->NeedSeek(); // seek before writing
Packit 8f70b4
   dst_peer->SetBase(0);
Packit 8f70b4
Packit 8f70b4
   FileCopy *c1=FileCopy::New(c->get->Clone(),dst_peer,false);
Packit 8f70b4
   c1->SetRange(start,limit);
Packit 8f70b4
   c1->SetSize(GetSize());
Packit 8f70b4
   c1->DontCopyDate();
Packit 8f70b4
   c1->DontVerify();
Packit 8f70b4
   c1->FailIfCannotSeek();
Packit 8f70b4
Packit 8f70b4
   ChunkXfer *chunk=new ChunkXfer(c1,remote,start,limit);
Packit 8f70b4
   chunk->cmdline.setf("\\chunk %lld-%lld",(long long)start,(long long)(limit-1));
Packit 8f70b4
   return chunk;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
pgetJob::ChunkXfer::ChunkXfer(FileCopy *c1,const char *name,
Packit 8f70b4
			      off_t s,off_t lim)
Packit 8f70b4
   : CopyJob(c1,name,"pget-chunk")
Packit 8f70b4
{
Packit 8f70b4
   start=s;
Packit 8f70b4
   limit=lim;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void pgetJob::SaveStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(!status_file)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   FILE *f=fopen(status_file,"w");
Packit 8f70b4
   if(!f)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   off_t size=GetSize();
Packit 8f70b4
   fprintf(f,"size=%lld\n",(long long)size);
Packit 8f70b4
Packit 8f70b4
   int i=0;
Packit 8f70b4
   fprintf(f,"%d.pos=%lld\n",i,(long long)GetPos());
Packit 8f70b4
   if(!chunks)
Packit 8f70b4
      goto out_close;
Packit 8f70b4
   fprintf(f,"%d.limit=%lld\n",i,(long long)limit0);
Packit 8f70b4
   for(int chunk=0; chunk
Packit 8f70b4
   {
Packit 8f70b4
      if(chunks[chunk]->Done())
Packit 8f70b4
	 continue;
Packit 8f70b4
      i++;
Packit 8f70b4
      fprintf(f,"%d.pos=%lld\n",i,(long long)chunks[chunk]->GetPos());
Packit 8f70b4
      fprintf(f,"%d.limit=%lld\n",i,(long long)chunks[chunk]->limit);
Packit 8f70b4
   }
Packit 8f70b4
out_close:
Packit 8f70b4
   fclose(f);
Packit 8f70b4
}
Packit 8f70b4
void pgetJob::LoadStatus0()
Packit 8f70b4
{
Packit 8f70b4
   if(!status_file)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   FILE *f=fopen(status_file,"r");
Packit 8f70b4
   if(!f) {
Packit 8f70b4
      int saved_errno=errno;
Packit 8f70b4
      // Probably the file is already complete
Packit 8f70b4
      // or it was previously downloaded by plain get.
Packit 8f70b4
      struct stat st;
Packit 8f70b4
      if(stat(c->put->GetLocal()->full_name,&st)==-1)
Packit 8f70b4
	 return;
Packit 8f70b4
      Log::global->Format(0,"pget: %s: cannot open (%s), resuming at the file end\n",
Packit 8f70b4
	 status_file.get(),strerror(saved_errno));
Packit 8f70b4
      c->SetRange(st.st_size,FILE_END);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   long long size;
Packit 8f70b4
   if(fscanf(f,"size=%lld\n",&size)<1)
Packit 8f70b4
      goto out_close;
Packit 8f70b4
Packit 8f70b4
   long long pos;
Packit 8f70b4
   int j;
Packit 8f70b4
   if(fscanf(f,"%d.pos=%lld\n",&j,&pos)<2 || j!=0)
Packit 8f70b4
      goto out_close;
Packit 8f70b4
   Log::global->Format(10,"pget: got chunk[%d] pos=%lld\n",j,pos);
Packit 8f70b4
   c->SetRange(pos,FILE_END);
Packit 8f70b4
Packit 8f70b4
out_close:
Packit 8f70b4
   fclose(f);
Packit 8f70b4
}
Packit 8f70b4
void pgetJob::LoadStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(!status_file)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   FILE *f=fopen(status_file,"r");
Packit 8f70b4
   if(!f)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   struct stat st;
Packit 8f70b4
   if(fstat(fileno(f),&st)<0)
Packit 8f70b4
   {
Packit 8f70b4
   out_close:
Packit 8f70b4
      fclose(f);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   long long size;
Packit 8f70b4
   if(fscanf(f,"size=%lld\n",&size)<1)
Packit 8f70b4
      goto out_close;
Packit 8f70b4
Packit 8f70b4
   int i=0;
Packit 8f70b4
   int max_chunks=st.st_size/20; // highest estimate - min 20 bytes per chunk in status file.
Packit 8f70b4
   long long *pos=(long long *)alloca(2*max_chunks*sizeof(*pos));
Packit 8f70b4
   long long *limit=pos+max_chunks;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      int j;
Packit 8f70b4
      if(fscanf(f,"%d.pos=%lld\n",&j,pos+i)<2 || j!=i)
Packit 8f70b4
	 break;
Packit 8f70b4
      if(fscanf(f,"%d.limit=%lld\n",&j,limit+i)<2 || j!=i)
Packit 8f70b4
	 goto out_close;
Packit 8f70b4
      if(i>0 && pos[i]>=limit[i])
Packit 8f70b4
	 continue;
Packit 8f70b4
      Log::global->Format(10,"pget: got chunk[%d] pos=%lld\n",j,pos[i]);
Packit 8f70b4
      Log::global->Format(10,"pget: got chunk[%d] limit=%lld\n",j,limit[i]);
Packit 8f70b4
      i++;
Packit 8f70b4
   }
Packit 8f70b4
   if(i<1)
Packit 8f70b4
      goto out_close;
Packit 8f70b4
   if(size<c->GetSize())  // file grew?
Packit 8f70b4
   {
Packit 8f70b4
      if(limit[i-1]==size)
Packit 8f70b4
	 limit[i-1]=c->GetSize();
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 pos[i]=size;
Packit 8f70b4
	 limit[i]=c->GetSize();
Packit 8f70b4
	 i++;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   int num_of_chunks=i-1;
Packit 8f70b4
   start0=pos[0];
Packit 8f70b4
   limit0=limit[0];
Packit 8f70b4
   c->SetRange(pos[0],FILE_END);
Packit 8f70b4
   if(num_of_chunks<1)
Packit 8f70b4
      goto out_close;
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      ChunkXfer *c=NewChunk(GetName(),pos[i+1],limit[i+1]);
Packit 8f70b4
      c->SetParentFg(this,false);
Packit 8f70b4
      chunks.append(c);
Packit 8f70b4
   }
Packit 8f70b4
   goto out_close;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void pgetJob::InitChunks(off_t offset,off_t size)
Packit 8f70b4
{
Packit 8f70b4
   /* initialize chunks */
Packit 8f70b4
   off_t chunk_size=(size-offset)/max_chunks;
Packit 8f70b4
   int min_chunk_size=ResMgr::Query("pget:min-chunk-size",0);
Packit 8f70b4
   if(chunk_size
Packit 8f70b4
      chunk_size=min_chunk_size;
Packit 8f70b4
   int num_of_chunks=(size-offset)/chunk_size-1;
Packit 8f70b4
   if(num_of_chunks<1)
Packit 8f70b4
      return;
Packit 8f70b4
   start0=0;
Packit 8f70b4
   limit0=size-chunk_size*num_of_chunks;
Packit 8f70b4
   off_t curr_offs=limit0;
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      ChunkXfer *c=NewChunk(GetName(),curr_offs,curr_offs+chunk_size);
Packit 8f70b4
      c->SetParentFg(this,false);
Packit 8f70b4
      chunks.append(c);
Packit 8f70b4
      curr_offs+=chunk_size;
Packit 8f70b4
   }
Packit 8f70b4
   assert(curr_offs==size);
Packit 8f70b4
}